Merge remote-tracking branch 'origin/main'

This commit is contained in:
778a69cd 2024-02-08 18:53:07 +01:00
commit 10d3d03da1
70 changed files with 3694 additions and 1410 deletions

View file

@ -108,7 +108,8 @@
{Credo.Check.Refactor.MatchInCondition, []}, {Credo.Check.Refactor.MatchInCondition, []},
{Credo.Check.Refactor.NegatedConditionsInUnless, []}, {Credo.Check.Refactor.NegatedConditionsInUnless, []},
{Credo.Check.Refactor.NegatedConditionsWithElse, []}, {Credo.Check.Refactor.NegatedConditionsWithElse, []},
{Credo.Check.Refactor.Nesting, [ {Credo.Check.Refactor.Nesting,
[
max_nesting: 3 max_nesting: 3
]}, ]},
{Credo.Check.Refactor.PipeChainStart, {Credo.Check.Refactor.PipeChainStart,
@ -159,8 +160,7 @@
# Removed checks # Removed checks
# #
{Credo.Check.Warning.LazyLogging, false}, {Credo.Check.Warning.LazyLogging, false},
{Credo.Check.Refactor.MapInto, false}, {Credo.Check.Refactor.MapInto, false}
{Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, false}
] ]
} }
] ]

View file

@ -1,3 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run pre-commit npm run pre-commit

View file

@ -36,7 +36,7 @@ config :mobilizon, :instance,
unconfirmed_user_grace_period_hours: 48, unconfirmed_user_grace_period_hours: 48,
activity_expire_days: 365, activity_expire_days: 365,
activity_keep_number: 100, activity_keep_number: 100,
enable_instance_feeds: false, enable_instance_feeds: true,
email_from: "noreply@localhost", email_from: "noreply@localhost",
email_reply_to: "noreply@localhost" email_reply_to: "noreply@localhost"

View file

@ -1,6 +1,7 @@
# Mobilizon instance configuration # Mobilizon instance configuration
import Config import Config
import Mobilizon.Service.Config.Helpers
{:ok, _} = Application.ensure_all_started(:tls_certificate_check) {:ok, _} = Application.ensure_all_started(:tls_certificate_check)
@ -49,9 +50,20 @@ config :mobilizon, :instance,
description: "Change this to a proper description of your instance", description: "Change this to a proper description of your instance",
hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan"), hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan"),
registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN", "false") == "true", registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN", "false") == "true",
demo: false, registration_email_allowlist:
allow_relay: true, System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_EMAIL_ALLOWLIST", "")
federating: true, |> String.split(",", trim: true),
registration_email_denylist:
System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_EMAIL_DENYLIST", "")
|> String.split(",", trim: true),
disable_database_login:
System.get_env("MOBILIZON_INSTANCE_DISABLE_DATABASE_LOGIN", "false") == "true",
default_language: System.get_env("MOBILIZON_INSTANCE_DEFAULT_LANGUAGE", "en"),
demo: System.get_env("MOBILIZON_INSTANCE_DEMO", "false") == "true",
allow_relay: System.get_env("MOBILIZON_INSTANCE_ALLOW_RELAY", "true") == "true",
federating: System.get_env("MOBILIZON_INSTANCE_FEDERATING", "true") == "true",
enable_instance_feeds:
System.get_env("MOBILIZON_INSTANCE_ENABLE_INSTANCE_FEEDS", "true") == "true",
email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan"), email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan"),
email_reply_to: System.get_env("MOBILIZON_REPLY_EMAIL", "noreply@mobilizon.lan") email_reply_to: System.get_env("MOBILIZON_REPLY_EMAIL", "noreply@mobilizon.lan")
@ -79,7 +91,7 @@ config :mobilizon, Mobilizon.Web.Email.Mailer,
ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"), ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"),
retries: 1, retries: 1,
no_mx_lookups: false, no_mx_lookups: false,
auth: :if_available auth: System.get_env("MOBILIZON_SMTP_AUTH", "if_available")
config :geolix, config :geolix,
databases: [ databases: [
@ -93,13 +105,30 @@ config :geolix,
config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, config :mobilizon, Mobilizon.Web.Upload.Uploader.Local,
uploads: System.get_env("MOBILIZON_UPLOADS", "/var/lib/mobilizon/uploads") uploads: System.get_env("MOBILIZON_UPLOADS", "/var/lib/mobilizon/uploads")
formats =
if System.get_env("MOBILIZON_EXPORTS_FORMAT_CSV_ENABLED", "true") == "true" do
[Mobilizon.Service.Export.Participants.CSV]
else
[]
end
formats =
if System.get_env("MOBILIZON_EXPORTS_FORMAT_PDF_ENABLED", "true") == "true" do
formats ++ [Mobilizon.Service.Export.Participants.PDF]
else
formats
end
formats =
if System.get_env("MOBILIZON_EXPORTS_FORMAT_ODS_ENABLED", "true") == "true" do
formats ++ [Mobilizon.Service.Export.Participants.ODS]
else
formats
end
config :mobilizon, :exports, config :mobilizon, :exports,
path: System.get_env("MOBILIZON_UPLOADS_EXPORTS", "/var/lib/mobilizon/uploads/exports"), path: System.get_env("MOBILIZON_UPLOADS_EXPORTS", "/var/lib/mobilizon/uploads/exports"),
formats: [ formats: formats
Mobilizon.Service.Export.Participants.CSV,
Mobilizon.Service.Export.Participants.PDF,
Mobilizon.Service.Export.Participants.ODS
]
config :tz_world, config :tz_world,
data_dir: System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/timezones") data_dir: System.get_env("MOBILIZON_TIMEZONES_DIR", "/var/lib/mobilizon/timezones")
@ -110,3 +139,131 @@ config :web_push_encryption, :vapid_details,
subject: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_SUBJECT", nil), subject: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_SUBJECT", nil),
public_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PUBLIC_KEY", nil), public_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PUBLIC_KEY", nil),
private_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PRIVATE_KEY", nil) private_key: System.get_env("MOBILIZON_WEB_PUSH_ENCRYPTION_PRIVATE_KEY", nil)
geospatial_service =
case System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") do
"Nominatim" -> Mobilizon.Service.Geospatial.Nominatim
"Addok" -> Mobilizon.Service.Geospatial.Addok
"Photon" -> Mobilizon.Service.Geospatial.Photon
"GoogleMaps" -> Mobilizon.Service.Geospatial.GoogleMaps
"MapQuest" -> Mobilizon.Service.Geospatial.MapQuest
"Mimirsbrunn" -> Mobilizon.Service.Geospatial.Mimirsbrunn
"Pelias" -> Mobilizon.Service.Geospatial.Pelias
end
config :mobilizon, Mobilizon.Service.Geospatial, service: geospatial_service
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "Nominatim" do
config :mobilizon, Mobilizon.Service.Geospatial.Nominatim,
endpoint:
System.get_env(
"MOBILIZON_GEOSPATIAL_NOMINATIM_ENDPOINT",
"https://nominatim.openstreetmap.org"
),
api_key: System.get_env("MOBILIZON_GEOSPATIAL_NOMINATIM_API_KEY", nil)
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "Addok" do
config :mobilizon, Mobilizon.Service.Geospatial.Addok,
endpoint:
System.get_env("MOBILIZON_GEOSPATIAL_ADDOK_ENDPOINT", "https://api-adresse.data.gouv.fr")
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "Photon" do
config :mobilizon, Mobilizon.Service.Geospatial.Photon,
endpoint: System.get_env("MOBILIZON_GEOSPATIAL_PHOTON_ENDPOINT", "https://photon.komoot.de")
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "GoogleMaps" do
config :mobilizon, Mobilizon.Service.Geospatial.GoogleMaps,
api_key: System.get_env("MOBILIZON_GEOSPATIAL_GOOGLE_MAPS_API_KEY", nil),
fetch_place_details: true
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "MapQuest" do
config :mobilizon, Mobilizon.Service.Geospatial.MapQuest,
api_key: System.get_env("MOBILIZON_GEOSPATIAL_MAP_QUEST_API_KEY", nil)
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "Mimirsbrunn" do
config :mobilizon, Mobilizon.Service.Geospatial.Mimirsbrunn,
endpoint: System.get_env("MOBILIZON_GEOSPATIAL_MIMIRSBRUNN_ENDPOINT", nil)
end
if System.get_env("MOBILIZON_GEOSPATIAL_SERVICE", "Nominatim") == "Pelias" do
config :mobilizon, Mobilizon.Service.Geospatial.Pelias,
endpoint: System.get_env("MOBILIZON_GEOSPATIAL_PELIAS_ENDPOINT", nil)
end
sentry_dsn = System.get_env("MOBILIZON_ERROR_REPORTING_SENTRY_DSN", nil)
included_environments = if sentry_dsn, do: ["prod"], else: []
config :sentry,
dsn: sentry_dsn,
included_environments: included_environments,
release: to_string(Application.spec(:mobilizon, :vsn))
config :logger, Sentry.LoggerBackend,
capture_log_messages: true,
level: :error
if sentry_dsn != nil do
config :mobilizon, Mobilizon.Service.ErrorReporting,
adapter: Mobilizon.Service.ErrorReporting.Sentry
end
matomo_enabled = System.get_env("MOBILIZON_FRONT_END_ANALYTICS_MATOMO_ENABLED", "false") == "true"
matomo_endpoint = System.get_env("MOBILIZON_FRONT_END_ANALYTICS_MATOMO_ENDPOINT", nil)
matomo_site_id = System.get_env("MOBILIZON_FRONT_END_ANALYTICS_MATOMO_SITE_ID", nil)
matomo_tracker_file_name =
System.get_env("MOBILIZON_FRONT_END_ANALYTICS_MATOMO_TRACKER_FILE_NAME", "matomo")
matomo_host = host_from_uri(matomo_endpoint)
analytics_providers =
if matomo_enabled do
[Mobilizon.Service.FrontEndAnalytics.Matomo]
else
[]
end
analytics_providers =
if sentry_dsn != nil do
analytics_providers ++ [Mobilizon.Service.FrontEndAnalytics.Sentry]
else
analytics_providers
end
config :mobilizon, :analytics, providers: analytics_providers
matomo_csp =
if matomo_enabled and matomo_host do
[
connect_src: [matomo_host],
script_src: [matomo_host],
img_src: [matomo_host]
]
else
[]
end
config :mobilizon, Mobilizon.Service.FrontEndAnalytics.Matomo,
enabled: matomo_enabled,
host: matomo_endpoint,
siteId: matomo_site_id,
trackerFileName: matomo_tracker_file_name,
csp: matomo_csp
config :mobilizon, Mobilizon.Service.FrontEndAnalytics.Sentry,
enabled: sentry_dsn != nil,
dsn: sentry_dsn,
tracesSampleRate: 1.0,
organization: System.get_env("MOBILIZON_ERROR_REPORTING_SENTRY_ORGANISATION", nil),
project: System.get_env("MOBILIZON_ERROR_REPORTING_SENTRY_PROJECT", nil),
host: System.get_env("MOBILIZON_ERROR_REPORTING_SENTRY_HOST", nil),
csp: [
connect_src:
System.get_env("MOBILIZON_ERROR_REPORTING_SENTRY_HOST", "") |> String.split(" ", trim: true)
]

View file

@ -61,8 +61,8 @@ mixRelease rec {
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "danhper"; owner = "danhper";
repo = "elixir-web-push-encryption"; repo = "elixir-web-push-encryption";
rev = "70f00d06cbd88c9ac382e0ad2539e54448e9d8da"; rev = "6e143dcde0a2854c4f0d72816b7ecab696432779";
sha256 = "sha256-b4ZMrt/8n2sPUFtCDRTwXS1qWm5VlYdbx8qC0R0boOA="; sha256 = "sha256-Da+/28SPZuUQBi8fQj31zmMvhMrYUaQIW4U4E+mRtMg=";
}; };
beamDeps = with final; [ httpoison jose ]; beamDeps = with final; [ httpoison jose ];
}; };

View file

@ -71,8 +71,10 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
case Discussions.get_comment_from_url_with_preload(object["id"]) do case Discussions.get_comment_from_url_with_preload(object["id"]) do
{:error, :comment_not_found} -> {:error, :comment_not_found} ->
case Converter.Comment.as_to_model_data(object) do case Converter.Comment.as_to_model_data(object) do
%{visibility: visibility, attributed_to_id: attributed_to_id} = object_data %{visibility: visibility, attributed_to_id: attributed_to_id, actor_id: actor_id} =
when visibility === :private and is_nil(attributed_to_id) -> object_data
when visibility === :private and
(is_nil(attributed_to_id) or actor_id == attributed_to_id) ->
Actions.Create.create(:conversation, object_data, false) Actions.Create.create(:conversation, object_data, false)
object_data when is_map(object_data) -> object_data when is_map(object_data) ->

View file

@ -6,10 +6,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
internal one, and back. internal one, and back.
""" """
alias Cldr.DateTime.Formatter
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Addresses.Address alias Mobilizon.Addresses.Address
alias Mobilizon.Events.Categories alias Mobilizon.Events.Categories
alias Mobilizon.Events.Event, as: EventModel alias Mobilizon.Events.Event, as: EventModel
alias Mobilizon.Events.EventOptions
alias Mobilizon.Medias.Media alias Mobilizon.Medias.Media
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
@ -29,7 +32,14 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
maybe_fetch_actor_and_attributed_to_id: 1, maybe_fetch_actor_and_attributed_to_id: 1,
process_pictures: 2, process_pictures: 2,
get_address: 1, get_address: 1,
fetch_actor: 1 fetch_actor: 1,
visibility_public?: 1
]
import Mobilizon.Service.Metadata.Utils,
only: [
datetime_to_string: 3,
render_address!: 1
] ]
require Logger require Logger
@ -146,7 +156,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"anonymousParticipationEnabled" => event.options.anonymous_participation, "anonymousParticipationEnabled" => event.options.anonymous_participation,
"attachment" => Enum.map(event.metadata, &EventMetadataConverter.metadata_to_as/1), "attachment" => Enum.map(event.metadata, &EventMetadataConverter.metadata_to_as/1),
"draft" => event.draft, "draft" => event.draft,
# Remove me in MBZ 5.x # TODO: Remove me in MBZ 5.x
"ical:status" => event.status |> to_string |> String.upcase(), "ical:status" => event.status |> to_string |> String.upcase(),
"status" => event.status |> to_string |> String.upcase(), "status" => event.status |> to_string |> String.upcase(),
"id" => event.url, "id" => event.url,
@ -154,7 +164,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"inLanguage" => event.language, "inLanguage" => event.language,
"timezone" => event.options.timezone, "timezone" => event.options.timezone,
"contacts" => Enum.map(event.contacts, & &1.url), "contacts" => Enum.map(event.contacts, & &1.url),
"isOnline" => event.options.is_online "isOnline" => event.options.is_online,
"summary" => event_summary(event)
} }
|> maybe_add_physical_address(event) |> maybe_add_physical_address(event)
|> maybe_add_event_picture(event) |> maybe_add_event_picture(event)
@ -216,7 +227,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
defp get_metdata(_), do: [] defp get_metdata(_), do: []
defp get_visibility(object), do: if(@ap_public in object["to"], do: :public, else: :unlisted) defp get_visibility(object),
do: if(visibility_public?(object["to"]), do: :public, else: :unlisted)
@spec date_to_string(DateTime.t() | nil) :: String.t() @spec date_to_string(DateTime.t() | nil) :: String.t()
defp date_to_string(nil), do: nil defp date_to_string(nil), do: nil
@ -341,4 +353,47 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
_participant_count _participant_count
), ),
do: nil do: nil
def event_summary(%EventModel{
begins_on: begins_on,
physical_address: address,
options: %EventOptions{timezone: timezone},
language: language
}) do
begins_on = build_begins_on(begins_on, timezone)
begins_on
|> datetime_to_string(language || "en", :long)
|> (&[&1]).()
|> add_timezone(begins_on)
|> maybe_build_address(address)
|> Enum.join(" - ")
end
@spec build_begins_on(DateTime.t(), String.t() | nil) :: DateTime.t()
defp build_begins_on(begins_on, nil), do: begins_on
defp build_begins_on(begins_on, timezone) do
case DateTime.shift_zone(begins_on, timezone) do
{:ok, begins_on} -> begins_on
{:error, _err} -> begins_on
end
end
defp add_timezone(elements, %DateTime{} = begins_on) do
elements ++ [Formatter.zone_gmt(begins_on)]
end
@spec maybe_build_address(list(String.t()), Address.t() | nil) :: list(String.t())
defp maybe_build_address(elements, %Address{} = address) do
elements ++ [render_address!(address)]
rescue
# If the address is not renderable
e in ArgumentError ->
require Logger
Logger.error(Exception.format(:error, e, __STACKTRACE__))
elements
end
defp maybe_build_address(elements, _address), do: elements
end end

View file

@ -15,7 +15,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
import Mobilizon.Federation.ActivityStream.Converter.Utils, import Mobilizon.Federation.ActivityStream.Converter.Utils,
only: [ only: [
process_pictures: 2 process_pictures: 2,
visibility_public?: 1
] ]
import Mobilizon.Service.Guards, only: [is_valid_string: 1] import Mobilizon.Service.Guards, only: [is_valid_string: 1]
@ -134,14 +135,12 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
) )
end end
@ap_public "https://www.w3.org/ns/activitystreams#Public"
defp get_visibility(%{"to" => to}, %Actor{ defp get_visibility(%{"to" => to}, %Actor{
followers_url: followers_url, followers_url: followers_url,
members_url: members_url members_url: members_url
}) do }) do
cond do cond do
@ap_public in to -> :public visibility_public?(to) -> :public
followers_url in to -> :unlisted followers_url in to -> :unlisted
members_url in to -> :private members_url in to -> :private
end end

View file

@ -205,9 +205,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
@spec process_pictures(map(), integer()) :: Keyword.t() @spec process_pictures(map(), integer()) :: Keyword.t()
def process_pictures(object, actor_id) do def process_pictures(object, actor_id) do
attachements = Map.get(object, "attachment", []) {banner, media_attachements} = get_medias(object)
{banner, media_attachements} = get_medias(attachements)
media_attachements_map = media_attachements_map =
media_attachements media_attachements
@ -259,24 +257,46 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
do: String.replace(body, old_url, new_url) do: String.replace(body, old_url, new_url)
@spec get_medias(list(map())) :: {map(), list(map())} @spec get_medias(list(map())) :: {map(), list(map())}
defp get_medias(attachments) do def get_medias(object) do
banner = get_banner_picture(attachments) banner = get_banner_picture(object)
{banner, Enum.filter(attachments, &(&1["type"] == "Document" && &1["url"] != banner["url"]))} attachments = Map.get(object, "attachment", [])
{banner, Enum.filter(attachments, &(valid_banner_media?(&1) && &1["url"] != banner["url"]))}
end end
@spec get_banner_picture(list(map())) :: map() @spec get_banner_picture(map()) :: map()
defp get_banner_picture(attachments) do defp get_banner_picture(object) do
# Prefer media with attachments = Map.get(object, "attachment", [])
image = Map.get(object, "image", %{})
media_with_picture_name = media_with_picture_name =
Enum.find(attachments, &(&1["type"] == "Document" && &1["name"] == @banner_picture_name)) Enum.find(attachments, &(valid_banner_media?(&1) && &1["name"] == @banner_picture_name))
case media_with_picture_name do cond do
# If no banner found, use the first media # Check if the "image" key is set and of type "Document" or "Image"
nil -> Enum.find(attachments, &(&1["type"] == "Document")) is_nil(media_with_picture_name) and valid_banner_media?(image) ->
media_with_picture_name -> media_with_picture_name image
is_nil(media_with_picture_name) and Enum.find(attachments, &valid_banner_media?/1) ->
Enum.find(attachments, &valid_banner_media?/1)
!is_nil(media_with_picture_name) ->
media_with_picture_name
true ->
nil
end end
end end
@spec valid_banner_media?(map()) :: boolean()
defp valid_banner_media?(media) do
media |> Map.get("type") |> valid_attachment_type?()
end
@spec valid_attachment_type?(any()) :: boolean()
defp valid_attachment_type?(type) do
type in ["Document", "Image"]
end
@spec get_address(map | binary | nil) :: Address.t() | nil @spec get_address(map | binary | nil) :: Address.t() | nil
def get_address(text_address) when is_binary(text_address) do def get_address(text_address) when is_binary(text_address) do
get_address(%{"type" => "Place", "name" => text_address}) get_address(%{"type" => "Place", "name" => text_address})
@ -315,4 +335,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
nil nil
end end
end end
@ap_public "https://www.w3.org/ns/activitystreams#Public"
@spec visibility_public?(String.t() | list(String.t())) :: boolean()
def visibility_public?(to) when is_binary(to), do: visibility_public?([to])
def visibility_public?(to) when is_list(to) do
!MapSet.disjoint?(MapSet.new(to), MapSet.new([@ap_public, "as:Public", "Public"]))
end
end end

View file

@ -156,6 +156,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Conversation do
{:ok, conversation_to_view(conversation, conversation_participant_actor)} {:ok, conversation_to_view(conversation, conversation_participant_actor)}
{:error, %Ecto.Changeset{} = changeset} ->
{:error, changeset}
{:error, :empty_participants} -> {:error, :empty_participants} ->
{:error, {:error,
dgettext( dgettext(

View file

@ -381,10 +381,15 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
visibility: :private visibility: :private
}) })
with {:member, true} <- with {:ok,
%Event{organizer_actor_id: organizer_actor_id, attributed_to_id: attributed_to_id} =
_event} <- Mobilizon.Events.get_event(event_id),
{:member, true} <-
{:member, {:member,
to_string(current_actor_id) == to_string(actor_id) or (to_string(current_actor_id) == to_string(organizer_actor_id) and
Actors.member?(current_actor_id, actor_id)}, to_string(current_actor_id) == to_string(actor_id)) or
(!is_nil(attributed_to_id) and Actors.member?(current_actor_id, attributed_to_id) and
to_string(attributed_to_id) == to_string(actor_id))},
{:ok, _activity, %Conversation{} = conversation} <- Comments.create_conversation(args) do {:ok, _activity, %Conversation{} = conversation} <- Comments.create_conversation(args) do
{:ok, conversation_to_view(conversation, Actors.get_actor(actor_id))} {:ok, conversation_to_view(conversation, Actors.get_actor(actor_id))}
else else

View file

@ -20,6 +20,7 @@ defmodule Mobilizon.GraphQL.Schema.ConversationType do
) )
field(:last_comment, :comment, description: "The last comment of the conversation") field(:last_comment, :comment, description: "The last comment of the conversation")
field(:origin_comment, :comment, description: "The first comment of the conversation")
field :comments, :paginated_comment_list do field :comments, :paginated_comment_list do
arg(:page, :integer, default_value: 1) arg(:page, :integer, default_value: 1)

View file

