forked from potsda.mn/mobilizon
Merge remote-tracking branch 'origin/main' into feature/calendar
This commit is contained in:
commit
dec26525c0
|
@ -50,8 +50,8 @@ LABEL org.opencontainers.image.title="mobilizon" \
|
|||
org.opencontainers.image.revision=$VCS_REF \
|
||||
org.opencontainers.image.created=$BUILD_DATE
|
||||
|
||||
RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell openssl1.1-compat
|
||||
RUN pip --no-cache-dir install weasyprint pyexcel-ods3
|
||||
RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell
|
||||
RUN pip --no-cache-dir install --break-system-packages weasyprint pyexcel-ods3
|
||||
|
||||
# Create every data directory
|
||||
RUN mkdir -p /var/lib/mobilizon/uploads && chown nobody:nobody /var/lib/mobilizon/uploads
|
||||
|
|
|
@ -135,7 +135,7 @@ defmodule Mobilizon.Federation.ActivityPub do
|
|||
|
||||
%Page{total: total_events, elements: events} =
|
||||
if actor_id == relay_actor_id do
|
||||
Events.list_public_local_events(page, limit)
|
||||
Events.list_public_local_events(page, limit, :publish_at, :desc)
|
||||
else
|
||||
Events.list_public_events_for_actor(actor, page, limit)
|
||||
end
|
||||
|
|
|
@ -13,10 +13,9 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
|
|||
|
||||
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier}
|
||||
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
|
||||
alias Mobilizon.Federation.WebFinger
|
||||
alias Mobilizon.Federation.{NodeInfo, WebFinger}
|
||||
alias Mobilizon.GraphQL.API.Follows
|
||||
alias Mobilizon.Service.Workers.Background
|
||||
import Mobilizon.Federation.ActivityPub.Utils, only: [create_full_domain_string: 1]
|
||||
|
||||
require Logger
|
||||
|
||||
|
@ -185,17 +184,17 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
|
|||
defp fetch_actor("http://" <> address), do: fetch_actor(address)
|
||||
|
||||
defp fetch_actor(address) do
|
||||
%URI{host: host} = uri = URI.parse("http://" <> address)
|
||||
%URI{host: host} = URI.parse("http://" <> address)
|
||||
|
||||
cond do
|
||||
String.contains?(address, "@") ->
|
||||
check_actor(address)
|
||||
|
||||
!is_nil(host) ->
|
||||
uri
|
||||
|> create_full_domain_string()
|
||||
|> then(&Kernel.<>("relay@", &1))
|
||||
|> check_actor()
|
||||
case NodeInfo.application_actor(host) do
|
||||
nil -> check_actor("relay@#{host}")
|
||||
actor_url when is_binary(actor_url) -> {:ok, actor_url}
|
||||
end
|
||||
|
||||
true ->
|
||||
{:error, :bad_url}
|
||||
|
|
|
@ -24,16 +24,23 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Address do
|
|||
}
|
||||
|
||||
res =
|
||||
if is_nil(object["address"]) or not is_map(object["address"]) do
|
||||
res
|
||||
else
|
||||
Map.merge(res, %{
|
||||
"country" => object["address"]["addressCountry"],
|
||||
"postal_code" => object["address"]["postalCode"],
|
||||
"region" => object["address"]["addressRegion"],
|
||||
"street" => object["address"]["streetAddress"],
|
||||
"locality" => object["address"]["addressLocality"]
|
||||
})
|
||||
cond do
|
||||
is_binary(object["address"]) ->
|
||||
Map.merge(res, %{
|
||||
"street" => object["address"]
|
||||
})
|
||||
|
||||
is_map(object["address"]) ->
|
||||
Map.merge(res, %{
|
||||
"country" => object["address"]["addressCountry"],
|
||||
"postal_code" => object["address"]["postalCode"],
|
||||
"region" => object["address"]["addressRegion"],
|
||||
"street" => object["address"]["streetAddress"],
|
||||
"locality" => object["address"]["addressLocality"]
|
||||
})
|
||||
|
||||
is_nil(object["address"]) ->
|
||||
res
|
||||
end
|
||||
|
||||
latitude = Map.get(object, "latitude")
|
||||
|
|
|
@ -198,6 +198,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|
|||
|
||||
defp calculate_timezone(_object, nil), do: nil
|
||||
|
||||
defp calculate_timezone(_object, %Address{geom: nil}), do: nil
|
||||
|
||||
defp calculate_timezone(_object, %Address{geom: geom}) do
|
||||
TimezoneDetector.detect(
|
||||
nil,
|
||||
|
|
|
@ -278,8 +278,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
|
|||
end
|
||||
|
||||
@spec get_address(map | binary | nil) :: Address.t() | nil
|
||||
def get_address(address_url) when is_binary(address_url) do
|
||||
get_address(%{"id" => address_url})
|
||||
def get_address(text_address) when is_binary(text_address) do
|
||||
get_address(%{"type" => "Place", "name" => text_address})
|
||||
end
|
||||
|
||||
def get_address(%{"id" => url} = map) when is_map(map) and is_binary(url) do
|
||||
|
|
105
lib/federation/node_info.ex
Normal file
105
lib/federation/node_info.ex
Normal file
|
@ -0,0 +1,105 @@
|
|||
defmodule Mobilizon.Federation.NodeInfo do
|
||||
@moduledoc """
|
||||
Performs NodeInfo requests
|
||||
"""
|
||||
|
||||
alias Mobilizon.Service.HTTP.WebfingerClient
|
||||
require Logger
|
||||
|
||||
@application_uri "https://www.w3.org/ns/activitystreams#Application"
|
||||
@nodeinfo_rel_2_0 "http://nodeinfo.diaspora.software/ns/schema/2.0"
|
||||
@nodeinfo_rel_2_1 "http://nodeinfo.diaspora.software/ns/schema/2.1"
|
||||
|
||||
@env Application.compile_env(:mobilizon, :env)
|
||||
|
||||
@spec application_actor(String.t()) :: String.t() | nil
|
||||
def application_actor(host) do
|
||||
Logger.debug("Fetching application actor from NodeInfo data for domain #{host}")
|
||||
|
||||
case fetch_nodeinfo_endpoint(host) do
|
||||
{:ok, body} ->
|
||||
extract_application_actor(body)
|
||||
|
||||
{:error, :node_info_meta_http_error} ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@spec nodeinfo(String.t()) :: {:ok, map()} | {:error, atom()}
|
||||
def nodeinfo(host) do
|
||||
Logger.debug("Fetching NodeInfo details for domain #{host}")
|
||||
|
||||
with {:ok, endpoint} when is_binary(endpoint) <- fetch_nodeinfo_details(host),
|
||||
:ok <- Logger.debug("Going to get NodeInfo information from URL #{endpoint}"),
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 <- WebfingerClient.get(endpoint) do
|
||||
Logger.debug("Found nodeinfo information for domain #{host}")
|
||||
{:ok, body}
|
||||
else
|
||||
{:error, err} ->
|
||||
{:error, err}
|
||||
|
||||
err ->
|
||||
Logger.debug("Failed to fetch NodeInfo data from endpoint #{inspect(err)}")
|
||||
{:error, :node_info_endpoint_http_error}
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_application_actor(body) do
|
||||
body
|
||||
|> Map.get("links", [])
|
||||
|> Enum.find(%{"rel" => @application_uri, "href" => nil}, fn %{"rel" => rel, "href" => href} ->
|
||||
rel == @application_uri and is_binary(href)
|
||||
end)
|
||||
|> Map.get("href")
|
||||
end
|
||||
|
||||
@spec fetch_nodeinfo_endpoint(String.t()) :: {:ok, map()} | {:error, atom()}
|
||||
defp fetch_nodeinfo_endpoint(host) do
|
||||
prefix = if @env !== :dev, do: "https", else: "http"
|
||||
|
||||
case WebfingerClient.get("#{prefix}://#{host}/.well-known/nodeinfo") do
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 ->
|
||||
{:ok, body}
|
||||
|
||||
err ->
|
||||
Logger.debug("Failed to fetch NodeInfo data #{inspect(err)}")
|
||||
{:error, :node_info_meta_http_error}
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_nodeinfo_details(String.t()) :: {:ok, String.t()} | {:error, atom()}
|
||||
defp fetch_nodeinfo_details(host) do
|
||||
with {:ok, body} <- fetch_nodeinfo_endpoint(host) do
|
||||
extract_nodeinfo_endpoint(body)
|
||||
end
|
||||
end
|
||||
|
||||
@spec extract_nodeinfo_endpoint(map()) ::
|
||||
{:ok, String.t()}
|
||||
| {:error, :no_node_info_endpoint_found | :no_valid_node_info_endpoint_found}
|
||||
defp extract_nodeinfo_endpoint(body) do
|
||||
links = Map.get(body, "links", [])
|
||||
|
||||
relation =
|
||||
find_nodeinfo_relation(links, @nodeinfo_rel_2_1) ||
|
||||
find_nodeinfo_relation(links, @nodeinfo_rel_2_0)
|
||||
|
||||
if is_nil(relation) do
|
||||
{:error, :no_node_info_endpoint_found}
|
||||
else
|
||||
endpoint = Map.get(relation, "href")
|
||||
|
||||
if is_nil(endpoint) do
|
||||
{:error, :no_valid_node_info_endpoint_found}
|
||||
else
|
||||
{:ok, endpoint}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp find_nodeinfo_relation(links, relation) do
|
||||
Enum.find(links, fn %{"rel" => rel, "href" => href} ->
|
||||
rel == relation and is_binary(href)
|
||||
end)
|
||||
end
|
||||
end
|
|
@ -490,19 +490,35 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
|
|||
context: %{current_user: %User{role: role}}
|
||||
})
|
||||
when is_admin(role) do
|
||||
remote_relay = Actors.get_relay(domain)
|
||||
remote_relay = Instances.get_instance_actor(domain)
|
||||
local_relay = Relay.get_actor()
|
||||
|
||||
result = %{
|
||||
has_relay: !is_nil(remote_relay),
|
||||
relay_address:
|
||||
if(is_nil(remote_relay),
|
||||
do: nil,
|
||||
else: "#{remote_relay.preferred_username}@#{remote_relay.domain}"
|
||||
),
|
||||
follower_status: follow_status(remote_relay, local_relay),
|
||||
followed_status: follow_status(local_relay, remote_relay)
|
||||
}
|
||||
result =
|
||||
if is_nil(remote_relay) do
|
||||
%{
|
||||
has_relay: false,
|
||||
relay_address: nil,
|
||||
follower_status: nil,
|
||||
followed_status: nil,
|
||||
software: nil,
|
||||
software_version: nil
|
||||
}
|
||||
else
|
||||
%{
|
||||
has_relay: !is_nil(remote_relay.actor),
|
||||
relay_address:
|
||||
if(is_nil(remote_relay.actor),
|
||||
do: nil,
|
||||
else: Actor.preferred_username_and_domain(remote_relay.actor)
|
||||
),
|
||||
follower_status: follow_status(remote_relay.actor, local_relay),
|
||||
followed_status: follow_status(local_relay, remote_relay.actor),
|
||||
instance_name: remote_relay.instance_name,
|
||||
instance_description: remote_relay.instance_description,
|
||||
software: remote_relay.software,
|
||||
software_version: remote_relay.software_version
|
||||
}
|
||||
end
|
||||
|
||||
case Instances.instance(domain) do
|
||||
nil -> {:error, :not_found}
|
||||
|
|
|
@ -227,6 +227,16 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
|||
field(:relay_address, :string,
|
||||
description: "If this instance has a relay, it's federated username"
|
||||
)
|
||||
|
||||
field(:instance_name, :string, description: "This instance's name")
|
||||
|
||||
field(:instance_description, :string, description: "This instance's description")
|
||||
|
||||
field(:software, :string, description: "The software this instance declares running")
|
||||
|
||||
field(:software_version, :string,
|
||||
description: "The software version this instance declares running"
|
||||
)
|
||||
end
|
||||
|
||||
@desc """
|
||||
|
|
|
@ -387,13 +387,15 @@ defmodule Mobilizon.Events do
|
|||
|> Repo.stream()
|
||||
end
|
||||
|
||||
@spec list_public_local_events(integer | nil, integer | nil) :: Page.t(Event.t())
|
||||
def list_public_local_events(page \\ nil, limit \\ nil) do
|
||||
@spec list_public_local_events(integer | nil, integer | nil, atom | nil, atom | nil) ::
|
||||
Page.t(Event.t())
|
||||
def list_public_local_events(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
|
||||
Event
|
||||
|> filter_public_visibility()
|
||||
|> filter_draft()
|
||||
|> filter_local()
|
||||
|> preload_for_event()
|
||||
|> event_order_by(sort, direction)
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
|
|
39
lib/mobilizon/instances/instance_actor.ex
Normal file
39
lib/mobilizon/instances/instance_actor.ex
Normal file
|
@ -0,0 +1,39 @@
|
|||
defmodule Mobilizon.Instances.InstanceActor do
|
||||
@moduledoc """
|
||||
An instance actor
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
alias Mobilizon.Actors.Actor
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
domain: String.t(),
|
||||
actor: Actor.t(),
|
||||
instance_name: String.t(),
|
||||
instance_description: String.t(),
|
||||
software: String.t(),
|
||||
software_version: String.t()
|
||||
}
|
||||
|
||||
schema "instance_actors" do
|
||||
field(:domain, :string)
|
||||
field(:instance_name, :string)
|
||||
field(:instance_description, :string)
|
||||
field(:software, :string)
|
||||
field(:software_version, :string)
|
||||
belongs_to(:actor, Actor)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@required_attrs [:domain]
|
||||
@optional_attrs [:actor_id, :instance_name, :instance_description, :software, :software_version]
|
||||
@attrs @required_attrs ++ @optional_attrs
|
||||
|
||||
def changeset(%__MODULE__{} = instance_actor, attrs) do
|
||||
instance_actor
|
||||
|> cast(attrs, @attrs)
|
||||
|> validate_required(@required_attrs)
|
||||
|> unique_constraint(:domain)
|
||||
end
|
||||
end
|
|
@ -4,7 +4,7 @@ defmodule Mobilizon.Instances do
|
|||
"""
|
||||
alias Ecto.Adapters.SQL
|
||||
alias Mobilizon.Actors.{Actor, Follower}
|
||||
alias Mobilizon.Instances.Instance
|
||||
alias Mobilizon.Instances.{Instance, InstanceActor}
|
||||
alias Mobilizon.Storage.{Page, Repo}
|
||||
import Ecto.Query
|
||||
|
||||
|
@ -12,13 +12,13 @@ defmodule Mobilizon.Instances do
|
|||
|
||||
@spec instances(Keyword.t()) :: Page.t(Instance.t())
|
||||
def instances(options) do
|
||||
page = Keyword.get(options, :page)
|
||||
limit = Keyword.get(options, :limit)
|
||||
order_by = Keyword.get(options, :order_by)
|
||||
direction = Keyword.get(options, :direction)
|
||||
page = Keyword.get(options, :page, 1)
|
||||
limit = Keyword.get(options, :limit, 10)
|
||||
order_by = Keyword.get(options, :order_by, :event_count)
|
||||
direction = Keyword.get(options, :direction, :desc)
|
||||
filter_domain = Keyword.get(options, :filter_domain)
|
||||
# suspend_status = Keyword.get(options, :filter_suspend_status)
|
||||
follow_status = Keyword.get(options, :filter_follow_status)
|
||||
# suspend_status = Keyword.get(options, :filter_suspend_status, :all)
|
||||
follow_status = Keyword.get(options, :filter_follow_status, :all)
|
||||
|
||||
order_by_options = Keyword.new([{direction, order_by}])
|
||||
|
||||
|
@ -42,7 +42,9 @@ defmodule Mobilizon.Instances do
|
|||
query =
|
||||
Instance
|
||||
|> join(:left, [i], s in subquery(subquery), on: i.domain == s.domain)
|
||||
|> select([i, s], {i, s})
|
||||
|> join(:left, [i], ia in InstanceActor, on: i.domain == ia.domain)
|
||||
|> join(:left, [_i, _s, ia], a in Actor, on: ia.actor_id == a.id)
|
||||
|> select([i, s, ia, a], {i, s, ia, a})
|
||||
|> order_by(^order_by_options)
|
||||
|
||||
query =
|
||||
|
@ -100,16 +102,72 @@ defmodule Mobilizon.Instances do
|
|||
following: following,
|
||||
following_approved: following_approved,
|
||||
has_relay: has_relay
|
||||
}}
|
||||
}, instance_meta, instance_actor}
|
||||
) do
|
||||
instance
|
||||
|> Map.put(:follower_status, follow_status(following, following_approved))
|
||||
|> Map.put(:followed_status, follow_status(follower, follower_approved))
|
||||
|> Map.put(:has_relay, has_relay)
|
||||
|> Map.put(:instance_actor, instance_actor)
|
||||
|> add_metadata_details(instance_meta)
|
||||
end
|
||||
|
||||
@spec add_metadata_details(map(), InstanceActor.t() | nil) :: map()
|
||||
defp add_metadata_details(instance, nil), do: instance
|
||||
|
||||
defp add_metadata_details(instance, instance_meta) do
|
||||
instance
|
||||
|> Map.put(:instance_name, instance_meta.instance_name)
|
||||
|> Map.put(:instance_description, instance_meta.instance_description)
|
||||
|> Map.put(:software, instance_meta.software)
|
||||
|> Map.put(:software_version, instance_meta.software_version)
|
||||
end
|
||||
|
||||
defp follow_status(true, true), do: :approved
|
||||
defp follow_status(true, false), do: :pending
|
||||
defp follow_status(false, _), do: :none
|
||||
defp follow_status(nil, _), do: :none
|
||||
|
||||
@spec get_instance_actor(String.t()) :: InstanceActor.t() | nil
|
||||
def get_instance_actor(domain) do
|
||||
InstanceActor
|
||||
|> Repo.get_by(domain: domain)
|
||||
|> Repo.preload(:actor)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates an instance actor.
|
||||
"""
|
||||
@spec create_instance_actor(map) :: {:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()}
|
||||
def create_instance_actor(attrs \\ %{}) do
|
||||
with {:ok, %InstanceActor{} = instance_actor} <-
|
||||
%InstanceActor{}
|
||||
|> InstanceActor.changeset(attrs)
|
||||
|> Repo.insert(on_conflict: :replace_all, conflict_target: :domain) do
|
||||
{:ok, Repo.preload(instance_actor, :actor)}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates an instance actor.
|
||||
"""
|
||||
@spec update_instance_actor(InstanceActor.t(), map) ::
|
||||
{:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_instance_actor(%InstanceActor{} = instance_actor, attrs) do
|
||||
with {:ok, %InstanceActor{} = instance_actor} <-
|
||||
instance_actor
|
||||
|> Repo.preload(:actor)
|
||||
|> InstanceActor.changeset(attrs)
|
||||
|> Repo.update() do
|
||||
{:ok, Repo.preload(instance_actor, :actor)}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a post
|
||||
"""
|
||||
@spec delete_instance_actor(InstanceActor.t()) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
|
||||
def delete_instance_actor(%InstanceActor{} = instance_actor) do
|
||||
Repo.delete(instance_actor)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,11 +22,13 @@ defmodule Mobilizon.Posts do
|
|||
:private
|
||||
])
|
||||
|
||||
@spec list_public_local_posts(integer | nil, integer | nil) :: Page.t(Post.t())
|
||||
def list_public_local_posts(page \\ nil, limit \\ nil) do
|
||||
@spec list_public_local_posts(integer | nil, integer | nil, atom | nil, atom | nil) ::
|
||||
Page.t(Post.t())
|
||||
def list_public_local_posts(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
|
||||
Post
|
||||
|> filter_public()
|
||||
|> filter_local()
|
||||
|> order_posts(sort, direction)
|
||||
|> preload_post_associations()
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
@ -171,4 +173,13 @@ defmodule Mobilizon.Posts do
|
|||
defp preload_post_associations(query, associations \\ @post_preloads) do
|
||||
preload(query, ^associations)
|
||||
end
|
||||
|
||||
@spec order_posts(Ecto.Queryable.t(), atom | nil, atom | nil) :: Ecto.Queryable.t()
|
||||
def order_posts(query, nil, _direction), do: query
|
||||
def order_posts(query, _sort, nil), do: query
|
||||
|
||||
def order_posts(query, sort, direction) do
|
||||
order_by_param = Keyword.new([{direction, sort}])
|
||||
order_by(query, ^order_by_param)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ defmodule Mobilizon.Storage.Page do
|
|||
@doc """
|
||||
Returns a Page struct for a query.
|
||||
|
||||
`field` is use to define the field that will be used for the count aggregate, which should be the same as the field used for order_by
|
||||
`field` is used to define the field that will be used for the count aggregate, which should be the same as the field used for order_by
|
||||
See https://stackoverflow.com/q/12693089/10204399
|
||||
"""
|
||||
@spec build_page(Ecto.Queryable.t(), integer | nil, integer | nil, atom()) :: t(any)
|
||||
|
|
|
@ -96,8 +96,8 @@ defmodule Mobilizon.Service.Export.Common do
|
|||
|
||||
@spec fetch_instance_public_content(integer()) :: {:ok, list(Event.t()), list(Post.t())}
|
||||
def fetch_instance_public_content(limit) do
|
||||
%Page{elements: events} = Events.list_public_local_events(1, limit)
|
||||
%Page{elements: posts} = Posts.list_public_local_posts(1, limit)
|
||||
%Page{elements: events} = Events.list_public_local_events(1, limit, :begins_on, :desc)
|
||||
%Page{elements: posts} = Posts.list_public_local_posts(1, limit, :publish_at, :desc)
|
||||
{:ok, events, posts}
|
||||
end
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ defmodule Mobilizon.Service.Export.Feed do
|
|||
|
||||
@behaviour Cachable
|
||||
|
||||
@item_limit 500
|
||||
@item_limit 5000
|
||||
|
||||
@spec version :: String.t()
|
||||
defp version, do: Config.instance_version()
|
||||
|
|
|
@ -3,14 +3,16 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
|
|||
Worker to refresh the instances materialized view and the relay actors
|
||||
"""
|
||||
|
||||
use Oban.Worker, unique: [period: :infinity, keys: [:event_uuid, :action]]
|
||||
use Oban.Worker
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
|
||||
alias Mobilizon.Federation.ActivityPub.Relay
|
||||
alias Mobilizon.Federation.NodeInfo
|
||||
alias Mobilizon.Instances
|
||||
alias Mobilizon.Instances.Instance
|
||||
alias Mobilizon.Instances.{Instance, InstanceActor}
|
||||
alias Oban.Job
|
||||
require Logger
|
||||
|
||||
@impl Oban.Worker
|
||||
@spec perform(Oban.Job.t()) :: :ok
|
||||
|
@ -30,6 +32,8 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
|
|||
{:error, :not_remote_instance}
|
||||
end
|
||||
|
||||
@spec refresh_instance_actor(Instance.t()) ::
|
||||
{:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()} | {:error, atom}
|
||||
def refresh_instance_actor(%Instance{domain: domain}) do
|
||||
%Actor{url: url} = Relay.get_actor()
|
||||
%URI{host: host} = URI.new!(url)
|
||||
|
@ -37,7 +41,67 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
|
|||
if host == domain do
|
||||
{:error, :not_remote_instance}
|
||||
else
|
||||
ActivityPubActor.find_or_make_actor_from_nickname("relay@#{domain}")
|
||||
actor_id =
|
||||
case fetch_actor(domain) do
|
||||
{:ok, %Actor{id: actor_id}} -> actor_id
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
with instance_metadata <- fetch_instance_metadata(domain),
|
||||
:ok <- Logger.debug("Ready to save instance actor details"),
|
||||
{:ok, %InstanceActor{}} <-
|
||||
Instances.create_instance_actor(%{
|
||||
domain: domain,
|
||||
actor_id: actor_id,
|
||||
instance_name: get_in(instance_metadata, ["metadata", "nodeName"]),
|
||||
instance_description: get_in(instance_metadata, ["metadata", "nodeDescription"]),
|
||||
software: get_in(instance_metadata, ["software", "name"]),
|
||||
software_version: get_in(instance_metadata, ["software", "version"])
|
||||
}) do
|
||||
Logger.info("Saved instance actor details for domain #{host}")
|
||||
else
|
||||
err ->
|
||||
Logger.error(inspect(err))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp mobilizon(domain), do: "relay@#{domain}"
|
||||
defp peertube(domain), do: "peertube@#{domain}"
|
||||
defp mastodon(domain), do: "#{domain}@#{domain}"
|
||||
|
||||
defp fetch_actor(domain) do
|
||||
case NodeInfo.application_actor(domain) do
|
||||
nil -> guess_application_actor(domain)
|
||||
url -> ActivityPubActor.get_or_fetch_actor_by_url(url)
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_instance_metadata(domain) do
|
||||
case NodeInfo.nodeinfo(domain) do
|
||||
{:error, _} ->
|
||||
%{}
|
||||
|
||||
{:ok, metadata} ->
|
||||
metadata
|
||||
end
|
||||
end
|
||||
|
||||
defp guess_application_actor(domain) do
|
||||
Enum.find_value(
|
||||
[
|
||||
&mobilizon/1,
|
||||
&peertube/1,
|
||||
&mastodon/1
|
||||
],
|
||||
{:error, :no_application_actor_found},
|
||||
fn username_pattern ->
|
||||
case ActivityPubActor.find_or_make_actor_from_nickname(username_pattern.(domain)) do
|
||||
{:ok, %Actor{type: :Application} = actor} -> {:ok, actor}
|
||||
{:error, _err} -> false
|
||||
{:ok, _actor} -> false
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,13 +7,17 @@ defmodule Mobilizon.Web.NodeInfoController do
|
|||
use Mobilizon.Web, :controller
|
||||
|
||||
alias Mobilizon.Config
|
||||
alias Mobilizon.Federation.ActivityPub.Relay
|
||||
alias Mobilizon.Service.Statistics
|
||||
|
||||
@node_info_supported_versions ["2.0", "2.1"]
|
||||
@node_info_schema_uri "http://nodeinfo.diaspora.software/ns/schema/"
|
||||
@application_uri "https://www.w3.org/ns/activitystreams#Application"
|
||||
|
||||
@spec schemas(Plug.Conn.t(), any) :: Plug.Conn.t()
|
||||
def schemas(conn, _params) do
|
||||
relay = Relay.get_actor()
|
||||
|
||||
links =
|
||||
@node_info_supported_versions
|
||||
|> Enum.map(fn version ->
|
||||
|
@ -22,6 +26,12 @@ defmodule Mobilizon.Web.NodeInfoController do
|
|||
href: url(~p"/.well-known/nodeinfo/#{version}")
|
||||
}
|
||||
end)
|
||||
|> Kernel.++([
|
||||
%{
|
||||
rel: @application_uri,
|
||||
href: relay.url
|
||||
}
|
||||
])
|
||||
|
||||
json(conn, %{
|
||||
links: links
|
||||
|
|
4
mix.exs
4
mix.exs
|
@ -143,7 +143,7 @@ defmodule Mobilizon.Mixfile do
|
|||
{:phoenix_html, "~> 3.0"},
|
||||
{:phoenix_live_view, "~> 0.20.0"},
|
||||
{:phoenix_view, "~> 2.0"},
|
||||
{:gettext, "~> 0.23.1"},
|
||||
{:gettext, "~> 0.24"},
|
||||
{:cowboy, "~> 2.6"},
|
||||
{:guardian, "~> 2.0"},
|
||||
{:guardian_db, "~> 3.0.0"},
|
||||
|
@ -185,7 +185,7 @@ defmodule Mobilizon.Mixfile do
|
|||
{:floki, "~> 0.31"},
|
||||
{:ip_reserved, "~> 0.1.0"},
|
||||
{:fast_sanitize, "~> 0.1"},
|
||||
{:ueberauth, "~> 0.10", override: true},
|
||||
{:ueberauth, "0.10.5", override: true},
|
||||
{:ueberauth_twitter, "~> 0.4"},
|
||||
{:ueberauth_discord, "~> 0.7"},
|
||||
{:ueberauth_github, "~> 0.8.1"},
|
||||
|
|
36
mix.lock
36
mix.lock
|
@ -6,7 +6,7 @@
|
|||
"atomex": {:hex, :atomex, "0.5.1", "706a8241fd6d1719b27a77b6d1192d2f85e6ecc78e6eadab29207d8cb9bb7ae5", [:mix], [{:xml_builder, "~> 2.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "6248891b5fcab8503982e090eedeeadb757a6311c2ef2e2998b874f7d319ab3f"},
|
||||
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
||||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
||||
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
|
||||
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"cldr_utils": {:hex, :cldr_utils, "2.24.2", "364fa30be55d328e704629568d431eb74cd2f085752b27f8025520b566352859", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "3362b838836a9f0fa309de09a7127e36e67310e797d556db92f71b548832c7cf"},
|
||||
"codepagex": {:hex, :codepagex, "0.1.6", "49110d09a25ee336a983281a48ef883da4c6190481e0b063afe2db481af6117e", [:mix], [], "hexpm", "1521461097dde281edf084062f525a4edc6a5e49f4fd1f5ec41c9c4955d5bd59"},
|
||||
|
@ -25,27 +25,27 @@
|
|||
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
|
||||
"digital_token": {:hex, :digital_token, "0.6.0", "13e6de581f0b1f6c686f7c7d12ab11a84a7b22fa79adeb4b50eec1a2d278d258", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2455d626e7c61a128b02a4a8caddb092548c3eb613ac6f6a85e4cbb6caddc4d1"},
|
||||
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.38", "b42252eddf63bda05554ba8be93a1262dc0920c721f1aaf989f5de0f73a2e367", [:mix], [], "hexpm", "2cd0907795aaef0c7e8442e376633c5b3bd6edc8dbbdc539b22f095501c1cdb6"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
|
||||
"eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"},
|
||||
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
|
||||
"ecto": {:hex, :ecto, "3.11.1", "4b4972b717e7ca83d30121b12998f5fcdc62ba0ed4f20fd390f16f3270d85c3e", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ebd3d3772cd0dfcd8d772659e41ed527c28b2a8bde4b00fe03e0463da0f1983b"},
|
||||
"ecto_autoslug_field": {:hex, :ecto_autoslug_field, "3.1.0", "ddf26e814baf3c32c6aebfed56a637f10a097db83f65d71e6f2d1e7faf2e9e51", [:mix], [{:ecto, ">= 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "b6ddd614805263e24b5c169532c934440d0289181cce873061fca3a8e92fd9ff"},
|
||||
"ecto_dev_logger": {:hex, :ecto_dev_logger, "0.9.0", "cb631469ac1940e97655d6fce85905b792ac9250ab18b19c664978b79f8dad59", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2e8bc98b4ae4fcc7108896eef7da5a109afad829f4fb2eb46d677fdc9101c2d5"},
|
||||
"ecto_dev_logger": {:hex, :ecto_dev_logger, "0.10.0", "5b3a3900b845e0d40127bed9bdf9d02bf20aa38198a60fe108cddff63ed0048f", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a55e58bad5d5c9b8ef2a3c3347dbdf7efa880a5371cf1457e44b41f489a43927"},
|
||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||
"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.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [: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", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"},
|
||||
"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_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"},
|
||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||
"erlport": {:hex, :erlport, "0.11.0", "8bb46a520e6eb9146e655fbf9b824433d9d532194667069d9aa45696aae9684b", [:rebar3], [], "hexpm", "8eb136ccaf3948d329b8d1c3278ad2e17e2a7319801bc4cc2da6db278204eee4"},
|
||||
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
|
||||
"ex_cldr": {:hex, :ex_cldr, "2.37.5", "9da6d97334035b961d2c2de167dc6af8cd3e09859301a5b8f49f90bd8b034593", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "74ad5ddff791112ce4156382e171a5f5d3766af9d5c4675e0571f081fe136479"},
|
||||
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.22.1", "3e5150f1fe7698e0fa118aeedcca1b5920d0a552bc40c81cf65ca9b0a4ea4cc3", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e7408cd9e8318b2ef93b76728e84484ddc3ea6d7c894fbc811c54122a7140169"},
|
||||
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.23.0", "b18d62e23d74f7b4ce567e53dc91f900ea7ebb81a48565b4415554b8bdd3676c", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "06d2407e699032d5cdc515593b7ce7869f10ce28e98a4ed68d9b21e5001036d4"},
|
||||
"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_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_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.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
|
||||
"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_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_optimizer": {:hex, :ex_optimizer, "0.1.1", "62da37e206fc2233ff7a4e54e40eae365c40f96c81992fcd15b782eb25169b80", [:mix], [{:file_info, "~> 0.0.4", [hex: :file_info, repo: "hexpm", optional: false]}], "hexpm", "e6f5c059bcd58b66be2f6f257fdc4f69b74b0fa5c9ddd669486af012e4b52286"},
|
||||
|
@ -53,7 +53,7 @@
|
|||
"excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"},
|
||||
"exgravatar": {:hex, :exgravatar, "2.0.3", "2349709832ee535f826f48db98cddd294ae62b01acb44d539a16419bd8ebc3e5", [:mix], [], "hexpm", "aca18ff9bd8991d3be3e5446d3bdefc051be084c1ffc9ab2d43b3e65339300e1"},
|
||||
"exkismet": {:git, "https://github.com/tcitworld/exkismet.git", "8b5485fde00fafbde20f315bec387a77f7358334", []},
|
||||
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
|
||||
"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"},
|
||||
"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_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"},
|
||||
|
@ -66,7 +66,7 @@
|
|||
"geohax": {:hex, :geohax, "1.0.0", "89717442c5474a57d242b2361590bb33a7fbe6d8ec02a31d9865c3f33756d348", [:mix], [], "hexpm", "893ef2f905213acb67c615d2c955d926b1be3676bfc2bd5ed7271b641dfa2224"},
|
||||
"geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"},
|
||||
"geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"},
|
||||
"gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"},
|
||||
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
||||
"guardian": {:hex, :guardian, "2.3.2", "78003504b987f2b189d76ccf9496ceaa6a454bb2763627702233f31eb7212881", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b189ff38cd46a22a8a824866a6867ca8722942347f13c33f7d23126af8821b52"},
|
||||
"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"},
|
||||
|
@ -87,7 +87,7 @@
|
|||
"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_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.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"},
|
||||
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
|
||||
|
@ -103,23 +103,23 @@
|
|||
"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"},
|
||||
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
|
||||
"oban": {:hex, :oban, "2.16.3", "33ebe7da637cce4da5438c1636bc25448a8628994a0c064ac6078bbe6dc97bd6", [: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", "4d8a7fb62f63cf2f2080c78954425f5fd8916ef57196b7f79b5bc657abb2ac5f"},
|
||||
"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"},
|
||||
"paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"},
|
||||
"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_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_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.1", "92a37acf07afca67ac98bd326532ba8f44ad7d4bdf3e4361b03f7f02594e5ae9", [: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", [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", "be494fd1215052729298b0e97d5c2ce8e719c00854b82cd8cf15c1cd7fcf6294"},
|
||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.2", "025391424257d6c1ed2fab5c9fb11b1e5b21a8b47a416659b9a36492217dd5cb", [: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", "381c7f8a5702be838a6bf440a59ea010b458018e1a85cc432e8628641c00f07a"},
|
||||
"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_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"},
|
||||
"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"},
|
||||
"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_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_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
|
||||
"postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [: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", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"},
|
||||
"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"},
|
||||
"progress_bar": {:hex, :progress_bar, "3.0.0", "f54ff038c2ac540cfbb4c2bfe97c75e7116ead044f3c2b10c9f212452194b5cd", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6981c2b25ab24aecc91a2dc46623658e1399c21a2ae24db986b90d678530f2b7"},
|
||||
"rajska": {:git, "https://github.com/tcitworld/rajska.git", "0c036448e261e8be6a512581c592fadf48982d84", [branch: "mobilizon"]},
|
||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||
|
@ -135,12 +135,12 @@
|
|||
"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"},
|
||||
"sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"},
|
||||
"swoosh": {:hex, :swoosh, "1.14.1", "d8813699ba410509008dd3dfdb2df057e3fce367d45d5e6d76b146a7c9d559cd", [: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", "87da72260b4351678f96aec61db5c2acc8a88cda2cf2c4f534eb4c9c461350c7"},
|
||||
"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"},
|
||||
"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"},
|
||||
"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.20.0", "1ac0c53f95e201feb8d398ef9d764ae74175231289d89f166ba88a7f50cd8e73", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "ab57b74b1a63dc5775650699a3ec032ec0065005eff1f020818742b7312a8426"},
|
||||
"tz_world": {:hex, :tz_world, "1.3.1", "dedb8373fce594098909ff36d37f5e5e30e47cb40ef846d1dfc91eb39f7ebaaf", [: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", "901ed2b4a4430ecab3765244da4a19e6f19141867c2ab3753924919b87ed2224"},
|
||||
"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"},
|
||||
"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_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"},
|
||||
|
@ -155,7 +155,7 @@
|
|||
"unplug": {:hex, :unplug, "1.0.0", "8ec2479de0baa9a6283c04a1cc616c5ca6c5b80b8ff1d857481bb2943368dbbc", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d171a85758aa412d4e85b809c203e1b1c4c76a4d6ab58e68dc9a8a8acd9b7c3a"},
|
||||
"unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"},
|
||||
"vite_phx": {:hex, :vite_phx, "0.3.1", "0f7d8cd98018547a7d97122552fff3e2d70df2820bc70e8d4f31156dcb60f23a", [:mix], [{:jason, ">= 0.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, ">= 0.0.0", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "08b1726094a131490ff0a2c7764c4cdd4b5cdf8ba9762638a5dd4bcd9e5fc936"},
|
||||
"web_push_encryption": {:git, "https://github.com/danhper/elixir-web-push-encryption.git", "70f00d06cbd88c9ac382e0ad2539e54448e9d8da", []},
|
||||
"web_push_encryption": {:git, "https://github.com/danhper/elixir-web-push-encryption.git", "6e143dcde0a2854c4f0d72816b7ecab696432779", []},
|
||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||
"websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"},
|
||||
"xml_builder": {:hex, :xml_builder, "2.2.0", "cc5f1eeefcfcde6e90a9b77fb6c490a20bc1b856a7010ce6396f6da9719cbbab", [:mix], [], "hexpm", "9d66d52fb917565d358166a4314078d39ef04d552904de96f8e73f68f64a62c9"},
|
||||
|
|
555
package-lock.json
generated
555
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -32,7 +32,8 @@
|
|||
"@fullcalendar/icalendar": "^6.1.10",
|
||||
"@fullcalendar/interaction": "^6.1.10",
|
||||
"@fullcalendar/vue3": "^6.1.10",
|
||||
"@oruga-ui/oruga-next": "^0.7.0",
|
||||
"@oruga-ui/oruga-next": "^0.8.2",
|
||||
"@oruga-ui/theme-oruga": "^0.2.0",
|
||||
"@sentry/tracing": "^7.1",
|
||||
"@sentry/vue": "^7.1",
|
||||
"@tiptap/core": "^2.0.0-beta.41",
|
||||
|
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-25 07:56+0000\n"
|
||||
"PO-Revision-Date: 2023-12-05 16:34+0000\n"
|
||||
"PO-Revision-Date: 2023-12-21 20:11+0000\n"
|
||||
"Last-Translator: Milo Ivir <mail@milotype.de>\n"
|
||||
"Language-Team: Croatian <https://weblate.framasoft.org/projects/mobilizon/"
|
||||
"backend/hr/>\n"
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 5.2.1\n"
|
||||
"X-Generator: Weblate 5.3.1\n"
|
||||
|
||||
#: lib/web/templates/email/password_reset.html.heex:66
|
||||
#, elixir-autogen, elixir-format
|
||||
|
@ -2125,14 +2125,14 @@ msgid "Events"
|
|||
msgstr "Događaji"
|
||||
|
||||
#: lib/web/templates/email/report.html.heex:115
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Flagged events"
|
||||
msgstr "Označeni komentari"
|
||||
msgstr "Označeni događaji"
|
||||
|
||||
#: lib/service/export/participants/common.ex:65
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Participant registration date"
|
||||
msgstr "Stanje sudionika"
|
||||
msgstr "Datum registracije sudionika"
|
||||
|
||||
#: lib/web/templates/email/anonymous_participation_confirmation.html.heex:113
|
||||
#: lib/web/templates/email/event_participation_confirmed.html.heex:122
|
||||
|
|
17
priv/repo/migrations/20231220092536_add_actor_instances.exs
Normal file
17
priv/repo/migrations/20231220092536_add_actor_instances.exs
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule Mobilizon.Storage.Repo.Migrations.AddActorInstances do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:instance_actors) do
|
||||
add(:domain, :string)
|
||||
add(:instance_name, :string)
|
||||
add(:instance_description, :string)
|
||||
add(:software, :string)
|
||||
add(:software_version, :string)
|
||||
add(:actor_id, references(:actors, on_delete: :delete_all))
|
||||
timestamps()
|
||||
end
|
||||
|
||||
create(unique_index(:instance_actors, [:domain]))
|
||||
end
|
||||
end
|
BIN
public/img/gancio.png
Normal file
BIN
public/img/gancio.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
21
public/img/wordpress-logo.svg
Normal file
21
public/img/wordpress-logo.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="800px" height="800px" viewBox="0 0 96.24 96.24" xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<path d="M48.122,0C21.587,0,0.001,21.585,0.001,48.118c0,26.535,21.587,48.122,48.12,48.122c26.532,0,48.117-21.587,48.117-48.122
|
||||
C96.239,21.586,74.654,0,48.122,0z M4.857,48.118c0-6.271,1.345-12.227,3.746-17.606l20.638,56.544
|
||||
C14.81,80.042,4.857,65.243,4.857,48.118z M48.122,91.385c-4.247,0-8.346-0.623-12.222-1.763L48.88,51.903l13.301,36.433
|
||||
c0.086,0.215,0.191,0.411,0.308,0.596C57.992,90.514,53.16,91.385,48.122,91.385z M54.083,27.834
|
||||
c2.604-0.137,4.953-0.412,4.953-0.412c2.33-0.276,2.057-3.701-0.277-3.564c0,0-7.007,0.549-11.532,0.549
|
||||
c-4.25,0-11.396-0.549-11.396-0.549c-2.332-0.137-2.604,3.427-0.273,3.564c0,0,2.208,0.275,4.537,0.412l6.74,18.469l-9.468,28.395
|
||||
L21.615,27.835c2.608-0.136,4.952-0.412,4.952-0.412c2.33-0.275,2.055-3.702-0.278-3.562c0,0-7.004,0.549-11.53,0.549
|
||||
c-0.813,0-1.77-0.021-2.784-0.052C19.709,12.611,33.008,4.856,48.122,4.856c11.265,0,21.519,4.306,29.215,11.357
|
||||
c-0.187-0.01-0.368-0.035-0.562-0.035c-4.248,0-7.264,3.702-7.264,7.679c0,3.564,2.055,6.582,4.248,10.146
|
||||
c1.647,2.882,3.567,6.585,3.567,11.932c0,3.704-1.422,8-3.293,13.986l-4.315,14.421L54.083,27.834z M69.871,85.516l13.215-38.208
|
||||
c2.471-6.171,3.29-11.106,3.29-15.497c0-1.591-0.104-3.07-0.292-4.449c3.38,6.163,5.303,13.236,5.301,20.758
|
||||
C91.384,64.08,82.732,78.016,69.871,85.516z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -139,24 +139,31 @@ body {
|
|||
@apply pl-10;
|
||||
}
|
||||
|
||||
/* InputItems */
|
||||
.inputitems-item {
|
||||
/* TagInput */
|
||||
.taginput {
|
||||
@apply rounded bg-white dark:bg-zinc-700;
|
||||
}
|
||||
.taginput-item {
|
||||
@apply bg-primary mr-2;
|
||||
}
|
||||
|
||||
.inputitems-item:first-child {
|
||||
.taginput-item:first-child {
|
||||
@apply ml-2;
|
||||
}
|
||||
|
||||
/* Autocomplete */
|
||||
.autocomplete-menu {
|
||||
@apply max-h-[200px] drop-shadow-md text-black;
|
||||
.autocomplete {
|
||||
@apply max-h-[200px] drop-shadow-md text-black z-10;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
@apply py-1.5 px-4 text-start;
|
||||
}
|
||||
|
||||
.autocomplete-item-group-title {
|
||||
@apply opacity-50 py-0 px-2;
|
||||
}
|
||||
|
||||
/* Dropdown */
|
||||
.dropdown {
|
||||
@apply inline-flex relative;
|
||||
|
@ -218,6 +225,9 @@ body {
|
|||
}
|
||||
|
||||
/* Radio */
|
||||
.radio {
|
||||
@apply mr-2;
|
||||
}
|
||||
.form-radio {
|
||||
@apply bg-none text-primary accent-primary;
|
||||
}
|
||||
|
@ -293,7 +303,7 @@ button.menubar__button {
|
|||
@apply px-3 dark:text-black;
|
||||
}
|
||||
.pagination-link-current {
|
||||
@apply bg-primary dark:bg-primary cursor-not-allowed pointer-events-none border-primary text-white dark:text-zinc-900;
|
||||
@apply bg-primary dark:bg-primary cursor-not-allowed pointer-events-none border-primary text-white;
|
||||
}
|
||||
.pagination-ellipsis {
|
||||
@apply text-center m-1 text-gray-300;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<o-inputitems
|
||||
<o-taginput
|
||||
:modelValue="modelValueWithDisplayName"
|
||||
@update:modelValue="(val: IActor[]) => $emit('update:modelValue', val)"
|
||||
:data="availableActors"
|
||||
|
@ -8,12 +8,12 @@
|
|||
:open-on-focus="false"
|
||||
field="displayName"
|
||||
:placeholder="t('Add a recipient')"
|
||||
@typing="getActors"
|
||||
@input="getActors"
|
||||
>
|
||||
<template #default="props">
|
||||
<ActorInline :actor="props.option" />
|
||||
</template>
|
||||
</o-inputitems>
|
||||
</o-taginput>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -235,7 +235,7 @@ import type { Locale } from "date-fns";
|
|||
import ReportModal from "@/components/Report/ReportModal.vue";
|
||||
import { useCreateReport } from "@/composition/apollo/report";
|
||||
import { Snackbar } from "@/plugins/snackbar";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import RouteName from "@/router/name";
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -372,7 +372,7 @@ const reportComment = async (
|
|||
};
|
||||
|
||||
const snackbar = inject<Snackbar>("snackbar");
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
onCreateReportError((e) => {
|
||||
isReportModalActive.value = false;
|
||||
|
@ -387,7 +387,7 @@ onCreateReportError((e) => {
|
|||
|
||||
oneCreateReportDone(() => {
|
||||
isReportModalActive.value = false;
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t("Comment from {'@'}{username} reported", {
|
||||
username: props.comment.actor?.preferredUsername,
|
||||
}),
|
||||
|
|
|
@ -157,7 +157,7 @@ import type { Locale } from "date-fns";
|
|||
import { useI18n } from "vue-i18n";
|
||||
import { useCreateReport } from "@/composition/apollo/report";
|
||||
import { Snackbar } from "@/plugins/snackbar";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import ReportModal from "@/components/Report/ReportModal.vue";
|
||||
|
||||
const Editor = defineAsyncComponent(
|
||||
|
@ -220,7 +220,7 @@ const reportComment = async (
|
|||
};
|
||||
|
||||
const snackbar = inject<Snackbar>("snackbar");
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
onCreateReportError((e) => {
|
||||
isReportModalActive.value = false;
|
||||
|
@ -235,7 +235,7 @@ onCreateReportError((e) => {
|
|||
|
||||
oneCreateReportDone(() => {
|
||||
isReportModalActive.value = false;
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t("Comment from {'@'}{username} reported", {
|
||||
username: props.modelValue.actor?.preferredUsername,
|
||||
}),
|
||||
|
|
|
@ -381,7 +381,7 @@ import { ApolloCache, FetchResult } from "@apollo/client/core";
|
|||
import { useMutation } from "@vue/apollo-composable";
|
||||
import { useCreateReport } from "@/composition/apollo/report";
|
||||
import { useDeleteEvent } from "@/composition/apollo/event";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import ExternalParticipationButton from "./ExternalParticipationButton.vue";
|
||||
import AccountMultiple from "vue-material-design-icons/AccountMultiple.vue";
|
||||
import Bullhorn from "vue-material-design-icons/Bullhorn.vue";
|
||||
|
@ -619,11 +619,11 @@ onJoinEventMutationDone(({ data }) => {
|
|||
}
|
||||
});
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
onJoinEventMutationError((error) => {
|
||||
if (error.message) {
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: error.message,
|
||||
variant: "danger",
|
||||
position: "bottom-right",
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<o-field
|
||||
grouped
|
||||
:label="$t('Find or add an element')"
|
||||
label-for="event-metadata-autocomplete"
|
||||
class="flex-wrap justify-center gap-2"
|
||||
>
|
||||
<o-autocomplete
|
||||
expanded
|
||||
|
@ -27,6 +27,7 @@
|
|||
id="event-metadata-autocomplete"
|
||||
@select="addElement"
|
||||
dir="auto"
|
||||
class="flex-1 min-w-[200px]"
|
||||
>
|
||||
<template v-slot="props">
|
||||
<div class="dark:bg-violet-3 p-1 flex items-center gap-1">
|
||||
|
|
|
@ -337,7 +337,7 @@ import ViewCompact from "vue-material-design-icons/ViewCompact.vue";
|
|||
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
|
||||
import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
|
||||
import Video from "vue-material-design-icons/Video.vue";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import { computed, inject } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Dialog } from "@/plugins/dialog";
|
||||
|
@ -401,7 +401,7 @@ const openDeleteEventModal = (
|
|||
});
|
||||
};
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
const snackbar = inject<Snackbar>("snackbar");
|
||||
|
||||
const {
|
||||
|
@ -419,7 +419,7 @@ onDeleteEventDone(() => {
|
|||
*/
|
||||
emit("eventDeleted", props.participation.event.id);
|
||||
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t("Event {eventTitle} deleted", {
|
||||
eventTitle: props.participation.event.title,
|
||||
}),
|
||||
|
@ -460,7 +460,7 @@ const gotToWithCheck = async (
|
|||
) {
|
||||
const organizerActor = participation.event.organizerActor as IPerson;
|
||||
await changeIdentity(organizerActor);
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t(
|
||||
"Current identity has been changed to {identityName} in order to manage this event.",
|
||||
{
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
:required="isRequired"
|
||||
v-model="queryTextWithDefault"
|
||||
:placeholder="placeholderWithDefault"
|
||||
:customFormatter="(elem: IAddress) => addressFullName(elem)"
|
||||
:debounceTyping="debounceDelay"
|
||||
@typing="asyncData"
|
||||
:formatter="(elem: IAddress) => addressFullName(elem)"
|
||||
:debounce="debounceDelay"
|
||||
@input="asyncData"
|
||||
:icon="canShowLocateMeButton ? null : 'map-marker'"
|
||||
expanded
|
||||
@select="setSelected"
|
||||
|
@ -115,30 +115,39 @@
|
|||
required
|
||||
v-model="selected.description"
|
||||
id="addressNameInput"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
<o-field :label="t('Street')" labelFor="streetInput">
|
||||
<o-input v-model="selected.street" id="streetInput" />
|
||||
<o-input v-model="selected.street" id="streetInput" expanded />
|
||||
</o-field>
|
||||
|
||||
<o-field grouped>
|
||||
<o-field :label="t('Postal Code')" labelFor="postalCodeInput">
|
||||
<o-input v-model="selected.postalCode" id="postalCodeInput" />
|
||||
<o-input
|
||||
v-model="selected.postalCode"
|
||||
id="postalCodeInput"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
<o-field :label="t('Locality')" labelFor="localityInput">
|
||||
<o-input v-model="selected.locality" id="localityInput" />
|
||||
<o-input
|
||||
v-model="selected.locality"
|
||||
id="localityInput"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
</o-field>
|
||||
|
||||
<o-field grouped>
|
||||
<o-field :label="t('Region')" labelFor="regionInput">
|
||||
<o-input v-model="selected.region" id="regionInput" />
|
||||
<o-input v-model="selected.region" id="regionInput" expanded />
|
||||
</o-field>
|
||||
|
||||
<o-field :label="t('Country')" labelFor="countryInput">
|
||||
<o-input v-model="selected.country" id="countryInput" />
|
||||
<o-input v-model="selected.country" id="countryInput" expanded />
|
||||
</o-field>
|
||||
</o-field>
|
||||
</section>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<HelpCircleOutline :size="16" />
|
||||
</o-tooltip>
|
||||
</template>
|
||||
<o-inputitems
|
||||
<o-taginput
|
||||
v-model="tagsStrings"
|
||||
:data="filteredTags"
|
||||
:allow-autocomplete="true"
|
||||
|
@ -21,11 +21,11 @@
|
|||
:maxlength="20"
|
||||
:maxitems="10"
|
||||
:placeholder="$t('Eg: Stockholm, Dance, Chess…')"
|
||||
@typing="debouncedGetFilteredTags"
|
||||
@input="debouncedGetFilteredTags"
|
||||
:id="id"
|
||||
dir="auto"
|
||||
>
|
||||
</o-inputitems>
|
||||
</o-taginput>
|
||||
</o-field>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
|
|
@ -17,21 +17,20 @@
|
|||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
maxlength="1024"
|
||||
expanded
|
||||
/>
|
||||
<o-button native-type="submit" icon-left="magnify">
|
||||
</o-button>
|
||||
<o-button native-type="submit" icon-left="magnify"> </o-button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { IAddress } from "@/types/address.model";
|
||||
import { AddressSearchType } from "@/types/enums";
|
||||
import { computed, defineAsyncComponent } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import RouteName from "@/router/name";
|
||||
|
||||
const FullAddressAutoComplete = defineAsyncComponent(
|
||||
defineAsyncComponent(
|
||||
() => import("@/components/Event/FullAddressAutoComplete.vue")
|
||||
);
|
||||
|
||||
|
@ -91,7 +90,7 @@ const submit = () => {
|
|||
const { t } = useI18n({ useScope: "global" });
|
||||
</script>
|
||||
<style scoped>
|
||||
#search-anchor :deep(.o-ctrl-input) {
|
||||
#search-anchor :deep(.o-input__wrapper) {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -239,7 +239,7 @@ import { useMutation } from "@vue/apollo-composable";
|
|||
import { UPDATE_DEFAULT_ACTOR } from "@/graphql/actor";
|
||||
import { changeIdentity } from "@/utils/identity";
|
||||
import { useRegistrationConfig } from "@/composition/apollo/config";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import SearchFields from "@/components/Home/SearchFields.vue";
|
||||
|
||||
const { currentUser } = useCurrentUserClient();
|
||||
|
@ -298,12 +298,12 @@ onDone(({ data }) => {
|
|||
|
||||
const showMobileMenu = ref(false);
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
const performLogout = async () => {
|
||||
console.debug("Logging out client...");
|
||||
await logout();
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t("You have been logged-out"),
|
||||
variant: "success",
|
||||
position: "bottom-right",
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
id="additional-comments"
|
||||
autofocus
|
||||
ref="reportAdditionalCommentsInput"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<li
|
||||
class="setting-menu-item"
|
||||
:class="{
|
||||
'cursor-pointer bg-mbz-yellow-alt-500 dark:bg-mbz-purple-500': isActive,
|
||||
'bg-mbz-yellow-alt-100 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-300 dark:hover:bg-mbz-purple-400 dark:text-white':
|
||||
'cursor-pointer bg-mbz-yellow-alt-500 dark:bg-mbz-purple-600': isActive,
|
||||
'bg-mbz-yellow-alt-100 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-500 dark:hover:bg-mbz-purple-600 dark:text-white':
|
||||
!isActive,
|
||||
}"
|
||||
>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<li
|
||||
class="bg-mbz-yellow-alt-300 text-violet-2 dark:bg-mbz-purple-500 dark:text-zinc-100 text-xl"
|
||||
class="bg-mbz-yellow-alt-300 text-violet-2 dark:bg-mbz-purple-700 dark:text-zinc-100 text-xl"
|
||||
>
|
||||
<router-link
|
||||
class="cursor-pointer my-2 mx-0 py-2 px-3 font-medium block no-underline"
|
||||
|
|
|
@ -13,19 +13,17 @@
|
|||
<o-tooltip
|
||||
:label="t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
>
|
||||
<o-button
|
||||
variant="primary"
|
||||
icon-right="content-paste"
|
||||
native-type="button"
|
||||
@click="copyURL"
|
||||
@keyup.enter="copyURL"
|
||||
:title="t('Copy URL to clipboard')"
|
||||
/>
|
||||
</o-tooltip>
|
||||
/>
|
||||
<o-button
|
||||
variant="primary"
|
||||
icon-right="content-paste"
|
||||
native-type="button"
|
||||
@click="copyURL"
|
||||
@keyup.enter="copyURL"
|
||||
:title="t('Copy URL to clipboard')"
|
||||
/>
|
||||
</p>
|
||||
</o-field>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
|
@ -132,7 +130,7 @@ const props = withDefaults(
|
|||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const URLInput = ref<{ $refs: { input: HTMLInputElement } } | null>(null);
|
||||
const URLInput = ref<{ $refs: { inputRef: HTMLInputElement } } | null>(null);
|
||||
|
||||
const showCopiedTooltip = ref(false);
|
||||
|
||||
|
@ -162,7 +160,7 @@ const mastodonShare = computed((): string | undefined =>
|
|||
);
|
||||
|
||||
const copyURL = (): void => {
|
||||
URLInput.value?.$refs.input.select();
|
||||
URLInput.value?.$refs.inputRef.select();
|
||||
document.execCommand("copy");
|
||||
showCopiedTooltip.value = true;
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -81,6 +81,10 @@ export const INSTANCE_FRAGMENT = gql`
|
|||
fragment InstanceFragment on Instance {
|
||||
domain
|
||||
hasRelay
|
||||
instanceName
|
||||
instanceDescription
|
||||
software
|
||||
softwareVersion
|
||||
relayAddress
|
||||
followerStatus
|
||||
followedStatus
|
||||
|
|
|
@ -1638,5 +1638,8 @@
|
|||
"Return to the event page": "Return to the event page",
|
||||
"Cancel participation": "Cancel participation",
|
||||
"Add a recipient": "Add a recipient",
|
||||
"Announcements for {eventTitle}": "Announcements for {eventTitle}"
|
||||
"Announcements for {eventTitle}": "Announcements for {eventTitle}",
|
||||
"Visit {instance_domain}": "Visit {instance_domain}",
|
||||
"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"
|
||||
}
|
|
@ -1632,5 +1632,8 @@
|
|||
"Return to the event page": "Retourner à la page de l'événement",
|
||||
"Cancel participation": "Annuler la participation",
|
||||
"Add a recipient": "Ajouter un·e destinataire",
|
||||
"Announcements for {eventTitle}": "Annonces pour {eventTitle}"
|
||||
"Announcements for {eventTitle}": "Annonces pour {eventTitle}",
|
||||
"Visit {instance_domain}": "Visiter {instance_domain}",
|
||||
"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"
|
||||
}
|
||||
|
|
193
src/i18n/gl.json
193
src/i18n/gl.json
|
@ -45,7 +45,7 @@
|
|||
"Accept": "Aceptar",
|
||||
"Accepted": "Aceptado",
|
||||
"Accessibility": "Accesibilidade",
|
||||
"Accessible only by link": "",
|
||||
"Accessible only by link": "Só accesible con ligazón",
|
||||
"Accessible only to members": "Accesible só para membros",
|
||||
"Accessible through link": "Accesible por ligazón",
|
||||
"Account": "Conta",
|
||||
|
@ -64,15 +64,15 @@
|
|||
"Add a todo": "Engadir tarefas pendentes",
|
||||
"Add an address": "Engadir un enderezo",
|
||||
"Add an instance": "Engadir unha instancia",
|
||||
"Add link": "",
|
||||
"Add link": "Engadir ligazón",
|
||||
"Add new…": "Engadir novo…",
|
||||
"Add picture": "",
|
||||
"Add picture": "Engadir imaxe",
|
||||
"Add some tags": "Engadir etiquetas",
|
||||
"Add to my calendar": "Engadir ó meu calendario",
|
||||
"Additional comments": "Comentarios adicionais",
|
||||
"Admin": "Admin",
|
||||
"Admin dashboard": "",
|
||||
"Admin settings": "",
|
||||
"Admin dashboard": "Taboleiro de Admin",
|
||||
"Admin settings": "Axustes de Admin",
|
||||
"Admin settings successfully saved.": "Gardáronse os axustes de Admin.",
|
||||
"Administration": "Administración",
|
||||
"Administrator": "Administradora",
|
||||
|
@ -93,15 +93,16 @@
|
|||
"An event from one of my groups has been published": "Publicouse un evento nun dos meus grupos",
|
||||
"An event from one of my groups has been updated or deleted": "Actualizouse ou eliminouse un evento nun dos meus grupos",
|
||||
"An instance is an installed version of the Mobilizon software running on a server. An instance can be run by anyone using the {mobilizon_software} or other federated apps, aka the “fediverse”. This instance's name is {instance_name}. Mobilizon is a federated network of multiple instances (just like email servers), users registered on different instances may communicate even though they didn't register on the same instance.": "Unha instancia é unha versión do software Mobilizon instalada nun servidor. Calquera persoa pode instalar unha instancia usando o {mobilizon_software} ou outras apps federadas, coñecidas como \"fediverso\". O nome desta instancia é {instance_name}. Mobilizon é unha rede federada de múltiples instancias (como os servidores de correo), usuarias rexistradas en diferentes servidores que poden comunicarse incluso se non están rexistradas na mesma instancia.",
|
||||
"An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events, automatically and remotely.": "Unha \"interface de programación de aplicacións\" ou \"API\" é un protocolo de comunicación que permite aos compoñentes do software comunicarse entre eles. A API de Mobilizon, por exemplo, pode permitir que ferramentas de terceiras partes se comuniquen con instancias Mobilizon para realizar certas accións, como publicar eventos de xeito automático e remoto.",
|
||||
"And {number} comments": "E {number} comentarios",
|
||||
"Announcements and mentions notifications are always sent straight away.": "As notificacións de mencións e anuncios sempre son enviadas de todas formas.",
|
||||
"Anonymous participant": "Participante anónimo",
|
||||
"Anonymous participants will be asked to confirm their participation through e-mail.": "Ós participantes anónimos pediráselle que confirmen a súa participación a través de email.",
|
||||
"Anonymous participations": "Participacións anónimas",
|
||||
"Any day": "Calquera día",
|
||||
"Any type": "",
|
||||
"Any type": "Calquera tipo",
|
||||
"Anyone can join freely": "Calquera pode unirse",
|
||||
"Anyone can request being a member, but an administrator needs to approve the membership.": "",
|
||||
"Anyone can request being a member, but an administrator needs to approve the membership.": "Calquera pode solicitar membresía, pero a administración ten que aprobar a solicitude.",
|
||||
"Anyone wanting to be a member from your group will be able to from your group page.": "Calquera que queira ser membro do teu grupo pode facelo desde a páxina do grupo.",
|
||||
"Application": "Aplicación",
|
||||
"Apply filters": "Aplicar filtros",
|
||||
|
@ -118,12 +119,12 @@
|
|||
"Are you sure you want to delete this entire discussion?": "Tes a certeza de querer eliminar o debate completo?",
|
||||
"Are you sure you want to delete this event? This action cannot be reverted.": "¿Tes a certeza de que queres eliminar este evento? Esta acción non é reversible.",
|
||||
"Are you sure you want to delete this post? This action cannot be reverted.": "Tes certeza de querer eliminar a publicación? Esta acción non ten volta.",
|
||||
"Are you sure you want to leave the group {groupName}? You'll loose access to this group's private content. This action cannot be undone.": "",
|
||||
"Are you sure you want to leave the group {groupName}? You'll loose access to this group's private content. This action cannot be undone.": "Tes certeza de querer deixar o grupo {groupName}? Non poderás acceder ao contido privado do grupo. Esta acción non ten volta atrás.",
|
||||
"As the event organizer has chosen to manually validate participation requests, your participation will be really confirmed only once you receive an email stating it's being accepted.": "Como a organización do evento escolleu validar manualmente as solicitudes, a túa participación estará realmente confirmada cando recibas un correo informándote.",
|
||||
"Ask your instance admin to {enable_feature}.": "Pídelle á administración da instancia que {enable_feature}.",
|
||||
"Assigned to": "Asignado a",
|
||||
"Atom feed for events and posts": "Fonte Atom para eventos e publicacións",
|
||||
"Attending": "",
|
||||
"Attending": "Participan",
|
||||
"Avatar": "Avatar",
|
||||
"Back to group list": "",
|
||||
"Back to previous page": "Volver á páxina anterior",
|
||||
|
@ -139,7 +140,7 @@
|
|||
"Booking": "Reservas",
|
||||
"Breadcrumbs": "Breadcrumbs",
|
||||
"Browser notifications": "Notificacións do navegador",
|
||||
"Bullet list": "",
|
||||
"Bullet list": "Lista de puntos",
|
||||
"By others": "Por outras",
|
||||
"By {group}": "Por {group}",
|
||||
"By {username}": "Por {username}",
|
||||
|
@ -147,10 +148,10 @@
|
|||
"Cancel": "Cancelar",
|
||||
"Cancel anonymous participation": "Cancelar participación anónima",
|
||||
"Cancel creation": "Cancelar creación",
|
||||
"Cancel discussion title edition": "",
|
||||
"Cancel discussion title edition": "Desbotar a edición do título da conversa",
|
||||
"Cancel edition": "Cancelar edición",
|
||||
"Cancel follow request": "",
|
||||
"Cancel membership request": "",
|
||||
"Cancel follow request": "Desbotar solicitude de seguimento",
|
||||
"Cancel membership request": "Desbotar a solicitude de membresía",
|
||||
"Cancel my participation request…": "Cancelar a miña solicitude de participación…",
|
||||
"Cancel my participation…": "Cancelar a miña participación…",
|
||||
"Cancelled": "Cancelado",
|
||||
|
@ -164,21 +165,21 @@
|
|||
"Change timezone": "Cambiar zona horaria",
|
||||
"Change user email": "Cambiar correo da usuaria",
|
||||
"Check your inbox (and your junk mail folder).": "Comproba a caixa de correo (e o cartafol de spam).",
|
||||
"Choose the source of the instance's Privacy Policy": "",
|
||||
"Choose the source of the instance's Terms": "",
|
||||
"Choose the source of the instance's Privacy Policy": "Escolle a orixe da Política de Privacidade da instancia",
|
||||
"Choose the source of the instance's Terms": "Elixe a orixe dos Termos da instancia",
|
||||
"City or region": "Cidade ou rexión",
|
||||
"Clear": "Baleirar",
|
||||
"Clear address field": "",
|
||||
"Clear date filter field": "",
|
||||
"Clear address field": "Limpar campo de enderezo",
|
||||
"Clear date filter field": "Limpar campo de data no filtro",
|
||||
"Clear participation data for all events": "Eliminar os datos de participación para todos os eventos",
|
||||
"Clear participation data for this event": "Eliminar os datos de participación para este evento",
|
||||
"Clear timezone field": "",
|
||||
"Clear timezone field": "Limpar o campo de zona horaria",
|
||||
"Click for more information": "Preme para saber máis",
|
||||
"Click to upload": "Preme para subir",
|
||||
"Close": "Pechar",
|
||||
"Close comments for all (except for admins)": "Pechar comentarios para todos (excepto admins)",
|
||||
"Closed": "Pechado",
|
||||
"Comment body": "",
|
||||
"Comment body": "Corpo do comentario",
|
||||
"Comment deleted": "Comentario eliminado",
|
||||
"Comment from {'@'}{username} reported": "Denuncia sobre o comentario de {'@'}{username}",
|
||||
"Comment text can't be empty": "O texto do comentario non pode estar baleiro",
|
||||
|
@ -212,7 +213,7 @@
|
|||
"Create discussion": "Crear conversa",
|
||||
"Create event": "Crear evento",
|
||||
"Create group": "Crear grupo",
|
||||
"Create identity": "",
|
||||
"Create identity": "Crear identidade",
|
||||
"Create my event": "Crear o meu evento",
|
||||
"Create my group": "Crear o meu grupo",
|
||||
"Create my profile": "Crear o meu perfil",
|
||||
|
@ -235,7 +236,7 @@
|
|||
"Date and time settings": "Axustes de data e hora",
|
||||
"Date parameters": "Parámetros da data",
|
||||
"Decline": "Rexeitar",
|
||||
"Decrease": "",
|
||||
"Decrease": "Diminuir",
|
||||
"Default": "Por omisión",
|
||||
"Default Mobilizon privacy policy": "Política de privacidade por omisión para Mobilizon",
|
||||
"Default Mobilizon terms": "Termos Mobilizon por omisión",
|
||||
|
@ -263,7 +264,7 @@
|
|||
"Didn't receive the instructions?": "Non recibiches as instruccións?",
|
||||
"Disabled": "Desactivado",
|
||||
"Discussions": "Comentando",
|
||||
"Discussions list": "",
|
||||
"Discussions list": "Lista de conversas",
|
||||
"Display name": "Mostrar nome",
|
||||
"Display participation price": "Mostrar prezo da participación",
|
||||
"Displayed nickname": "Nome mostrado",
|
||||
|
@ -272,7 +273,7 @@
|
|||
"Do you really want to suspend the account « {emailAccount} » ?": "Tes certeza de querer suspender a conta « {emailAccount} » ?",
|
||||
"Do you wish to {create_event} or {explore_events}?": "Queres {create_event} ou {explore_events}?",
|
||||
"Do you wish to {create_group} or {explore_groups}?": "Queres {create_group} ou {explore_groups}?",
|
||||
"Does the event needs to be confirmed later or is it cancelled?": "",
|
||||
"Does the event needs to be confirmed later or is it cancelled?": "Precísase confirmar posteriormente o evento ou se foi cancelado?",
|
||||
"Domain": "Dominio",
|
||||
"Draft": "Borrador",
|
||||
"Drafts": "Borradores",
|
||||
|
@ -323,12 +324,12 @@
|
|||
"Event already passed": "O evento xa rematou",
|
||||
"Event cancelled": "Evento cancelado",
|
||||
"Event creation": "Evento creado",
|
||||
"Event description body": "",
|
||||
"Event description body": "Corpo da descrición do evento",
|
||||
"Event edition": "Edición do evento",
|
||||
"Event list": "Lista de eventos",
|
||||
"Event metadata": "Metadatos do evento",
|
||||
"Event page settings": "Páxina de axustes do evento",
|
||||
"Event timezone will default to the timezone of the event's address if there is one, or to your own timezone setting.": "",
|
||||
"Event timezone will default to the timezone of the event's address if there is one, or to your own timezone setting.": "A zona horaria do evento terá por defecto a zona horaria do enderezo do evento (se está indicada), ou a túa zona horaria nos axustes.",
|
||||
"Event to be confirmed": "Evento agardando confirmación",
|
||||
"Event {eventTitle} deleted": "Eliminado o evento {eventTitle}",
|
||||
"Event {eventTitle} reported": "Denunciado o evento {eventTitle}",
|
||||
|
@ -341,7 +342,7 @@
|
|||
"Explore": "Descubre",
|
||||
"Explore events": "Descubrir eventos",
|
||||
"Explore!": "Descubre!",
|
||||
"Export": "",
|
||||
"Export": "Exportar",
|
||||
"Failed to get location.": "Non se obtivo a localización.",
|
||||
"Failed to save admin settings": "Fallo ó gardar os axustes de admin",
|
||||
"Featured events": "Eventos destacados",
|
||||
|
@ -356,19 +357,19 @@
|
|||
"Find an instance": "Atopar unha instancia",
|
||||
"Find another instance": "Atopa outra instancia",
|
||||
"Find or add an element": "Atopa ou engade un elemento",
|
||||
"First steps": "",
|
||||
"First steps": "Primeiros pasos",
|
||||
"Follow": "Seguir",
|
||||
"Follower": "Seguidora",
|
||||
"Followers": "Seguidoras",
|
||||
"Followers will receive new public events and posts.": "As seguidoras recibirán os novos eventos públicos e publicacións.",
|
||||
"Following the group will allow you to be informed of the {group_upcoming_public_events}, whereas joining the group means you will {access_to_group_private_content_as_well}, including group discussions, group resources and members-only posts.": "",
|
||||
"Following the group will allow you to be informed of the {group_upcoming_public_events}, whereas joining the group means you will {access_to_group_private_content_as_well}, including group discussions, group resources and members-only posts.": "Ao seguir o grupo poderás recibir información sobre {group_upcoming_public_events}, mentras que ao unirte ao grupo poderás {access_to_group_private_content_as_well}, incluíndo conversas do grupo, recursos do grupo e publicacións só para membros.",
|
||||
"Followings": "Seguindo",
|
||||
"For instance: London": "Por exemplo: Allariz",
|
||||
"For instance: London, Taekwondo, Architecture…": "Por exemplo: Leiro, Natación, Arquitectura…",
|
||||
"Forgot your password ?": "¿Esqueceches o contrasinal?",
|
||||
"Forgot your password?": "Esqueceches o contrasinal?",
|
||||
"Framadate poll": "Enquisa Framadate",
|
||||
"From my groups": "",
|
||||
"From my groups": "Dos meus grupos",
|
||||
"From the {startDate} at {startTime} to the {endDate}": "Desde o {startDate} ás {startTime} ata o {endDate}",
|
||||
"From the {startDate} at {startTime} to the {endDate} at {endTime}": "Desde o {startDate} ás {startTime} ata o {endDate} ás {endTime}",
|
||||
"From the {startDate} to the {endDate}": "Desde o {startDate} ata o {endDate}",
|
||||
|
@ -378,7 +379,7 @@
|
|||
"General": "Xeral",
|
||||
"General information": "Información xeral",
|
||||
"General settings": "Axustes xerais",
|
||||
"Geolocation was not determined in time.": "",
|
||||
"Geolocation was not determined in time.": "Non se obtivo a xeolocalización a tempo.",
|
||||
"Getting location": "Obtendo localización",
|
||||
"Getting there": "Chegar alí",
|
||||
"Glossary": "Glosario",
|
||||
|
@ -392,10 +393,10 @@
|
|||
"Group URL": "URL do grupo",
|
||||
"Group activity": "Actividade do grupo",
|
||||
"Group address": "Enderezo do grupo",
|
||||
"Group description body": "",
|
||||
"Group description body": "Corpo da descrición do grupo",
|
||||
"Group display name": "Nome mostrado do grupo",
|
||||
"Group name": "Nome do grupo",
|
||||
"Group profiles": "",
|
||||
"Group profiles": "Perfís do grupo",
|
||||
"Group settings": "Axustes do grupo",
|
||||
"Group settings saved": "Gardáronse os axustes",
|
||||
"Group short description": "Descrición curta do grupo",
|
||||
|
@ -405,9 +406,9 @@
|
|||
"Groups": "Grupos",
|
||||
"Groups are not enabled on this instance.": "Os grupos non están activados nesta instancia.",
|
||||
"Groups are spaces for coordination and preparation to better organize events and manage your community.": "Os grupos son espazos de coordinación e preparación, para organizar os eventos e xestionar a comunidade.",
|
||||
"Heading Level 1": "",
|
||||
"Heading Level 2": "",
|
||||
"Heading Level 3": "",
|
||||
"Heading Level 1": "Cabeceira Nivel 1",
|
||||
"Heading Level 2": "Cabeceira Nivel 2",
|
||||
"Heading Level 3": "Cabeceira Nivel 3",
|
||||
"Headline picture": "Imaxe de cabeceira",
|
||||
"Hide replies": "Agochar respostas",
|
||||
"Home": "Inicio",
|
||||
|
@ -437,11 +438,11 @@
|
|||
"If you have opted for manual validation of participants, Mobilizon will send you an email to inform you of new participations to be processed. You can choose the frequency of these notifications below.": "Se optaches pola validación manual das participantes, Mobilizon enviarache un correo para informarte das novas solicitudes a tratar. Podes escoller a frecuencia destas notificacións.",
|
||||
"If you want, you may send a message to the event organizer here.": "Se o desexas, aquí podes enviar unha mensaxe á organización do evento.",
|
||||
"Ignore": "Ignorar",
|
||||
"In person": "",
|
||||
"In person": "En persoa",
|
||||
"In the following context, an application is a software, either provided by the Mobilizon team or by a 3rd-party, used to interact with your instance.": "No seguinte contexto, unha aplicación é un software, proporcionado polo equipo Mobilizon ou por terceiros, utilizado para interactuar coa túa instancia.",
|
||||
"In the past": "",
|
||||
"In the past": "No pasado",
|
||||
"In this instance's network": "Na rede desta instancia",
|
||||
"Increase": "",
|
||||
"Increase": "Aumentar",
|
||||
"Instance": "Instancia",
|
||||
"Instance Long Description": "Descrición longa da instancia",
|
||||
"Instance Name": "Nome da instancia",
|
||||
|
@ -464,7 +465,7 @@
|
|||
"Instances following you": "Instancias que te seguen",
|
||||
"Instances you follow": "Instancias que segues",
|
||||
"Integrate this event with 3rd-party tools and show metadata for the event.": "Integrar este evento en ferramentas de terceiras parte e mostrar metadatos do evento.",
|
||||
"Interact": "",
|
||||
"Interact": "Interactuar",
|
||||
"Interact with a remote content": "Interactuar cun evento remoto",
|
||||
"Invite a new member": "Convida a un novo membro",
|
||||
"Invite member": "Convida a persoa",
|
||||
|
@ -492,7 +493,7 @@
|
|||
"Least recently published": "Publicado máis antigo",
|
||||
"Leave": "Saír",
|
||||
"Leave event": "Deixar o evento",
|
||||
"Leave group": "",
|
||||
"Leave group": "Deixar o grupo",
|
||||
"Leaving event \"{title}\"": "Saíndo do evento \"{title}\"",
|
||||
"Legal": "Legal",
|
||||
"Let's define a few settings": "Define algúns parámetros",
|
||||
|
@ -504,7 +505,8 @@
|
|||
"Load more activities": "Cargar máis actividades",
|
||||
"Loading comments…": "Cargando comentarios…",
|
||||
"Local": "Local",
|
||||
"Local time ({timezone})": "",
|
||||
"Local time ({timezone})": "Hora local ({timezone})",
|
||||
"Local times ({timezone})": "Horas locais ({timezone})",
|
||||
"Locality": "Localidade",
|
||||
"Location": "Localización",
|
||||
"Log in": "Acceder",
|
||||
|
@ -535,7 +537,7 @@
|
|||
"Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want.": "Mobilizon utiliza un sistema de perfís para organizar as túas actividades. Poderás crear tantos perfís como queiras.",
|
||||
"Mobilizon version": "Versión Mobilizon",
|
||||
"Mobilizon will send you an email when the events you are attending have important changes: date and time, address, confirmation or cancellation, etc.": "Mobilizón enviarache un correo cando os eventos nos que participes teñan cambios importantes: data e hora, enderezos, confirmación ou cancelación, etc.",
|
||||
"Moderate new members": "",
|
||||
"Moderate new members": "Moderar novos membros",
|
||||
"Moderated comments (shown after approval)": "Comentarios moderados (mostrados após aprobación)",
|
||||
"Moderation": "Moderación",
|
||||
"Moderation log": "Rexistro da moderación",
|
||||
|
@ -551,7 +553,7 @@
|
|||
"My identities": "Identidades",
|
||||
"NOTE! The default terms have not been checked over by a lawyer and thus are unlikely to provide full legal protection for all situations for an instance admin using them. They are also not specific to all countries and jurisdictions. If you are unsure, please check with a lawyer.": "AVISO! Os termos por omisión non foron revisados por avogados e dificilmente proporcionan cobertura legal completa para todas as situacións que se dean nun instancia. Tampouco son específicas para todos os países e xurisdicións. Se non estás segura, consúltaas cun avogado.",
|
||||
"Name": "Nome",
|
||||
"Navigated to {pageTitle}": "",
|
||||
"Navigated to {pageTitle}": "Ir a {pageTitle}",
|
||||
"New discussion": "Novo debate",
|
||||
"New email": "Novo correo",
|
||||
"New folder": "Novo cartafol",
|
||||
|
@ -629,7 +631,7 @@
|
|||
"On {date} from {startTime} to {endTime}": "O {date} desde {startTime} ás {endTime}",
|
||||
"On {date} starting at {startTime}": "O {date} comezando ás {startTime}",
|
||||
"On {instance} and other federated instances": "En {instance} e outras instancias federadas",
|
||||
"Online": "",
|
||||
"Online": "En liña",
|
||||
"Online ticketing": "Entradas por internet",
|
||||
"Only accessible through link": "Accesible só a través da ligazón",
|
||||
"Only accessible through link (private)": "Só accesible desde a ligazón {private}",
|
||||
|
@ -638,13 +640,13 @@
|
|||
"Only group members can access discussions": "Só os membros do grupo poden acceder ás conversas",
|
||||
"Only group moderators can create, edit and delete events.": "Só os moderadores do grupo poden crear, editar e eliminar eventos.",
|
||||
"Only group moderators can create, edit and delete posts.": "Só as moderadoras do grupo poden crear, editar e eliminar publicacións.",
|
||||
"Only registered users may fetch remote events from their URL.": "",
|
||||
"Only registered users may fetch remote events from their URL.": "Só as usuarias rexistradas poden recibir elementos remotos co seu URL.",
|
||||
"Open": "Abrir",
|
||||
"Open a topic on our forum": "Abrir un tema no noso foro",
|
||||
"Open an issue on our bug tracker (advanced users)": "Abrir un informe no noso seguimento de fallos (usuarias avanzadas)",
|
||||
"Opened reports": "Denuncias abertas",
|
||||
"Or": "Ou",
|
||||
"Ordered list": "",
|
||||
"Ordered list": "Lista ordenada",
|
||||
"Organized": "Organizado",
|
||||
"Organized by": "Organizado por",
|
||||
"Organized by {name}": "Organizado por {name}",
|
||||
|
@ -670,8 +672,8 @@
|
|||
"Participation confirmation": "Confirmación da participación",
|
||||
"Participation notifications": "Notificacións da participación",
|
||||
"Participation requested!": "Participación solicitada!",
|
||||
"Participation with account": "",
|
||||
"Participation without account": "",
|
||||
"Participation with account": "Participación coa conta",
|
||||
"Participation without account": "Participación sen conta",
|
||||
"Participations": "Participacións",
|
||||
"Password": "Contrasinal",
|
||||
"Password (confirmation)": "Contrasinal (confirmación)",
|
||||
|
@ -693,19 +695,19 @@
|
|||
"Please make sure the address is correct and that the page hasn't been moved.": "Comproba ben o enderezo e que a páxina non foi movida a outro lugar.",
|
||||
"Please read the {fullRules} published by {instance}'s administrators.": "Por favor le as {fullRules} publicadas pola administración de {instance}.",
|
||||
"Post": "Publicación",
|
||||
"Post URL": "",
|
||||
"Post URL": "URL da publicación",
|
||||
"Post a comment": "Comenta",
|
||||
"Post a reply": "Publica unha resposta",
|
||||
"Post body": "",
|
||||
"Post body": "Corpo da publicación",
|
||||
"Post {eventTitle} reported": "Denunciado o evento {eventTitle}",
|
||||
"Postal Code": "Código Postal",
|
||||
"Posts": "Publicacións",
|
||||
"Powered by Mobilizon": "",
|
||||
"Powered by Mobilizon": "Grazas a Mobilizon",
|
||||
"Powered by {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Made with the financial support of {contributors}.": "Funciona grazas a {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Co soporte financieiro das {contributors}.",
|
||||
"Preferences": "Preferencias",
|
||||
"Previous": "Anterior",
|
||||
"Previous email": "Correo anterior",
|
||||
"Previous month": "",
|
||||
"Previous month": "Mes anterior",
|
||||
"Previous page": "Páxina anterior",
|
||||
"Price sheet": "Tarifa",
|
||||
"Privacy": "Privacidade",
|
||||
|
@ -727,7 +729,7 @@
|
|||
"Public preview": "Vista previa pública",
|
||||
"Publication date": "Data de publicación",
|
||||
"Publish": "Publicar",
|
||||
"Published by {name}": "",
|
||||
"Published by {name}": "Publicado por {name}",
|
||||
"Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations": "Eventos publicados con <b>{comments}</b> comentarios e <b>{participations}</b> participacións confirmadas",
|
||||
"Push": "Push",
|
||||
"Quote": "Citar",
|
||||
|
@ -737,9 +739,9 @@
|
|||
"Receive one email for each activity": "Recibir un correo por cada actividade",
|
||||
"Receive one email per request": "Recibir un correo por solicitude",
|
||||
"Redirecting in progress…": "Redireccionando…",
|
||||
"Redirecting to Mobilizon": "",
|
||||
"Redirecting to Mobilizon": "Redirixindo a Mobilizon",
|
||||
"Redirecting to content…": "Redirixindo ó contido…",
|
||||
"Redo": "",
|
||||
"Redo": "Volver facer",
|
||||
"Refresh profile": "Actualiza perfil",
|
||||
"Regenerate new links": "Recrear novas ligazóns",
|
||||
"Region": "Rexión",
|
||||
|
@ -767,7 +769,7 @@
|
|||
"Report this comment": "Denunciar este comentario",
|
||||
"Report this event": "Denunciar este evento",
|
||||
"Report this group": "Denunciar este grupo",
|
||||
"Report this post": "",
|
||||
"Report this post": "Denuncia esta publicación",
|
||||
"Reported": "Denunciado",
|
||||
"Reported by": "Denunciado por",
|
||||
"Reported by someone on {domain}": "Foi denunciado por alguén desde {domain}",
|
||||
|
@ -775,7 +777,7 @@
|
|||
"Reported group": "Grupo denunciado",
|
||||
"Reported identity": "Identidade denunciada",
|
||||
"Reports": "Denuncias",
|
||||
"Reports list": "",
|
||||
"Reports list": "Lista de denuncias",
|
||||
"Request for participation confirmation sent": "Solicitar o envío da confirmación de participación",
|
||||
"Resend confirmation email": "Reenviar correo de confirmación",
|
||||
"Resent confirmation email": "Reenviado o correo de confirmación",
|
||||
|
@ -806,7 +808,7 @@
|
|||
"Send": "Enviar",
|
||||
"Send email": "Enviar correo",
|
||||
"Send notification e-mails": "Enviar emails de notificacións",
|
||||
"Send password reset": "",
|
||||
"Send password reset": "Enviar restablecemento de contrasinal",
|
||||
"Send the confirmation email again": "Enviar o correo de confirmación outra vez",
|
||||
"Send the report": "Enviar a denuncia",
|
||||
"Set an URL to a page with your own privacy policy.": "Establece o URL da páxina coa túa política de privacidade.",
|
||||
|
@ -815,20 +817,22 @@
|
|||
"Share": "Compartir",
|
||||
"Share this event": "Compartir este evento",
|
||||
"Share this group": "Compartir este grupo",
|
||||
"Share this post": "",
|
||||
"Share this post": "Comparte esta publicación",
|
||||
"Short bio": "Bio curta",
|
||||
"Show map": "Mostrar mapa",
|
||||
"Show me where I am": "Mostra onde me atopo",
|
||||
"Show remaining number of places": "Mostrar o número de prazas restantes",
|
||||
"Show the time when the event begins": "Mostrar a hora á que comeza o evento",
|
||||
"Show the time when the event ends": "Mostar a hora na que remata o evento",
|
||||
"Showing events before": "",
|
||||
"Showing events starting on": "",
|
||||
"Showing events before": "Mostrando eventos antes do",
|
||||
"Showing events starting on": "Mostrando eventos a partir do",
|
||||
"Sign Language": "Idioma de signos",
|
||||
"Sign in with": "Acceder con",
|
||||
"Sign up": "Rexistro",
|
||||
"Since you are a new member, private content can take a few minutes to appear.": "Como es un novo membro, o contido privado podería tardar uns minutos en aparecer.",
|
||||
"Skip to main content": "",
|
||||
"Skip to main content": "Ir directamente ao contido",
|
||||
"Smoke free": "Libre de fume",
|
||||
"Smoking allowed": "Permítese fumar",
|
||||
"Social": "Social",
|
||||
"Some terms, technical or otherwise, used in the text below may cover concepts that are difficult to grasp. We have provided a glossary here to help you understand them better:": "Algúns termos, técnicos ou doutro tipo, utilizados no texto poderían referirse a conceptos difíciles de entender. Aquí tes un glosario para axudarche a entendelos mellor:",
|
||||
"Starts on…": "Comeza o…",
|
||||
|
@ -839,7 +843,7 @@
|
|||
"Suspend": "Suspender",
|
||||
"Suspend group": "Suspende grupo",
|
||||
"Suspended": "Suspendida",
|
||||
"Tag search": "",
|
||||
"Tag search": "Buscar etiqueta",
|
||||
"Task lists": "Listas de tarefas",
|
||||
"Technical details": "Detalles técnicos",
|
||||
"Tentative": "Provisional",
|
||||
|
@ -847,7 +851,7 @@
|
|||
"Terms": "Termos",
|
||||
"Terms of service": "Termos do servizo",
|
||||
"Text": "Texto",
|
||||
"That you follow or of which you are a member": "",
|
||||
"That you follow or of which you are a member": "Que sigues ou do que es membro",
|
||||
"The Big Blue Button video teleconference URL": "URL da videoconferencia utilizando The Big Blue Button",
|
||||
"The Google Meet video teleconference URL": "URL da videoconferencia a través de Google Meet",
|
||||
"The Jitsi Meet video teleconference URL": "URL da videoconferencia usando Jitsi Meet",
|
||||
|
@ -867,7 +871,7 @@
|
|||
"The event has been updated": "O evento foi actualizado",
|
||||
"The event has been updated and published": "O evento foi actualizado e publicado",
|
||||
"The event hasn't got a sign language interpreter": "O evento non ten intérprete de lingua de signos",
|
||||
"The event is fully online": "",
|
||||
"The event is fully online": "Evento totalmente en liña",
|
||||
"The event live video contains subtitles": "O vídeo do evento en directo contén subtítulos",
|
||||
"The event live video does not contain subtitles": "O vídeo do evento en directo non contén subtítulos",
|
||||
"The event organiser has chosen to validate manually participations. Do you want to add a little note to explain why you want to participate to this event?": "O organizador do evento escolleu validar manualmente as participacións. Desexas engadir unha nota explicando a razón pola que queres participar no evento?",
|
||||
|
@ -882,7 +886,7 @@
|
|||
"The event {event} was updated by {profile}.": "{profile} actualizou o evento {evento}.",
|
||||
"The events you created are not shown here.": "Os eventos que ti creaches non se mostran aquí.",
|
||||
"The geolocation prompt was denied.": "",
|
||||
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "",
|
||||
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "Agora calquera pode unirse ao grupo, pero os novos membros teñen que ser aprobados pola administración.",
|
||||
"The group can now be joined by anyone.": "Agora calquera pode unirse ao grupo.",
|
||||
"The group can now only be joined with an invite.": "Agora só se pode acceder ao grupo cun convite.",
|
||||
"The group will be publicly listed in search results and may be suggested in the explore section. Only public informations will be shown on it's page.": "Este grupo aparecerá en resultados de buscas e podería ser suxerido na sección descubrir. Só se mostrará información pública nesta páxina.",
|
||||
|
@ -933,7 +937,7 @@
|
|||
"This month": "Este mes",
|
||||
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator.": "Esta publicación só é accesible para membros. Ti tes acceso a ela para poder moderala xa que es moderadora da instancia.",
|
||||
"This post is accessible only through it's link. Be careful where you post this link.": "Este evento só é accesible coa súa ligazón. Ten tino de onde publicas esta ligazón.",
|
||||
"This profile is from another instance, the informations shown here may be incomplete.": "",
|
||||
"This profile is from another instance, the informations shown here may be incomplete.": "Este perfil pertence a outra instancia, a información que ves podería estar incompleta.",
|
||||
"This profile was not found": "",
|
||||
"This setting will be used to display the website and send you emails in the correct language.": "Este axuste usarase para mostrar o sitio web e enviarche os correos no idioma correcto.",
|
||||
"This user was not found": "",
|
||||
|
@ -941,8 +945,8 @@
|
|||
"This week": "Esta semana",
|
||||
"This weekend": "Este fin de semana",
|
||||
"This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "Esto eliminará / anonimazará todo o contido (eventos, comentarios, mensaxes, participacións...) creado con esta identidade.",
|
||||
"Time in your timezone ({timezone})": "",
|
||||
"Times in your timezone ({timezone})": "",
|
||||
"Time in your timezone ({timezone})": "Hora na túa zona horaria ({timezone})",
|
||||
"Times in your timezone ({timezone})": "Horas na túa zona horaria ({timezone})",
|
||||
"Timezone": "Zona horaria",
|
||||
"Timezone detected as {timezone}.": "Zona horaria detectada como {timezone}.",
|
||||
"Title": "Título",
|
||||
|
@ -973,24 +977,24 @@
|
|||
"Unable to load event for participation. The error details are provided below:": "Non se puido cargar a participación no evento. Aquí abaixo tes detalles do erro:",
|
||||
"Unable to save your participation in this browser.": "Non se puido gardar a túa participación neste navegador.",
|
||||
"Unable to update the profile. The avatar picture may be too heavy.": "Non se puido actualizar o perfil. A imaxe do avatar podería ser demasiado grande.",
|
||||
"Underline": "",
|
||||
"Undo": "",
|
||||
"Underline": "Subliñar",
|
||||
"Undo": "Desfacer",
|
||||
"Unfollow": "Deixar de seguir",
|
||||
"Unfortunately, your participation request was rejected by the organizers.": "Desgraciadamente a túa solicitude de participación foi rexeitada pola organización.",
|
||||
"Unknown": "Descoñecido",
|
||||
"Unknown actor": "Actor descoñecido",
|
||||
"Unknown error.": "Erro descoñecido.",
|
||||
"Unknown value for the openness setting.": "Axuste descoñecido para o acceso ao grupo.",
|
||||
"Unlogged participation": "",
|
||||
"Unlogged participation": "Participación sen rexistrarse",
|
||||
"Unsaved changes": "Cambios non gardados",
|
||||
"Unsubscribe to browser push notifications": "Retirar subscrición ás notificacións push do navegdor",
|
||||
"Unsuspend": "Reactivar",
|
||||
"Upcoming": "Próximamente",
|
||||
"Upcoming events": "Eventos próximos",
|
||||
"Upcoming events from your groups": "",
|
||||
"Upcoming events from your groups": "Eventos previstos para os teus grupos",
|
||||
"Update": "Actualizar",
|
||||
"Update app": "Actualizar app",
|
||||
"Update discussion title": "",
|
||||
"Update discussion title": "Actualiza o título da conversa",
|
||||
"Update event {name}": "Actualizar evento {name}",
|
||||
"Update group": "Actualizar grupo",
|
||||
"Update my event": "Actualizar o meu evento",
|
||||
|
@ -1002,7 +1006,7 @@
|
|||
"User settings": "Axustes da usuaria",
|
||||
"Username": "Identificador",
|
||||
"Users": "Usuarias",
|
||||
"Validating account": "",
|
||||
"Validating account": "Validando a conta",
|
||||
"Validating email": "Validando o correo",
|
||||
"Video Conference": "Videoconferencia",
|
||||
"View a reply": "|Ver unha resposta|Ver {totalReplies} respostas",
|
||||
|
@ -1012,7 +1016,7 @@
|
|||
"View all posts": "Ver publicacións",
|
||||
"View event page": "Ver páxina do evento",
|
||||
"View everything": "Velo todo",
|
||||
"View full profile": "",
|
||||
"View full profile": "Ver perfil completo",
|
||||
"View less": "Ver menos",
|
||||
"View more": "Ver máis",
|
||||
"View page on {hostname} (in a new window)": "Ver páxina en {hostname} (nova ventá)",
|
||||
|
@ -1040,12 +1044,13 @@
|
|||
"What can I do to help?": "Que podo facer para axudar?",
|
||||
"Wheelchair accessibility": "Accesible con cadeira de rodas",
|
||||
"When a moderator from the group creates an event and attributes it to the group, it will show up here.": "Aquí aparecerá un evento cando un moderador do grupo o cree e o atribúa ó grupo.",
|
||||
"When the event is private, you'll need to share the link around.": "",
|
||||
"When the post is private, you'll need to share the link around.": "",
|
||||
"When the event is private, you'll need to share the link around.": "Se o evento é privado, deberás compartir ti a ligazón.",
|
||||
"When the post is private, you'll need to share the link around.": "Se a publicación é privada, deberás compartir ti a ligazón.",
|
||||
"Whether smoking is prohibited during the event": "Se está prohibido fumar durante o evento",
|
||||
"Whether the event is accessible with a wheelchair": "Se o evento é accesible ou non con cadeira de rodas",
|
||||
"Whether the event is interpreted in sign language": "Se o evento está interpretado en lingua de signos",
|
||||
"Whether the event live video is subtitled": "Se o evento está subtitulado ou non",
|
||||
"Who can post a comment?": "",
|
||||
"Who can post a comment?": "Quen pode comentar?",
|
||||
"Who can view this event and participate": "Quen pode ver e participar neste evento",
|
||||
"Who can view this post": "Quen pode ver esta publicación",
|
||||
"Who published {number} events": "Que publicaron {number} eventos",
|
||||
|
@ -1055,7 +1060,7 @@
|
|||
"Yesterday": "Onte",
|
||||
"You accepted the invitation to join the group.": "Aceptaches o convite para unirte ao grupo.",
|
||||
"You added the member {member}.": "Engadiches a {member}.",
|
||||
"You approved {member}'s membership.": "",
|
||||
"You approved {member}'s membership.": "Aprobaches a membresía de {member}.",
|
||||
"You archived the discussion {discussion}.": "Arquivaches o debate {discussion}.",
|
||||
"You are not an administrator for this group.": "Non es administradora deste grupo.",
|
||||
"You are not part of any group.": "Non formas parte de ningún grupo.",
|
||||
|
@ -1084,9 +1089,9 @@
|
|||
"You demoted {member} to simple member.": "Degradaches a {member] a membresía básica.",
|
||||
"You didn't create or join any event yet.": "Aínda non te uniches nin creaches un evento.",
|
||||
"You don't follow any instances yet.": "Aínda non segues ningunha instancia.",
|
||||
"You don't have any upcoming events. Maybe try another filter?": "",
|
||||
"You don't have any upcoming events. Maybe try another filter?": "Non tes eventos previstos. Queres probar con outro filtro?",
|
||||
"You excluded member {member}.": "Excluíches a {member}.",
|
||||
"You have attended {count} events in the past.": "",
|
||||
"You have attended {count} events in the past.": "Non participaches en ningún evento no pasado.|Participaches nun evento no pasado.|Participaches en {count} eventos no pasado.",
|
||||
"You have been invited by {invitedBy} to the following group:": "Foches convidada por {invitedBy} ó seguinte grupo:",
|
||||
"You have been removed from this group's members.": "Sacáronte deste grupo.",
|
||||
"You have cancelled your participation": "Cancelaches a túa participación",
|
||||
|
@ -1126,7 +1131,7 @@
|
|||
"You will be able to add an avatar and set other options in your account settings.": "Poderás engadir un avatar e establecer outras opcións nos axustes da conta.",
|
||||
"You will be redirected to the original instance": "Vas ser redirixida á instancia orixinal",
|
||||
"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.": "Aquí atoparás tódolos eventos que creaches ou dos que es partícipe, tamén os eventos organizados por grupos que segues ou aos que pertences.",
|
||||
"You will receive notifications about this group's public activity depending on %{notification_settings}.": "",
|
||||
"You will receive notifications about this group's public activity depending on %{notification_settings}.": "Vas recibir notificacións sobre a actividade pública deste grupo dependendo dos %{notification_settings}.",
|
||||
"You wish to participate to the following event": "Queres participar no seguinte evento",
|
||||
"You'll get a weekly recap every Monday for upcoming events, if you have any.": "Recibirás un resumen semanal cada Luns para os próximos eventos, se os tes.",
|
||||
"You'll need to change the URLs where there were previously entered.": "Precisarás cambiar os URLs alá onde fosen previamente utilizados.",
|
||||
|
@ -1146,7 +1151,7 @@
|
|||
"Your email is being changed": "Estase cambiando o teu correo",
|
||||
"Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer.": "O teu correo só se utilizará para confirmar que es unha persoa real e enviarche posibles actualizacións para o evento. NON será transferido a outras instancias ou á organización do evento.",
|
||||
"Your federated identity": "A túa identidade federada",
|
||||
"Your membership was approved by {profile}.": "",
|
||||
"Your membership was approved by {profile}.": "{profile} aprobou a túa membresía.",
|
||||
"Your participation has been confirmed": "A túa participación foi confirmada",
|
||||
"Your participation has been rejected": "A túa participación foi rexeitada",
|
||||
"Your participation has been requested": "A túa participación foi solicitada",
|
||||
|
@ -1157,7 +1162,7 @@
|
|||
"Your participation still has to be approved by the organisers.": "A participación aínda debe ser aprobada pola organización.",
|
||||
"Your participation will be validated once you click the confirmation link into the email, and after the organizer manually validates your participation.": "A túa participación validarase unha vez premas na ligazón de confirmación do correo, e após a organización validará manualmente a participación.",
|
||||
"Your participation will be validated once you click the confirmation link into the email.": "A túa participación será validada unha vez premas na ligazón de confirmación do correo.",
|
||||
"Your position was not available.": "",
|
||||
"Your position was not available.": "A túa posición non estaba dispoñible.",
|
||||
"Your profile will be shown as contact.": "O teu perfil será mostrado como contacto.",
|
||||
"Your timezone is currently set to {timezone}.": "Zona horaria actual establecida a {timezone}.",
|
||||
"Your timezone was detected as {timezone}.": "Zona horaria detectada como {timezone}.",
|
||||
|
@ -1170,7 +1175,7 @@
|
|||
"[This comment has been deleted]": "[Este comentario foi eliminado]",
|
||||
"[deleted]": "[eliminado]",
|
||||
"a non-existent report": "denuncia non existente",
|
||||
"access to the group's private content as well": "",
|
||||
"access to the group's private content as well": "accede tamén ao contido privado do grupo",
|
||||
"and {number} groups": "e {number} grupos",
|
||||
"any distance": "calquera distancia",
|
||||
"as {identity}": "como {identity}",
|
||||
|
@ -1185,7 +1190,7 @@
|
|||
"explore the events": "descubre os eventos",
|
||||
"explore the groups": "atopar grupos",
|
||||
"full rules": "normas completas",
|
||||
"group's upcoming public events": "",
|
||||
"group's upcoming public events": "Próximos eventos públicos do grupo",
|
||||
"https://mensuel.framapad.org/p/some-secret-token": "https://mensuel.framapad.org/p/some-secret-token",
|
||||
"iCal Feed": "Fonte iCal",
|
||||
"instance rules": "normas da instancia",
|
||||
|
@ -1198,21 +1203,21 @@
|
|||
"terms of service": "termos do servizo",
|
||||
"tool designed to serve you": "ferramenta creada para servirte",
|
||||
"with another identity…": "con outra identidade…",
|
||||
"your notification settings": "",
|
||||
"your notification settings": "axustes das túas notificacións",
|
||||
"{'@'}{username}": "{'@'}{username}",
|
||||
"{approved} / {total} seats": "{approved} / {total} prazas",
|
||||
"{available}/{capacity} available places": "Non quedan prazas|{available}/{capacity} prazas dispoñibles",
|
||||
"{count} km": "{count} km",
|
||||
"{count} members": "Sen membros|Un membro|{count} membros",
|
||||
"{count} members or followers": "",
|
||||
"{count} members or followers": "Sen membros ou seguidoras|Un membro ou seguidora|{count} membros ou seguidoras",
|
||||
"{count} participants": "Sen participantes | Un participante | {count} participantes",
|
||||
"{count} requests waiting": "{count} solicitudes agardando",
|
||||
"{folder} - Resources": "{folder} - Recursos",
|
||||
"{group} activity timeline": "Cronoloxía de actividade de {group}",
|
||||
"{group} events": "Eventos de {group}",
|
||||
"{group} posts": "",
|
||||
"{group} posts": "{group} publicacións",
|
||||
"{group}'s events": "Eventos de {group}",
|
||||
"{group}'s todolists": "",
|
||||
"{group}'s todolists": "Tarefas pendentes de {group}",
|
||||
"{instanceName} is an instance of the {mobilizon} software.": "{instanceName} é unha instancia do software {mobilizon}.",
|
||||
"{instanceName} is an instance of {mobilizon_link}, a free software built with the community.": "{instanceName} é unha instancia de {mobilizon_link}, software libre feito para a comunidade.",
|
||||
"{member} accepted the invitation to join the group.": "{member} aceptou o convite para unirse ao grupo.",
|
||||
|
@ -1239,11 +1244,11 @@
|
|||
"{number} organized events": "Sen eventos organizados|Un evento organizado|{number} eventos organizados",
|
||||
"{number} participations": "Sen participacións|Unha participación|{number} participacións",
|
||||
"{number} posts": "Sen publicacións|Unha publicación|{number} publicacións",
|
||||
"{number} seats left": "",
|
||||
"{number} seats left": "Quedan {number} asentos",
|
||||
"{old_group_name} was renamed to {group}.": "{old_group_name} mudou de nome a {group}.",
|
||||
"{profile} (by default)": "{profile} (by default)",
|
||||
"{profile} added the member {member}.": "{profile} engadiu a {member}.",
|
||||
"{profile} approved {member}'s membership.": "",
|
||||
"{profile} approved {member}'s membership.": "{profile} aprobou a membresía de {member}.",
|
||||
"{profile} archived the discussion {discussion}.": "{profile} arquivou o debate {discussion}.",
|
||||
"{profile} created the discussion {discussion}.": "{profile} creou o debate {discussion}.",
|
||||
"{profile} created the folder {resource}.": "{profile} creou o cartafol {resource}.",
|
||||
|
@ -1273,7 +1278,7 @@
|
|||
"{profile} replied to the discussion {discussion}.": "{profile} respondeu ao debate {discussion}.",
|
||||
"{profile} updated the group {group}.": "{profile} actualizou o grupo {group}.",
|
||||
"{profile} updated the member {member}.": "{profile} actualizou a participante {member].",
|
||||
"{timezoneLongName} ({timezoneShortName})": "",
|
||||
"{timezoneLongName} ({timezoneShortName})": "{timezoneLongName} ({timezoneShortName})",
|
||||
"{title} ({count} todos)": "{title} ({count} pendentes)",
|
||||
"{username} was invited to {group}": "{username} foi convidada a {group}",
|
||||
"© The OpenStreetMap Contributors": "© The OpenStreetMap Contributors"
|
||||
|
|
1
src/i18n/th.json
Normal file
1
src/i18n/th.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -18,7 +18,7 @@ import { notifierPlugin } from "./plugins/notifier";
|
|||
import FloatingVue from "floating-vue";
|
||||
import "floating-vue/dist/style.css";
|
||||
import Oruga from "@oruga-ui/oruga-next";
|
||||
import "@oruga-ui/oruga-next/dist/oruga.css";
|
||||
import "@oruga-ui/theme-oruga/dist/oruga.css";
|
||||
import "./assets/oruga-tailwindcss.css";
|
||||
import { orugaConfig } from "./oruga-config";
|
||||
import MaterialIcon from "./components/core/MaterialIcon.vue";
|
||||
|
|
|
@ -22,22 +22,24 @@ export const orugaConfig = {
|
|||
roundedClass: "rounded",
|
||||
variantClass: "input-",
|
||||
iconRightClass: "input-icon-right",
|
||||
sizeClass: "input-size-",
|
||||
},
|
||||
inputitems: {
|
||||
itemClass: "inputitems-item",
|
||||
containerClass: "rounded",
|
||||
taginput: {
|
||||
itemClass: "taginput-item",
|
||||
rootClass: "taginput",
|
||||
},
|
||||
autocomplete: {
|
||||
menuClass: "autocomplete-menu",
|
||||
rootClass: "autocomplete",
|
||||
itemClass: "autocomplete-item",
|
||||
itemGroupTitleClass: "autocomplete-item-group-title",
|
||||
},
|
||||
icon: {
|
||||
variantClass: "icon-",
|
||||
},
|
||||
checkbox: {
|
||||
rootClass: "checkbox",
|
||||
checkClass: "checkbox-check",
|
||||
checkCheckedClass: "checkbox-checked",
|
||||
inputClass: "checkbox-check",
|
||||
inputCheckedClass: "checkbox-checked",
|
||||
labelClass: "checkbox-label",
|
||||
},
|
||||
dropdown: {
|
||||
|
@ -62,14 +64,15 @@ export const orugaConfig = {
|
|||
},
|
||||
switch: {
|
||||
labelClass: "switch-label",
|
||||
checkCheckedClass: "switch-check-checked",
|
||||
switchCheckedClass: "switch-check-checked",
|
||||
},
|
||||
select: {
|
||||
selectClass: "select",
|
||||
},
|
||||
radio: {
|
||||
checkCheckedClass: "radio-checked",
|
||||
checkClass: "form-radio",
|
||||
rootClass: "radio",
|
||||
inputCheckedClass: "radio-checked",
|
||||
inputClass: "form-radio",
|
||||
labelClass: "radio-label",
|
||||
},
|
||||
notification: {
|
||||
|
@ -90,8 +93,8 @@ export const orugaConfig = {
|
|||
linkClass: "pagination-link",
|
||||
linkCurrentClass: "pagination-link-current",
|
||||
linkDisabledClass: "pagination-link-disabled",
|
||||
nextBtnClass: "pagination-next",
|
||||
prevBtnClass: "pagination-previous",
|
||||
nextButtonClass: "pagination-next",
|
||||
prevButtonClass: "pagination-previous",
|
||||
ellipsisClass: "pagination-ellipsis",
|
||||
},
|
||||
tabs: {
|
||||
|
@ -99,7 +102,7 @@ export const orugaConfig = {
|
|||
navTabsClass: "tabs-nav",
|
||||
navTypeClass: "tabs-nav-",
|
||||
navSizeClass: "tabs-nav-",
|
||||
tabItemWrapperClass: "tabs-nav-item-wrapper",
|
||||
itemWrapperClass: "tabs-nav-item-wrapper",
|
||||
itemHeaderTypeClass: "tabs-nav-item-",
|
||||
itemHeaderActiveClass: "tabs-nav-item-active-",
|
||||
},
|
||||
|
|
|
@ -26,7 +26,6 @@ export class Notifier {
|
|||
duration: 5000,
|
||||
position: "bottom-right",
|
||||
type,
|
||||
hasIcon: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ import { InstanceFollowStatus } from "./enums";
|
|||
export interface IInstance {
|
||||
domain: string;
|
||||
hasRelay: boolean;
|
||||
instanceName: string | null;
|
||||
instanceDescription: string | null;
|
||||
software: string | null;
|
||||
softwareVersion: string | null;
|
||||
relayAddress: string | null;
|
||||
followerStatus: InstanceFollowStatus;
|
||||
followedStatus: InstanceFollowStatus;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
@input="(event: any) => updateUsername(event.target.value)"
|
||||
id="identity-display-name"
|
||||
dir="auto"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -69,6 +70,7 @@
|
|||
aria-required="false"
|
||||
v-model="identity.summary"
|
||||
id="identity-summary"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -118,41 +120,37 @@
|
|||
<o-tooltip
|
||||
:label="t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.atom"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
/>
|
||||
<o-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ t("RSS/Atom Feed") }}</o-button
|
||||
>
|
||||
<o-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ t("RSS/Atom Feed") }}</o-button
|
||||
>
|
||||
</o-tooltip>
|
||||
<o-tooltip
|
||||
:label="t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.ics"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
/>
|
||||
<o-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ t("ICS/WebCal Feed") }}</o-button
|
||||
>
|
||||
<o-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ t("ICS/WebCal Feed") }}</o-button
|
||||
>
|
||||
</o-tooltip>
|
||||
<o-button
|
||||
icon-left="refresh"
|
||||
variant="text"
|
||||
|
|
|
@ -2,138 +2,187 @@
|
|||
<div v-if="instance">
|
||||
<breadcrumbs-nav
|
||||
:links="[
|
||||
{ name: RouteName.ADMIN, text: $t('Admin') },
|
||||
{ name: RouteName.INSTANCES, text: $t('Instances') },
|
||||
{ name: RouteName.ADMIN, text: t('Admin') },
|
||||
{ name: RouteName.INSTANCES, text: t('Instances') },
|
||||
{ text: instance.domain },
|
||||
]"
|
||||
/>
|
||||
<h1 class="text-2xl">{{ instance.domain }}</h1>
|
||||
<div
|
||||
class="grid md:grid-cols-2 xl:grid-cols-4 gap-2 content-center text-center mt-2"
|
||||
<section
|
||||
class="flex flex-wrap md:flex-nowrap items-center justify-between gap-4"
|
||||
>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.PROFILES,
|
||||
query: { domain: instance.domain },
|
||||
}"
|
||||
<div>
|
||||
<h2 class="text-4xl font-bold" v-if="instance.instanceName">
|
||||
{{ instance.instanceName }}
|
||||
</h2>
|
||||
<h2 class="text-4xl font-bold" v-else>{{ instance.domain }}</h2>
|
||||
<p
|
||||
v-if="instance.instanceDescription"
|
||||
class="text-slate-700 dark:text-slate-400 my-4"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.personCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Profiles") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-gray-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.ADMIN_GROUPS,
|
||||
query: { domain: instance.domain },
|
||||
}"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.groupCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Groups") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.followingsCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Followings") }}</span>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.followersCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Followers") }}</span>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORTS, query: { domain: instance.domain } }"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.reportsCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Reports") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 font-semibold block">{{
|
||||
formatBytes(instance.mediaSize)
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ $t("Uploaded media size") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 grid xl:grid-cols-2 gap-4">
|
||||
<div
|
||||
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md"
|
||||
v-if="instance.hasRelay"
|
||||
>
|
||||
<button
|
||||
@click="
|
||||
removeInstanceFollow({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followedStatus == InstanceFollowStatus.APPROVED"
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ $t("Stop following instance") }}
|
||||
</button>
|
||||
<button
|
||||
@click="
|
||||
removeInstanceFollow({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-else-if="instance.followedStatus == InstanceFollowStatus.PENDING"
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ $t("Cancel follow request") }}
|
||||
</button>
|
||||
<button
|
||||
@click="followInstance"
|
||||
v-else
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ $t("Follow instance") }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="md:h-48 py-16 text-center opacity-50">
|
||||
{{ $t("Only Mobilizon instances can be followed") }}
|
||||
</div>
|
||||
<div
|
||||
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2"
|
||||
>
|
||||
<button
|
||||
@click="
|
||||
acceptInstance({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followerStatus == InstanceFollowStatus.PENDING"
|
||||
class="bg-green-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ $t("Accept follow") }}
|
||||
</button>
|
||||
<button
|
||||
@click="
|
||||
rejectInstance({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followerStatus != InstanceFollowStatus.NONE"
|
||||
class="bg-red-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ $t("Reject follow") }}
|
||||
</button>
|
||||
<p v-if="instance.followerStatus == InstanceFollowStatus.NONE">
|
||||
{{ $t("This instance doesn't follow yours.") }}
|
||||
{{ instance.instanceDescription }}
|
||||
</p>
|
||||
<i18n-t
|
||||
v-if="instance.software && instance.softwareVersion"
|
||||
keypath="Software details: {software_details}"
|
||||
class="my-4"
|
||||
>
|
||||
<template #software_details>
|
||||
<span class="capitalize">
|
||||
{{ instance.software }} - {{ instance.softwareVersion }}
|
||||
</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
<o-button
|
||||
tag="a"
|
||||
:href="`https://${instance.domain}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
icon-right="open-in-new"
|
||||
class="mx-auto md:mx-0"
|
||||
>{{
|
||||
t("Visit {instance_domain}", { instance_domain: instance.domain })
|
||||
}}
|
||||
</o-button>
|
||||
</section>
|
||||
<section>
|
||||
<div
|
||||
class="grid md:grid-cols-2 xl:grid-cols-4 gap-2 content-center text-center mt-2"
|
||||
>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.PROFILES,
|
||||
query: { domain: instance.domain },
|
||||
}"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.personCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Profiles") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-gray-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.ADMIN_GROUPS,
|
||||
query: { domain: instance.domain },
|
||||
}"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.groupCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Groups") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.followingsCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Followings") }}</span>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.followersCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Followers") }}</span>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.REPORTS,
|
||||
query: { domain: instance.domain },
|
||||
}"
|
||||
>
|
||||
<span class="mb-4 text-xl font-semibold block">{{
|
||||
instance.reportsCount
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Reports") }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
|
||||
<span class="mb-4 font-semibold block">{{
|
||||
formatBytes(instance.mediaSize)
|
||||
}}</span>
|
||||
<span class="text-sm block">{{ t("Uploaded media size") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="mt-3 grid xl:grid-cols-2 gap-4">
|
||||
<div
|
||||
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md"
|
||||
v-if="
|
||||
instance.hasRelay &&
|
||||
!['mastodon', 'peertube'].includes(
|
||||
instance.software?.toLowerCase() ?? ''
|
||||
)
|
||||
"
|
||||
>
|
||||
<button
|
||||
@click="
|
||||
removeInstanceFollow({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followedStatus == InstanceFollowStatus.APPROVED"
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ t("Stop following instance") }}
|
||||
</button>
|
||||
<button
|
||||
@click="
|
||||
removeInstanceFollow({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-else-if="instance.followedStatus == InstanceFollowStatus.PENDING"
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ t("Cancel follow request") }}
|
||||
</button>
|
||||
<button
|
||||
@click="followInstance"
|
||||
v-else
|
||||
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ t("Follow instance") }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="md:h-48 py-16 text-center opacity-50">
|
||||
{{ t("Only instances with an application actor can be followed") }}
|
||||
</div>
|
||||
<div
|
||||
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2"
|
||||
>
|
||||
<button
|
||||
@click="
|
||||
acceptInstance({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followerStatus == InstanceFollowStatus.PENDING"
|
||||
class="bg-green-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ t("Accept follow") }}
|
||||
</button>
|
||||
<button
|
||||
@click="
|
||||
rejectInstance({
|
||||
address: instance?.relayAddress,
|
||||
})
|
||||
"
|
||||
v-if="instance.followerStatus != InstanceFollowStatus.NONE"
|
||||
class="bg-red-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
|
||||
>
|
||||
{{ t("Reject follow") }}
|
||||
</button>
|
||||
<p v-if="instance.followerStatus == InstanceFollowStatus.NONE">
|
||||
{{ t("This instance doesn't follow yours.") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
@ -152,6 +201,7 @@ import { InstanceFollowStatus } from "@/types/enums";
|
|||
import { useMutation, useQuery } from "@vue/apollo-composable";
|
||||
import { computed, inject } from "vue";
|
||||
import { Notifier } from "@/plugins/notifier";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ domain: string }>();
|
||||
|
||||
|
@ -164,6 +214,8 @@ const instance = computed(() => instanceResult.value?.instance);
|
|||
|
||||
const notifier = inject<Notifier>("notifier");
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const { mutate: acceptInstance, onError: onAcceptInstanceError } = useMutation(
|
||||
ACCEPT_RELAY,
|
||||
() => ({
|
||||
|
|
|
@ -72,21 +72,65 @@
|
|||
name: RouteName.INSTANCE,
|
||||
params: { domain: instance.domain },
|
||||
}"
|
||||
class="flex items-center mb-2 rounded bg-mbz-yellow-alt-300 dark:bg-mbz-purple-400 p-4 flex-wrap justify-center gap-x-2 gap-y-3"
|
||||
class="flex items-center mb-2 rounded bg-mbz-yellow-alt-300 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-600 dark:hover:bg-mbz-purple-700 p-4 flex-wrap justify-center gap-x-2 gap-y-3"
|
||||
v-for="instance in instances.elements"
|
||||
:key="instance.domain"
|
||||
>
|
||||
<div class="grow overflow-hidden flex items-center gap-1">
|
||||
<img
|
||||
class="w-12"
|
||||
v-if="instance.hasRelay"
|
||||
v-if="instance.software === 'Mobilizon'"
|
||||
src="/img/logo.svg"
|
||||
alt=""
|
||||
/>
|
||||
<CloudQuestion v-else :size="36" />
|
||||
<mastodon-logo
|
||||
class="w-8 mx-2"
|
||||
alt=""
|
||||
v-else-if="instance.software?.toLowerCase() === 'mastodon'"
|
||||
/>
|
||||
<img
|
||||
class="w-8 mx-2"
|
||||
v-else-if="instance.software?.toLowerCase() === 'gancio'"
|
||||
src="/img/gancio.png"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="w-8 mx-2"
|
||||
v-else-if="instance.software?.toLowerCase() === 'wordpress'"
|
||||
src="/img/wordpress-logo.svg"
|
||||
alt=""
|
||||
/>
|
||||
<CloudQuestion class="mx-1.5" v-else :size="36" />
|
||||
|
||||
<div class="">
|
||||
<h3 class="text-lg truncate">{{ instance.domain }}</h3>
|
||||
<h3
|
||||
class="text-lg truncate font-bold text-slate-800 dark:text-slate-100"
|
||||
v-if="instance.instanceName"
|
||||
>
|
||||
{{ instance.instanceName }}
|
||||
</h3>
|
||||
<h3
|
||||
class="text-lg truncate font-bold text-slate-800 dark:text-slate-100"
|
||||
v-else
|
||||
>
|
||||
{{ instance.domain }}
|
||||
</h3>
|
||||
<p
|
||||
v-if="instance.instanceName"
|
||||
class="inline-flex gap-2 text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
<span class="capitalize" v-if="instance.software">{{
|
||||
instance.software
|
||||
}}</span>
|
||||
-
|
||||
<span>{{ instance.domain }}</span>
|
||||
</p>
|
||||
<p
|
||||
v-else-if="instance.software"
|
||||
class="capitalize text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
{{ instance.software }}
|
||||
</p>
|
||||
<span
|
||||
class="text-sm"
|
||||
v-if="instance.followedStatus === InstanceFollowStatus.APPROVED"
|
||||
|
@ -186,6 +230,7 @@ import { useRouter } from "vue-router";
|
|||
import { useHead } from "@unhead/vue";
|
||||
import CloudQuestion from "../../../node_modules/vue-material-design-icons/CloudQuestion.vue";
|
||||
import { Notifier } from "@/plugins/notifier";
|
||||
import MastodonLogo from "@/components/Share/MastodonLogo.vue";
|
||||
|
||||
const INSTANCES_PAGE_LIMIT = 10;
|
||||
|
||||
|
|
|
@ -10,7 +10,11 @@
|
|||
<section v-if="settingsToWrite">
|
||||
<form @submit.prevent="updateSettings">
|
||||
<o-field :label="t('Instance Name')" label-for="instance-name">
|
||||
<o-input v-model="settingsToWrite.instanceName" id="instance-name" />
|
||||
<o-input
|
||||
v-model="settingsToWrite.instanceName"
|
||||
id="instance-name"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<div class="field flex flex-col">
|
||||
<label class="" for="instance-description">{{
|
||||
|
@ -74,7 +78,7 @@
|
|||
<small>
|
||||
{{ t("Main languages you/your moderators speak") }}
|
||||
</small>
|
||||
<o-inputitems
|
||||
<o-taginput
|
||||
v-model="instanceLanguages"
|
||||
:data="filteredLanguages"
|
||||
allow-autocomplete
|
||||
|
@ -82,11 +86,11 @@
|
|||
field="name"
|
||||
icon="label"
|
||||
:placeholder="t('Select languages')"
|
||||
@typing="getFilteredLanguages"
|
||||
@input="getFilteredLanguages"
|
||||
id="instance-languages"
|
||||
>
|
||||
<template #empty>{{ t("No languages found") }}</template>
|
||||
</o-inputitems>
|
||||
</o-taginput>
|
||||
</div>
|
||||
<div class="field flex flex-col">
|
||||
<label class="" for="instance-long-description">{{
|
||||
|
|
|
@ -56,7 +56,7 @@ import ConversationListItem from "../../components/Conversations/ConversationLis
|
|||
import EmptyContent from "../../components/Utils/EmptyContent.vue";
|
||||
import { useHead } from "@unhead/vue";
|
||||
import { IPerson } from "@/types/actor";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import { arrayTransformer } from "@/utils/route";
|
||||
|
||||
const page = useRouteQuery("page", 1, integerTransformer);
|
||||
|
@ -89,14 +89,14 @@ const conversations = computed(
|
|||
}
|
||||
);
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { modal } = useOruga();
|
||||
|
||||
const NewConversation = defineAsyncComponent(
|
||||
() => import("@/components/Conversations/NewConversation.vue")
|
||||
);
|
||||
|
||||
const openNewMessageModal = () => {
|
||||
oruga.modal.open({
|
||||
modal.open({
|
||||
component: NewConversation,
|
||||
props: {
|
||||
personMentions: personMentions.value,
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<o-input
|
||||
aria-required="true"
|
||||
required
|
||||
expanded
|
||||
v-model="discussion.title"
|
||||
id="discussion-title"
|
||||
/>
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
<o-input
|
||||
:value="discussion.title"
|
||||
v-model="newTitle"
|
||||
expanded
|
||||
id="discussion-title"
|
||||
/>
|
||||
</o-field>
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
v-model="event.title"
|
||||
id="title"
|
||||
dir="auto"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -72,7 +73,6 @@
|
|||
:locale="$i18n.locale.replace('_', '-')"
|
||||
v-model="beginsOn"
|
||||
horizontal-time-picker
|
||||
editable
|
||||
:tz-offset="tzOffset(beginsOn)"
|
||||
:first-day-of-week="firstDayOfWeek"
|
||||
:datepicker="{
|
||||
|
@ -99,7 +99,6 @@
|
|||
horizontal-time-picker
|
||||
:min-datetime="beginsOn"
|
||||
:tz-offset="tzOffset(endsOn)"
|
||||
editable
|
||||
:first-day-of-week="firstDayOfWeek"
|
||||
:datepicker="{
|
||||
id: 'ends-on-field',
|
||||
|
@ -145,6 +144,7 @@
|
|||
v-model="event.onlineAddress"
|
||||
placeholder="URL"
|
||||
id="website-url"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -373,44 +373,55 @@
|
|||
<o-radio
|
||||
v-model="event.status"
|
||||
name="status"
|
||||
class="mr-2 p-2 rounded border"
|
||||
:class="{
|
||||
'btn-warning': event.status === EventStatus.TENTATIVE,
|
||||
'btn-outlined-warning': event.status !== EventStatus.TENTATIVE,
|
||||
}"
|
||||
variant="warning"
|
||||
:native-value="EventStatus.TENTATIVE"
|
||||
>
|
||||
<o-icon icon="calendar-question" />
|
||||
{{ t("Tentative: Will be confirmed later") }}
|
||||
<div
|
||||
class="mr-2 p-2 rounded border flex gap-x-1"
|
||||
:class="{
|
||||
'btn-warning': event.status === EventStatus.TENTATIVE,
|
||||
'btn-outlined-warning':
|
||||
event.status !== EventStatus.TENTATIVE,
|
||||
}"
|
||||
>
|
||||
<o-icon icon="calendar-question" />
|
||||
{{ t("Tentative: Will be confirmed later") }}
|
||||
</div>
|
||||
</o-radio>
|
||||
<o-radio
|
||||
v-model="event.status"
|
||||
name="status"
|
||||
variant="success"
|
||||
class="mr-2 p-2 rounded border"
|
||||
:class="{
|
||||
'btn-success': event.status === EventStatus.CONFIRMED,
|
||||
'btn-outlined-success': event.status !== EventStatus.CONFIRMED,
|
||||
}"
|
||||
:native-value="EventStatus.CONFIRMED"
|
||||
>
|
||||
<o-icon icon="calendar-check" />
|
||||
{{ t("Confirmed: Will happen") }}
|
||||
<div
|
||||
class="mr-2 p-2 rounded border flex gap-x-1"
|
||||
:class="{
|
||||
'btn-success': event.status === EventStatus.CONFIRMED,
|
||||
'btn-outlined-success':
|
||||
event.status !== EventStatus.CONFIRMED,
|
||||
}"
|
||||
>
|
||||
<o-icon icon="calendar-check" />
|
||||
{{ t("Confirmed: Will happen") }}
|
||||
</div>
|
||||
</o-radio>
|
||||
<o-radio
|
||||
v-model="event.status"
|
||||
name="status"
|
||||
class="p-2 rounded border"
|
||||
:class="{
|
||||
'btn-danger': event.status === EventStatus.CANCELLED,
|
||||
'btn-outlined-danger': event.status !== EventStatus.CANCELLED,
|
||||
}"
|
||||
variant="danger"
|
||||
:native-value="EventStatus.CANCELLED"
|
||||
>
|
||||
<o-icon icon="calendar-remove" />
|
||||
{{ t("Cancelled: Won't happen") }}
|
||||
<div
|
||||
class="p-2 rounded border flex gap-x-1"
|
||||
:class="{
|
||||
'btn-danger': event.status === EventStatus.CANCELLED,
|
||||
'btn-outlined-danger': event.status !== EventStatus.CANCELLED,
|
||||
}"
|
||||
>
|
||||
<o-icon icon="calendar-remove" />
|
||||
{{ t("Cancelled: Won't happen") }}
|
||||
</div>
|
||||
</o-radio>
|
||||
</o-field>
|
||||
</fieldset>
|
||||
|
@ -625,7 +636,7 @@ import { useMutation } from "@vue/apollo-composable";
|
|||
import { Dialog } from "@/plugins/dialog";
|
||||
import { Notifier } from "@/plugins/notifier";
|
||||
import { useHead } from "@unhead/vue";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
import type { Locale } from "date-fns";
|
||||
import sortBy from "lodash/sortBy";
|
||||
import { escapeHtml } from "@/utils/html";
|
||||
|
@ -806,10 +817,10 @@ const {
|
|||
postRefetchQueries(updatedData?.createEvent),
|
||||
}));
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
onCreateEventMutationDone(async ({ data }) => {
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: (event.value.draft
|
||||
? t("The event has been created as a draft")
|
||||
: t("The event has been published")) as string,
|
||||
|
@ -852,7 +863,7 @@ const {
|
|||
}));
|
||||
|
||||
onEditEventMutationDone(() => {
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: updateEventMessage.value,
|
||||
variant: "success",
|
||||
position: "bottom-right",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<o-input
|
||||
aria-required="true"
|
||||
required
|
||||
expanded
|
||||
v-model="group.name"
|
||||
id="group-display-name"
|
||||
/>
|
||||
|
|
|
@ -27,7 +27,11 @@
|
|||
>
|
||||
<form @submit.prevent="updateGroup(buildVariables)" v-if="editableGroup">
|
||||
<o-field :label="t('Group name')" label-for="group-settings-name">
|
||||
<o-input v-model="editableGroup.name" id="group-settings-name" />
|
||||
<o-input
|
||||
v-model="editableGroup.name"
|
||||
id="group-settings-name"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<o-field :label="t('Group short description')">
|
||||
<Editor
|
||||
|
@ -83,24 +87,22 @@
|
|||
)
|
||||
}}</small>
|
||||
</o-radio>
|
||||
<p class="pl-6">
|
||||
<p class="pl-6 flex items-center gap-2">
|
||||
<code>{{ group.url }}</code>
|
||||
<o-tooltip
|
||||
v-if="canShowCopyButton"
|
||||
:label="t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
>
|
||||
<o-button
|
||||
variant="primary"
|
||||
icon-right="content-paste"
|
||||
native-type="button"
|
||||
@click="copyURL"
|
||||
@keyup.enter="copyURL"
|
||||
/>
|
||||
</o-tooltip>
|
||||
/>
|
||||
<o-button
|
||||
variant="primary"
|
||||
icon-right="content-paste"
|
||||
native-type="button"
|
||||
@click="copyURL"
|
||||
@keyup.enter="copyURL"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -668,7 +668,7 @@ import EventMinimalistCard from "@/components/Event/EventMinimalistCard.vue";
|
|||
import MultiPostListItem from "@/components/Post/MultiPostListItem.vue";
|
||||
import { Address, addressFullName } from "@/types/address.model";
|
||||
import InvitationsList from "@/components/Group/InvitationsList.vue";
|
||||
import addMinutes from "date-fns/addMinutes";
|
||||
import { addMinutes } from "date-fns";
|
||||
import { JOIN_GROUP } from "@/graphql/member";
|
||||
import { MemberRole, Openness, PostVisibility } from "@/types/enums";
|
||||
import { IMember } from "@/types/actor/member.model";
|
||||
|
|
|
@ -387,6 +387,7 @@
|
|||
<o-input
|
||||
type="textarea"
|
||||
v-model="noteContent"
|
||||
expanded
|
||||
id="newNoteInput"
|
||||
></o-input>
|
||||
</o-field>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
v-model="editablePost.title"
|
||||
id="post-title"
|
||||
dir="auto"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
</div>
|
||||
<o-dropdown position="bottom-left" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button role="button" icon-right="dots-horizontal">
|
||||
<o-button icon-right="dots-horizontal">
|
||||
{{ t("Actions") }}
|
||||
</o-button>
|
||||
</template>
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
ref="resourceRenameInput"
|
||||
aria-required="true"
|
||||
v-model="updatedResource.title"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -133,6 +134,7 @@
|
|||
aria-required="true"
|
||||
v-model="newResource.title"
|
||||
id="new-resource-title"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -167,6 +169,7 @@
|
|||
id="new-resource-url"
|
||||
type="url"
|
||||
required
|
||||
expanded
|
||||
v-model="newResource.resourceUrl"
|
||||
@blur="previewResource"
|
||||
ref="modalNewResourceLinkInput"
|
||||
|
@ -187,6 +190,7 @@
|
|||
aria-required="true"
|
||||
v-model="newResource.title"
|
||||
id="new-resource-link-title"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -200,6 +204,7 @@
|
|||
type="textarea"
|
||||
v-model="newResource.summary"
|
||||
id="new-resource-summary"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
type="email"
|
||||
id="account-email"
|
||||
v-model="newEmail"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<p class="help">{{ t("You'll receive a confirmation email.") }}</p>
|
||||
|
@ -72,6 +73,7 @@
|
|||
password-reveal
|
||||
minlength="6"
|
||||
v-model="passwordForEmailChange"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<o-button class="mt-2" variant="primary" nativeType="submit">
|
||||
|
@ -117,6 +119,7 @@
|
|||
minlength="6"
|
||||
id="account-old-password"
|
||||
v-model="oldPassword"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<o-field :label="t('New password')" label-for="account-new-password">
|
||||
|
@ -128,6 +131,7 @@
|
|||
minlength="6"
|
||||
id="account-new-password"
|
||||
v-model="newPassword"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<o-button class="mt-2" variant="primary" nativeType="submit">
|
||||
|
@ -243,7 +247,7 @@ import {
|
|||
} from "../../graphql/user";
|
||||
import RouteName from "../../router/name";
|
||||
import { logout, SELECTED_PROVIDERS } from "../../utils/auth";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import { useOruga } from "@oruga-ui/oruga-next";
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
|
@ -341,12 +345,12 @@ const {
|
|||
DELETE_ACCOUNT
|
||||
);
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
const { notification } = useOruga();
|
||||
|
||||
deleteAccountMutationDone(async () => {
|
||||
console.debug("Deleted account, logging out client...");
|
||||
await logout(false);
|
||||
oruga.notification.open({
|
||||
notification.open({
|
||||
message: t("Your account has been successfully deleted"),
|
||||
variant: "success",
|
||||
position: "bottom-right",
|
||||
|
|
|
@ -240,49 +240,46 @@
|
|||
<o-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.atom"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
/>
|
||||
<o-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
@keyup.enter="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ $t("RSS/Atom Feed") }}</o-button
|
||||
>
|
||||
<o-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
@keyup.enter="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ $t("RSS/Atom Feed") }}</o-button
|
||||
>
|
||||
</o-tooltip>
|
||||
|
||||
<o-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.ics"
|
||||
always
|
||||
variant="success"
|
||||
position="left"
|
||||
/>
|
||||
<o-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
@keyup.enter="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ $t("ICS/WebCal Feed") }}</o-button
|
||||
>
|
||||
<o-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
@keyup.enter="
|
||||
(e: Event) =>
|
||||
copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ $t("ICS/WebCal Feed") }}</o-button
|
||||
>
|
||||
</o-tooltip>
|
||||
<o-button
|
||||
icon-left="refresh"
|
||||
variant="text"
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
<o-input
|
||||
aria-required="true"
|
||||
required
|
||||
expanded
|
||||
id="email"
|
||||
type="email"
|
||||
v-model="credentials.email"
|
||||
|
@ -63,6 +64,7 @@
|
|||
aria-required="true"
|
||||
id="password"
|
||||
required
|
||||
expanded
|
||||
type="password"
|
||||
password-reveal
|
||||
v-model="credentials.password"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
password-reveal
|
||||
minlength="6"
|
||||
v-model="credentials.password"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<o-field :label="$t('Password (confirmation)')">
|
||||
|
@ -29,6 +30,7 @@
|
|||
password-reveal
|
||||
minlength="6"
|
||||
v-model="credentials.passwordConfirmation"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<button class="button is-primary">
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
v-model="credentials.email"
|
||||
@blur="showGravatar = true"
|
||||
@focus="showGravatar = false"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
@ -120,6 +121,7 @@
|
|||
password-reveal
|
||||
minlength="6"
|
||||
v-model="credentials.password"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
type="email"
|
||||
id="emailAddress"
|
||||
v-model="emailValue"
|
||||
expanded
|
||||
/>
|
||||
</o-field>
|
||||
<p class="flex flex-wrap gap-1 mt-2">
|
||||
|
|
74
test/federation/activity_stream/converter/address_test.exs
Normal file
74
test/federation/activity_stream/converter/address_test.exs
Normal file
|
@ -0,0 +1,74 @@
|
|||
defmodule Mobilizon.Federation.ActivityStream.Converter.AddressTest do
|
||||
use Mobilizon.DataCase
|
||||
|
||||
alias Mobilizon.Addresses.Address
|
||||
|
||||
alias Mobilizon.Federation.ActivityStream.Converter.Address, as: AddressConverter
|
||||
|
||||
describe "address to AS" do
|
||||
test "valid address to as" do
|
||||
data =
|
||||
AddressConverter.model_to_as(%Address{
|
||||
country: "France",
|
||||
locality: "Lyon",
|
||||
description: "Locaux'Motiv",
|
||||
postal_code: "69007",
|
||||
street: "10Bis Rue Jangot"
|
||||
})
|
||||
|
||||
assert is_map(data)
|
||||
assert data["type"] == "Place"
|
||||
assert data["name"] == "Locaux'Motiv"
|
||||
assert data["address"]["type"] == "PostalAddress"
|
||||
assert data["address"]["addressLocality"] == "Lyon"
|
||||
assert data["address"]["postalCode"] == "69007"
|
||||
assert data["address"]["addressCountry"] == "France"
|
||||
assert data["address"]["streetAddress"] == "10Bis Rue Jangot"
|
||||
end
|
||||
end
|
||||
|
||||
describe "AS to Address" do
|
||||
test "basic AS data to model" do
|
||||
address =
|
||||
AddressConverter.as_to_model_data(%{
|
||||
"type" => "Place",
|
||||
"name" => "Federazione Anarchica Torinese",
|
||||
"address" => "corso Palermo 46",
|
||||
"latitude" => nil,
|
||||
"longitude" => nil
|
||||
})
|
||||
|
||||
assert address["description"] == "Federazione Anarchica Torinese"
|
||||
assert address["street"] == "corso Palermo 46"
|
||||
assert address["locality"] == nil
|
||||
assert address["geom"] == nil
|
||||
end
|
||||
|
||||
test "AS data with PostalAddress to model" do
|
||||
address =
|
||||
AddressConverter.as_to_model_data(%{
|
||||
"type" => "Place",
|
||||
"name" => "Federazione Anarchica Torinese",
|
||||
"address" => %{
|
||||
"streetAddress" => "Corso Palermo, 46",
|
||||
"addressCountry" => "Italia",
|
||||
"postalCode" => "10152",
|
||||
"addressLocality" => "Torino"
|
||||
},
|
||||
"latitude" => 45.08281,
|
||||
"longitude" => 7.69311
|
||||
})
|
||||
|
||||
assert address["description"] == "Federazione Anarchica Torinese"
|
||||
assert address["street"] == "Corso Palermo, 46"
|
||||
assert address["locality"] == "Torino"
|
||||
assert address["postal_code"] == "10152"
|
||||
assert address["country"] == "Italia"
|
||||
|
||||
assert address["geom"] == %Geo.Point{
|
||||
coordinates: {7.69311, 45.08281},
|
||||
srid: 4326
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
188
test/federation/node_info_test.exs
Normal file
188
test/federation/node_info_test.exs
Normal file
|
@ -0,0 +1,188 @@
|
|||
defmodule Mobilizon.Federation.NodeInfoTest do
|
||||
use Mobilizon.DataCase
|
||||
import Mox
|
||||
|
||||
alias Mobilizon.Federation.NodeInfo
|
||||
alias Mobilizon.Service.HTTP.WebfingerClient.Mock, as: WebfingerClientMock
|
||||
|
||||
@instance_domain "event-federation.eu"
|
||||
@nodeinfo_fixture_path "test/fixtures/nodeinfo/wp-event-federation.json"
|
||||
@nodeinfo_regular_fixture_path "test/fixtures/nodeinfo/regular.json"
|
||||
@nodeinfo_both_versions_fixture_path "test/fixtures/nodeinfo/both_versions.json"
|
||||
@nodeinfo_older_versions_fixture_path "test/fixtures/nodeinfo/older_versions.json"
|
||||
@nodeinfo_data_fixture_path "test/fixtures/nodeinfo/data.json"
|
||||
@nodeinfo_wp_data_fixture_path "test/fixtures/nodeinfo/wp-data.json"
|
||||
|
||||
describe "getting application actor" do
|
||||
test "from wordpress event federation" do
|
||||
nodeinfo_data = @nodeinfo_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://event-federation.eu/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
|
||||
end)
|
||||
|
||||
assert "https://event-federation.eu/actor-relay" ==
|
||||
NodeInfo.application_actor(@instance_domain)
|
||||
end
|
||||
|
||||
test "with no FEP-2677 information" do
|
||||
nodeinfo_data = @nodeinfo_regular_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://event-federation.eu/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
|
||||
end)
|
||||
|
||||
assert nil == NodeInfo.application_actor(@instance_domain)
|
||||
end
|
||||
|
||||
test "with no result" do
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://event-federation.eu/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 404, body: ""}}
|
||||
end)
|
||||
|
||||
assert nil == NodeInfo.application_actor(@instance_domain)
|
||||
end
|
||||
end
|
||||
|
||||
describe "getting informations" do
|
||||
test "with both 2.0 and 2.1 endpoints" do
|
||||
nodeinfo_end_point_data =
|
||||
@nodeinfo_both_versions_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
nodeinfo_data = @nodeinfo_data_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mobilizon.fr/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
|
||||
end)
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mobilizon.fr/.well-known/nodeinfo/2.1"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
|
||||
end)
|
||||
|
||||
assert {:ok, data} = NodeInfo.nodeinfo("mobilizon.fr")
|
||||
assert data["version"] == "2.1"
|
||||
assert data["software"]["name"] == "Mobilizon"
|
||||
# Added in 2.1
|
||||
refute is_nil(data["software"]["repository"])
|
||||
end
|
||||
|
||||
test "with only 2.0 endpoint" do
|
||||
nodeinfo_end_point_data = @nodeinfo_regular_fixture_path |> File.read!() |> Jason.decode!()
|
||||
nodeinfo_wp_data = @nodeinfo_wp_data_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://event-federation.eu/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
|
||||
end)
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_wp_data}}
|
||||
end)
|
||||
|
||||
assert {:ok, data} = NodeInfo.nodeinfo("event-federation.eu")
|
||||
assert data["version"] == "2.0"
|
||||
assert data["software"]["name"] == "wordpress"
|
||||
# Added in 2.1
|
||||
assert is_nil(data["software"]["repository"])
|
||||
end
|
||||
|
||||
test "with no valid endpoint" do
|
||||
nodeinfo_end_point_data =
|
||||
@nodeinfo_older_versions_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://somewhere.tld/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
|
||||
end)
|
||||
|
||||
assert {:error, :no_node_info_endpoint_found} = NodeInfo.nodeinfo("somewhere.tld")
|
||||
end
|
||||
|
||||
test "with no answer from well-known endpoint" do
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://somewhere.tld/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
end)
|
||||
|
||||
assert {:error, :node_info_meta_http_error} = NodeInfo.nodeinfo("somewhere.tld")
|
||||
end
|
||||
|
||||
test "with no answer from version endpoint" do
|
||||
nodeinfo_end_point_data =
|
||||
@nodeinfo_both_versions_fixture_path |> File.read!() |> Jason.decode!()
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mobilizon.fr/.well-known/nodeinfo"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
|
||||
end)
|
||||
|
||||
WebfingerClientMock
|
||||
|> expect(:call, fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mobilizon.fr/.well-known/nodeinfo/2.1"
|
||||
},
|
||||
_opts ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
end)
|
||||
|
||||
assert {:error, :node_info_endpoint_http_error} = NodeInfo.nodeinfo("mobilizon.fr")
|
||||
end
|
||||
end
|
||||
end
|
12
test/fixtures/nodeinfo/both_versions.json
vendored
Normal file
12
test/fixtures/nodeinfo/both_versions.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||
"href": "https://mobilizon.fr/.well-known/nodeinfo/2.0"
|
||||
},
|
||||
{
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||
"href": "https://mobilizon.fr/.well-known/nodeinfo/2.1"
|
||||
}
|
||||
]
|
||||
}
|
29
test/fixtures/nodeinfo/data.json
vendored
Normal file
29
test/fixtures/nodeinfo/data.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"version": "2.1",
|
||||
"protocols": [
|
||||
"activitypub"
|
||||
],
|
||||
"metadata": {
|
||||
"nodeDescription": "Mobilizon.fr est l'instance Mobilizon de Framasoft.",
|
||||
"nodeName": "Mobilizon"
|
||||
},
|
||||
"usage": {
|
||||
"users": {
|
||||
"total": 9204
|
||||
},
|
||||
"localComments": 3253,
|
||||
"localPosts": 7545
|
||||
},
|
||||
"services": {
|
||||
"outbound": [
|
||||
"atom1.0"
|
||||
],
|
||||
"inbound": []
|
||||
},
|
||||
"software": {
|
||||
"name": "Mobilizon",
|
||||
"version": "4.0.2",
|
||||
"repository": "https://framagit.org/framasoft/mobilizon"
|
||||
},
|
||||
"openRegistrations": true
|
||||
}
|
8
test/fixtures/nodeinfo/older_versions.json
vendored
Normal file
8
test/fixtures/nodeinfo/older_versions.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/1.1",
|
||||
"href": "https://mobilizon.fr/.well-known/nodeinfo/1.1"
|
||||
}
|
||||
]
|
||||
}
|
8
test/fixtures/nodeinfo/regular.json
vendored
Normal file
8
test/fixtures/nodeinfo/regular.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||
"href": "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
|
||||
}
|
||||
]
|
||||
}
|
24
test/fixtures/nodeinfo/wp-data.json
vendored
Normal file
24
test/fixtures/nodeinfo/wp-data.json
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"software": {
|
||||
"name": "wordpress",
|
||||
"version": "6.4.2"
|
||||
},
|
||||
"usage": {
|
||||
"users": {
|
||||
"total": 1,
|
||||
"activeMonth": 1,
|
||||
"activeHalfyear": 1
|
||||
},
|
||||
"localPosts": 3,
|
||||
"localComments": 3
|
||||
},
|
||||
"openRegistrations": false,
|
||||
"protocols": [
|
||||
"activitypub"
|
||||
],
|
||||
"services": {
|
||||
"inbound": [],
|
||||
"outbound": []
|
||||
}
|
||||
}
|
12
test/fixtures/nodeinfo/wp-event-federation.json
vendored
Normal file
12
test/fixtures/nodeinfo/wp-event-federation.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||
"href": "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
|
||||
},
|
||||
{
|
||||
"rel": "https://www.w3.org/ns/activitystreams#Application",
|
||||
"href": "https://event-federation.eu/actor-relay"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,12 +2,16 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
|
|||
use Mobilizon.Web.ConnCase
|
||||
|
||||
alias Mobilizon.Config
|
||||
alias Mobilizon.Federation.ActivityPub.Relay
|
||||
|
||||
use Mobilizon.Web, :verified_routes
|
||||
|
||||
test "Get node info schemas", %{conn: conn} do
|
||||
conn = get(conn, url(~p"/.well-known/nodeinfo"))
|
||||
|
||||
relay = Relay.get_actor()
|
||||
relay_url = relay.url
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"links" => [
|
||||
%{
|
||||
|
@ -17,6 +21,10 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
|
|||
%{
|
||||
"href" => url(~p"/.well-known/nodeinfo/2.1"),
|
||||
"rel" => "http://nodeinfo.diaspora.software/ns/schema/2.1"
|
||||
},
|
||||
%{
|
||||
"href" => relay_url,
|
||||
"rel" => "https://www.w3.org/ns/activitystreams#Application"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ exports[`CommentTree > renders a comment tree with comments 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
<div data-v-5d0380ab=\\"\\" class=\\"\\">
|
||||
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\"></o-button-stub>
|
||||
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\"></o-button-stub>
|
||||
</div>
|
||||
</article>
|
||||
</form>
|
||||
|
@ -56,7 +56,7 @@ exports[`CommentTree > renders an empty comment tree 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
<div data-v-5d0380ab=\\"\\" class=\\"\\">
|
||||
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\"></o-button-stub>
|
||||
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\"></o-button-stub>
|
||||
</div>
|
||||
</article>
|
||||
</form>
|
||||
|
|
Loading…
Reference in a new issue