@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
""" """
use Absinthe.Schema.Notation use Absinthe.Schema.Notation
import Absinthe.Resolution.Helpers, only: [dataloader: 1] import Absinthe.Resolution.Helpers, only: [dataloader: 1, dataloader: 2]
alias Mobilizon.{Actors, Discussions, Events} alias Mobilizon.{Actors, Discussions, Events}
alias Mobilizon.GraphQL.Resolvers.Comment alias Mobilizon.GraphQL.Resolvers.Comment
@ -23,7 +23,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
field(:replies, list_of(:comment)) do field(:replies, list_of(:comment)) do
description("A list of replies to the comment") description("A list of replies to the comment")
resolve(dataloader(Discussions)) resolve(dataloader(Discussions, args: %{replies: true}))
end end
field(:total_replies, :integer, field(:total_replies, :integer,
@ -47,6 +47,12 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
field(:threadLanguages, non_null(list_of(:string)), description: "The thread languages") field(:threadLanguages, non_null(list_of(:string)), description: "The thread languages")
field(:actor, :person, resolve: dataloader(Actors), description: "The comment's author") field(:actor, :person, resolve: dataloader(Actors), description: "The comment's author")
field(:attributed_to, :actor,
resolve: dataloader(Actors),
description: "The comment's attributed to actor"
)
field(:inserted_at, :datetime, description: "When was the comment inserted in database") field(:inserted_at, :datetime, description: "When was the comment inserted in database")
field(:updated_at, :datetime, description: "When was the comment updated") field(:updated_at, :datetime, description: "When was the comment updated")
field(:deleted_at, :datetime, description: "When was the comment deleted") field(:deleted_at, :datetime, description: "When was the comment deleted")

View file

@ -104,6 +104,10 @@ defmodule Mobilizon.Conversations do
|> join(:inner, [_cp, _c, _e, _a, _lc, _oc, p], ap in Actor, on: p.actor_id == ap.id) |> join(:inner, [_cp, _c, _e, _a, _lc, _oc, p], ap in Actor, on: p.actor_id == ap.id)
|> where([_cp, c], c.event_id == ^event_id) |> where([_cp, c], c.event_id == ^event_id)
|> where([cp], cp.actor_id == ^actor_id) |> where([cp], cp.actor_id == ^actor_id)
|> where(
[_cp, _c, _e, _a, _lc, oc],
oc.actor_id == ^actor_id or oc.attributed_to_id == ^actor_id
)
|> order_by([cp], desc: cp.unread, desc: cp.updated_at) |> order_by([cp], desc: cp.unread, desc: cp.updated_at)
|> preload([_cp, c, e, a, lc, oc, p, ap], |> preload([_cp, c, e, a, lc, oc, p, ap],
actor: a, actor: a,
@ -113,6 +117,14 @@ defmodule Mobilizon.Conversations do
|> Page.build_page(page, limit) |> Page.build_page(page, limit)
end end
def find_all_conversations_for_event(event_id) do
ConversationParticipant
|> join(:inner, [cp], c in Conversation, on: cp.conversation_id == c.id)
|> join(:left, [_cp, c], e in Event, on: c.event_id == e.id)
|> where([_cp, c], c.event_id == ^event_id)
|> Repo.all()
end
@spec list_conversation_participants_for_actor( @spec list_conversation_participants_for_actor(
integer | String.t(), integer | String.t(),
integer | nil, integer | nil,
@ -133,7 +145,7 @@ defmodule Mobilizon.Conversations do
subquery subquery
|> subquery() |> subquery()
|> order_by([cp], desc: cp.unread, desc: cp.updated_at) |> order_by([cp], desc: cp.unread, desc: cp.updated_at)
|> preload([:actor, conversation: [:last_comment, :participants]]) |> preload([:actor, conversation: [:last_comment, :origin_comment, :participants, :event]])
|> Page.build_page(page, limit) |> Page.build_page(page, limit)
end end
@ -147,7 +159,7 @@ defmodule Mobilizon.Conversations do
ConversationParticipant ConversationParticipant
|> join(:inner, [cp], a in Actor, on: cp.actor_id == a.id) |> join(:inner, [cp], a in Actor, on: cp.actor_id == a.id)
|> where([_cp, a], a.user_id == ^user_id) |> where([_cp, a], a.user_id == ^user_id)
|> preload([:actor, conversation: [:last_comment, :participants]]) |> preload([:actor, conversation: [:last_comment, :origin_comment, :participants, :event]])
|> Page.build_page(page, limit) |> Page.build_page(page, limit)
end end

View file

@ -85,6 +85,13 @@ defmodule Mobilizon.Discussions do
|> select([c, r], %{c | total_replies: count(r.id)}) |> select([c, r], %{c | total_replies: count(r.id)})
end end
# Replies are only used on event comments, so we always use public visibily here
def query(Comment, %{replies: true}) do
Comment
|> where([c], c.visibility in ^@public_visibility)
|> order_by([c], asc: :is_announcement, asc: :published_at)
end
def query(Comment, _) do def query(Comment, _) do
order_by(Comment, [c], asc: :is_announcement, asc: :published_at) order_by(Comment, [c], asc: :is_announcement, asc: :published_at)
end end

View file

@ -792,7 +792,7 @@ defmodule Mobilizon.Events do
end end
end end
def get_participant(event_id, actor_id, %{}) do def get_participant(event_id, actor_id, _params) do
case Participant case Participant
|> Repo.get_by(event_id: event_id, actor_id: actor_id) |> Repo.get_by(event_id: event_id, actor_id: actor_id)
|> Repo.preload(@participant_preloads) do |> Repo.preload(@participant_preloads) do

View file

@ -2,7 +2,7 @@ defmodule Mobilizon.Service.Activity.Conversation do
@moduledoc """ @moduledoc """
Insert a conversation activity Insert a conversation activity
""" """
alias Mobilizon.Conversations alias Mobilizon.{Actors, Conversations}
alias Mobilizon.Conversations.{Conversation, ConversationParticipant} alias Mobilizon.Conversations.{Conversation, ConversationParticipant}
alias Mobilizon.Discussions.Comment alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
@ -38,7 +38,7 @@ defmodule Mobilizon.Service.Activity.Conversation do
%Conversation{ %Conversation{
id: conversation_id id: conversation_id
} = conversation, } = conversation,
%Comment{actor_id: actor_id, text: last_comment_text}, %Comment{actor_id: actor_id, text: last_comment_text} = comment,
_options _options
) )
when subject in [ when subject in [
@ -55,7 +55,8 @@ defmodule Mobilizon.Service.Activity.Conversation do
actor_id: conversation_participant_actor_id actor_id: conversation_participant_actor_id
} = } =
conversation_participant -> conversation_participant ->
if actor_id != conversation_participant_actor_id do if actor_id != conversation_participant_actor_id and
can_send_event_announcement?(conversation, comment) do
LegacyNotifierBuilder.enqueue( LegacyNotifierBuilder.enqueue(
:legacy_notify, :legacy_notify,
%{ %{
@ -98,4 +99,31 @@ defmodule Mobilizon.Service.Activity.Conversation do
} }
defp event_subject_params(_), do: %{} defp event_subject_params(_), do: %{}
@spec can_send_event_announcement?(Conversation.t(), Comment.t()) :: boolean()
defp can_send_event_announcement?(
%Conversation{
event: %Event{
attributed_to_id: attributed_to_id
}
},
%Comment{actor_id: actor_id}
)
when not is_nil(attributed_to_id) do
attributed_to_id == actor_id or Actors.member?(actor_id, attributed_to_id)
end
defp can_send_event_announcement?(
%Conversation{
event: %Event{
organizer_actor_id: organizer_actor_id
}
},
%Comment{actor_id: actor_id}
)
when not is_nil(organizer_actor_id) do
organizer_actor_id == actor_id
end
defp can_send_event_announcement?(_, _), do: false
end end

View file

@ -85,7 +85,7 @@ defmodule Mobilizon.Service.Address do
defined?(street) -> defined?(street) ->
if defined?(locality), do: "#{street} (#{locality})", else: street if defined?(locality), do: "#{street} (#{locality})", else: street
defined?(locality) -> defined?(locality) and locality != region ->
"#{locality}, #{region}, #{country}" "#{locality}, #{region}, #{country}"
defined?(region) -> defined?(region) ->

View file

@ -0,0 +1,12 @@
defmodule Mobilizon.Service.Config.Helpers do
@moduledoc """
Provide some helpers to configuration files
"""
@spec host_from_uri(String.t() | nil) :: String.t() | nil
def host_from_uri(nil), do: nil
def host_from_uri(uri) when is_binary(uri) do
URI.parse(uri).host
end
end

View file

@ -136,15 +136,13 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Events.Event do
defp build_language(language, locale), do: language || locale defp build_language(language, locale), do: language || locale
@spec build_begins_on(DateTime.t(), String.t() | nil) :: DateTime.t() @spec build_begins_on(DateTime.t(), String.t() | nil) :: DateTime.t()
defp build_begins_on(begins_on, nil), do: begins_on
defp build_begins_on(begins_on, timezone) do defp build_begins_on(begins_on, timezone) do
if timezone do
case DateTime.shift_zone(begins_on, timezone) do case DateTime.shift_zone(begins_on, timezone) do
{:ok, begins_on} -> begins_on {:ok, begins_on} -> begins_on
{:error, _err} -> begins_on {:error, _err} -> begins_on
end end
else
begins_on
end
end end
defp add_timezone(elements, %DateTime{} = begins_on) do defp add_timezone(elements, %DateTime{} = begins_on) do

View file

@ -56,7 +56,6 @@ defmodule Mobilizon.Service.SiteMap do
end) end)
|> Sitemapper.generate(config) |> Sitemapper.generate(config)
|> Sitemapper.persist(config) |> Sitemapper.persist(config)
|> Sitemapper.ping(config)
|> Stream.run() |> Stream.run()
end, end,
timeout: :infinity timeout: :infinity

View file

@ -4,7 +4,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
""" """
alias Mobilizon.Activities.Activity alias Mobilizon.Activities.Activity
alias Mobilizon.{Actors, Events, Users} alias Mobilizon.{Actors, Config, Events, Users}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Events.{Event, Participant} alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Service.Notifier alias Mobilizon.Service.Notifier
@ -37,9 +37,10 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
end end
defp special_handling("conversation_created", args, activity) do defp special_handling("conversation_created", args, activity) do
notify_participants( notify_participant(
get_in(args, ["subject_params", "conversation_event_id"]), get_in(args, ["subject_params", "conversation_event_id"]),
activity, activity,
get_in(args, ["participant", "actor_id"]),
args["author_id"] args["author_id"]
) )
end end
@ -143,6 +144,24 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
notify_anonymous_participants(event_id, activity) notify_anonymous_participants(event_id, activity)
end end
defp notify_participant(nil, _activity, _conversation_participant_actor_id, _author_id),
do: :ok
defp notify_participant(event_id, activity, conversation_participant_actor_id, author_id) do
# Anonymous participation
if conversation_participant_actor_id == Config.anonymous_actor_id() do
notify_anonymous_participants(event_id, activity)
else
[conversation_participant_actor_id]
|> users_from_actor_ids(author_id)
|> Enum.each(fn user ->
Notifier.Email.send_anonymous_activity(user.email, activity,
locale: Map.get(user, :locale, "en")
)
end)
end
end
defp notify_anonymous_participants(nil, _activity), do: :ok defp notify_anonymous_participants(nil, _activity), do: :ok
defp notify_anonymous_participants(event_id, activity) do defp notify_anonymous_participants(event_id, activity) do
@ -154,7 +173,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilder do
|> Enum.map(fn %Participant{metadata: metadata} -> metadata end) |> Enum.map(fn %Participant{metadata: metadata} -> metadata end)
|> Enum.map(fn %{email: email} = metadata -> |> Enum.map(fn %{email: email} = metadata ->
Notifier.Email.send_anonymous_activity(email, activity, Notifier.Email.send_anonymous_activity(email, activity,
locale: Map.get(metadata, :locale, "en") locale: Map.get(metadata, :locale, "en") || "en"
) )
end) end)
end end

View file

@ -95,7 +95,7 @@ defmodule Mobilizon.Web.Router do
forward("/", Absinthe.Plug, forward("/", Absinthe.Plug,
schema: Mobilizon.GraphQL.Schema, schema: Mobilizon.GraphQL.Schema,
analyze_complexity: true, analyze_complexity: true,
max_complexity: 250 max_complexity: 300
) )
end end

14
mix.exs
View file

@ -121,10 +121,12 @@ defmodule Mobilizon.Mixfile do
|> to_string() |> to_string()
|> String.split() |> String.split()
|> Enum.map(fn strategy_entry -> |> Enum.map(fn strategy_entry ->
with [_strategy, dependency] <- String.split(strategy_entry, ":") do case String.split(strategy_entry, ":") do
[_strategy, dependency] ->
dependency dependency
else
[strategy] -> "ueberauth_#{strategy}" [strategy] ->
"ueberauth_#{strategy}"
end end
end) end)
@ -185,7 +187,7 @@ defmodule Mobilizon.Mixfile do
{:floki, "~> 0.31"}, {:floki, "~> 0.31"},
{:ip_reserved, "~> 0.1.0"}, {:ip_reserved, "~> 0.1.0"},
{:fast_sanitize, "~> 0.1"}, {:fast_sanitize, "~> 0.1"},
{:ueberauth, "0.10.5", override: true}, {:ueberauth, "0.10.7", override: true},
{:ueberauth_twitter, "~> 0.4"}, {:ueberauth_twitter, "~> 0.4"},
{:ueberauth_discord, "~> 0.7"}, {:ueberauth_discord, "~> 0.7"},
{:ueberauth_github, "~> 0.8.1"}, {:ueberauth_github, "~> 0.8.1"},
@ -283,7 +285,7 @@ defmodule Mobilizon.Mixfile do
File.rm_rf!("test/uploads") File.rm_rf!("test/uploads")
end end
defp docs() do defp docs do
[ [
source_ref: "v#{@version}", source_ref: "v#{@version}",
groups_for_modules: groups_for_modules(), groups_for_modules: groups_for_modules(),
@ -323,7 +325,7 @@ defmodule Mobilizon.Mixfile do
] ]
end end
defp groups_for_modules() do defp groups_for_modules do
[ [
Models: [ Models: [
~r/Mobilizon.Actors~r/, ~r/Mobilizon.Actors~r/,

View file

@ -14,10 +14,10 @@
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"}, "comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"}, "cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"},
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy": {:hex, :cowboy, "2.11.0", "356bf784599cf6f2cdc6ad12fdcfb8413c2d35dab58404cf000e1feaed3f5645", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "0fa395437f1b0e104e0e00999f39d2ac5f4082ac5049b67a5b6d56ecc31b1403"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"},
"credo": {:hex, :credo, "1.7.2", "fdee3a7cb553d8f2e773569181f0a4a2bb7d192e27e325404cc31b354f59d68c", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd15d6fbc280f6cf9b269f41df4e4992dee6615939653b164ef951f60afcb68e"}, "credo": {:hex, :credo, "1.7.3", "05bb11eaf2f2b8db370ecaa6a6bda2ec49b2acd5e0418bc106b73b07128c0436", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535"},
"credo_code_climate": {:hex, :credo_code_climate, "0.1.0", "1c4efbd11cb0244622ed5f09246b9afbbf796316ce03e78f67db6d81271d2978", [:mix], [{:credo, "~> 1.5", [hex: :credo, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "75529fe38056f4e229821d604758282838b8397c82e2c12e409fda16b16821ca"}, "credo_code_climate": {:hex, :credo_code_climate, "0.1.0", "1c4efbd11cb0244622ed5f09246b9afbbf796316ce03e78f67db6d81271d2978", [:mix], [{:credo, "~> 1.5", [hex: :credo, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "75529fe38056f4e229821d604758282838b8397c82e2c12e409fda16b16821ca"},
"dataloader": {:hex, :dataloader, "2.0.0", "49b42d60b9bb06d761a71d7b034c4b34787957e713d4fae15387a25fcd639112", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "09d61781b76ce216e395cdbc883ff00d00f46a503e215c22722dba82507dfef0"}, "dataloader": {:hex, :dataloader, "2.0.0", "49b42d60b9bb06d761a71d7b034c4b34787957e713d4fae15387a25fcd639112", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "09d61781b76ce216e395cdbc883ff00d00f46a503e215c22722dba82507dfef0"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
@ -34,7 +34,7 @@
"ecto_shortuuid": {:hex, :ecto_shortuuid, "0.2.0", "57cae7b6016cc56a04457b4fc8f63957398dfd9023ff3e900eaf6805a40f8043", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:shortuuid, "~> 2.1 or ~> 3.0", [hex: :shortuuid, repo: "hexpm", optional: false]}], "hexpm", "b92e3b71e86be92f5a7ef6f3de170e7864454e630f7b01dd930414baf38efb65"}, "ecto_shortuuid": {:hex, :ecto_shortuuid, "0.2.0", "57cae7b6016cc56a04457b4fc8f63957398dfd9023ff3e900eaf6805a40f8043", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:shortuuid, "~> 2.1 or ~> 3.0", [hex: :shortuuid, repo: "hexpm", optional: false]}], "hexpm", "b92e3b71e86be92f5a7ef6f3de170e7864454e630f7b01dd930414baf38efb65"},
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"}, "ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
"elixir_feed_parser": {:hex, :elixir_feed_parser, "2.1.0", "bb96fb6422158dc7ad59de62ef211cc69d264acbbe63941a64a5dce97bbbc2e6", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2d3c62fe7b396ee3b73d7160bc8fadbd78bfe9597c98c7d79b3f1038d9cba28f"}, "elixir_feed_parser": {:hex, :elixir_feed_parser, "2.1.0", "bb96fb6422158dc7ad59de62ef211cc69d264acbbe63941a64a5dce97bbbc2e6", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2d3c62fe7b396ee3b73d7160bc8fadbd78bfe9597c98c7d79b3f1038d9cba28f"},
"elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"erlport": {:hex, :erlport, "0.11.0", "8bb46a520e6eb9146e655fbf9b824433d9d532194667069d9aa45696aae9684b", [:rebar3], [], "hexpm", "8eb136ccaf3948d329b8d1c3278ad2e17e2a7319801bc4cc2da6db278204eee4"}, "erlport": {:hex, :erlport, "0.11.0", "8bb46a520e6eb9146e655fbf9b824433d9d532194667069d9aa45696aae9684b", [:rebar3], [], "hexpm", "8eb136ccaf3948d329b8d1c3278ad2e17e2a7319801bc4cc2da6db278204eee4"},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
@ -43,9 +43,9 @@
"ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.15.1", "e92ba17c41e7405b7784e0e65f406b5f17cfe313e0e70de9befd653e12854822", [:mix], [{:ex_cldr, "~> 2.34", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "31df8bd37688340f8819bdd770eb17d659652078d34db632b85d4a32864d6a25"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.15.1", "e92ba17c41e7405b7784e0e65f406b5f17cfe313e0e70de9befd653e12854822", [:mix], [{:ex_cldr, "~> 2.34", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "31df8bd37688340f8819bdd770eb17d659652078d34db632b85d4a32864d6a25"},
"ex_cldr_dates_times": {:hex, :ex_cldr_dates_times, "2.16.0", "d9848a5de83b6f1bcba151cc43d63b5c6311813cd605b1df1afd896dfdd21001", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_calendars, "~> 1.22", [hex: :ex_cldr_calendars, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:tz, "~> 0.26", [hex: :tz, repo: "hexpm", optional: true]}], "hexpm", "0f2f250d479cadda4e0ef3a5e3d936ae7ba1a3f1199db6791e284e86203495b1"}, "ex_cldr_dates_times": {:hex, :ex_cldr_dates_times, "2.16.0", "d9848a5de83b6f1bcba151cc43d63b5c6311813cd605b1df1afd896dfdd21001", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_calendars, "~> 1.22", [hex: :ex_cldr_calendars, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:tz, "~> 0.26", [hex: :tz, repo: "hexpm", optional: true]}], "hexpm", "0f2f250d479cadda4e0ef3a5e3d936ae7ba1a3f1199db6791e284e86203495b1"},
"ex_cldr_languages": {:hex, :ex_cldr_languages, "0.3.3", "9787002803552b15a7ade19496c9e46fc921baca992ea80d0394e11fe3acea45", [:mix], [{:ex_cldr, "~> 2.25", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "22fb1fef72b7b4b4872d243b34e7b83734247a78ad87377986bf719089cc447a"}, "ex_cldr_languages": {:hex, :ex_cldr_languages, "0.3.3", "9787002803552b15a7ade19496c9e46fc921baca992ea80d0394e11fe3acea45", [:mix], [{:ex_cldr, "~> 2.25", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "22fb1fef72b7b4b4872d243b34e7b83734247a78ad87377986bf719089cc447a"},
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.32.3", "b631ff94c982ec518e46bf4736000a30a33d6b58facc085d5f240305f512ad4a", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, ">= 2.14.2", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7b626ff1e59a0ec9c3c5db5ce9ca91a6995e2ab56426b71f3cbf67181ea225f5"}, "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.32.4", "5562148dfc631b04712983975093d2aac29df30b3bf2f7257e0c94b85b72e91b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, ">= 2.14.2", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "6fd5a82f0785418fa8b698c0be2b1845dff92b77f1b3172c763d37868fb503d2"},
"ex_cldr_plugs": {:hex, :ex_cldr_plugs, "1.3.1", "ae58748df815ad21b8618830374a28b2ab593230e5df70ed9f647e953a884bec", [:mix], [{:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "4f7b4a5fe061734cef7b62ff29118ed6ac72698cdd7bcfc97495db73611fe0fe"}, "ex_cldr_plugs": {:hex, :ex_cldr_plugs, "1.3.1", "ae58748df815ad21b8618830374a28b2ab593230e5df70ed9f647e953a884bec", [:mix], [{:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "4f7b4a5fe061734cef7b62ff29118ed6ac72698cdd7bcfc97495db73611fe0fe"},
"ex_doc": {:hex, :ex_doc, "0.31.0", "06eb1dfd787445d9cab9a45088405593dd3bb7fe99e097eaa71f37ba80c7a676", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5350cafa6b7f77bdd107aa2199fe277acf29d739aba5aee7e865fc680c62a110"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"},
"ex_ical": {:hex, :ex_ical, "0.2.0", "4b928b554614704016cc0c9ee226eb854da9327a1cc460457621ceacb1ac29a6", [:mix], [{:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "db76473b2ae0259e6633c6c479a5a4d8603f09497f55c88f9ef4d53d2b75befb"}, "ex_ical": {:hex, :ex_ical, "0.2.0", "4b928b554614704016cc0c9ee226eb854da9327a1cc460457621ceacb1ac29a6", [:mix], [{:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "db76473b2ae0259e6633c6c479a5a4d8603f09497f55c88f9ef4d53d2b75befb"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_optimizer": {:hex, :ex_optimizer, "0.1.1", "62da37e206fc2233ff7a4e54e40eae365c40f96c81992fcd15b782eb25169b80", [:mix], [{:file_info, "~> 0.0.4", [hex: :file_info, repo: "hexpm", optional: false]}], "hexpm", "e6f5c059bcd58b66be2f6f257fdc4f69b74b0fa5c9ddd669486af012e4b52286"}, "ex_optimizer": {:hex, :ex_optimizer, "0.1.1", "62da37e206fc2233ff7a4e54e40eae365c40f96c81992fcd15b782eb25169b80", [:mix], [{:file_info, "~> 0.0.4", [hex: :file_info, repo: "hexpm", optional: false]}], "hexpm", "e6f5c059bcd58b66be2f6f257fdc4f69b74b0fa5c9ddd669486af012e4b52286"},
@ -55,11 +55,11 @@
"exkismet": {:git, "https://github.com/tcitworld/exkismet.git", "8b5485fde00fafbde20f315bec387a77f7358334", []}, "exkismet": {:git, "https://github.com/tcitworld/exkismet.git", "8b5485fde00fafbde20f315bec387a77f7358334", []},
"expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"}, "expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"},
"export": {:hex, :export, "0.1.1", "6dfd268b0692428f89b9285859a2dc02b6dcd2e8fdfbca34ac6e6a331351df91", [:mix], [{:erlport, "~> 0.9", [hex: :erlport, repo: "hexpm", optional: false]}], "hexpm", "3da7444ff4053f1824352f4bdb13fbd2c28c93c2011786fb686b649fdca1021f"}, "export": {:hex, :export, "0.1.1", "6dfd268b0692428f89b9285859a2dc02b6dcd2e8fdfbca34ac6e6a331351df91", [:mix], [{:erlport, "~> 0.9", [hex: :erlport, repo: "hexpm", optional: false]}], "hexpm", "3da7444ff4053f1824352f4bdb13fbd2c28c93c2011786fb686b649fdca1021f"},
"fast_html": {:hex, :fast_html, "2.2.0", "6c5ef1be087a4ed613b0379c13f815c4d11742b36b67bb52cee7859847c84520", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "064c4f23b4a6168f9187dac8984b056f2c531bb0787f559fd6a8b34b38aefbae"}, "fast_html": {:hex, :fast_html, "2.3.0", "08c1d8ead840dd3060ba02c761bed9f37f456a1ddfe30bcdcfee8f651cec06a6", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "f18e3c7668f82d3ae0b15f48d48feeb257e28aa5ab1b0dbf781c7312e5da029d"},
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"}, "fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
"file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"}, "floki": {:hex, :floki, "0.35.3", "0c8c6234aa71cb2b069cf801e8f8f30f8d096eb452c3dae2ccc409510ec32720", [:mix], [], "hexpm", "6d9f07f3fc76599f3b66c39f4a81ac62c8f4d9631140268db92aacad5d0e56d4"},
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
"geo": {:hex, :geo, "3.6.0", "00c9c6338579f67e91cd5950af4ae2eb25cdce0c3398718c232539f61625d0bd", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "1dbdebf617183b54bc3c8ad7a36531a9a76ada8ca93f75f573b0ae94006168da"}, "geo": {:hex, :geo, "3.6.0", "00c9c6338579f67e91cd5950af4ae2eb25cdce0c3398718c232539f61625d0bd", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "1dbdebf617183b54bc3c8ad7a36531a9a76ada8ca93f75f573b0ae94006168da"},
"geo_postgis": {:hex, :geo_postgis, "3.5.0", "e3675b6276b8c2166dc20a6fa9d992eb73c665de2b09b666d09c7824dc8a8300", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:geo, "~> 3.5", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "0bebc5b00f8b11835066bd6213fbeeec03704b4a1c206920b81c1ec2201d185f"}, "geo_postgis": {:hex, :geo_postgis, "3.5.0", "e3675b6276b8c2166dc20a6fa9d992eb73c665de2b09b666d09c7824dc8a8300", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:geo, "~> 3.5", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "0bebc5b00f8b11835066bd6213fbeeec03704b4a1c206920b81c1ec2201d185f"},
@ -71,14 +71,14 @@
"guardian_db": {:hex, :guardian_db, "3.0.0", "c42902e3f1af1ba1e2d0c10913b926a1421f3a7e38eb4fc382b715c17489abdb", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "9c2ec4278efa34f9f1cc6ba795e552d41fdc7ffba5319d67eeb533b89392d183"}, "guardian_db": {:hex, :guardian_db, "3.0.0", "c42902e3f1af1ba1e2d0c10913b926a1421f3a7e38eb4fc382b715c17489abdb", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "9c2ec4278efa34f9f1cc6ba795e552d41fdc7ffba5319d67eeb533b89392d183"},
"guardian_phoenix": {:hex, :guardian_phoenix, "2.0.1", "89a817265af09a6ddf7cb1e77f17ffca90cea2db10ff888375ef34502b2731b1", [:mix], [{:guardian, "~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "21f439246715192b231f228680465d1ed5fbdf01555a4a3b17165532f5f9a08c"}, "guardian_phoenix": {:hex, :guardian_phoenix, "2.0.1", "89a817265af09a6ddf7cb1e77f17ffca90cea2db10ff888375ef34502b2731b1", [:mix], [{:guardian, "~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "21f439246715192b231f228680465d1ed5fbdf01555a4a3b17165532f5f9a08c"},
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
"hammer": {:hex, :hammer, "6.1.0", "f263e3c3e9946bd410ea0336b2abe0cb6260af4afb3a221e1027540706e76c55", [:make, :mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b47e415a562a6d072392deabcd58090d8a41182cf9044cdd6b0d0faaaf68ba57"}, "hammer": {:hex, :hammer, "6.2.0", "956e578f210ee67f7801caf7109b0e1145d2dad77ed5a0e5c0041a04739ede36", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "1431a30e1f9c816e0fc58d2587de2d5f4c709b74bf81be77515dc902e35bb3a7"},
"haversine": {:hex, :haversine, "0.1.0", "14240e90dae07c9459f538d12a811492f655d95fc68f999403503b4f6c4ec522", [:mix], [], "hexpm", "54dc48e895bc18a59437a37026c873634e17b648a64cb87bfafb96f64d607060"}, "haversine": {:hex, :haversine, "0.1.0", "14240e90dae07c9459f538d12a811492f655d95fc68f999403503b4f6c4ec522", [:mix], [], "hexpm", "54dc48e895bc18a59437a37026c873634e17b648a64cb87bfafb96f64d607060"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"}, "http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"},
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
"icalendar": {:git, "https://github.com/tcitworld/icalendar.git", "1033d922c82a7223db0ec138e2316557b70ff49f", []}, "icalendar": {:git, "https://github.com/tcitworld/icalendar.git", "1033d922c82a7223db0ec138e2316557b70ff49f", []},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"},
"ip_reserved": {:hex, :ip_reserved, "0.1.1", "e5112d71f1abf05207f82fd9597d369a5fde1e0b6d1bbe77c02a99bb26ecdc33", [:mix], [{:inet_cidr, "~> 1.0.0", [hex: :inet_cidr, repo: "hexpm", optional: false]}], "hexpm", "55fcd2b6e211caef09ea3f54ef37d43030bec486325d12fe865ab5ed8140a4fe"}, "ip_reserved": {:hex, :ip_reserved, "0.1.1", "e5112d71f1abf05207f82fd9597d369a5fde1e0b6d1bbe77c02a99bb26ecdc33", [:mix], [{:inet_cidr, "~> 1.0.0", [hex: :inet_cidr, repo: "hexpm", optional: false]}], "hexpm", "55fcd2b6e211caef09ea3f54ef37d43030bec486325d12fe865ab5ed8140a4fe"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"}, "jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
@ -87,7 +87,7 @@
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"}, "linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.4", "29563475afa9b8a2add1b7a9c8fb68d06ca7737648f28398e04461f008b69521", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
@ -103,20 +103,20 @@
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"}, "oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"}, "oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
"oban": {:hex, :oban, "2.17.1", "42d6221a1c17b63d81c19e3bad9ea82b59e39c47c1f9b7670ee33628569a449b", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c02686ada7979b00e259c0efbafeae2749f8209747b3460001fe695c5bdbeee6"}, "oban": {:hex, :oban, "2.17.3", "ddfd5710aadcd550d2e174c8d73ce5f1865601418cf54a91775f20443fb832b7", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "452eada8bfe0d0fefd0740ab5fa8cf3ef6c375df0b4a3c3805d179022a04738a"},
"paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"}, "paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"}, "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"},
"phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.3", "8b6406bc0a451f295407d7acff7f234a6314be5bbe0b3f90ed82b07f50049878", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8e4385e05618b424779f894ed2df97d3c7518b7285fcd11979077ae6226466b"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.4", "0dc21e89dbf5b1f3a69090a92d1a2724bfa951d5cbccff6c5b318e12eac107e3", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d8930c9c79dd25775646874abdf3d8d24356b88d58fa14f637c8e3418d36bce3"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"}, "phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"},
"plug": {:hex, :plug, "1.15.2", "94cf1fa375526f30ff8770837cb804798e0045fd97185f0bb9e5fcd858c792a3", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02731fa0c2dcb03d8d21a1d941bdbbe99c2946c0db098eee31008e04c6283615"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.2", "753611b23b29231fb916b0cdd96028084b12aff57bfd7b71781bd04b1dbeb5c9", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "951ed2433df22f4c97b85fdb145d4cee561f36b74854d64c06d896d7cd2921a7"},
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"}, "postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"},
@ -127,7 +127,7 @@
"replug": {:hex, :replug, "0.1.0", "61d35f8c873c0078a23c49579a48f36e45789414b1ec0daee3fd5f4e34221f23", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f71f7a57e944e854fe4946060c6964098e53958074c69fb844b96e0bd58cfa60"}, "replug": {:hex, :replug, "0.1.0", "61d35f8c873c0078a23c49579a48f36e45789414b1ec0daee3fd5f4e34221f23", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f71f7a57e944e854fe4946060c6964098e53958074c69fb844b96e0bd58cfa60"},
"sentry": {:hex, :sentry, "8.1.0", "8d235b62fce5f8e067ea1644e30939405b71a5e1599d9529ff82899d11d03f2b", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f9fc7641ef61e885510f5e5963c2948b9de1de597c63f781e9d3d6c9c8681ab4"}, "sentry": {:hex, :sentry, "8.1.0", "8d235b62fce5f8e067ea1644e30939405b71a5e1599d9529ff82899d11d03f2b", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "f9fc7641ef61e885510f5e5963c2948b9de1de597c63f781e9d3d6c9c8681ab4"},
"shortuuid": {:hex, :shortuuid, "3.0.0", "028684d9eeed0ad4b800e8481afd854e1a61c526f35952455b2ee4248601e7b8", [:mix], [], "hexpm", "dfd8f80f514cbb91622cb83f4ac0d6e2f06d98cc6d4aeba94444a212289d0d39"}, "shortuuid": {:hex, :shortuuid, "3.0.0", "028684d9eeed0ad4b800e8481afd854e1a61c526f35952455b2ee4248601e7b8", [:mix], [], "hexpm", "dfd8f80f514cbb91622cb83f4ac0d6e2f06d98cc6d4aeba94444a212289d0d39"},
"sitemapper": {:hex, :sitemapper, "0.7.0", "4aee7930327a9a01b1c9b81d1d42f60c1a295e9f420108eb2d130c317415abd7", [:mix], [{:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:xml_builder, "~> 2.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "60f7a684e5e9fe7f10ac5b69f48b0be2bcbba995afafcb3c143fc0c8ef1f223f"}, "sitemapper": {:hex, :sitemapper, "0.8.0", "50c8c85ed38c013829ce700e8a8d195a2faf4aed8685659b14529dcb6f91fee0", [:mix], [{:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:xml_builder, "~> 2.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "7cd42b454035da457151c9b6a314b688b5bbe5383add95badc65d013c25989c5"},
"sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"}, "sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"},
"slugger": {:hex, :slugger, "0.3.0", "efc667ab99eee19a48913ccf3d038b1fb9f165fa4fbf093be898b8099e61b6ed", [:mix], [], "hexpm", "20d0ded0e712605d1eae6c5b4889581c3460d92623a930ddda91e0e609b5afba"}, "slugger": {:hex, :slugger, "0.3.0", "efc667ab99eee19a48913ccf3d038b1fb9f165fa4fbf093be898b8099e61b6ed", [:mix], [], "hexpm", "20d0ded0e712605d1eae6c5b4889581c3460d92623a930ddda91e0e609b5afba"},
"slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"}, "slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"},
@ -135,14 +135,14 @@
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"struct_access": {:hex, :struct_access, "1.1.2", "a42e6ceedd9b9ea090ee94a6da089d56e16f374dbbc010c3eebdf8be17df286f", [:mix], [], "hexpm", "e4c411dcc0226081b95709909551fc92b8feb1a3476108348ea7e3f6c12e586a"}, "struct_access": {:hex, :struct_access, "1.1.2", "a42e6ceedd9b9ea090ee94a6da089d56e16f374dbbc010c3eebdf8be17df286f", [:mix], [], "hexpm", "e4c411dcc0226081b95709909551fc92b8feb1a3476108348ea7e3f6c12e586a"},
"sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"}, "sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"},
"swoosh": {:hex, :swoosh, "1.14.3", "949e6bf6dd469449238a94ec6f19ec10b63fc8753de7f3ebe3d3aeaf772f4c6b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6c565103fc8f086bdd96e5c948660af8e20922b7a90a75db261f06a34f805c8b"}, "swoosh": {:hex, :swoosh, "1.15.2", "490ea85a98e8fb5178c07039e0d8519839e38127724a58947a668c00db7574ee", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9f7739c02f6c7c0ca82ee397f3bfe0465dbe4c8a65372ac2a5584bf147dd5831"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"}, "tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"},
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
"tls_certificate_check": {:hex, :tls_certificate_check, "1.21.0", "042ab2c0c860652bc5cf69c94e3a31f96676d14682e22ec7813bd173ceff1788", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6cee6cffc35a390840d48d463541d50746a7b0e421acaadb833cfc7961e490e7"}, "tls_certificate_check": {:hex, :tls_certificate_check, "1.21.0", "042ab2c0c860652bc5cf69c94e3a31f96676d14682e22ec7813bd173ceff1788", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6cee6cffc35a390840d48d463541d50746a7b0e421acaadb833cfc7961e490e7"},
"tz_world": {:hex, :tz_world, "1.3.2", "15d331ad1ff22735dfcc8c98bfc7b2a9fdc17f1f071e31e21cdafe2d9318a300", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d1a345e07b3378c4c902ad54fbd5d54c8c3dd55dba883b7407fe57bcec45ff2a"}, "tz_world": {:hex, :tz_world, "1.3.2", "15d331ad1ff22735dfcc8c98bfc7b2a9fdc17f1f071e31e21cdafe2d9318a300", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d1a345e07b3378c4c902ad54fbd5d54c8c3dd55dba883b7407fe57bcec45ff2a"},
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
"ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "ueberauth": {:hex, :ueberauth, "0.10.7", "5a31cbe11e7ce5c7484d745dc9e1f11948e89662f8510d03c616de03df581ebd", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "0bccf73e2ffd6337971340832947ba232877aa8122dba4c95be9f729c8987377"},
"ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"}, "ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"},
"ueberauth_discord": {:hex, :ueberauth_discord, "0.7.0", "463f6dfe1ed10a76739331ce8e1dd3600ab611f10524dd828eb3aa50e76e9d43", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "d6f98ef91abb4ddceada4b7acba470e0e68c4d2de9735ff2f24172a8e19896b4"}, "ueberauth_discord": {:hex, :ueberauth_discord, "0.7.0", "463f6dfe1ed10a76739331ce8e1dd3600ab611f10524dd828eb3aa50e76e9d43", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "d6f98ef91abb4ddceada4b7acba470e0e68c4d2de9735ff2f24172a8e19896b4"},
"ueberauth_facebook": {:hex, :ueberauth_facebook, "0.10.0", "0d607fbd1b7c6e0449981571027d869c2d156b8ad20c42e3672346678c05ccf1", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "bf8ce5d66b1c50da8abff77e8086c1b710bdde63f4acaef19a651ba43a9537a8"}, "ueberauth_facebook": {:hex, :ueberauth_facebook, "0.10.0", "0d607fbd1b7c6e0449981571027d869c2d156b8ad20c42e3672346678c05ccf1", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "bf8ce5d66b1c50da8abff77e8086c1b710bdde63f4acaef19a651ba43a9537a8"},

74
mix.nix
View file

@ -205,12 +205,12 @@ let
cowboy = buildErlangMk rec { cowboy = buildErlangMk rec {
name = "cowboy"; name = "cowboy";
version = "2.10.0"; version = "2.11.0";
src = fetchHex { src = fetchHex {
pkg = "cowboy"; pkg = "cowboy";
version = "${version}"; version = "${version}";
sha256 = "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"; sha256 = "0fa395437f1b0e104e0e00999f39d2ac5f4082ac5049b67a5b6d56ecc31b1403";
}; };
beamDeps = [ cowlib ranch ]; beamDeps = [ cowlib ranch ];
@ -244,12 +244,12 @@ let
credo = buildMix rec { credo = buildMix rec {
name = "credo"; name = "credo";
version = "1.7.2"; version = "1.7.3";
src = fetchHex { src = fetchHex {
pkg = "credo"; pkg = "credo";
version = "${version}"; version = "${version}";
sha256 = "dd15d6fbc280f6cf9b269f41df4e4992dee6615939653b164ef951f60afcb68e"; sha256 = "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535";
}; };
beamDeps = [ bunt file_system jason ]; beamDeps = [ bunt file_system jason ];
@ -465,15 +465,15 @@ let
elixir_make = buildMix rec { elixir_make = buildMix rec {
name = "elixir_make"; name = "elixir_make";
version = "0.7.7"; version = "0.7.8";
src = fetchHex { src = fetchHex {
pkg = "elixir_make"; pkg = "elixir_make";
version = "${version}"; version = "${version}";
sha256 = "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"; sha256 = "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07";
}; };
beamDeps = [ castore ]; beamDeps = [ castore certifi ];
}; };
erlex = buildMix rec { erlex = buildMix rec {
@ -582,12 +582,12 @@ let
ex_cldr_numbers = buildMix rec { ex_cldr_numbers = buildMix rec {
name = "ex_cldr_numbers"; name = "ex_cldr_numbers";
version = "2.32.3"; version = "2.32.4";
src = fetchHex { src = fetchHex {
pkg = "ex_cldr_numbers"; pkg = "ex_cldr_numbers";
version = "${version}"; version = "${version}";
sha256 = "7b626ff1e59a0ec9c3c5db5ce9ca91a6995e2ab56426b71f3cbf67181ea225f5"; sha256 = "6fd5a82f0785418fa8b698c0be2b1845dff92b77f1b3172c763d37868fb503d2";
}; };
beamDeps = [ decimal digital_token ex_cldr ex_cldr_currencies jason ]; beamDeps = [ decimal digital_token ex_cldr ex_cldr_currencies jason ];
@ -608,12 +608,12 @@ let
ex_doc = buildMix rec { ex_doc = buildMix rec {
name = "ex_doc"; name = "ex_doc";
version = "0.31.0"; version = "0.31.1";
src = fetchHex { src = fetchHex {
pkg = "ex_doc"; pkg = "ex_doc";
version = "${version}"; version = "${version}";
sha256 = "5350cafa6b7f77bdd107aa2199fe277acf29d739aba5aee7e865fc680c62a110"; sha256 = "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0";
}; };
beamDeps = [ earmark_parser makeup_elixir makeup_erlang ]; beamDeps = [ earmark_parser makeup_elixir makeup_erlang ];
@ -725,12 +725,12 @@ let
fast_html = buildMix rec { fast_html = buildMix rec {
name = "fast_html"; name = "fast_html";
version = "2.2.0"; version = "2.3.0";
src = fetchHex { src = fetchHex {
pkg = "fast_html"; pkg = "fast_html";
version = "${version}"; version = "${version}";
sha256 = "064c4f23b4a6168f9187dac8984b056f2c531bb0787f559fd6a8b34b38aefbae"; sha256 = "f18e3c7668f82d3ae0b15f48d48feeb257e28aa5ab1b0dbf781c7312e5da029d";
}; };
beamDeps = [ elixir_make nimble_pool ]; beamDeps = [ elixir_make nimble_pool ];
@ -777,12 +777,12 @@ let
floki = buildMix rec { floki = buildMix rec {
name = "floki"; name = "floki";
version = "0.35.2"; version = "0.35.3";
src = fetchHex { src = fetchHex {
pkg = "floki"; pkg = "floki";
version = "${version}"; version = "${version}";
sha256 = "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"; sha256 = "6d9f07f3fc76599f3b66c39f4a81ac62c8f4d9631140268db92aacad5d0e56d4";
}; };
beamDeps = []; beamDeps = [];
@ -933,12 +933,12 @@ let
hammer = buildMix rec { hammer = buildMix rec {
name = "hammer"; name = "hammer";
version = "6.1.0"; version = "6.2.0";
src = fetchHex { src = fetchHex {
pkg = "hammer"; pkg = "hammer";
version = "${version}"; version = "${version}";
sha256 = "b47e415a562a6d072392deabcd58090d8a41182cf9044cdd6b0d0faaaf68ba57"; sha256 = "1431a30e1f9c816e0fc58d2587de2d5f4c709b74bf81be77515dc902e35bb3a7";
}; };
beamDeps = [ poolboy ]; beamDeps = [ poolboy ];
@ -1011,12 +1011,12 @@ let
inet_cidr = buildMix rec { inet_cidr = buildMix rec {
name = "inet_cidr"; name = "inet_cidr";
version = "1.0.4"; version = "1.0.8";
src = fetchHex { src = fetchHex {
pkg = "inet_cidr"; pkg = "inet_cidr";
version = "${version}"; version = "${version}";
sha256 = "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"; sha256 = "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e";
}; };
beamDeps = []; beamDeps = [];
@ -1128,12 +1128,12 @@ let
makeup_erlang = buildMix rec { makeup_erlang = buildMix rec {
name = "makeup_erlang"; name = "makeup_erlang";
version = "0.1.3"; version = "0.1.4";
src = fetchHex { src = fetchHex {
pkg = "makeup_erlang"; pkg = "makeup_erlang";
version = "${version}"; version = "${version}";
sha256 = "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"; sha256 = "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29";
}; };
beamDeps = [ makeup ]; beamDeps = [ makeup ];
@ -1336,12 +1336,12 @@ let
oban = buildMix rec { oban = buildMix rec {
name = "oban"; name = "oban";
version = "2.17.1"; version = "2.17.3";
src = fetchHex { src = fetchHex {
pkg = "oban"; pkg = "oban";
version = "${version}"; version = "${version}";
sha256 = "c02686ada7979b00e259c0efbafeae2749f8209747b3460001fe695c5bdbeee6"; sha256 = "452eada8bfe0d0fefd0740ab5fa8cf3ef6c375df0b4a3c3805d179022a04738a";
}; };
beamDeps = [ ecto_sql jason postgrex telemetry ]; beamDeps = [ ecto_sql jason postgrex telemetry ];
@ -1427,12 +1427,12 @@ let
phoenix_live_view = buildMix rec { phoenix_live_view = buildMix rec {
name = "phoenix_live_view"; name = "phoenix_live_view";
version = "0.20.3"; version = "0.20.4";
src = fetchHex { src = fetchHex {
pkg = "phoenix_live_view"; pkg = "phoenix_live_view";
version = "${version}"; version = "${version}";
sha256 = "a8e4385e05618b424779f894ed2df97d3c7518b7285fcd11979077ae6226466b"; sha256 = "d8930c9c79dd25775646874abdf3d8d24356b88d58fa14f637c8e3418d36bce3";
}; };
beamDeps = [ jason phoenix phoenix_html phoenix_template phoenix_view plug telemetry ]; beamDeps = [ jason phoenix phoenix_html phoenix_template phoenix_view plug telemetry ];
@ -1453,12 +1453,12 @@ let
phoenix_swoosh = buildMix rec { phoenix_swoosh = buildMix rec {
name = "phoenix_swoosh"; name = "phoenix_swoosh";
version = "1.2.0"; version = "1.2.1";
src = fetchHex { src = fetchHex {
pkg = "phoenix_swoosh"; pkg = "phoenix_swoosh";
version = "${version}"; version = "${version}";
sha256 = "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"; sha256 = "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2";
}; };
beamDeps = [ hackney phoenix phoenix_html phoenix_view swoosh ]; beamDeps = [ hackney phoenix phoenix_html phoenix_view swoosh ];
@ -1492,12 +1492,12 @@ let
plug = buildMix rec { plug = buildMix rec {
name = "plug"; name = "plug";
version = "1.15.2"; version = "1.15.3";
src = fetchHex { src = fetchHex {
pkg = "plug"; pkg = "plug";
version = "${version}"; version = "${version}";
sha256 = "02731fa0c2dcb03d8d21a1d941bdbbe99c2946c0db098eee31008e04c6283615"; sha256 = "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2";
}; };
beamDeps = [ mime plug_crypto telemetry ]; beamDeps = [ mime plug_crypto telemetry ];
@ -1505,12 +1505,12 @@ let
plug_cowboy = buildMix rec { plug_cowboy = buildMix rec {
name = "plug_cowboy"; name = "plug_cowboy";
version = "2.6.1"; version = "2.6.2";
src = fetchHex { src = fetchHex {
pkg = "plug_cowboy"; pkg = "plug_cowboy";
version = "${version}"; version = "${version}";
sha256 = "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"; sha256 = "951ed2433df22f4c97b85fdb145d4cee561f36b74854d64c06d896d7cd2921a7";
}; };
beamDeps = [ cowboy cowboy_telemetry plug ]; beamDeps = [ cowboy cowboy_telemetry plug ];
@ -1635,12 +1635,12 @@ let
sitemapper = buildMix rec { sitemapper = buildMix rec {
name = "sitemapper"; name = "sitemapper";
version = "0.7.0"; version = "0.8.0";
src = fetchHex { src = fetchHex {
pkg = "sitemapper"; pkg = "sitemapper";
version = "${version}"; version = "${version}";
sha256 = "60f7a684e5e9fe7f10ac5b69f48b0be2bcbba995afafcb3c143fc0c8ef1f223f"; sha256 = "7cd42b454035da457151c9b6a314b688b5bbe5383add95badc65d013c25989c5";
}; };
beamDeps = [ xml_builder ]; beamDeps = [ xml_builder ];
@ -1739,12 +1739,12 @@ let
swoosh = buildMix rec { swoosh = buildMix rec {
name = "swoosh"; name = "swoosh";
version = "1.14.3"; version = "1.15.2";
src = fetchHex { src = fetchHex {
pkg = "swoosh"; pkg = "swoosh";
version = "${version}"; version = "${version}";
sha256 = "6c565103fc8f086bdd96e5c948660af8e20922b7a90a75db261f06a34f805c8b"; sha256 = "9f7739c02f6c7c0ca82ee397f3bfe0465dbe4c8a65372ac2a5584bf147dd5831";
}; };
beamDeps = [ cowboy gen_smtp hackney jason mime plug plug_cowboy telemetry ]; beamDeps = [ cowboy gen_smtp hackney jason mime plug plug_cowboy telemetry ];
@ -1830,12 +1830,12 @@ let
ueberauth = buildMix rec { ueberauth = buildMix rec {
name = "ueberauth"; name = "ueberauth";
version = "0.10.5"; version = "0.10.7";
src = fetchHex { src = fetchHex {
pkg = "ueberauth"; pkg = "ueberauth";
version = "${version}"; version = "${version}";
sha256 = "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"; sha256 = "0bccf73e2ffd6337971340832947ba232877aa8122dba4c95be9f729c8987377";
}; };
beamDeps = [ plug ]; beamDeps = [ plug ];

2808
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,8 @@
"story:preview": "histoire preview", "story:preview": "histoire preview",
"test": "vitest", "test": "vitest",
"coverage": "vitest run --coverage", "coverage": "vitest run --coverage",
"pre-commit": "lint-staged" "pre-commit": "lint-staged",
"postinstall": "patch-package"
}, },
"lint-staged": { "lint-staged": {
"**/*.{js,ts,vue}": [ "**/*.{js,ts,vue}": [
@ -27,6 +28,7 @@
"mix credo" "mix credo"
] ]
}, },
"type": "module",
"dependencies": { "dependencies": {
"@apollo/client": "^3.3.16", "@apollo/client": "^3.3.16",
"@framasoft/socket": "^1.0.0", "@framasoft/socket": "^1.0.0",
@ -77,7 +79,7 @@
"blurhash": "^2.0.0", "blurhash": "^2.0.0",
"date-fns": "^2.16.0", "date-fns": "^2.16.0",
"date-fns-tz": "^2.0.0", "date-fns-tz": "^2.0.0",
"floating-vue": "^2.0.0-beta.24", "floating-vue": "^5.0.0",
"graphql": "^16.8.1", "graphql": "^16.8.1",
"graphql-tag": "^2.10.3", "graphql-tag": "^2.10.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
@ -90,6 +92,7 @@
"lodash": "^4.17.11", "lodash": "^4.17.11",
"ngeohash": "^0.6.3", "ngeohash": "^0.6.3",
"p-debounce": "^4.0.0", "p-debounce": "^4.0.0",
"patch-package": "^8.0.0",
"phoenix": "^1.6", "phoenix": "^1.6",
"postcss": "^8", "postcss": "^8",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
@ -97,7 +100,7 @@
"tailwindcss": "^3", "tailwindcss": "^3",
"tippy.js": "^6.2.3", "tippy.js": "^6.2.3",
"unfetch": "^5.0.0", "unfetch": "^5.0.0",
"vue": "^3.2.37", "vue": "3.4.16",
"vue-i18n": "9", "vue-i18n": "9",
"vue-material-design-icons": "^5.1.2", "vue-material-design-icons": "^5.1.2",
"vue-matomo": "^4.1.0", "vue-matomo": "^4.1.0",
@ -121,9 +124,9 @@
"@types/ngeohash": "^0.6.2", "@types/ngeohash": "^0.6.2",
"@types/phoenix": "^1.5.2", "@types/phoenix": "^1.5.2",
"@types/sanitize-html": "^2.5.0", "@types/sanitize-html": "^2.5.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^5.0.0",
"@vitest/coverage-v8": "^0.34.1", "@vitest/coverage-v8": "^1.2.2",
"@vitest/ui": "^0.34.1", "@vitest/ui": "^1.2.2",
"@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^12.0.0", "@vue/eslint-config-typescript": "^12.0.0",
"@vue/test-utils": "^2.0.2", "@vue/test-utils": "^2.0.2",
@ -134,8 +137,8 @@
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"histoire": "^0.17.0", "histoire": "^0.17.0",
"husky": "^8.0.3", "husky": "^9.0.10",
"jsdom": "^22.0.0", "jsdom": "^24.0.0",
"lint-staged": "^15.1.0", "lint-staged": "^15.1.0",
"mock-apollo-client": "^1.1.0", "mock-apollo-client": "^1.1.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",
@ -143,10 +146,10 @@
"rollup-plugin-visualizer": "^5.7.1", "rollup-plugin-visualizer": "^5.7.1",
"sass": "^1.34.1", "sass": "^1.34.1",
"typescript": "~5.3.2", "typescript": "~5.3.2",
"vite": "^4.5.0", "vite": "^5.0.12",
"vite-plugin-pwa": "^0.17.0", "vite-plugin-pwa": "^0.17.0",
"vite-svg-loader": "^4.0.0", "vite-svg-loader": "^4.0.0",
"vitest": "^0.34.1", "vitest": "^1.2.2",
"vue-i18n-extract": "^2.0.4", "vue-i18n-extract": "^2.0.4",
"vue-router-mock": "^1.0.0" "vue-router-mock": "^1.0.0"
} }

View file

@ -0,0 +1,66 @@
diff --git a/node_modules/vue-i18n-extract/dist/vue-i18n-extract.modern.mjs b/node_modules/vue-i18n-extract/dist/vue-i18n-extract.modern.mjs
index 670733e..872d1af 100644
--- a/node_modules/vue-i18n-extract/dist/vue-i18n-extract.modern.mjs
+++ b/node_modules/vue-i18n-extract/dist/vue-i18n-extract.modern.mjs
@@ -38,7 +38,7 @@ var defaultConfig = {
};
function initCommand() {
- fs.writeFileSync(path.resolve(process.cwd(), './vue-i18n-extract.config.js'), `module.exports = ${JSON.stringify(defaultConfig, null, 2)}`);
+ fs.writeFileSync(path.resolve(process.cwd(), './vue-i18n-extract.config.cjs'), `module.exports = ${JSON.stringify(defaultConfig, null, 2)}`);
}
function resolveConfig() {
const argvOptions = cac().parse(process.argv, {
@@ -47,7 +47,7 @@ function resolveConfig() {
let options;
try {
- const pathToConfigFile = path.resolve(process.cwd(), './vue-i18n-extract.config.js'); // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const pathToConfigFile = path.resolve(process.cwd(), './vue-i18n-extract.config.cjs'); // eslint-disable-next-line @typescript-eslint/no-var-requires
const configOptions = require(pathToConfigFile);
diff --git a/node_modules/vue-i18n-extract/dist/vue-i18n-extract.umd.js b/node_modules/vue-i18n-extract/dist/vue-i18n-extract.umd.js
index ca19c7a..11cb846 100644
--- a/node_modules/vue-i18n-extract/dist/vue-i18n-extract.umd.js
+++ b/node_modules/vue-i18n-extract/dist/vue-i18n-extract.umd.js
@@ -45,7 +45,7 @@
};
function initCommand() {
- fs__default["default"].writeFileSync(path__default["default"].resolve(process.cwd(), './vue-i18n-extract.config.js'), `module.exports = ${JSON.stringify(defaultConfig, null, 2)}`);
+ fs__default["default"].writeFileSync(path__default["default"].resolve(process.cwd(), './vue-i18n-extract.config.cjs'), `module.exports = ${JSON.stringify(defaultConfig, null, 2)}`);
}
function resolveConfig() {
const argvOptions = cac__default["default"]().parse(process.argv, {
@@ -54,7 +54,7 @@
let options;
try {
- const pathToConfigFile = path__default["default"].resolve(process.cwd(), './vue-i18n-extract.config.js'); // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const pathToConfigFile = path__default["default"].resolve(process.cwd(), './vue-i18n-extract.config.cjs'); // eslint-disable-next-line @typescript-eslint/no-var-requires
const configOptions = require(pathToConfigFile);
diff --git a/node_modules/vue-i18n-extract/src/config-file/index.ts b/node_modules/vue-i18n-extract/src/config-file/index.ts
index 3db836f..744bd74 100644
--- a/node_modules/vue-i18n-extract/src/config-file/index.ts
+++ b/node_modules/vue-i18n-extract/src/config-file/index.ts
@@ -5,7 +5,7 @@ import defaultConfig from './vue-i18n-extract.config';
export function initCommand(): void {
fs.writeFileSync(
- path.resolve(process.cwd(), './vue-i18n-extract.config.js'),
+ path.resolve(process.cwd(), './vue-i18n-extract.config.cjs'),
`module.exports = ${JSON.stringify(defaultConfig, null, 2)}`,
);
}
@@ -16,7 +16,7 @@ export function resolveConfig (): Record<string, string> {
let options;
try {
- const pathToConfigFile = path.resolve(process.cwd(), './vue-i18n-extract.config.js');
+ const pathToConfigFile = path.resolve(process.cwd(), './vue-i18n-extract.config.cjs');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const configOptions = require(pathToConfigFile);

View file

@ -1,4 +1,4 @@
module.exports = { export default {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},

View file

@ -146,22 +146,37 @@ body {
.taginput-item { .taginput-item {
@apply bg-primary mr-2; @apply bg-primary mr-2;
} }
.taginput-autocomplete {
@apply flex-1 !drop-shadow-none;
}
.taginput-expanded {
@apply w-full;
}
.taginput .autocomplete .dropdown-menu {
@apply w-full;
}
.taginput-container {
@apply border-none;
}
.taginput-item:first-child { .taginput-item:first-child {
@apply ml-2; @apply ml-2;
} }
.taginput-input-wrapper {
@apply block;
}
/* Autocomplete */ /* Autocomplete */
.autocomplete { .autocomplete {
@apply max-h-[200px] drop-shadow-md text-black z-10; @apply max-h-[200px] drop-shadow-md text-black z-10;
} }
.autocomplete-item { .autocomplete .autocomplete-item {
@apply py-1.5 px-4 text-start; @apply text-start p-2;
} }
.autocomplete-item-group-title { .autocomplete .autocomplete-item-group-title {
@apply opacity-50 py-0 px-2; @apply opacity-50 py-1.5 px-2 dark:text-white dark:opacity-75;
} }
/* Dropdown */ /* Dropdown */
@ -173,7 +188,7 @@ body {
@apply bg-white dark:bg-zinc-700 shadow-lg rounded text-start py-2; @apply bg-white dark:bg-zinc-700 shadow-lg rounded text-start py-2;
} }
.dropdown-item { .dropdown-item {
@apply relative inline-flex gap-1 no-underline p-2 cursor-pointer w-full; @apply relative inline-flex gap-1 no-underline p-2 cursor-pointer w-full hover:bg-[#f5f5f5] hover:text-black;
} }
.dropdown-item-active { .dropdown-item-active {
@ -343,8 +358,8 @@ button.menubar__button {
.o-drop__menu--active { .o-drop__menu--active {
@apply z-50; @apply z-50;
} }
.o-dpck__box { .datepicker-box {
@apply px-4 py-1; @apply block px-4 py-1 hover:bg-transparent;
} }
.o-dpck__header { .o-dpck__header {
@apply pb-2 mb-2; @apply pb-2 mb-2;
@ -352,7 +367,7 @@ button.menubar__button {
} }
.o-dpck__header__next, .o-dpck__header__next,
.o-dpck__header__previous { .o-dpck__header__previous {
@apply justify-center text-center no-underline cursor-pointer items-center shadow-none inline-flex relative select-none leading-6 border rounded h-10 p-2 m-1 dark:text-white; @apply justify-center text-center no-underline cursor-pointer items-center shadow-none inline-flex relative select-none leading-6 border rounded h-10 p-2 m-1 dark:text-white hover:px-2;
min-width: 2.25em; min-width: 2.25em;
} }
.o-dpck__header__list { .o-dpck__header__list {

View file

@ -30,7 +30,9 @@
class="flex-1 min-w-[200px]" class="flex-1 min-w-[200px]"
> >
<template v-slot="props"> <template v-slot="props">
<div class="dark:bg-violet-3 p-1 flex items-center gap-1"> <div
class="dark:bg-violet-3 p-1 flex items-center gap-1 flex-1 dark:text-white"
>
<div class=""> <div class="">
<img <img
v-if=" v-if="
@ -41,6 +43,7 @@
width="24" width="24"
height="24" height="24"
alt="" alt=""
class="dark:fill-white"
/> />
<o-icon v-else-if="props.option.icon" :icon="props.option.icon" /> <o-icon v-else-if="props.option.icon" :icon="props.option.icon" />
<o-icon v-else icon="help-circle" /> <o-icon v-else icon="help-circle" />

View file

@ -44,7 +44,9 @@
<o-icon :icon="addressToPoiInfos(option).poiIcon.icon" /> <o-icon :icon="addressToPoiInfos(option).poiIcon.icon" />
<b>{{ addressToPoiInfos(option).name }}</b> <b>{{ addressToPoiInfos(option).name }}</b>
</p> </p>
<small>{{ addressToPoiInfos(option).alternativeName }}</small> <p class="text-small">
{{ addressToPoiInfos(option).alternativeName }}
</p>
</template> </template>
<template #empty> <template #empty>
<template v-if="isFetching">{{ t("Searching") }}</template> <template v-if="isFetching">{{ t("Searching") }}</template>

View file

@ -1,15 +1,17 @@
<template> <template>
<o-field :label-for="id"> <o-field :label-for="id" class="taginput-field">
<template #label> <template #label>
{{ $t("Add some tags") }} <p class="inline-flex items-center gap-0.5">
{{ t("Add some tags") }}
<o-tooltip <o-tooltip
variant="dark" variant="dark"
:label=" :label="
$t('You can add tags by hitting the Enter key or by adding a comma') t('You can add tags by hitting the Enter key or by adding a comma')
" "
> >
<HelpCircleOutline :size="16" /> <HelpCircleOutline :size="16" />
</o-tooltip> </o-tooltip>
</p>
</template> </template>
<o-taginput <o-taginput
v-model="tagsStrings" v-model="tagsStrings"
@ -20,10 +22,11 @@
icon="label" icon="label"
:maxlength="20" :maxlength="20"
:maxitems="10" :maxitems="10"
:placeholder="$t('Eg: Stockholm, Dance, Chess…')" :placeholder="t('Eg: Stockholm, Dance, Chess…')"
@input="debouncedGetFilteredTags" @input="getFilteredTags"
:id="id" :id="id"
dir="auto" dir="auto"
expanded
> >
</o-taginput> </o-taginput>
</o-field> </o-field>
@ -31,11 +34,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import differenceBy from "lodash/differenceBy"; import differenceBy from "lodash/differenceBy";
import { ITag } from "../../types/tag.model"; import { ITag } from "../../types/tag.model";
import debounce from "lodash/debounce";
import { computed, onBeforeMount, ref } from "vue"; import { computed, onBeforeMount, ref } from "vue";
import HelpCircleOutline from "vue-material-design-icons/HelpCircleOutline.vue"; import HelpCircleOutline from "vue-material-design-icons/HelpCircleOutline.vue";
import { useFetchTags } from "@/composition/apollo/tags"; import { useFetchTags } from "@/composition/apollo/tags";
import { FILTER_TAGS } from "@/graphql/tags"; import { FILTER_TAGS } from "@/graphql/tags";
import { useI18n } from "vue-i18n";
const props = defineProps<{ const props = defineProps<{
modelValue: ITag[]; modelValue: ITag[];
@ -47,6 +50,8 @@ const text = ref("");
const tags = ref<ITag[]>([]); const tags = ref<ITag[]>([]);
const { t } = useI18n({ useScope: "global" });
let componentId = 0; let componentId = 0;
onBeforeMount(() => { onBeforeMount(() => {
@ -61,14 +66,16 @@ const { load: fetchTags } = useFetchTags();
const getFilteredTags = async (newText: string): Promise<void> => { const getFilteredTags = async (newText: string): Promise<void> => {
text.value = newText; text.value = newText;
const res = await fetchTags(FILTER_TAGS, { filter: newText }); const res = await fetchTags(
FILTER_TAGS,
{ filter: newText },
{ debounce: 200 }
);
if (res) { if (res) {
tags.value = res.tags; tags.value = res.tags;
} }
}; };
const debouncedGetFilteredTags = debounce(getFilteredTags, 200);
const filteredTags = computed((): ITag[] => { const filteredTags = computed((): ITag[] => {
return differenceBy(tags.value, props.modelValue, "id").filter( return differenceBy(tags.value, props.modelValue, "id").filter(
(option) => (option) =>

View file

@ -32,6 +32,7 @@
v-if="currentActor" v-if="currentActor"
:currentActor="currentActor" :currentActor="currentActor"
:placeholder="t('Write a new message')" :placeholder="t('Write a new message')"
:required="true"
/> />
<o-notification <o-notification
class="my-2" class="my-2"
@ -133,6 +134,7 @@ const sendForm = (e: Event) => {
e.preventDefault(); e.preventDefault();
console.debug("Sending new private message"); console.debug("Sending new private message");
if (!currentActor.value?.id || !event.value.id) return; if (!currentActor.value?.id || !event.value.id) return;
errors.value = [];
eventPrivateMessageMutate({ eventPrivateMessageMutate({
text: text.value, text: text.value,
actorId: actorId:
@ -150,7 +152,10 @@ onEventPrivateMessageMutated(() => {
onEventPrivateMessageError((err) => { onEventPrivateMessageError((err) => {
err.graphQLErrors.forEach((error) => { err.graphQLErrors.forEach((error) => {
errors.value.push(error.message); const message = Array.isArray(error.message)
? error.message
: [error.message];
errors.value.push(...message);
}); });
}); });

View file

@ -6,7 +6,7 @@
<section> <section>
<div <div
class="flex gap-1 flex-row mb-3 bg-mbz-yellow p-3 rounded items-center" class="flex gap-1 flex-row mb-3 bg-mbz-yellow dark:text-black p-3 rounded items-center"
> >
<o-icon <o-icon
icon="alert" icon="alert"

View file

@ -1,6 +1,6 @@
<template> <template>
<span <span
class="rounded-md truncate text-sm text-violet-title px-2 py-1" class="rounded-md truncate text-sm text-black px-2 py-1"
:class="[ :class="[
typeClasses, typeClasses,
capitalize, capitalize,

View file

@ -217,7 +217,16 @@
</button> </button>
</bubble-menu> </bubble-menu>
<editor-content class="editor__content" :editor="editor" v-if="editor" /> <editor-content
class="editor__content"
:class="{ editorErrorStatus, editorIsFocused: focused }"
:editor="editor"
v-if="editor"
ref="editorContentRef"
/>
<p v-if="editorErrorMessage" class="text-sm text-mbz-danger">
{{ editorErrorMessage }}
</p>
</div> </div>
</div> </div>
</template> </template>
@ -249,7 +258,7 @@ import Underline from "@tiptap/extension-underline";
import Link from "@tiptap/extension-link"; import Link from "@tiptap/extension-link";
import { AutoDir } from "./Editor/Autodir"; import { AutoDir } from "./Editor/Autodir";
// import sanitizeHtml from "sanitize-html"; // import sanitizeHtml from "sanitize-html";
import { computed, inject, onBeforeUnmount, watch } from "vue"; import { computed, inject, onBeforeUnmount, ref, watch } from "vue";
import { Dialog } from "@/plugins/dialog"; import { Dialog } from "@/plugins/dialog";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useMutation } from "@vue/apollo-composable"; import { useMutation } from "@vue/apollo-composable";
@ -269,6 +278,7 @@ import FormatQuoteClose from "vue-material-design-icons/FormatQuoteClose.vue";
import Undo from "vue-material-design-icons/Undo.vue"; import Undo from "vue-material-design-icons/Undo.vue";
import Redo from "vue-material-design-icons/Redo.vue"; import Redo from "vue-material-design-icons/Redo.vue";
import Placeholder from "@tiptap/extension-placeholder"; import Placeholder from "@tiptap/extension-placeholder";
import { useFocusWithin } from "@vueuse/core";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -279,11 +289,13 @@ const props = withDefaults(
currentActor: IPerson; currentActor: IPerson;
placeholder?: string; placeholder?: string;
headingLevel?: Level[]; headingLevel?: Level[];
required?: boolean;
}>(), }>(),
{ {
mode: "description", mode: "description",
maxSize: 100_000_000, maxSize: 100_000_000,
headingLevel: () => [3, 4, 5], headingLevel: () => [3, 4, 5],
required: false,
} }
); );
@ -333,7 +345,7 @@ const editor = useEditor({
"aria-label": ariaLabel.value ?? "", "aria-label": ariaLabel.value ?? "",
role: "textbox", role: "textbox",
class: class:
"prose dark:prose-invert prose-sm lg:prose-lg xl:prose-xl bg-zinc-50 dark:bg-zinc-700 focus:outline-none !max-w-full", "prose dark:prose-invert prose-sm lg:prose-lg xl:prose-xl bg-white dark:bg-zinc-700 !max-w-full",
}, },
transformPastedHTML: transformPastedHTML, transformPastedHTML: transformPastedHTML,
}, },
@ -373,8 +385,18 @@ const editor = useEditor({
onUpdate: () => { onUpdate: () => {
emit("update:modelValue", editor.value?.getHTML()); emit("update:modelValue", editor.value?.getHTML());
}, },
onBlur: () => {
checkEditorEmpty();
},
onFocus: () => {
editorErrorStatus.value = false;
editorErrorMessage.value = "";
},
}); });
const editorContentRef = ref(null);
const { focused } = useFocusWithin(editorContentRef);
watch(value, (val: string) => { watch(value, (val: string) => {
if (!editor.value) return; if (!editor.value) return;
if (val !== editor.value.getHTML()) { if (val !== editor.value.getHTML()) {
@ -470,6 +492,18 @@ defineExpose({ replyToComment, focus });
onBeforeUnmount(() => { onBeforeUnmount(() => {
editor.value?.destroy(); editor.value?.destroy();
}); });
const editorErrorStatus = ref(false);
const editorErrorMessage = ref("");
const isEmpty = computed(
() => props.required === true && editor.value?.isEmpty === true
);
const checkEditorEmpty = () => {
editorErrorStatus.value = isEmpty.value;
editorErrorMessage.value = isEmpty.value ? t("You need to enter a text") : "";
};
</script> </script>
<style lang="scss"> <style lang="scss">
@use "@/styles/_mixins" as *; @use "@/styles/_mixins" as *;
@ -523,14 +557,8 @@ onBeforeUnmount(() => {
&__content { &__content {
div.ProseMirror { div.ProseMirror {
min-height: 2.5rem; min-height: 2.5rem;
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);
border-radius: 4px; border-radius: 4px;
border: 1px solid #dbdbdb;
padding: 12px 6px; padding: 12px 6px;
&:focus {
outline: none;
}
} }
h1 { h1 {
@ -655,4 +683,19 @@ onBeforeUnmount(() => {
.mention[data-id] { .mention[data-id] {
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1; @apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
} }
.editor__content > div {
@apply border rounded border-[#6b7280];
}
.editorIsFocused > div {
@apply ring-2 ring-[#2563eb] outline-2 outline outline-offset-2 outline-transparent;
}
.editorErrorStatus {
@apply border-red-500;
}
.editor__content p.is-editor-empty:first-child::before {
@apply text-slate-300;
}
</style> </style>

View file

@ -17,7 +17,6 @@ export function useGroupMembers(
groupName: Ref<string>, groupName: Ref<string>,
options: useGroupMembersOptions = {} options: useGroupMembersOptions = {}
) { ) {
console.debug("useGroupMembers", options);
const { result, error, loading, onResult, onError, refetch, fetchMore } = const { result, error, loading, onResult, onError, refetch, fetchMore } =
useQuery< useQuery<
{ {

View file

@ -13,6 +13,9 @@ export const COMMENT_FIELDS_FRAGMENT = gql`
actor { actor {
...ActorFragment ...ActorFragment
} }
attributedTo {
...ActorFragment
}
totalReplies totalReplies
insertedAt insertedAt
updatedAt updatedAt

View file

@ -12,6 +12,9 @@ export const CONVERSATION_QUERY_FRAGMENT = gql`
lastComment { lastComment {
...CommentFields ...CommentFields
} }
originComment {
...CommentFields
}
participants { participants {
...ActorFragment ...ActorFragment
} }
@ -19,6 +22,12 @@ export const CONVERSATION_QUERY_FRAGMENT = gql`
id id
uuid uuid
title title
organizerActor {
id
}
attributedTo {
id
}
picture { picture {
id id
url url

View file

@ -1643,5 +1643,7 @@
"Visit {instance_domain}": "Visit {instance_domain}", "Visit {instance_domain}": "Visit {instance_domain}",
"Software details: {software_details}": "Software details: {software_details}", "Software details: {software_details}": "Software details: {software_details}",
"Only instances with an application actor can be followed": "Only instances with an application actor can be followed", "Only instances with an application actor can be followed": "Only instances with an application actor can be followed",
"Domain or instance name": "Domain or instance name" "Domain or instance name": "Domain or instance name",
"You need to enter a text": "You need to enter a text",
"Error while adding tag: {error}": "Error while adding tag: {error}"
} }

View file

@ -1636,5 +1636,7 @@
"Visit {instance_domain}": "Visiter {instance_domain}", "Visit {instance_domain}": "Visiter {instance_domain}",
"Software details: {software_details}": "Détails du logiciel : {software_details}", "Software details: {software_details}": "Détails du logiciel : {software_details}",
"Only instances with an application actor can be followed": "Seules les instances avec un acteur application peuvent être suivies", "Only instances with an application actor can be followed": "Seules les instances avec un acteur application peuvent être suivies",
"Domain or instance name": "Domaine ou nom de l'instance" "Domain or instance name": "Domaine ou nom de l'instance",
"You need to enter a text": "Vous devez entrer un texte",
"Error while adding tag: {error}": "Erreur lors de l'ajout d'un tag : {error}"
} }

View file

@ -16,7 +16,7 @@
"A cookie is a small file containing information that is sent to your computer when you visit a website. When you visit the site again, the cookie allows that site to recognize your browser. Cookies may store user preferences and other information. You can configure your browser to refuse all cookies. However, this may result in some website features or services partially working. Local storage works the same way but allows you to store more data.": "A süti egy információt tartalmazó kis fájl, amelyet akkor küld el a számítógépe, ha meglátogat egy weboldalt. Amikor újra meglátogatja a weboldalt, akkor a süti lehetővé teszi annak az oldalnak, hogy felismerje a böngészőjét. A sütik tárolhatnak felhasználói beállításokat vagy egyéb információkat. Beállíthatja a böngészőjét úgy, hogy utasítson vissza minden sütit. Azonban ez azt eredményezheti, hogy néhány weboldalon a funkciók vagy a szolgáltatások csak részlegesen működnek. A helyi tároló hasonlóan működik, de több adat tárolását teszi lehetővé.", "A cookie is a small file containing information that is sent to your computer when you visit a website. When you visit the site again, the cookie allows that site to recognize your browser. Cookies may store user preferences and other information. You can configure your browser to refuse all cookies. However, this may result in some website features or services partially working. Local storage works the same way but allows you to store more data.": "A süti egy információt tartalmazó kis fájl, amelyet akkor küld el a számítógépe, ha meglátogat egy weboldalt. Amikor újra meglátogatja a weboldalt, akkor a süti lehetővé teszi annak az oldalnak, hogy felismerje a böngészőjét. A sütik tárolhatnak felhasználói beállításokat vagy egyéb információkat. Beállíthatja a böngészőjét úgy, hogy utasítson vissza minden sütit. Azonban ez azt eredményezheti, hogy néhány weboldalon a funkciók vagy a szolgáltatások csak részlegesen működnek. A helyi tároló hasonlóan működik, de több adat tárolását teszi lehetővé.",
"A discussion has been created or updated": "Egy megbeszélés létre lett hozva vagy frissítve lett", "A discussion has been created or updated": "Egy megbeszélés létre lett hozva vagy frissítve lett",
"A federated software": "Egy föderált szoftver", "A federated software": "Egy föderált szoftver",
"A fediverse account URL to follow for event updates": "Egy födiverzum-fiók URL az esemény frissítéseinek követéséhez", "A fediverse account URL to follow for event updates": "Egy Födiverzum-fiók webcíme az esemény frissítéseinek követéséhez",
"A few lines about your group": "Néhány sor a csoportjáról", "A few lines about your group": "Néhány sor a csoportjáról",
"A link to a page presenting the event schedule": "Az esemény ütemtervét bemutató oldalra mutató hivatkozás", "A link to a page presenting the event schedule": "Az esemény ütemtervét bemutató oldalra mutató hivatkozás",
"A link to a page presenting the price options": "Az árválasztékot bemutató oldalra mutató hivatkozás", "A link to a page presenting the price options": "Az árválasztékot bemutató oldalra mutató hivatkozás",
@ -41,8 +41,8 @@
"About Mobilizon": "A Mobilizon névjegye", "About Mobilizon": "A Mobilizon névjegye",
"About anonymous participation": "A névtelen részvételről", "About anonymous participation": "A névtelen részvételről",
"About instance": "A példány névjegye", "About instance": "A példány névjegye",
"About this event": "Erről az eseményről", "About this event": "Az esemény nébjegye",
"About this instance": "Erről a példányról", "About this instance": "A példány névjegye",
"About {instance}": "A(z) {instance} névjegye", "About {instance}": "A(z) {instance} névjegye",
"Accept": "Elfogadás", "Accept": "Elfogadás",
"Accept follow": "Követés elfogadása", "Accept follow": "Követés elfogadása",
@ -57,7 +57,7 @@
"Access group todo-lists": "Csoport teendőlistáinak elérése", "Access group todo-lists": "Csoport teendőlistáinak elérése",
"Access organized events": "Szervezett események elérése", "Access organized events": "Szervezett események elérése",
"Access participations": "Résztvevők elérése", "Access participations": "Résztvevők elérése",
"Access your group's resources": "Csoport bejegyzéseinek elérése", "Access your group's resources": "A csoport erőforrásainak elérése",
"Accessibility": "Akadálymentesítés", "Accessibility": "Akadálymentesítés",
"Accessible only by link": "Csak hivatkozáson keresztül érhető el", "Accessible only by link": "Csak hivatkozáson keresztül érhető el",
"Accessible only to members": "Csak tagoknak érhető el", "Accessible only to members": "Csak tagoknak érhető el",
@ -84,7 +84,7 @@
"Add new…": "Új hozzáadása…", "Add new…": "Új hozzáadása…",
"Add picture": "Kép hozzáadása", "Add picture": "Kép hozzáadása",
"Add some tags": "Címkék hozzáadása", "Add some tags": "Címkék hozzáadása",
"Add to my calendar": "Hozzáadás a naptáramhoz", "Add to my calendar": "Hozzáadás a saját naptárhoz",
"Additional comments": "További hozzászólások", "Additional comments": "További hozzászólások",
"Admin": "Adminisztrátor", "Admin": "Adminisztrátor",
"Admin dashboard": "Adminisztrátori vezérlőpult", "Admin dashboard": "Adminisztrátori vezérlőpult",
@ -98,7 +98,7 @@
"All the places have already been taken": "Már az összes helyet elfoglalták", "All the places have already been taken": "Már az összes helyet elfoglalták",
"Allow all comments from users with accounts": "Az összes hozzászólás engedélyezése a bejelentkezett felhasználóktól", "Allow all comments from users with accounts": "Az összes hozzászólás engedélyezése a bejelentkezett felhasználóktól",
"Allow registrations": "Regisztrációk engedélyezése", "Allow registrations": "Regisztrációk engedélyezése",
"An URL to an external ticketing platform": "Egy URL egy külső jegyértékesítő platformhoz", "An URL to an external ticketing platform": "Egy külső jegyértékesítő platform webcíme",
"An error has occured while refreshing the page.": "Hiba történt az oldal frissítésekor.", "An error has occured while refreshing the page.": "Hiba történt az oldal frissítésekor.",
"An error has occured. Sorry about that. You may try to reload the page.": "Hiba történt. Sajnáljuk. Megpróbálhatja újratölteni az oldalt.", "An error has occured. Sorry about that. You may try to reload the page.": "Hiba történt. Sajnáljuk. Megpróbálhatja újratölteni az oldalt.",
"An ethical alternative": "Egy etikus alternatíva", "An ethical alternative": "Egy etikus alternatíva",
@ -146,11 +146,11 @@
"Ask your instance admin to {enable_feature}.": "Kérje meg a példány adminisztrátorát, hogy {enable_feature}.", "Ask your instance admin to {enable_feature}.": "Kérje meg a példány adminisztrátorát, hogy {enable_feature}.",
"Assigned to": "Hozzárendelve ehhez", "Assigned to": "Hozzárendelve ehhez",
"Atom feed for events and posts": "Atom hírforrás az eseményekhez és a bejegyzésekhez", "Atom feed for events and posts": "Atom hírforrás az eseményekhez és a bejegyzésekhez",
"Attending": "Részt vesz", "Attending": "Részvétel",
"Authorize": "Engedélyezés", "Authorize": "Engedélyezés",
"Authorize application": "Alkalmazás engedélyezése", "Authorize application": "Alkalmazás engedélyezése",
"Authorized on {authorization_date}": "Engedélyezve: {authorization_date}", "Authorized on {authorization_date}": "Engedélyezve: {authorization_date}",
"Autorize this application to access your account?": "Engedélyezi, hogy ez az alkalmazás hozzáférjen a fiókjához?", "Autorize this application to access your account?": "Engedélyezi, hogy az alkalmazás hozzáférjen a fiókjához?",
"Avatar": "Profilkép", "Avatar": "Profilkép",
"Back to group list": "Vissza a csoportokhoz", "Back to group list": "Vissza a csoportokhoz",
"Back to homepage": "Vissza a kezdőlapra", "Back to homepage": "Vissza a kezdőlapra",
@ -183,8 +183,8 @@
"Cancel edition": "Szerkesztés megszakítása", "Cancel edition": "Szerkesztés megszakítása",
"Cancel follow request": "Követési kérés megszakítása", "Cancel follow request": "Követési kérés megszakítása",
"Cancel membership request": "Tagsági kérés megszakítása", "Cancel membership request": "Tagsági kérés megszakítása",
"Cancel my participation request…": "Részvételi kérésem megszakítása…", "Cancel my participation request…": "Saját részvételi kérés visszavonása…",
"Cancel my participation…": "Részvételem megszakítása…", "Cancel my participation…": "Saját részvétel visszavonása…",
"Cancelled": "Törölve", "Cancelled": "Törölve",
"Cancelled: Won't happen": "Törölve: nem fog megtörténni", "Cancelled: Won't happen": "Törölve: nem fog megtörténni",
"Categories": "Kategóriák", "Categories": "Kategóriák",
@ -193,9 +193,9 @@
"Category list": "Kategóriák", "Category list": "Kategóriák",
"Change": "Változtatás", "Change": "Változtatás",
"Change email": "E-mail-cím megváltoztatása", "Change email": "E-mail-cím megváltoztatása",
"Change my email": "E-mail-címem megváltoztatása", "Change my email": "Saját e-mail-cím megváltoztatása",
"Change my identity…": "Személyazonosságom megváltoztatása…", "Change my identity…": "Saját személyazonosság megváltoztatása…",
"Change my password": "Jelszavam megváltoztatása", "Change my password": "Saját jelszó megváltoztatása",
"Change role": "Szerep megváltoztatása", "Change role": "Szerep megváltoztatása",
"Change the filters.": "Módosítsa a szűrőket.", "Change the filters.": "Módosítsa a szűrőket.",
"Change timezone": "Időzóna megváltoztatása", "Change timezone": "Időzóna megváltoztatása",
@ -224,8 +224,8 @@
"Comments": "Hozzászólások", "Comments": "Hozzászólások",
"Comments are closed for everybody else.": "A hozzászólások le vannak zárva mindenki más számára.", "Comments are closed for everybody else.": "A hozzászólások le vannak zárva mindenki más számára.",
"Confirm": "Megerősítés", "Confirm": "Megerősítés",
"Confirm my participation": "Részvételem megerősítése", "Confirm my participation": "Részvétel megerősítése",
"Confirm my particpation": "Részvételem megerősítése", "Confirm my particpation": "Részvétel megerősítése",
"Confirm participation": "Részvétel megerősítése", "Confirm participation": "Részvétel megerősítése",
"Confirm user": "Felhasználó megerősítése", "Confirm user": "Felhasználó megerősítése",
"Confirmed": "Megerősítve", "Confirmed": "Megerősítve",
@ -236,7 +236,7 @@
"Continue": "Folytatás", "Continue": "Folytatás",
"Continue editing": "Szerkesztés folytatása", "Continue editing": "Szerkesztés folytatása",
"Cookies and Local storage": "Sütik és helyi tároló", "Cookies and Local storage": "Sütik és helyi tároló",
"Copy URL to clipboard": "URL másolás a vágólapra", "Copy URL to clipboard": "Webcím másolás a vágólapra",
"Copy details to clipboard": "Részletek másolása a vágólapra", "Copy details to clipboard": "Részletek másolása a vágólapra",
"Country": "Ország", "Country": "Ország",
"Create": "Létrehozás", "Create": "Létrehozás",
@ -272,7 +272,7 @@
"Current identity has been changed to {identityName} in order to manage this event.": "A jelenlegi személyazonosság megváltozott {identityName} személyazonosságra az esemény kezelése érdekében.", "Current identity has been changed to {identityName} in order to manage this event.": "A jelenlegi személyazonosság megváltozott {identityName} személyazonosságra az esemény kezelése érdekében.",
"Current page": "Jelenlegi oldal", "Current page": "Jelenlegi oldal",
"Custom": "Egyéni", "Custom": "Egyéni",
"Custom URL": "Egyéni URL", "Custom URL": "Egyéni webcím",
"Custom text": "Egyéni szöveg", "Custom text": "Egyéni szöveg",
"Daily email summary": "Napi e-mailes összegzés", "Daily email summary": "Napi e-mailes összegzés",
"Dark": "Sötét", "Dark": "Sötét",
@ -297,7 +297,7 @@
"Delete everything": "Minden törlése", "Delete everything": "Minden törlése",
"Delete group": "Csoport törlése", "Delete group": "Csoport törlése",
"Delete group discussions": "Csoport témáinak törlése", "Delete group discussions": "Csoport témáinak törlése",
"Delete group posts": "Csoport bejegyzéseinek törlése", "Delete group posts": "Csoportbejegyzések törlése",
"Delete group resources": "Csoport erőforrásainak törlése", "Delete group resources": "Csoport erőforrásainak törlése",
"Delete my account": "Saját fiók törlése", "Delete my account": "Saját fiók törlése",
"Delete post": "Bejegyzés törlése", "Delete post": "Bejegyzés törlése",
@ -310,7 +310,7 @@
"Deleting comment": "Hozzászólás törlése", "Deleting comment": "Hozzászólás törlése",
"Deleting event": "Esemény törlése", "Deleting event": "Esemény törlése",
"Deleting my account will delete all of my identities.": "A saját fiókom törlése törölni fogja az összes személyazonosságomat is.", "Deleting my account will delete all of my identities.": "A saját fiókom törlése törölni fogja az összes személyazonosságomat is.",
"Deleting your Mobilizon account": "Az Ön Mobilizon-fiókjának törlése", "Deleting your Mobilizon account": "A saját Mobilizon-fiókjának törlése",
"Demote": "Lefokozás", "Demote": "Lefokozás",
"Describe your event": "Írja le az eseményt", "Describe your event": "Írja le az eseményt",
"Description": "Leírás", "Description": "Leírás",
@ -355,7 +355,7 @@
"Enabled": "Engedélyezve", "Enabled": "Engedélyezve",
"Ends on…": "Befejeződik…", "Ends on…": "Befejeződik…",
"Enter the code displayed on your device": "Adja meg az eszközén megjelenített kódot", "Enter the code displayed on your device": "Adja meg az eszközén megjelenített kódot",
"Enter the link URL": "Adja meg a hivatkozás URL-ét", "Enter the link URL": "Adja meg a hivatkozás webcímét",
"Enter your email address below, and we'll email you instructions on how to change your password.": "Adja meg lent az e-mail-címét, és elküldjük e-mailben az utasításokat, hogy hogyan változtathatja meg a jelszavát.", "Enter your email address below, and we'll email you instructions on how to change your password.": "Adja meg lent az e-mail-címét, és elküldjük e-mailben az utasításokat, hogy hogyan változtathatja meg a jelszavát.",
"Enter your own privacy policy. HTML tags allowed. The {mobilizon_privacy_policy} is provided as template.": "Adja meg a saját adatvédelmi irányelveit. A HTML címkék engedélyezettek. A {mobilizon_privacy_policy} meg van adva sablonként.", "Enter your own privacy policy. HTML tags allowed. The {mobilizon_privacy_policy} is provided as template.": "Adja meg a saját adatvédelmi irányelveit. A HTML címkék engedélyezettek. A {mobilizon_privacy_policy} meg van adva sablonként.",
"Enter your own terms. HTML tags allowed. The {mobilizon_terms} are provided as template.": "Adja meg a saját használati feltételeit. A HTML címkék engedélyezettek. A {mobilizon_terms} meg van adva sablonként.", "Enter your own terms. HTML tags allowed. The {mobilizon_terms} are provided as template.": "Adja meg a saját használati feltételeit. A HTML címkék engedélyezettek. A {mobilizon_terms} meg van adva sablonként.",
@ -377,7 +377,7 @@
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Etikus alternatíva a Facebook eseményekre, csoportokra és oldalakra. A Mobilizon egy olyan <b>eszköz, amelyet úgy terveztek, hogy Önt szolgálja</b>. És pont.", "Ethical alternative to Facebook events, groups and pages, Mobilizon is a <b>tool designed to serve you</b>. Period.": "Etikus alternatíva a Facebook eseményekre, csoportokra és oldalakra. A Mobilizon egy olyan <b>eszköz, amelyet úgy terveztek, hogy Önt szolgálja</b>. És pont.",
"Ethical alternative to Facebook events, groups and pages, Mobilizon is a {tool_designed_to_serve_you}. Period.": "A facebookos események, csoportok és oldalak etikus alternatívája, a Mobilizon egy olyan {tool_designed_to_serve_you}. Pont.", "Ethical alternative to Facebook events, groups and pages, Mobilizon is a {tool_designed_to_serve_you}. Period.": "A facebookos események, csoportok és oldalak etikus alternatívája, a Mobilizon egy olyan {tool_designed_to_serve_you}. Pont.",
"Event": "Esemény", "Event": "Esemény",
"Event URL": "Esemény URL", "Event URL": "Esemény webcíme",
"Event already passed": "Az esemény már elmúlt", "Event already passed": "Az esemény már elmúlt",
"Event cancelled": "Esemény törölve", "Event cancelled": "Esemény törölve",
"Event creation": "Eseménylétrehozás", "Event creation": "Eseménylétrehozás",
@ -464,7 +464,7 @@
"Group": "Csoport", "Group": "Csoport",
"Group Followers": "Követők csoportosítása", "Group Followers": "Követők csoportosítása",
"Group Members": "Csoporttagok", "Group Members": "Csoporttagok",
"Group URL": "Csoport URL", "Group URL": "Csoport webcíme",
"Group activity": "Csoporttevékenység", "Group activity": "Csoporttevékenység",
"Group address": "Csoport címe", "Group address": "Csoport címe",
"Group description body": "Csoport leírásának törzse", "Group description body": "Csoport leírásának törzse",
@ -528,13 +528,13 @@
"Instance Name": "Példány neve", "Instance Name": "Példány neve",
"Instance Privacy Policy": "Példány adatvédelmi irányelve", "Instance Privacy Policy": "Példány adatvédelmi irányelve",
"Instance Privacy Policy Source": "Példány adatvédelmi irányelvének forrása", "Instance Privacy Policy Source": "Példány adatvédelmi irányelvének forrása",
"Instance Privacy Policy URL": "Példány adatvédelmi irányelvének URL-je", "Instance Privacy Policy URL": "Példány adatvédelmi irányelvének webcíme",
"Instance Rules": "Példány szabályai", "Instance Rules": "Példány szabályai",
"Instance Short Description": "Példány rövid leírása", "Instance Short Description": "Példány rövid leírása",
"Instance Slogan": "Példány szlogenje", "Instance Slogan": "Példány szlogenje",
"Instance Terms": "Példány használati feltételei", "Instance Terms": "Példány használati feltételei",
"Instance Terms Source": "Példány használati feltételeinek forrása", "Instance Terms Source": "Példány használati feltételeinek forrása",
"Instance Terms URL": "Példány használati feltételeinek URL-e", "Instance Terms URL": "Példány használati feltételeinek webcíme",
"Instance administrator": "Példány adminisztrátora", "Instance administrator": "Példány adminisztrátora",
"Instance configuration": "Példány beállítása", "Instance configuration": "Példány beállítása",
"Instance feeds": "Példány hírforrásai", "Instance feeds": "Példány hírforrásai",
@ -759,7 +759,7 @@
"Only group members can access discussions": "Csak csoporttagok férhetnek hozzá a megbeszélésekhez", "Only group members can access discussions": "Csak csoporttagok férhetnek hozzá a megbeszélésekhez",
"Only group moderators can create, edit and delete events.": "Csak a csoport moderátorai hozhatnak létre, szerkeszthetnek és törölhetnek eseményeket.", "Only group moderators can create, edit and delete events.": "Csak a csoport moderátorai hozhatnak létre, szerkeszthetnek és törölhetnek eseményeket.",
"Only group moderators can create, edit and delete posts.": "Csak a csoport moderátorai hozhatnak létre, szerkeszthetnek és törölhetnek bejegyzéseket.", "Only group moderators can create, edit and delete posts.": "Csak a csoport moderátorai hozhatnak létre, szerkeszthetnek és törölhetnek bejegyzéseket.",
"Only registered users may fetch remote events from their URL.": "Csak regisztrált felhasználók kérhetik le a távoli eseményeket az URL-ükről.", "Only registered users may fetch remote events from their URL.": "Csak regisztrált felhasználók kérhetik le a távoli eseményeket a webcímükről.",
"Open": "Megnyitás", "Open": "Megnyitás",
"Open a topic on our forum": "Téma nyitása a fórumunkon", "Open a topic on our forum": "Téma nyitása a fórumunkon",
"Open an issue on our bug tracker (advanced users)": "Jegy nyitása a hibakövetőnkben (haladó felhasználóknak)", "Open an issue on our bug tracker (advanced users)": "Jegy nyitása a hibakövetőnkben (haladó felhasználóknak)",
@ -784,7 +784,7 @@
"Otherwise this identity will just be removed from the group administrators.": "Egyébként ez a személyazonosság el lesz távolítva a csoport adminisztrátoraiból.", "Otherwise this identity will just be removed from the group administrators.": "Egyébként ez a személyazonosság el lesz távolítva a csoport adminisztrátoraiból.",
"Owncast": "Owncast", "Owncast": "Owncast",
"Page": "Oldal", "Page": "Oldal",
"Page limited to my group (asks for auth)": "Az oldal korlátozva van a csoportomra (hitelesítést kér)", "Page limited to my group (asks for auth)": "Az oldal korlátozva van a saját csoportomra (hitelesítést kér)",
"Page not found": "Az oldal nem található", "Page not found": "Az oldal nem található",
"Parent folder": "Szülőmappa", "Parent folder": "Szülőmappa",
"Partially accessible with a wheelchair": "Részlegesen közelíthető meg kerekesszékkel", "Partially accessible with a wheelchair": "Részlegesen közelíthető meg kerekesszékkel",
@ -822,7 +822,7 @@
"Popular groups close to you": "Önhöz közeli népszerű csoportok", "Popular groups close to you": "Önhöz közeli népszerű csoportok",
"Popular groups nearby {position}": "Népszerű csoportok {position} közelében", "Popular groups nearby {position}": "Népszerű csoportok {position} közelében",
"Post": "Bejegyzés", "Post": "Bejegyzés",
"Post URL": "Bejegyzés URL-e", "Post URL": "Bejegyzés webcíme",
"Post a comment": "Hozzászólás beküldése", "Post a comment": "Hozzászólás beküldése",
"Post a reply": "Válasz beküldése", "Post a reply": "Válasz beküldése",
"Post body": "Bejegyzés törzse", "Post body": "Bejegyzés törzse",
@ -858,7 +858,7 @@
"Publication date": "Közzététel dátuma", "Publication date": "Közzététel dátuma",
"Publish": "Közzététel", "Publish": "Közzététel",
"Publish events": "Események közzététele", "Publish events": "Események közzététele",
"Publish group posts": "Csoport bejegyzéseinek közzététele", "Publish group posts": "Csoportbejegyzések közzététele",
"Published by {name}": "Közzétette: {name}", "Published by {name}": "Közzétette: {name}",
"Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations": "Közzétett események <b>{comments}</b> hozzászólással és <b>{participations}</b> megerősített részvétellel", "Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations": "Közzétett események <b>{comments}</b> hozzászólással és <b>{participations}</b> megerősített részvétellel",
"Published events with {comments} comments and {participations} confirmed participations": "Események közzétéve {comments} hozzászólással és {participations} megerősített résztvevővel", "Published events with {comments} comments and {participations} confirmed participations": "Események közzétéve {comments} hozzászólással és {participations} megerősített résztvevővel",
@ -892,7 +892,7 @@
"Remember my participation in this browser": "Emlékezzen a részvételemre ebben a böngészőben", "Remember my participation in this browser": "Emlékezzen a részvételemre ebben a böngészőben",
"Remove": "Eltávolítás", "Remove": "Eltávolítás",
"Remove link": "Hivatkozás eltávolítása", "Remove link": "Hivatkozás eltávolítása",
"Remove uploaded media": "Feltöltött média eltávolítása", "Remove uploaded media": "Feltöltött média törlése",
"Rename": "Átnevezés", "Rename": "Átnevezés",
"Rename resource": "Erőforrás átnevezése", "Rename resource": "Erőforrás átnevezése",
"Reopen": "Újranyitás", "Reopen": "Újranyitás",
@ -900,8 +900,8 @@
"Reply": "Válasz", "Reply": "Válasz",
"Report": "Jelentés", "Report": "Jelentés",
"Report #{reportNumber}": "#{reportNumber} jelentés", "Report #{reportNumber}": "#{reportNumber} jelentés",
"Report as ham": "Jelentés nem kéretlenként", "Report as ham": "Jelentés hasznosként",
"Report as spam": "Jelentés kéretlenként", "Report as spam": "Jelentés kéretlen tartalomként",
"Report as undetected spam": "Jelentés nem észlelt kéretlen tartalomként", "Report as undetected spam": "Jelentés nem észlelt kéretlen tartalomként",
"Report reason": "Jelentés oka", "Report reason": "Jelentés oka",
"Report status": "Állapotjelentés", "Report status": "Állapotjelentés",
@ -924,10 +924,10 @@
"Resent confirmation email": "Megerősítő e-mail újraküldve", "Resent confirmation email": "Megerősítő e-mail újraküldve",
"Reset": "Visszaállítás", "Reset": "Visszaállítás",
"Reset filters": "Szűrők alaphelyzetbe állítása", "Reset filters": "Szűrők alaphelyzetbe állítása",
"Reset my password": "Jelszavam visszaállítása", "Reset my password": "Saját jelszó visszaállítása",
"Reset password": "Jelszó visszaállítása", "Reset password": "Jelszó visszaállítása",
"Resolved": "Megoldva", "Resolved": "Megoldva",
"Resource provided is not an URL": "A megadott erőforrás nem URL", "Resource provided is not an URL": "A megadott erőforrás nem webcím",
"Resources": "Erőforrások", "Resources": "Erőforrások",
"Restricted": "Korlátozott", "Restricted": "Korlátozott",
"Return to the group page": "Visszatérés a csoport oldalára", "Return to the group page": "Visszatérés a csoport oldalára",
@ -935,13 +935,13 @@
"Right now": "Épp most", "Right now": "Épp most",
"Role": "Szerep", "Role": "Szerep",
"Rules": "Szabályok", "Rules": "Szabályok",
"SSL and it's successor TLS are encryption technologies to secure data communications when using the service. You can recognize an encrypted connection in your browser's address line when the URL begins with {https} and the lock icon is displayed in your browser's address bar.": "Az SSL és utódja, a TLS, titkosítási technológiák a szolgáltatás használatakor történő adatkommunikációk biztonságossá tételéhez. Arról ismerhet fel egy titkosított kapcsolatot a böngészője címsorában, hogy az URL {https} kezdetű, és lakat ikon jelenik meg a böngészője címsávjában.", "SSL and it's successor TLS are encryption technologies to secure data communications when using the service. You can recognize an encrypted connection in your browser's address line when the URL begins with {https} and the lock icon is displayed in your browser's address bar.": "Az SSL és utódja, a TLS, titkosítási technológiák a szolgáltatás használatakor történő adatkommunikációk biztonságossá tételéhez. Arról ismerhet fel egy titkosított kapcsolatot a böngészője címsorában, hogy a webcím {https} kezdetű, és lakat ikon jelenik meg a böngészője címsávjában.",
"SSL/TLS": "SSL/TLS", "SSL/TLS": "SSL/TLS",
"Save": "Mentés", "Save": "Mentés",
"Save draft": "Piszkozat mentése", "Save draft": "Piszkozat mentése",
"Schedule": "Ütemterv", "Schedule": "Ütemterv",
"Search": "Keresés", "Search": "Keresés",
"Search events, groups, etc.": "Események, csoportok és egyebek keresése", "Search events, groups, etc.": "Események, csoportok stb. keresése",
"Search target": "Cél keresése", "Search target": "Cél keresése",
"Searching…": "Keresés…", "Searching…": "Keresés…",
"Select a category": "Válasszon egy kategóriát", "Select a category": "Válasszon egy kategóriát",
@ -959,8 +959,8 @@
"Send password reset": "Jelszó-visszaállítás küldése", "Send password reset": "Jelszó-visszaállítás küldése",
"Send the confirmation email again": "A megerősítő e-mail újraküldése", "Send the confirmation email again": "A megerősítő e-mail újraküldése",
"Send the report": "A jelentés küldése", "Send the report": "A jelentés küldése",
"Set an URL to a page with your own privacy policy.": "URL beállítása a saját adatvédelmi irányelveit tartalmazó oldalra.", "Set an URL to a page with your own privacy policy.": "Állítsa be a webcímet a saját adatvédelmi irányelveit tartalmazó oldalra.",
"Set an URL to a page with your own terms.": "URL beállítása a saját használati feltételeit tartalmazó oldalra.", "Set an URL to a page with your own terms.": "Állítsa be a webcímet a saját felhasználási feltételeit tartalmazó oldalra.",
"Settings": "Beállítások", "Settings": "Beállítások",
"Share": "Megosztás", "Share": "Megosztás",
"Share this event": "Az esemény megosztása", "Share this event": "Az esemény megosztása",
@ -1010,15 +1010,15 @@
"Text": "Szöveg", "Text": "Szöveg",
"Thanks a lot, your feedback was submitted!": "Köszönjük, beküldte a visszajelzést!", "Thanks a lot, your feedback was submitted!": "Köszönjük, beküldte a visszajelzést!",
"That you follow or of which you are a member": "Amelyet Ön követ, vagy amelynek Ön tagja", "That you follow or of which you are a member": "Amelyet Ön követ, vagy amelynek Ön tagja",
"The Big Blue Button video teleconference URL": "A Big Blue Button videótelefon-konferencia URL-e", "The Big Blue Button video teleconference URL": "A Big Blue Button videókonferencia webcíme",
"The Google Meet video teleconference URL": "A Google Meet videótelefon-konferencia URL-e", "The Google Meet video teleconference URL": "A Google Meet videókonferencia webcíme",
"The Jitsi Meet video teleconference URL": "A Jitsi Meet videótelefon-konferencia URL-e", "The Jitsi Meet video teleconference URL": "A Jitsi Meet videókonferencia webcíme",
"The Microsoft Teams video teleconference URL": "A Microsoft Teams videótelefon-konferencia URL-e", "The Microsoft Teams video teleconference URL": "A Microsoft Teams videókonferencia webcíme",
"The URL of a pad where notes are being taken collaboratively": "A jegyzettömb URL-e, ahol a jegyzeteket közösen készítik el", "The URL of a pad where notes are being taken collaboratively": "A jegyzettömb webcíme, ahol a jegyzeteket közösen készítik el",
"The URL of a poll where the choice for the event date is happening": "Egy szavazás URL-e, ahol az esemény dátumának kiválasztása történik", "The URL of a poll where the choice for the event date is happening": "Egy szavazás webcíme, ahol az esemény dátumának kiválasztása történik",
"The URL where the event can be watched live": "Az URL, ahol az esemény élőben nézhető", "The URL where the event can be watched live": "A webcím, ahol az esemény élőben nézhető",
"The URL where the event live can be watched again after it has ended": "Az URL, ahol az élő esemény újra megnézhető, miután befejeződött", "The URL where the event live can be watched again after it has ended": "A webcím, ahol az élő esemény újra megnézhető, miután befejeződött",
"The Zoom video teleconference URL": "A Zoom videótelefon-konferencia URL-e", "The Zoom video teleconference URL": "A Zoom videókonferencia webcíme",
"The account's email address was changed. Check your emails to verify it.": "A fiók e-mail-címe megváltozott. Nézze meg az e-mailjeit az ellenőrzéséhez.", "The account's email address was changed. Check your emails to verify it.": "A fiók e-mail-címe megváltozott. Nézze meg az e-mailjeit az ellenőrzéséhez.",
"The actual number of participants may differ, as this event is hosted on another instance.": "A résztvevők tényleges száma eltérhet, mivel ez az esemény egy másik példányon van kiszolgálva.", "The actual number of participants may differ, as this event is hosted on another instance.": "A résztvevők tényleges száma eltérhet, mivel ez az esemény egy másik példányon van kiszolgálva.",
"The calc will be created on {service}": "A táblázat itt lesz létrehozva: {service}", "The calc will be created on {service}": "A táblázat itt lesz létrehozva: {service}",
@ -1067,7 +1067,7 @@
"The post {post} was deleted by {profile}.": "A(z) {post} bejegyzést {profile} törölte.", "The post {post} was deleted by {profile}.": "A(z) {post} bejegyzést {profile} törölte.",
"The post {post} was updated by {profile}.": "A(z) {post} bejegyzést {profile} frissítette.", "The post {post} was updated by {profile}.": "A(z) {post} bejegyzést {profile} frissítette.",
"The provided application was not found.": "A megadott alkalmazás nem található.", "The provided application was not found.": "A megadott alkalmazás nem található.",
"The report contents (eventual comments and event) and the reported profile details will be transmitted to Akismet.": "A jelentés tartalma (a hozzászólások és az esemény) és a jelentett profil részletei el lesznek küldve az Akismetnek.", "The report contents (eventual comments and event) and the reported profile details will be transmitted to Akismet.": "A jelentés tartalma (az esetleges hozzászólások és az esemény), valamint a jelentett profil részletei el lesznek küldve az Akismetnek.",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "A jelentés el lesz küldve a példánya moderátorainak. Elmagyarázhatja alább, hogy miért jelenti ezt a tartalmat.", "The report will be sent to the moderators of your instance. You can explain why you report this content below.": "A jelentés el lesz küldve a példánya moderátorainak. Elmagyarázhatja alább, hogy miért jelenti ezt a tartalmat.",
"The selected picture is too heavy. You need to select a file smaller than {size}.": "A kiválasztott kép túl nagy. Egy {size} méretűnél kisebb fájlt kell kiválasztania.", "The selected picture is too heavy. You need to select a file smaller than {size}.": "A kiválasztott kép túl nagy. Egy {size} méretűnél kisebb fájlt kell kiválasztania.",
"The technical details of the error can help developers solve the problem more easily. Please add them to your feedback.": "A hiba műszaki részletei segítenek a fejlesztőknek, hogy könnyebben megoldják a problémát. Adja hozzá a visszajelzéséhez.", "The technical details of the error can help developers solve the problem more easily. Please add them to your feedback.": "A hiba műszaki részletei segítenek a fejlesztőknek, hogy könnyebben megoldják a problémát. Adja hozzá a visszajelzéséhez.",
@ -1084,17 +1084,17 @@
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ezek a hírforrások eseményadatokat tartalmaznak azon eseményekhez, amelyeknél a profiljai bármelyike résztvevő vagy létrehozó. Ezeket bizalmasan kell tartania. Adott profilokhoz találhat hírforrásokat az egyes profilszerkesztő oldalakon.", "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ezek a hírforrások eseményadatokat tartalmaznak azon eseményekhez, amelyeknél a profiljai bármelyike résztvevő vagy létrehozó. Ezeket bizalmasan kell tartania. Adott profilokhoz találhat hírforrásokat az egyes profilszerkesztő oldalakon.",
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "Ezek a hírforrások eseményadatokat tartalmaznak azon eseményekhez, amelyeknél ez a bizonyos profil résztvevő vagy létrehozó. Ezeket bizalmasan kell tartania. Az összes profiljához találhat hírforrásokat az értesítési beállításaiban.", "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "Ezek a hírforrások eseményadatokat tartalmaznak azon eseményekhez, amelyeknél ez a bizonyos profil résztvevő vagy létrehozó. Ezeket bizalmasan kell tartania. Az összes profiljához találhat hírforrásokat az értesítési beállításaiban.",
"This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "Ez a Mobilizon példány és ez az eseményszervező megengedi a névtelen részvételeket, de ellenőrzés szükséges e-mailen keresztüli megerősítéssel.", "This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "Ez a Mobilizon példány és ez az eseményszervező megengedi a névtelen részvételeket, de ellenőrzés szükséges e-mailen keresztüli megerősítéssel.",
"This URL doesn't seem to be valid": "Ez az URL nem tűnik érvényesnek", "This URL doesn't seem to be valid": "Ez a webcím nem tűnik érvényesnek",
"This URL is not supported": "Ez az URL nem támogatott", "This URL is not supported": "Ez a webcím nem támogatott",
"This application will be able to access all of your informations and post content. Make sure you only approve applications you trust.": "Ez az alkalmazás hozzá fog férni az összes információjához és a bejegyzései tartalmához. Győződjön meg arról, hogy csak azokat az alkalmazásokat engedélyezi, melyekben megbízik.", "This application will be able to access all of your informations and post content. Make sure you only approve applications you trust.": "Az alkalmazás eléri az összes információját és bejegyzését. Csak azokat az alkalmazásokat engedélyezze, melyekben megbízik.",
"This application will be allowed to delete events": "Az alkalmazás törölheti az eseményeket", "This application will be allowed to delete events": "Az alkalmazás jogosult lesz az események törlésére",
"This application will be allowed to delete group posts": "Az alkalmazás törölheti a bejegyzéseket a csoportokból", "This application will be allowed to delete group posts": "Az alkalmazás jogosult lesz a csoportbejegyzések törlésére",
"This application will be allowed to publish events": "Az alkalmazás közzétehet eseményeket", "This application will be allowed to publish events": "Az alkalmazás jogosult lesz az események közzétételére",
"This application will be allowed to publish group posts": "Az alkalmazás közzétehet bejegyzéseket a csoportokban", "This application will be allowed to publish group posts": "Az alkalmazás jogosult lesz csoportbejegyzések közzétételére",
"This application will be allowed to remove uploaded media": "Az alkalmazás törölheti a feltöltött médiafájlokat", "This application will be allowed to remove uploaded media": "Az alkalmazás jogosult lesz a feltöltött médiatartalmak törlésére",
"This application will be allowed to update events": "Az alkalmazás frissítheti az eseményeket", "This application will be allowed to update events": "Az alkalmazás jogosult lesz az események frissítésére",
"This application will be allowed to update group posts": "Az alkalmazás frissítheti a bejegyzéseket a csoportokban", "This application will be allowed to update group posts": "Az alkalmazás jogosult lesz a csoportbejegyzések frissítésére",
"This application will be allowed to upload media": "Az alkalmazás tölthet fel médiafájlokat", "This application will be allowed to upload media": "Az alkalmazás jogosult lesz médiatartalmak feltöltésére",
"This event has been cancelled.": "Ezt az eseményt törölték.", "This event has been cancelled.": "Ezt az eseményt törölték.",
"This event is accessible only through it's link. Be careful where you post this link.": "Ez az esemény csak a hivatkozásán keresztül érhető el. Legyen óvatos, hogy hova küldi be ezt a hivatkozást.", "This event is accessible only through it's link. Be careful where you post this link.": "Ez az esemény csak a hivatkozásán keresztül érhető el. Legyen óvatos, hogy hova küldi be ezt a hivatkozást.",
"This group doesn't have a description yet.": "Ennek a csoportnak még nincs leírása.", "This group doesn't have a description yet.": "Ennek a csoportnak még nincs leírása.",
@ -1123,7 +1123,7 @@
"This user was not found": "Ez a felhasználó nem található", "This user was not found": "Ez a felhasználó nem található",
"This website isn't moderated and the data that you enter will be automatically destroyed every day at 00:01 (Paris timezone).": "Ez a weboldal nincs moderálva, és a beírt adatok automatikusan meg lesznek semmisítve minden nap 00:01-kor (Párizs időzóna).", "This website isn't moderated and the data that you enter will be automatically destroyed every day at 00:01 (Paris timezone).": "Ez a weboldal nincs moderálva, és a beírt adatok automatikusan meg lesznek semmisítve minden nap 00:01-kor (Párizs időzóna).",
"This week": "Ez a hét", "This week": "Ez a hét",
"This weekend": "Ezen a hétvégén", "This weekend": "Ez a hétvége",
"This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "Ez törölni vagy névteleníteni fogja az ezzel a személyazonossággal létrehozott összes tartalmat (eseményeket, hozzászólásokat, üzeneteket, részvételeket…).", "This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "Ez törölni vagy névteleníteni fogja az ezzel a személyazonossággal létrehozott összes tartalmat (eseményeket, hozzászólásokat, üzeneteket, részvételeket…).",
"Time in your timezone ({timezone})": "Az idő az Ön időzónájában ({timezone})", "Time in your timezone ({timezone})": "Az idő az Ön időzónájában ({timezone})",
"Times in your timezone ({timezone})": "Az idők az Ön időzónájában ({timezone})", "Times in your timezone ({timezone})": "Az idők az Ön időzónájában ({timezone})",
@ -1152,8 +1152,8 @@
"Twitter account": "Twitter-fiók", "Twitter account": "Twitter-fiók",
"Type": "Típus", "Type": "Típus",
"Type or select a date…": "Gépeljen be vagy válasszon egy dátumot…", "Type or select a date…": "Gépeljen be vagy válasszon egy dátumot…",
"URL": "URL", "URL": "Webcím",
"URL copied to clipboard": "URL másolva a vágólapra", "URL copied to clipboard": "Webcím a vágólapra másolva",
"Unable to copy to clipboard": "Nem lehet a vágólapra másolni", "Unable to copy to clipboard": "Nem lehet a vágólapra másolni",
"Unable to create the group. One of the pictures may be too heavy.": "Nem lehet létrehozni a csoportot. A képek egyike esetleg túl nagy.", "Unable to create the group. One of the pictures may be too heavy.": "Nem lehet létrehozni a csoportot. A képek egyike esetleg túl nagy.",
"Unable to create the profile. The avatar picture may be too heavy.": "Nem lehet létrehozni a profilt. A profilkép esetleg túl nagy.", "Unable to create the profile. The avatar picture may be too heavy.": "Nem lehet létrehozni a profilt. A profilkép esetleg túl nagy.",
@ -1184,7 +1184,7 @@
"Update events": "Események frissítése", "Update events": "Események frissítése",
"Update group": "Csoport frissítése", "Update group": "Csoport frissítése",
"Update group discussions": "Csoport témáinak frissítése", "Update group discussions": "Csoport témáinak frissítése",
"Update group posts": "Csoport bejegyzéseinek frissítése", "Update group posts": "Csoportbejegyzések frissítése",
"Update group resources": "Csoport erőforrásainak frissítése", "Update group resources": "Csoport erőforrásainak frissítése",
"Update my event": "Saját esemény frissítése", "Update my event": "Saját esemény frissítése",
"Update post": "Bejegyzés frissítése", "Update post": "Bejegyzés frissítése",
@ -1236,7 +1236,7 @@
"We'll send you an email one hour before the event begins, to be sure you won't forget about it.": "Egy e-mailt fogunk küldeni Önnek az esemény kezdete előtt egy órával, hogy biztosan ne felejtse el azt.", "We'll send you an email one hour before the event begins, to be sure you won't forget about it.": "Egy e-mailt fogunk küldeni Önnek az esemény kezdete előtt egy órával, hogy biztosan ne felejtse el azt.",
"We'll use your timezone settings to send a recap of the morning of the event.": "Az Ön időzóna-beállításait fogjuk használni az esemény reggeli rövid összegzésének küldéséhez.", "We'll use your timezone settings to send a recap of the morning of the event.": "Az Ön időzóna-beállításait fogjuk használni az esemény reggeli rövid összegzésének küldéséhez.",
"Website": "Weboldal", "Website": "Weboldal",
"Website / URL": "Weboldal vagy URL", "Website / URL": "Weboldal vagy webcím",
"Weekly email summary": "Heti e-mailes összegzés", "Weekly email summary": "Heti e-mailes összegzés",
"Welcome back {username}!": "Üdvözöljük, {username}!", "Welcome back {username}!": "Üdvözöljük, {username}!",
"Welcome back!": "Üdvözöljük!", "Welcome back!": "Üdvözöljük!",
@ -1346,10 +1346,10 @@
"You will find here all the events you have created or of which you are a participant, as well as events organized by groups you follow or are a member of.": "Itt megtalálja az összes eseményt, amelyeket létrehozott vagy amelyeknél Ön résztvevő, illetve az olyan csoportok által szervezett eseményeket, amelyeket Ön követ vagy amelyeknek tagja.", "You will find here all the events you have created or of which you are a participant, as well as events organized by groups you follow or are a member of.": "Itt megtalálja az összes eseményt, amelyeket létrehozott vagy amelyeknél Ön résztvevő, illetve az olyan csoportok által szervezett eseményeket, amelyeket Ön követ vagy amelyeknek tagja.",
"You will receive notifications about this group's public activity depending on %{notification_settings}.": "Értesítéseket fog kapni ennek a csoportnak a nyilvános tevékenységéről az %{notification_settings} függően.", "You will receive notifications about this group's public activity depending on %{notification_settings}.": "Értesítéseket fog kapni ennek a csoportnak a nyilvános tevékenységéről az %{notification_settings} függően.",
"You wish to participate to the following event": "Részt kíván venni a következő eseményen", "You wish to participate to the following event": "Részt kíván venni a következő eseményen",
"You'll be able to revoke access for this application in your account settings.": "Az alkalmazás hozzáférését a fiókbeállításokban vonhatja vissza.", "You'll be able to revoke access for this application in your account settings.": "Az alkalmazás hozzáférése a fiókbeállításokban vonható vissza.",
"You'll get a weekly recap every Monday for upcoming events, if you have any.": "Heti rövid összegzést fog kapni minden hétfőn a közelgő eseményekről, ha van ilyen.", "You'll get a weekly recap every Monday for upcoming events, if you have any.": "Heti rövid összegzést fog kapni minden hétfőn a közelgő eseményekről, ha van ilyen.",
"You'll need to change the URLs where there were previously entered.": "Meg kell majd változtatnia az URL-eket, ahol korábban meg lettek adva.", "You'll need to change the URLs where there were previously entered.": "Meg kell majd változtatnia az webcímeket, ahol korábban meg lettek adva.",
"You'll need to transmit the group URL so people may access the group's profile. The group won't be findable in Mobilizon's search or regular search engines.": "Át kell küldenie a csoport URL-jét, hogy az emberek hozzáférhessenek a csoport profiljához. A csoport nem lesz megtalálható a Mobilizon keresőjében vagy a szokásos keresőmotorokban.", "You'll need to transmit the group URL so people may access the group's profile. The group won't be findable in Mobilizon's search or regular search engines.": "Át kell küldenie a csoport webcímét, hogy az emberek hozzáférhessenek a csoport profiljához. A csoport nem lesz megtalálható a Mobilizon keresőjében vagy a szokásos keresőmotorokban.",
"You'll receive a confirmation email.": "Kapni fog egy megerősítő e-mailt.", "You'll receive a confirmation email.": "Kapni fog egy megerősítő e-mailt.",
"YouTube live": "YouTube élő", "YouTube live": "YouTube élő",
"YouTube replay": "YouTube ismétlés", "YouTube replay": "YouTube ismétlés",

View file

@ -27,6 +27,16 @@ export const orugaConfig = {
taginput: { taginput: {
itemClass: "taginput-item", itemClass: "taginput-item",
rootClass: "taginput", rootClass: "taginput",
containerClass: "taginput-container",
expandedClass: "taginput-expanded",
autocompleteClasses: {
rootClass: "taginput-autocomplete",
itemClass: "taginput-autocomplete-item",
inputClasses: {
rootClass: "taginput-input-wrapper",
inputClass: "taginput-input",
},
},
}, },
autocomplete: { autocomplete: {
rootClass: "autocomplete", rootClass: "autocomplete",
@ -57,6 +67,7 @@ export const orugaConfig = {
datepicker: { datepicker: {
iconNext: "ChevronRight", iconNext: "ChevronRight",
iconPrev: "ChevronLeft", iconPrev: "ChevronLeft",
boxClass: "datepicker-box",
}, },
modal: { modal: {
rootClass: "modal", rootClass: "modal",

View file

@ -155,6 +155,7 @@ export function iconForAddress(address: IAddress): IPOIIcon {
} }
export function addressFullName(address: IAddress): string { export function addressFullName(address: IAddress): string {
if (!address) return "";
const { name, alternativeName } = addressToPoiInfos(address); const { name, alternativeName } = addressToPoiInfos(address);
if (name && alternativeName) { if (name && alternativeName) {
return `${name}, ${alternativeName}`; return `${name}, ${alternativeName}`;

View file

@ -8,6 +8,7 @@ export interface IConversation {
id?: string; id?: string;
actor?: IActor; actor?: IActor;
lastComment?: IComment; lastComment?: IComment;
originComment?: IComment;
comments: Paginate<IComment>; comments: Paginate<IComment>;
participants: IActor[]; participants: IActor[];
updatedAt: string; updatedAt: string;

View file

@ -25,7 +25,7 @@
aria-required="true" aria-required="true"
required required
v-model="identity.name" v-model="identity.name"
@input="(event: any) => updateUsername(event.target.value)" @update:modelValue="(value: string) => updateUsername(value)"
id="identity-display-name" id="identity-display-name"
dir="auto" dir="auto"
expanded expanded
@ -740,6 +740,7 @@ const breadcrumbsLinks = computed(
); );
const updateUsername = (value: string) => { const updateUsername = (value: string) => {
if (props.isUpdate) return;
identity.value.preferredUsername = convertToUsername(value); identity.value.preferredUsername = convertToUsername(value);
}; };

View file

@ -233,7 +233,7 @@ import {
useRouteQuery, useRouteQuery,
} from "vue-use-route-query"; } from "vue-use-route-query";
import { useMutation, useQuery } from "@vue/apollo-composable"; import { useMutation, useQuery } from "@vue/apollo-composable";
import { computed, inject, ref } from "vue"; import { computed, inject, ref, watch } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useHead } from "@unhead/vue"; import { useHead } from "@unhead/vue";
import CloudQuestion from "../../../node_modules/vue-material-design-icons/CloudQuestion.vue"; import CloudQuestion from "../../../node_modules/vue-material-design-icons/CloudQuestion.vue";
@ -263,8 +263,26 @@ const { result: instancesResult } = useQuery<{
{ debounce: 500 } { debounce: 500 }
); );
watch([filterDomain, followStatus], () => {
instancePage.value = 1;
});
const instances = computed(() => instancesResult.value?.instances); const instances = computed(() => instancesResult.value?.instances);
const instancesTotal = computed(() => instancesResult.value?.instances.total);
const currentPageInstancesNumber = computed(
() => instancesResult.value?.instances.elements.length
);
// If we didn't found any instances on this page
watch(instancesTotal, (newInstancesTotal) => {
if (newInstancesTotal === 0) {
instancePage.value = 1;
} else if (currentPageInstancesNumber.value === 0) {
instancePage.value = instancePage.value - 1;
}
});
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
useHead({ useHead({
title: computed(() => t("Federation")), title: computed(() => t("Federation")),

View file

@ -14,7 +14,11 @@
]" ]"
/> />
<div <div
v-if="conversation.event && !isCurrentActorAuthor" v-if="
conversation.event &&
!isCurrentActorAuthor &&
isOriginCommentAuthorEventOrganizer
"
class="bg-mbz-yellow p-6 mb-3 rounded flex gap-2 items-center" class="bg-mbz-yellow p-6 mb-3 rounded flex gap-2 items-center"
> >
<Calendar :size="36" /> <Calendar :size="36" />
@ -132,7 +136,11 @@
> >
</form> </form>
<div <div
v-else-if="conversation.event" v-else-if="
conversation.event &&
!isCurrentActorAuthor &&
isOriginCommentAuthorEventOrganizer
"
class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-3" class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-3"
> >
<Calendar :size="36" /> <Calendar :size="36" />
@ -292,6 +300,16 @@ const isCurrentActorAuthor = computed(
currentActor.value.id !== conversation.value?.actor?.id currentActor.value.id !== conversation.value?.actor?.id
); );
const isOriginCommentAuthorEventOrganizer = computed(
() =>
conversation.value?.originComment?.actor &&
conversation.value?.event &&
[
conversation.value?.event?.organizerActor?.id,
conversation.value?.event?.attributedTo?.id,
].includes(conversation.value?.originComment?.actor.id)
);
useHead({ useHead({
title: () => title.value, title: () => title.value,
}); });

View file

@ -924,9 +924,28 @@ const handleError = (err: any) => {
console.error(err); console.error(err);
if (err.graphQLErrors !== undefined) { if (err.graphQLErrors !== undefined) {
err.graphQLErrors.forEach(({ message }: { message: string }) => { err.graphQLErrors.forEach(
({
message,
field,
}: {
message: string | { slug?: string[] }[];
field: string;
}) => {
if (
field === "tags" &&
Array.isArray(message) &&
message.some((msg) => msg.slug)
) {
const finalMsg = message.find((msg) => msg.slug?.[0]);
notifier?.error(
t("Error while adding tag: {error}", { error: finalMsg?.slug?.[0] })
);
} else if (typeof message === "string") {
notifier?.error(message); notifier?.error(message);
}); }
}
);
} }
}; };

View file

@ -349,8 +349,7 @@ const { result: loggedUserResult } = useQuery<{ loggedUser: IUser }>(
USER_NOTIFICATIONS USER_NOTIFICATIONS
); );
const loggedUser = computed(() => loggedUserResult.value?.loggedUser); const loggedUser = computed(() => loggedUserResult.value?.loggedUser);
const feedTokens = computed( const feedTokens = computed(() =>
() =>
loggedUser.value?.feedTokens.filter( loggedUser.value?.feedTokens.filter(
(token: IFeedToken) => token.actor === null (token: IFeedToken) => token.actor === null
) )

View file

@ -94,7 +94,6 @@
<o-field grouped> <o-field grouped>
<o-field :label="t('City or region')" expanded label-for="setting-city"> <o-field :label="t('City or region')" expanded label-for="setting-city">
<full-address-auto-complete <full-address-auto-complete
v-if="loggedUser?.settings"
:resultType="AddressSearchType.ADMINISTRATIVE" :resultType="AddressSearchType.ADMINISTRATIVE"
v-model="address" v-model="address"
:default-text="address?.description" :default-text="address?.description"

View file

@ -6,6 +6,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CreateTest do
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Conversation alias Mobilizon.Conversations.Conversation
alias Mobilizon.Discussions.Comment alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityPub.Transmogrifier alias Mobilizon.Federation.ActivityPub.Transmogrifier
alias Mobilizon.Service.HTTP.ActivityPub.Mock alias Mobilizon.Service.HTTP.ActivityPub.Mock
@ -103,5 +104,57 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CreateTest do
{:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin") {:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin")
assert participant_ids == MapSet.new([actor.id, admin.id]) assert participant_ids == MapSet.new([actor.id, admin.id])
end end
test "it creates conversations for received comments if we're concerned even with reply to an event" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, 1, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/admin")
|> Map.put("preferredUsername", "admin")
}}
end)
actor = insert(:actor)
data = File.read!("test/fixtures/mastodon-post-activity-private.json") |> Jason.decode!()
data = Map.put(data, "to", [actor.url])
%Event{id: event_id, organizer_actor_id: organizer_actor_id} = event = insert(:event)
%Comment{url: reply_to_url, id: first_reply_comment_id} =
insert(:comment, visibility: :private, event: event)
object =
data["object"]
|> Map.put("to", [actor.url])
|> Map.put("inReplyTo", reply_to_url)
|> Map.put("tag", [
data["object"]["tag"]
|> hd()
|> Map.put("href", actor.url)
|> Map.put("name", Actor.preferred_username_and_domain(actor))
])
data = Map.put(data, "object", object)
{:ok, _activity,
%Conversation{
origin_comment: %Comment{visibility: :private, id: origin_comment_id},
last_comment: %Comment{visibility: :private, id: _last_comment_id},
participants: participants,
event: %Event{id: ^event_id}
}} = Transmogrifier.handle_incoming(data)
assert origin_comment_id == first_reply_comment_id
participant_ids = participants |> Enum.map(& &1.id) |> MapSet.new()
{:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin")
assert participant_ids == MapSet.new([actor.id, organizer_actor_id, admin.id])
end
end end
end end

View file

@ -0,0 +1,76 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.UtilsTest do
@moduledoc """
Module to test converting from EventMetadata to AS
"""
use Mobilizon.DataCase
import Mobilizon.Factory
alias Mobilizon.Federation.ActivityStream.Converter.Utils
describe "get_medias/1" do
test "getting banner from Document attachment" do
data =
File.read!("test/fixtures/mobilizon-post-activity-media.json")
|> Jason.decode!()
|> Map.get("object")
assert Utils.get_medias(data) ==
{%{
"blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{",
"mediaType" => "image/png",
"name" => nil,
"type" => "Document",
"url" => "https://mobilizon.fr/some-image"
}, []}
end
test "getting banner from image property" do
data =
File.read!("test/fixtures/mobilizon-post-activity-media-1.json")
|> Jason.decode!()
|> Map.get("object")
assert Utils.get_medias(data) ==
{%{
"blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{",
"mediaType" => "image/png",
"name" => nil,
"type" => "Image",
"url" => "https://mobilizon.fr/some-image-1"
},
[
%{
"blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{",
"mediaType" => "image/png",
"name" => nil,
"type" => "Document",
"url" => "https://mobilizon.fr/some-image"
}
]}
end
test "getting banner from attachment named \"Banner\"" do
data =
File.read!("test/fixtures/mobilizon-post-activity-media-2.json")
|> Jason.decode!()
|> Map.get("object")
assert Utils.get_medias(data) ==
{%{
"blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{",
"mediaType" => "image/png",
"name" => "Banner",
"type" => "Document",
"url" => "https://mobilizon.fr/some-image-2"
},
[
%{
"blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{",
"mediaType" => "image/png",
"name" => nil,
"type" => "Document",
"url" => "https://mobilizon.fr/some-image-1"
}
]}
end
end
end

View file

@ -0,0 +1,106 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://litepub.social/litepub/context.jsonld",
{
"Hashtag": "as:Hashtag",
"category": "sc:category",
"ical": "http://www.w3.org/2002/12/cal/ical#",
"joinMode": {
"@id": "mz:joinMode",
"@type": "mz:joinModeType"
},
"joinModeType": {
"@id": "mz:joinModeType",
"@type": "rdfs:Class"
},
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
"mz": "https://joinmobilizon.org/ns#",
"repliesModerationOption": {
"@id": "mz:repliesModerationOption",
"@type": "mz:repliesModerationOptionType"
},
"repliesModerationOptionType": {
"@id": "mz:repliesModerationOptionType",
"@type": "rdfs:Class"
},
"sc": "http://schema.org#",
"uuid": "sc:identifier"
}
],
"actor": "https://mobilizon.fr/@metacartes",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity",
"object": {
"attachment": [
{
"href": "https://something.org",
"mediaType": "text/html",
"name": "Another link",
"type": "Link"
},
{
"href": "https://google.com",
"mediaType": "text/html",
"name": "Website",
"type": "Link"
},
{
"type": "Document",
"mediaType": "image/png",
"url": "https://mobilizon.fr/some-image",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
}
],
"attributedTo": "https://mobilizon.fr/@metacartes",
"startTime": "2018-02-12T14:08:20Z",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"content": "<p><span class=\"h-card\"><a href=\"https://framapiaf.org/users/tcit\" class=\"u-url mention\">@<span>tcit</span></a></span></p>",
"category": "TODO remove me",
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"image": {
"type": "Image",
"mediaType": "image/png",
"url": "https://mobilizon.fr/some-image-1",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
},
"inReplyTo": null,
"location": {
"type": "Place",
"name": "Locaux de Framasoft",
"id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd",
"address": {
"type": "PostalAddress",
"streetAddress": "10 Rue Jangot",
"postalCode": "69007",
"addressLocality": "Lyon",
"addressRegion": "Auvergne Rhône Alpes",
"addressCountry": "France"
}
},
"name": "My first event",
"published": "2018-02-12T14:08:20Z",
"tag": [
{
"href": "https://framapiaf.org/users/tcit",
"name": "@tcit@framapiaf.org",
"type": "Mention"
}
],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Event",
"url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c"
},
"published": "2018-02-12T14:08:20Z",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Create"
}

View file

@ -0,0 +1,106 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://litepub.social/litepub/context.jsonld",
{
"Hashtag": "as:Hashtag",
"category": "sc:category",
"ical": "http://www.w3.org/2002/12/cal/ical#",
"joinMode": {
"@id": "mz:joinMode",
"@type": "mz:joinModeType"
},
"joinModeType": {
"@id": "mz:joinModeType",
"@type": "rdfs:Class"
},
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
"mz": "https://joinmobilizon.org/ns#",
"repliesModerationOption": {
"@id": "mz:repliesModerationOption",
"@type": "mz:repliesModerationOptionType"
},
"repliesModerationOptionType": {
"@id": "mz:repliesModerationOptionType",
"@type": "rdfs:Class"
},
"sc": "http://schema.org#",
"uuid": "sc:identifier"
}
],
"actor": "https://mobilizon.fr/@metacartes",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity",
"object": {
"attachment": [
{
"href": "https://something.org",
"mediaType": "text/html",
"name": "Another link",
"type": "Link"
},
{
"href": "https://google.com",
"mediaType": "text/html",
"name": "Website",
"type": "Link"
},
{
"type": "Document",
"mediaType": "image/png",
"url": "https://mobilizon.fr/some-image-1",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
},
{
"type": "Document",
"mediaType": "image/png",
"url": "https://mobilizon.fr/some-image-2",
"name": "Banner",
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
}
],
"attributedTo": "https://mobilizon.fr/@metacartes",
"startTime": "2018-02-12T14:08:20Z",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"content": "<p><span class=\"h-card\"><a href=\"https://framapiaf.org/users/tcit\" class=\"u-url mention\">@<span>tcit</span></a></span></p>",
"category": "TODO remove me",
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"inReplyTo": null,
"location": {
"type": "Place",
"name": "Locaux de Framasoft",
"id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd",
"address": {
"type": "PostalAddress",
"streetAddress": "10 Rue Jangot",
"postalCode": "69007",
"addressLocality": "Lyon",
"addressRegion": "Auvergne Rhône Alpes",
"addressCountry": "France"
}
},
"name": "My first event",
"published": "2018-02-12T14:08:20Z",
"tag": [
{
"href": "https://framapiaf.org/users/tcit",
"name": "@tcit@framapiaf.org",
"type": "Mention"
}
],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Event",
"url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c"
},
"published": "2018-02-12T14:08:20Z",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Create"
}

View file

@ -0,0 +1,99 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://litepub.social/litepub/context.jsonld",
{
"Hashtag": "as:Hashtag",
"category": "sc:category",
"ical": "http://www.w3.org/2002/12/cal/ical#",
"joinMode": {
"@id": "mz:joinMode",
"@type": "mz:joinModeType"
},
"joinModeType": {
"@id": "mz:joinModeType",
"@type": "rdfs:Class"
},
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
"mz": "https://joinmobilizon.org/ns#",
"repliesModerationOption": {
"@id": "mz:repliesModerationOption",
"@type": "mz:repliesModerationOptionType"
},
"repliesModerationOptionType": {
"@id": "mz:repliesModerationOptionType",
"@type": "rdfs:Class"
},
"sc": "http://schema.org#",
"uuid": "sc:identifier"
}
],
"actor": "https://mobilizon.fr/@metacartes",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity",
"object": {
"attachment": [
{
"href": "https://something.org",
"mediaType": "text/html",
"name": "Another link",
"type": "Link"
},
{
"href": "https://google.com",
"mediaType": "text/html",
"name": "Website",
"type": "Link"
},
{
"type": "Document",
"mediaType": "image/png",
"url": "https://mobilizon.fr/some-image",
"name": null,
"blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{"
}
],
"attributedTo": "https://mobilizon.fr/@metacartes",
"startTime": "2018-02-12T14:08:20Z",
"cc": [
"https://framapiaf.org/users/admin/followers",
"https://framapiaf.org/users/tcit"
],
"content": "<p><span class=\"h-card\"><a href=\"https://framapiaf.org/users/tcit\" class=\"u-url mention\">@<span>tcit</span></a></span></p>",
"category": "TODO remove me",
"id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"inReplyTo": null,
"location": {
"type": "Place",
"name": "Locaux de Framasoft",
"id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd",
"address": {
"type": "PostalAddress",
"streetAddress": "10 Rue Jangot",
"postalCode": "69007",
"addressLocality": "Lyon",
"addressRegion": "Auvergne Rhône Alpes",
"addressCountry": "France"
}
},
"name": "My first event",
"published": "2018-02-12T14:08:20Z",
"tag": [
{
"href": "https://framapiaf.org/users/tcit",
"name": "@tcit@framapiaf.org",
"type": "Mention"
}
],
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Event",
"url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93",
"uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c"
},
"published": "2018-02-12T14:08:20Z",
"to": ["https://www.w3.org/ns/activitystreams#Public"],
"type": "Create"
}

View file

@ -44,7 +44,8 @@ defmodule Mobilizon.GraphQL.Resolvers.ConversationTest do
describe "Find conversations for event" do describe "Find conversations for event" do
test "for a given event", %{conn: conn, user: user, actor: actor} do test "for a given event", %{conn: conn, user: user, actor: actor} do
event = insert(:event, organizer_actor: actor) event = insert(:event, organizer_actor: actor)
conversation = insert(:conversation, event: event) origin_comment = insert(:comment, actor: actor)
conversation = insert(:conversation, event: event, origin_comment: origin_comment)
another_comment = insert(:comment, origin_comment: conversation.origin_comment) another_comment = insert(:comment, origin_comment: conversation.origin_comment)
Discussions.update_comment(conversation.origin_comment, %{conversation_id: conversation.id}) Discussions.update_comment(conversation.origin_comment, %{conversation_id: conversation.id})

View file

@ -1,11 +1,13 @@
defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do
use Mobilizon.Web.ConnCase use Mobilizon.Web.ConnCase
use Mobilizon.Tests.Helpers use Mobilizon.Tests.Helpers
use Oban.Testing, repo: Mobilizon.Storage.Repo
alias Mobilizon.Config alias Mobilizon.Actors.Actor
alias Mobilizon.Events alias Mobilizon.{Actors, Config, Conversations, Events}
alias Mobilizon.Events.{Event, EventParticipantStats, Participant} alias Mobilizon.Events.{Event, EventParticipantStats, Participant}
alias Mobilizon.GraphQL.AbsintheHelpers alias Mobilizon.GraphQL.AbsintheHelpers
alias Mobilizon.Service.Workers.LegacyNotifierBuilder
alias Mobilizon.Storage.Page alias Mobilizon.Storage.Page
import Mobilizon.Factory import Mobilizon.Factory
@ -1381,4 +1383,393 @@ defmodule Mobilizon.GraphQL.Resolvers.ParticipantTest do
assert_email_sent(to: @email) assert_email_sent(to: @email)
end end
end end
describe "Send private messages to participants" do
@send_event_private_message_mutation """
mutation SendEventPrivateMessageMutation(
$text: String!
$actorId: ID!
$eventId: ID!
$roles: [ParticipantRoleEnum]
$language: String
) {
sendEventPrivateMessage(
text: $text
actorId: $actorId
eventId: $eventId
roles: $roles
language: $language
) {
id
conversationParticipantId
actor {
id
}
lastComment {
id
text
}
originComment {
id
text
}
participants {
id
}
event {
id
uuid
title
organizerActor {
id
}
attributedTo {
id
}
}
unread
insertedAt
updatedAt
}
}
"""
setup %{conn: conn} do
user = insert(:user)
actor = insert(:actor, user: user, preferred_username: "test")
{:ok, conn: conn, actor: actor, user: user}
end
test "Without being logged-in", %{conn: conn} do
%Actor{id: actor_id} = insert(:actor)
%Event{id: event_id} = insert(:event)
res =
conn
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor_id, eventId: event_id, text: "Hello dear participants"}
)
assert hd(res["errors"])["message"] == "You need to be logged in"
end
test "With actor not allowed", %{conn: conn, actor: actor, user: user} do
%Event{id: event_id} = insert(:event)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor.id, eventId: event_id, text: "Hello dear participants"}
)
assert hd(res["errors"])["message"] == "You don't have permission to do this"
end
test "With actor as event organizer", %{conn: conn, actor: actor, user: user} do
%Event{id: event_id, title: event_title, uuid: event_uuid} =
event = insert(:event, organizer_actor: actor)
%Participant{actor_id: participant_actor_id} = insert(:participant, event: event)
%Participant{actor_id: participant_actor_id_2} = insert(:participant, event: event)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor.id, eventId: event_id, text: "Hello dear participants"}
)
assert res["errors"] == nil
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["id"] ==
res["data"]["sendEventPrivateMessage"]["originComment"]["id"]
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["text"] ==
"Hello dear participants"
participants_ids =
Enum.map(res["data"]["sendEventPrivateMessage"]["participants"], fn participant ->
String.to_integer(participant["id"])
end)
assert length(participants_ids) == 3
assert MapSet.new(participants_ids) ==
MapSet.new([actor.id, participant_actor_id, participant_actor_id_2])
assert res["data"]["sendEventPrivateMessage"]["actor"]["id"] == to_string(actor.id)
conversation_id = res["data"]["sendEventPrivateMessage"]["id"]
all_conversation_participants_ids = Conversations.find_all_conversations_for_event(event_id)
notified_conversation_participant_ids =
all_conversation_participants_ids
|> Enum.filter(&(&1.actor_id != actor.id))
|> Enum.map(&[&1.id, &1.actor_id])
Enum.each(notified_conversation_participant_ids, fn [pa_id, participant_actor_id] ->
assert_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => actor.id,
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => String.to_integer(conversation_id),
"conversation_participant_id" => pa_id,
"conversation_text" => "Hello dear participants",
"conversation_event_id" => event_id,
"conversation_event_title" => event_title,
"conversation_event_uuid" => event_uuid
},
"type" => "conversation",
"participant" => %{
"actor_id" => participant_actor_id,
"id" => pa_id
}
}
)
end)
ignored_conversation_participant_id =
all_conversation_participants_ids
|> Enum.filter(&(&1.actor_id == actor.id))
|> Enum.map(& &1.id)
refute_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => actor.id,
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => String.to_integer(conversation_id),
"conversation_participant_id" => ignored_conversation_participant_id,
"conversation_text" => "Hello dear participants",
"conversation_event_id" => event_id,
"conversation_event_title" => event_title,
"conversation_event_uuid" => event_uuid
},
"type" => "conversation",
"participant" => %{
"actor_id" => actor.id,
"id" => ignored_conversation_participant_id
}
}
)
end
test "With actor as event organizer with customized roles", %{
conn: conn,
actor: actor,
user: user
} do
%Event{id: event_id, title: event_title, uuid: event_uuid} =
event = insert(:event, organizer_actor: actor)
%Participant{actor_id: _participant_actor_id} = insert(:participant, event: event)
%Participant{actor_id: participant_actor_id_2} =
insert(:participant, event: event, role: :not_approved)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{
actorId: actor.id,
eventId: event_id,
text: "Hello dear participants",
roles: ["NOT_APPROVED"]
}
)
assert res["errors"] == nil
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["id"] ==
res["data"]["sendEventPrivateMessage"]["originComment"]["id"]
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["text"] ==
"Hello dear participants"
participants_ids =
Enum.map(res["data"]["sendEventPrivateMessage"]["participants"], fn participant ->
String.to_integer(participant["id"])
end)
assert length(participants_ids) == 2
assert MapSet.new(participants_ids) ==
MapSet.new([actor.id, participant_actor_id_2])
conversation_id = res["data"]["sendEventPrivateMessage"]["id"]
all_conversation_participants_ids = Conversations.find_all_conversations_for_event(event_id)
notified_conversation_participant_ids =
all_conversation_participants_ids
|> Enum.filter(&(&1.actor_id == participant_actor_id_2))
|> Enum.map(&[&1.id, &1.actor_id])
Enum.each(notified_conversation_participant_ids, fn [pa_id, participant_actor_id] ->
assert_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => actor.id,
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => String.to_integer(conversation_id),
"conversation_participant_id" => pa_id,
"conversation_text" => "Hello dear participants",
"conversation_event_id" => event_id,
"conversation_event_title" => event_title,
"conversation_event_uuid" => event_uuid
},
"type" => "conversation",
"participant" => %{
"actor_id" => participant_actor_id,
"id" => pa_id
}
}
)
end)
ignored_conversation_participant_id =
all_conversation_participants_ids
|> Enum.filter(&(&1.actor_id != participant_actor_id_2))
|> Enum.map(&[&1.id, &1.actor_id])
Enum.each(ignored_conversation_participant_id, fn [pa_id, participant_actor_id] ->
refute_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => participant_actor_id,
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => String.to_integer(conversation_id),
"conversation_participant_id" => pa_id,
"conversation_text" => "Hello dear participants",
"conversation_event_id" => event_id,
"conversation_event_title" => event_title,
"conversation_event_uuid" => event_uuid
},
"type" => "conversation",
"participant" => %{
"actor_id" => participant_actor_id,
"id" => pa_id
}
}
)
end)
end
test "With actor as member of group event organizer", %{conn: conn, actor: actor, user: user} do
%Actor{id: group_id} = group = insert(:group)
insert(:member, parent: group, actor: actor, role: :moderator)
%Event{id: event_id} = event = insert(:event, organizer_actor: actor, attributed_to: group)
%Participant{actor_id: participant_actor_id} = insert(:participant, event: event)
%Participant{actor_id: participant_actor_id_2} = insert(:participant, event: event)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: group_id, eventId: event_id, text: "Hello dear participants"}
)
assert res["errors"] == nil
participants_ids =
Enum.map(res["data"]["sendEventPrivateMessage"]["participants"], fn participant ->
String.to_integer(participant["id"])
end)
assert MapSet.new(participants_ids) ==
MapSet.new([group_id, participant_actor_id, participant_actor_id_2])
assert res["data"]["sendEventPrivateMessage"]["actor"]["id"] == to_string(group_id)
end
test "With event not found", %{conn: conn, actor: actor, user: user} do
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor.id, eventId: "5019438457", text: "Hello dear participants"}
)
assert hd(res["errors"])["message"] ==
"Event not found"
end
test "With no participants matching the audience", %{conn: conn, actor: actor, user: user} do
%Event{id: event_id} = insert(:event, organizer_actor: actor)
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor.id, eventId: event_id, text: "Hello dear participants"}
)
assert hd(res["errors"])["message"] ==
"There are no participants matching the audience you've selected."
end
end
test "With several anonymous participants", %{conn: conn, actor: actor, user: user} do
%Event{id: event_id} =
event = insert(:event, organizer_actor: actor)
{:ok, anonymous_actor} = Actors.get_or_create_internal_actor("anonymous")
refute is_nil(anonymous_actor)
insert(:participant, event: event, actor: anonymous_actor, metadata: %{email: "anon@mou.se"})
insert(:participant, event: event, actor: anonymous_actor, metadata: %{email: "other@mou.se"})
res =
conn
|> auth_conn(user)
|> AbsintheHelpers.graphql_query(
query: @send_event_private_message_mutation,
variables: %{actorId: actor.id, eventId: event_id, text: "Hello dear participants"}
)
assert res["errors"] == nil
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["id"] ==
res["data"]["sendEventPrivateMessage"]["originComment"]["id"]
assert res["data"]["sendEventPrivateMessage"]["lastComment"]["text"] ==
"Hello dear participants"
participants_ids =
Enum.map(res["data"]["sendEventPrivateMessage"]["participants"], fn participant ->
String.to_integer(participant["id"])
end)
# Anonymous actor is only added once
assert length(participants_ids) == 2
assert Enum.sort(participants_ids) == Enum.sort([actor.id, anonymous_actor.id])
end
end end

View file

@ -7,6 +7,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
alias Mobilizon.Conversations alias Mobilizon.Conversations
alias Mobilizon.Conversations.{Conversation, ConversationParticipant} alias Mobilizon.Conversations.{Conversation, ConversationParticipant}
alias Mobilizon.Discussions.Comment alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Service.Activity.Conversation, as: ConversationActivity alias Mobilizon.Service.Activity.Conversation, as: ConversationActivity
alias Mobilizon.Service.Workers.LegacyNotifierBuilder alias Mobilizon.Service.Workers.LegacyNotifierBuilder
alias Mobilizon.Users.User alias Mobilizon.Users.User
@ -15,16 +16,93 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
use Oban.Testing, repo: Mobilizon.Storage.Repo use Oban.Testing, repo: Mobilizon.Storage.Repo
import Mobilizon.Factory import Mobilizon.Factory
describe "handle conversation" do describe "handle activity from event private announcement conversation" do
test "with participants" do test "when conversation initial comment author is not an organizer" do
%User{} = user = insert(:user) %User{} = user = insert(:user)
%Actor{id: actor_id} = actor = insert(:actor, user: user) %Actor{id: actor_id} = actor = insert(:actor, user: user)
%Conversation{ %Actor{} = organizer_actor = insert(:actor)
id: conversation_id,
last_comment: %Comment{actor_id: last_comment_actor_id} %Event{} = event = insert(:event)
} =
conversation = insert(:conversation, event: nil) %Comment{} = comment = insert(:comment, actor: organizer_actor)
%Conversation{id: conversation_id} =
conversation =
insert(:conversation, event: event, last_comment: comment, origin_comment: comment)
%ConversationParticipant{id: conversation_participant_actor_id} =
insert(:conversation_participant, actor: actor, conversation: conversation)
%ConversationParticipant{
id: conversation_participant_id,
actor: %Actor{id: conversation_other_participant_actor_id}
} = insert(:conversation_participant, conversation: conversation)
conversation = Conversations.get_conversation(conversation_id)
assert {:ok, _} =
ConversationActivity.insert_activity(conversation, subject: "conversation_created")
refute_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => organizer_actor.id,
"participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id},
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_actor_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"type" => "conversation"
}
)
refute_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => organizer_actor.id,
"participant" => %{
"actor_id" => conversation_other_participant_actor_id,
"id" => conversation_participant_id
},
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"type" => "conversation"
}
)
assert [] = all_enqueued()
end
test "an author who is the event organizer" do
%User{} = user = insert(:user)
%Actor{id: actor_id} = actor = insert(:actor, user: user)
%Actor{} = organizer_actor = insert(:actor)
%Event{} = event = insert(:event, organizer_actor: organizer_actor)
%Comment{} = comment = insert(:comment, actor: organizer_actor)
%Conversation{id: conversation_id} =
conversation =
insert(:conversation, event: event, last_comment: comment, origin_comment: comment)
%ConversationParticipant{id: conversation_participant_actor_id} = %ConversationParticipant{id: conversation_participant_actor_id} =
insert(:conversation_participant, actor: actor, conversation: conversation) insert(:conversation_participant, actor: actor, conversation: conversation)
@ -42,7 +120,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
assert_enqueued( assert_enqueued(
worker: LegacyNotifierBuilder, worker: LegacyNotifierBuilder,
args: %{ args: %{
"author_id" => last_comment_actor_id, "author_id" => organizer_actor.id,
"participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id}, "participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id},
"object_id" => to_string(conversation_id), "object_id" => to_string(conversation_id),
"object_type" => "conversation", "object_type" => "conversation",
@ -50,7 +128,10 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
"subject" => "conversation_created", "subject" => "conversation_created",
"subject_params" => %{ "subject_params" => %{
"conversation_id" => conversation_id, "conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_actor_id "conversation_participant_id" => conversation_participant_actor_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
}, },
"type" => "conversation" "type" => "conversation"
} }
@ -59,7 +140,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
assert_enqueued( assert_enqueued(
worker: LegacyNotifierBuilder, worker: LegacyNotifierBuilder,
args: %{ args: %{
"author_id" => last_comment_actor_id, "author_id" => organizer_actor.id,
"participant" => %{ "participant" => %{
"actor_id" => conversation_other_participant_actor_id, "actor_id" => conversation_other_participant_actor_id,
"id" => conversation_participant_id "id" => conversation_participant_id
@ -70,7 +151,81 @@ defmodule Mobilizon.Service.Activity.ConversationTest do
"subject" => "conversation_created", "subject" => "conversation_created",
"subject_params" => %{ "subject_params" => %{
"conversation_id" => conversation_id, "conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_id "conversation_participant_id" => conversation_participant_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"type" => "conversation"
}
)
end
test "an author who is member of the event organizer group" do
%User{} = user = insert(:user)
%Actor{id: actor_id} = actor = insert(:actor, user: user)
%Actor{} = organizer_group = insert(:group)
%Event{} = event = insert(:event, attributed_to: organizer_group)
%Comment{} = comment = insert(:comment, actor: organizer_group)
%Conversation{id: conversation_id} =
conversation =
insert(:conversation, event: event, last_comment: comment, origin_comment: comment)
%ConversationParticipant{id: conversation_participant_actor_id} =
insert(:conversation_participant, actor: actor, conversation: conversation)
%ConversationParticipant{
id: conversation_participant_id,
actor: %Actor{id: conversation_other_participant_actor_id}
} = insert(:conversation_participant, conversation: conversation)
conversation = Conversations.get_conversation(conversation_id)
assert {:ok, _} =
ConversationActivity.insert_activity(conversation, subject: "conversation_created")
assert_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => organizer_group.id,
"participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id},
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_actor_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"type" => "conversation"
}
)
assert_enqueued(
worker: LegacyNotifierBuilder,
args: %{
"author_id" => organizer_group.id,
"participant" => %{
"actor_id" => conversation_other_participant_actor_id,
"id" => conversation_participant_id
},
"object_id" => to_string(conversation_id),
"object_type" => "conversation",
"op" => "legacy_notify",
"subject" => "conversation_created",
"subject_params" => %{
"conversation_id" => conversation_id,
"conversation_participant_id" => conversation_participant_id,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
}, },
"type" => "conversation" "type" => "conversation"
} }

View file

@ -11,7 +11,7 @@ defmodule Mobilizon.Service.Metadata.InstanceTest do
assert Instance.build_tags() |> Utils.stringify_tags() == assert Instance.build_tags() |> Utils.stringify_tags() ==
""" """
<title>#{title}</title><meta content="#{description}" name="description"><meta content="#{title}" property="og:title"><meta content="#{Endpoint.url()}" property="og:url"><meta content="#{description}" property="og:description"><meta content="website" property="og:type"><script type="application/ld+json">{"@context":"http://schema.org","@type":"WebSite","name":"#{title}","potentialAction":{"@type":"SearchAction","query-input":"required name=search_term","target":"#{Endpoint.url()}/search?term={search_term}"},"url":"#{Endpoint.url()}"}</script> <title>#{title}</title><meta content="#{description}" name="description"><meta content="#{title}" property="og:title"><meta content="#{Endpoint.url()}" property="og:url"><meta content="#{description}" property="og:description"><meta content="website" property="og:type"><script type="application/ld+json">{"@context":"http://schema.org","@type":"WebSite","name":"#{title}","potentialAction":{"@type":"SearchAction","query-input":"required name=search_term","target":"#{Endpoint.url()}/search?term={search_term}"},"url":"#{Endpoint.url()}"}</script>\n<link href=\"#{Endpoint.url()}/feed/instance/atom\" rel=\"alternate\" title=\"Test instance's feed\" type=\"application/atom+xml\"><link href=\"#{Endpoint.url()}/feed/instance/ics\" rel=\"alternate\" title=\"Test instance's feed\" type=\"text/calendar\">\
""" """
end end
end end

View file

@ -4,6 +4,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
""" """
alias Mobilizon.Activities.Activity alias Mobilizon.Activities.Activity
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
@ -15,6 +16,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
use Mobilizon.Tests.Helpers use Mobilizon.Tests.Helpers
import Mox import Mox
import Mobilizon.Factory import Mobilizon.Factory
import Mobilizon.Tests.SwooshAssertions
setup_all do setup_all do
Mox.defmock(NotifierMock, for: Mobilizon.Service.Notifier) Mox.defmock(NotifierMock, for: Mobilizon.Service.Notifier)
@ -42,7 +44,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
"op" => "legacy_notify" "op" => "legacy_notify"
} }
@announcement %{ @public_announcement %{
"type" => "comment", "type" => "comment",
"subject" => "participation_event_comment", "subject" => "participation_event_comment",
"object_type" => "comment", "object_type" => "comment",
@ -50,6 +52,14 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
"op" => "legacy_notify" "op" => "legacy_notify"
} }
@private_announcement %{
"type" => "conversation",
"subject" => "conversation_created",
"object_type" => "conversation",
"inserted_at" => DateTime.utc_now(),
"op" => "legacy_notify"
}
setup :verify_on_exit! setup :verify_on_exit!
describe "Generates a comment mention notification " do describe "Generates a comment mention notification " do
@ -138,7 +148,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
%Comment{id: comment_id} = insert(:comment, event: event, actor: actor) %Comment{id: comment_id} = insert(:comment, event: event, actor: actor)
args = args =
Map.merge(@announcement, %{ Map.merge(@public_announcement, %{
"subject_params" => %{ "subject_params" => %{
"event_uuid" => uuid, "event_uuid" => uuid,
"event_title" => title, "event_title" => title,
@ -177,7 +187,7 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
insert(:participant, event: event, actor: actor2) insert(:participant, event: event, actor: actor2)
args = args =
Map.merge(@announcement, %{ Map.merge(@public_announcement, %{
"subject_params" => %{ "subject_params" => %{
"event_uuid" => uuid, "event_uuid" => uuid,
"event_title" => title, "event_title" => title,
@ -334,4 +344,91 @@ defmodule Mobilizon.Service.Workers.LegacyNotifierBuilderTest do
assert :ok == LegacyNotifierBuilder.perform(%Oban.Job{args: args}) assert :ok == LegacyNotifierBuilder.perform(%Oban.Job{args: args})
end end
end end
describe "Generates a private event announcement notification" do
test "sends emails to target users" do
user1 = insert(:user, email: "user1@do.main")
actor1 = insert(:actor, user: user1)
user2 = insert(:user, email: "user2@do.main")
actor2 = insert(:actor, user: user2)
event = insert(:event)
comment = insert(:comment, actor: actor2, visibility: :private)
conversation =
insert(:conversation, event: event, last_comment: comment, origin_comment: comment)
conversation_participant =
insert(:conversation_participant, conversation: conversation, actor: actor1)
args =
Map.merge(@private_announcement, %{
"subject_params" => %{
"conversation_id" => conversation.id,
"conversation_participant_id" => conversation_participant.id,
"conversation_text" => conversation.last_comment.text,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"author_id" => conversation.last_comment.actor.id,
"object_id" => conversation.last_comment.id,
"participant" => %{
"actor_id" => actor1.id,
"id" => conversation_participant.id
}
})
LegacyNotifierBuilder.perform(%Oban.Job{args: args})
assert_email_sent(%Swoosh.Email{to: [{"", "user1@do.main"}]})
refute_email_sent(%Swoosh.Email{to: [{"", "user2@do.main"}]})
refute_email_sent(%Swoosh.Email{to: [{"", "user1@do.main"}]})
end
test "sends emails to anonymous participants" do
{:ok, anonymous_actor} = Actors.get_or_create_internal_actor("anonymous")
refute is_nil(anonymous_actor)
user2 = insert(:user, email: "user2@do.main")
actor2 = insert(:actor, user: user2)
event = insert(:event)
comment = insert(:comment, actor: actor2, visibility: :private)
insert(:participant,
event: event,
actor: anonymous_actor,
metadata: %{email: "anon@mou.se"}
)
conversation =
insert(:conversation, event: event, last_comment: comment, origin_comment: comment)
conversation_participant =
insert(:conversation_participant, conversation: conversation, actor: anonymous_actor)
args =
Map.merge(@private_announcement, %{
"subject_params" => %{
"conversation_id" => conversation.id,
"conversation_participant_id" => conversation_participant.id,
"conversation_text" => conversation.last_comment.text,
"conversation_event_id" => event.id,
"conversation_event_title" => event.title,
"conversation_event_uuid" => event.uuid
},
"author_id" => conversation.last_comment.actor.id,
"object_id" => conversation.last_comment.id,
"participant" => %{
"actor_id" => anonymous_actor.id,
"id" => conversation_participant.id
}
})
LegacyNotifierBuilder.perform(%Oban.Job{args: args})
assert_email_sending(%Swoosh.Email{to: [{"", "anon@mou.se"}]}, 10_000)
refute_email_sent(%Swoosh.Email{to: [{"", "user2@do.main"}]})
# Because of timeouts, can't do that currently
# refute_email_sent(%Swoosh.Email{to: [{"", "anon@mou.se"}]})
end
end
end end

View file

@ -0,0 +1,91 @@
# The following module is taken from this issue
# https://github.com/swoosh/swoosh/issues/488#issuecomment-1671224765
defmodule Mobilizon.Tests.SwooshAssertions do
@moduledoc ~S"""
Assertions for emails.
The assertions provided by this module work by pattern matching
against all emails received by the test process against the
`Swoosh.Email` struct. For example:
assert_email_sent %{subject: "You got a message"}
If you want to be additionally explicit, you might:
assert_email_sent %Swoosh.Email{subject: "You got a message"}
If emails are being sent concurrently, you can use `assert_email_sending/2`:
assert_email_sending %{subject: "You got a message"}
Both functions will return the matched email if the assertion succeeds.
You can then perform further matches on it:
email = assert_email_sent %Swoosh.Email{subject: "You got a message"}
assert email.from == {"MyApp", "no-reply@example.com"}
Using pattern matching imposes two limitations. The first one is that you
must match precisely the Swoosh.Email structure. For example, the following
will not work:
assert_email_sent %{to: "foobar@example.com"}
That's because `Swoosh.Email` keeps the field as a list. This will work:
assert_email_sent %{to: [{"FooBar", "foobar@example.com"}]}
You are also not allowed to have interpolations. For example, the following
will not work:
assert_email_sent %{
subject: "You have been invited to #{org.name}",
to: [{user.name, user.email}]
}
However, you can rely on pattern matching and rewrite it as:
email = assert_email_sent %{subject: "You have been invited to " <> org_name}
assert org_name == org.name
assert email.to == [{user.name, user.email}]
"""
@doc """
Matches an email has been sent.
See moduledoc for more information.
"""
defmacro assert_email_sent(pattern) do
quote do
{:email, email} = assert_received({:email, unquote(pattern)})
email
end
end
@doc """
Matches an email is sending (within a timeout).
See moduledoc for more information.
"""
defmacro assert_email_sending(
pattern,
timeout \\ Application.fetch_env!(:ex_unit, :assert_receive_timeout)
) do
quote do
{:email, email} = assert_receive({:email, unquote(pattern)}, unquote(timeout))
email
end
end
@doc """
Refutes an email matching pattern has been sent.
The opposite of `assert_email_sent`.
"""
defmacro refute_email_sent(pattern) do
quote do
refute_received({:email, unquote(pattern)})
end
end
end

View file

@ -1,5 +0,0 @@
// vetur.config.js
/** @type {import('vls').VeturConfig} */
module.exports = {
projects: ["./js"],
};