mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-08 12:04:10 +00:00
Merge branch 'events' into 'develop'
Mobilizon-compatible events See merge request pleroma/pleroma!3955
This commit is contained in:
commit
fd1a614520
77 changed files with 3884 additions and 94 deletions
1
changelog.d/events.add
Normal file
1
changelog.d/events.add
Normal file
|
@ -0,0 +1 @@
|
|||
Mobilizon-compatible events
|
|
@ -499,7 +499,7 @@ config :pleroma, :shout,
|
|||
enabled: true,
|
||||
limit: 5_000
|
||||
|
||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason
|
||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason, ics: ICalendar
|
||||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
|
@ -725,6 +725,7 @@ config :pleroma, :rate_limit,
|
|||
relation_id_action: {60_000, 2},
|
||||
statuses_actions: {10_000, 15},
|
||||
status_id_action: {60_000, 3},
|
||||
events_actions: {10_000, 15},
|
||||
password_reset: {1_800_000, 5},
|
||||
account_confirmation_resend: {8_640_000, 5},
|
||||
ap_routes: {60_000, 15}
|
||||
|
@ -957,6 +958,22 @@ config :pleroma, Pleroma.Search.QdrantSearch,
|
|||
vectors: %{size: 384, distance: "Cosine"}
|
||||
}
|
||||
|
||||
config :geospatial, Geospatial.Service, service: Geospatial.Providers.Nominatim
|
||||
|
||||
config :geospatial, Geospatial.Providers.GoogleMaps,
|
||||
api_key: nil,
|
||||
fetch_place_details: true
|
||||
|
||||
config :geospatial, Geospatial.Providers.Nominatim,
|
||||
endpoint: "https://nominatim.openstreetmap.org",
|
||||
api_key: nil
|
||||
|
||||
config :geospatial, Geospatial.Providers.Pelias,
|
||||
endpoint: "https://api.geocode.earth",
|
||||
api_key: nil
|
||||
|
||||
config :geospatial, Geospatial.HTTP, user_agent: &Pleroma.Application.user_agent/0
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -2613,6 +2613,12 @@ config :pleroma, :config_description, [
|
|||
"For fav / unfav or reblog / unreblog actions on the same status by the same user",
|
||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
||||
},
|
||||
%{
|
||||
key: :events_actions,
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
description: "For create / update / join / leave actions on any statuses",
|
||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
||||
},
|
||||
%{
|
||||
key: :authentication,
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
|
@ -3566,5 +3572,82 @@ config :pleroma, :config_description, [
|
|||
suggestions: ["YOUR_API_KEY"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :geospatial,
|
||||
key: Geospatial.Service,
|
||||
type: :group,
|
||||
description: "Geospatial service providers",
|
||||
children: [
|
||||
%{
|
||||
key: :service,
|
||||
type: :module,
|
||||
label: "Geospatial service provider",
|
||||
suggestions: [
|
||||
Geospatial.Providers.GoogleMaps,
|
||||
Geospatial.Providers.Nominatim,
|
||||
Geospatial.Providers.Pelias
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :geospatial,
|
||||
key: Geospatial.Providers.Nominatim,
|
||||
type: :group,
|
||||
description: "Nominatim provider configuration",
|
||||
children: [
|
||||
%{
|
||||
key: :endpoint,
|
||||
type: :string,
|
||||
description: "Nominatim endpoint",
|
||||
suggestions: ["https://nominatim.openstreetmap.org"]
|
||||
},
|
||||
%{
|
||||
key: :api_key,
|
||||
type: :string,
|
||||
description: "Nominatim API key",
|
||||
suggestions: [nil]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :geospatial,
|
||||
key: Geospatial.Providers.GoogleMaps,
|
||||
type: :group,
|
||||
description: "Google Maps provider configuration",
|
||||
children: [
|
||||
%{
|
||||
key: :api_key,
|
||||
type: :string,
|
||||
description: "Google Maps API key",
|
||||
suggestions: [nil]
|
||||
},
|
||||
%{
|
||||
key: :fetch_place_details,
|
||||
type: :boolean,
|
||||
description: "Fetch place details"
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :geospatial,
|
||||
key: Geospatial.Providers.Pelias,
|
||||
type: :group,
|
||||
description: "Pelias provider configuration",
|
||||
children: [
|
||||
%{
|
||||
key: :endpoint,
|
||||
type: :string,
|
||||
description: "Pelias endpoint",
|
||||
suggestions: ["https://api.geocode.earth"]
|
||||
},
|
||||
%{
|
||||
key: :api_key,
|
||||
type: :string,
|
||||
description: "Pelias API key",
|
||||
suggestions: [nil]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -62,6 +62,8 @@ config :pleroma, :password, iterations: 1
|
|||
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
config :tesla, Geospatial.HTTP, adapter: Tesla.Mock
|
||||
|
||||
config :pleroma, :rich_media,
|
||||
enabled: false,
|
||||
ignore_hosts: [],
|
||||
|
|
|
@ -525,6 +525,7 @@ Supported rate limiters:
|
|||
* `:relation_id_action` - Following/Unfollowing for a specific user.
|
||||
* `:statuses_actions` - Status actions such as: (un)repeating, (un)favouriting, creating, deleting.
|
||||
* `:status_id_action` - (un)Repeating/(un)Favouriting a particular status.
|
||||
* `:events_actions` - Events actions such as: creating, joining, leaving.
|
||||
* `:authentication` - Authentication actions, i.e getting an OAuth token.
|
||||
* `:password_reset` - Requesting password reset emails.
|
||||
* `:account_confirmation_resend` - Requesting resending account confirmation emails.
|
||||
|
|
|
@ -43,11 +43,39 @@ Has these additional fields under the `pleroma` object:
|
|||
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
|
||||
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
|
||||
- `list_id`: the ID of the list the post is addressed to (if any, only returned to author).
|
||||
- `event`: event information if the post is an event, `null` otherwise.
|
||||
|
||||
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
|
||||
|
||||
- `content_type`: The content type of the status source.
|
||||
|
||||
### Event
|
||||
|
||||
Event object includes following fields:
|
||||
|
||||
- `name`: event name.
|
||||
- `start_time`: datetime, if specified, the time when the event starts, `null` otherwise.
|
||||
- `end_time`: datetime, if specified, the time when the event finishes, `null` otherwise.
|
||||
- `join_mode`: who can join the event. Possible values, if specified: `free`, `restricted` and `invite`. `null` otherwise.
|
||||
- `participants_count`: the number of users who joined the event.
|
||||
- `location`: event location, if specified, `null` otherwise.
|
||||
- `join_state`: whether the user joined the event. Possible values: `pending`, `reject`, `accept`. `null`, if no `Join` exists.
|
||||
- `participation_request_count`: the number of users who requested to join the event.
|
||||
|
||||
### Event location
|
||||
|
||||
Event location object includes following fields:
|
||||
|
||||
- `name`: place name.
|
||||
- `url`: location url address or `null`.
|
||||
- `longitude`: X-coordinate of the place or `null`.
|
||||
- `latitude`: Y-coordinate of the place or `null`.
|
||||
- `street`: place street or `null`.
|
||||
- `postal_code`: place postal code or `null`.
|
||||
- `locality`: place city or `null`.
|
||||
- `region`: place region or `null`.
|
||||
- `country`: place country or `null`.
|
||||
|
||||
## Scheduled statuses
|
||||
|
||||
Has these additional fields in `params`:
|
||||
|
@ -175,6 +203,32 @@ The `type` value is `pleroma:emoji_reaction`. Has these fields:
|
|||
- `account`: The account of the user who reacted
|
||||
- `status`: The status that was reacted on
|
||||
|
||||
### EventReminder Notification
|
||||
|
||||
The `type` value is `pleroma:event_reminder`. Has these fields:
|
||||
|
||||
- `status`: The event status
|
||||
|
||||
### EventUpdate Notification
|
||||
|
||||
The `type` value is `pleroma:event_update`. Has these fields:
|
||||
|
||||
- `status`: The event status
|
||||
|
||||
### ParticipationAccepted Notification
|
||||
|
||||
The `type` value is `pleroma:participation_accepted`. Has these fields:
|
||||
|
||||
- `status`: The event status
|
||||
- `participation_message`: Participation request message
|
||||
|
||||
### ParticipationRequest Notification
|
||||
|
||||
The `type` value is `pleroma:participation_request`. Has these fields:
|
||||
|
||||
- `status`: The event status
|
||||
- `participation_message`: Participation request message
|
||||
|
||||
### ChatMention Notification (not default)
|
||||
|
||||
This notification has to be requested explicitly.
|
||||
|
|
|
@ -333,6 +333,117 @@ Deprecated. `notify` parameter in `POST /api/v1/accounts/:id/follow` should be u
|
|||
* `id`: folder id
|
||||
* Response: JSON. Returns a single bookmark folder.
|
||||
|
||||
## `/api/v1/pleroma/events`
|
||||
### Creates an event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `name`: name of the event
|
||||
* `status`: optional, description of the event
|
||||
* `banner_id`: optional, event banner attachment ID
|
||||
* `start_time`: start time of the event
|
||||
* `end_time`: optional, end time of the event
|
||||
* `join_mode`: optional, event join mode, either `free` or `restricted`, defaults to `free`
|
||||
* `location_id`: optional, location ID from the location provider used by server
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/joined_events`
|
||||
### Gets user's joined events
|
||||
* Method `GET`
|
||||
* Authentication: required
|
||||
|
||||
* Response: JSON. Returns a list of Mastodon Status entities.
|
||||
|
||||
## `/api/v1/pleroma/events/:id`
|
||||
### Edits an event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
* `name`: optional, name of the event
|
||||
* `status`: optional, description of the event
|
||||
* `banner_id`: optional, event banner attachment ID
|
||||
* `start_time`: optional, start time of the event
|
||||
* `end_time`: optional, end time of the event
|
||||
* `location_id`: optional, location ID from the location provider used by server
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/participations`
|
||||
### Gets event participants
|
||||
* Method `GET`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
|
||||
* Response: JSON. Returns a list of Mastodon Account entities.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/participation_requests`
|
||||
### Gets event participation requests
|
||||
* Method `GET`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
|
||||
* Response: JSON. Returns a list of `{"account": "[Mastodon Account entity]", "participation_message": "[Participation request message]"}` entities.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/participation_requests/:participant_id/authorize`
|
||||
### Accepts user to the event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
* `participant_id`: ID of the account
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/participation_requests/:participant_id/reject`
|
||||
### Rejects user from the event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
* `participant_id`: ID of the account
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/join`
|
||||
### Joins the event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/leave`
|
||||
### Leaves the event
|
||||
* Method `POST`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
|
||||
* Response: JSON. Returns a Mastodon Status entity.
|
||||
|
||||
## `/api/v1/pleroma/events/:id/ics`
|
||||
### Event ICS file
|
||||
* Method `GET`
|
||||
* Authentication: not required
|
||||
* Params:
|
||||
* `id`: ID of the status
|
||||
|
||||
* Response: ICS. Returns calendar file for the event.
|
||||
|
||||
## `/api/v1/pleroma/search/location`
|
||||
### Searches for locations
|
||||
* Method `GET`
|
||||
* Authentication: required
|
||||
* Params:
|
||||
* `q`: Search query
|
||||
|
||||
* Response: JSON. Returns a list of found locations.
|
||||
|
||||
## `/api/v1/pleroma/mascot`
|
||||
### Gets user mascot image
|
||||
* Method `GET`
|
||||
|
|
|
@ -297,12 +297,18 @@ defmodule Pleroma.Activity do
|
|||
def normalize(_), do: nil
|
||||
|
||||
def delete_all_by_object_ap_id(id) when is_binary(id) do
|
||||
id
|
||||
|> Queries.by_object_id()
|
||||
|> Queries.exclude_type("Delete")
|
||||
|> select([u], u)
|
||||
|> Repo.delete_all(timeout: :infinity)
|
||||
|> elem(1)
|
||||
activities =
|
||||
id
|
||||
|> Queries.by_object_id()
|
||||
|> Queries.exclude_type("Delete")
|
||||
|> select([u], u)
|
||||
|> Repo.delete_all(timeout: :infinity)
|
||||
|> elem(1)
|
||||
|
||||
activities
|
||||
|> Enum.each(fn %{data: %{"id" => ap_id}} -> delete_all_by_object_ap_id(ap_id) end)
|
||||
|
||||
activities
|
||||
|> Enum.find(fn
|
||||
%{data: %{"type" => "Create", "object" => ap_id}} when is_binary(ap_id) -> ap_id == id
|
||||
%{data: %{"type" => "Create", "object" => %{"id" => ap_id}}} -> ap_id == id
|
||||
|
|
|
@ -111,7 +111,8 @@ defmodule Pleroma.Application do
|
|||
Pleroma.JobQueueMonitor,
|
||||
{Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]},
|
||||
{Oban, Config.get(Oban)},
|
||||
Pleroma.Web.Endpoint
|
||||
Pleroma.Web.Endpoint,
|
||||
TzWorld.Backend.DetsWithIndexCache
|
||||
] ++
|
||||
task_children() ++
|
||||
streamer_registry() ++
|
||||
|
|
|
@ -271,7 +271,7 @@ defmodule Pleroma.ConfigDB do
|
|||
":#{entity}"
|
||||
end
|
||||
|
||||
def to_json_types(entity) when is_atom(entity), do: inspect(entity)
|
||||
def to_json_types(entity) when is_atom(entity) or is_function(entity), do: inspect(entity)
|
||||
|
||||
@spec to_elixir_types(boolean() | String.t() | map() | list()) :: term()
|
||||
def to_elixir_types(%{"tuple" => [":args", args]}) when is_list(args) do
|
||||
|
|
|
@ -21,7 +21,11 @@ defmodule Pleroma.Constants do
|
|||
"pleroma_internal",
|
||||
"generator",
|
||||
"rules",
|
||||
"language"
|
||||
"language",
|
||||
"participations",
|
||||
"participation_count",
|
||||
"participation_request_count",
|
||||
"location_id"
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -42,7 +46,13 @@ defmodule Pleroma.Constants do
|
|||
"sensitive",
|
||||
"attachment",
|
||||
"generator",
|
||||
"language"
|
||||
"language",
|
||||
"startTime",
|
||||
"endTime",
|
||||
"location",
|
||||
"location_id",
|
||||
"location_provider",
|
||||
"name"
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -104,7 +114,9 @@ defmodule Pleroma.Constants do
|
|||
"Undo",
|
||||
"Flag",
|
||||
"EmojiReact",
|
||||
"Listen"
|
||||
"Listen",
|
||||
"Join",
|
||||
"Leave"
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -74,6 +74,10 @@ defmodule Pleroma.Notification do
|
|||
reblog
|
||||
poll
|
||||
status
|
||||
pleroma:participation_accepted
|
||||
pleroma:participation_request
|
||||
pleroma:event_reminder
|
||||
pleroma:event_update
|
||||
}
|
||||
|
||||
def changeset(%Notification{} = notification, attrs) do
|
||||
|
@ -367,23 +371,37 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
|
||||
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update"] do
|
||||
when type in [
|
||||
"Follow",
|
||||
"Like",
|
||||
"Announce",
|
||||
"Move",
|
||||
"EmojiReact",
|
||||
"Flag",
|
||||
"Update",
|
||||
"Accept",
|
||||
"Join"
|
||||
] do
|
||||
do_create_notifications(activity)
|
||||
end
|
||||
|
||||
def create_notifications(_), do: {:ok, []}
|
||||
|
||||
defp do_create_notifications(%Activity{} = activity) do
|
||||
enabled_participants = get_notified_participants_from_activity(activity)
|
||||
enabled_receivers = get_notified_from_activity(activity)
|
||||
|
||||
enabled_subscribers = get_notified_subscribers_from_activity(activity)
|
||||
|
||||
notifications =
|
||||
(Enum.map(enabled_receivers, fn user ->
|
||||
(Enum.map(enabled_receivers -- enabled_participants, fn user ->
|
||||
create_notification(activity, user)
|
||||
end) ++
|
||||
Enum.map(enabled_subscribers -- enabled_receivers, fn user ->
|
||||
create_notification(activity, user, type: "status")
|
||||
end) ++
|
||||
Enum.map(enabled_participants, fn user ->
|
||||
create_notification(activity, user, type: "pleroma:event_update")
|
||||
end))
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
|
@ -425,6 +443,12 @@ defmodule Pleroma.Notification do
|
|||
"Update" ->
|
||||
"update"
|
||||
|
||||
"Accept" ->
|
||||
"pleroma:participation_accepted"
|
||||
|
||||
"Join" ->
|
||||
"pleroma:participation_request"
|
||||
|
||||
t ->
|
||||
raise "No notification type for activity type #{t}"
|
||||
end
|
||||
|
@ -483,6 +507,28 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
end
|
||||
|
||||
def create_event_notifications(%Activity{} = activity) do
|
||||
with %Object{data: %{"type" => "Event", "actor" => actor} = data} <-
|
||||
Object.normalize(activity) do
|
||||
participations =
|
||||
case data do
|
||||
%{"participations" => participations} when is_list(participations) -> participations
|
||||
_ -> []
|
||||
end
|
||||
|
||||
notifications =
|
||||
Enum.reduce([actor | participations], [], fn ap_id, acc ->
|
||||
with %User{local: true} = user <- User.get_by_ap_id(ap_id) do
|
||||
[create_notification(activity, user, type: "pleroma:event_reminder") | acc]
|
||||
else
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
{:ok, notifications}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a tuple with 2 elements:
|
||||
{notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)}
|
||||
|
@ -501,7 +547,9 @@ defmodule Pleroma.Notification do
|
|||
"Move",
|
||||
"EmojiReact",
|
||||
"Flag",
|
||||
"Update"
|
||||
"Update",
|
||||
"Accept",
|
||||
"Join"
|
||||
] do
|
||||
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
|
||||
|
||||
|
@ -537,6 +585,24 @@ defmodule Pleroma.Notification do
|
|||
|
||||
def get_notified_subscribers_from_activity(_, _), do: []
|
||||
|
||||
def get_notified_participants_from_activity(activity, local_only \\ true)
|
||||
|
||||
def get_notified_participants_from_activity(
|
||||
%Activity{data: %{"type" => "Update"}} = activity,
|
||||
local_only
|
||||
) do
|
||||
notification_enabled_ap_ids =
|
||||
[]
|
||||
|> Utils.maybe_notify_participants(activity)
|
||||
|
||||
potential_receivers =
|
||||
User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only)
|
||||
|
||||
Enum.filter(potential_receivers, fn u -> u.ap_id in notification_enabled_ap_ids end)
|
||||
end
|
||||
|
||||
def get_notified_participants_from_activity(_, _), do: []
|
||||
|
||||
# For some activities, only notify the author of the object
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => type, "object" => object_id}})
|
||||
when type in ~w{Like Announce EmojiReact} do
|
||||
|
@ -549,6 +615,35 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => "Accept", "object" => join_id}}) do
|
||||
case Activity.get_by_ap_id_with_object(join_id) do
|
||||
%Activity{
|
||||
data: %{"type" => "Join"},
|
||||
object: %Object{data: %{"type" => "Event", "joinMode" => "free"}}
|
||||
} ->
|
||||
[]
|
||||
|
||||
%Activity{data: %{"type" => "Join", "actor" => actor_id}} ->
|
||||
[actor_id]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => "Join", "object" => object_id}}) do
|
||||
case Object.get_by_ap_id(object_id) do
|
||||
%Object{data: %{"type" => "Event", "joinMode" => "free"}} ->
|
||||
[]
|
||||
|
||||
%Object{data: %{"type" => "Event", "actor" => actor_id}} ->
|
||||
[actor_id]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => "Follow", "object" => object_id}}) do
|
||||
[object_id]
|
||||
end
|
||||
|
|
|
@ -20,11 +20,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.Upload
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.Streamer
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
alias Pleroma.Workers.EventReminderWorker
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
import Ecto.Query
|
||||
|
@ -321,6 +324,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_schedule_poll_notifications(activity),
|
||||
:ok <- maybe_handle_group_posts(activity),
|
||||
:ok <- maybe_schedule_event_notifications(activity),
|
||||
:ok <- maybe_join_own_event(actor, activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
@ -340,6 +345,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
:ok
|
||||
end
|
||||
|
||||
defp maybe_schedule_event_notifications(activity) do
|
||||
EventReminderWorker.schedule_event_reminder(activity)
|
||||
:ok
|
||||
end
|
||||
|
||||
defp maybe_join_own_event(actor, %{object: %{data: %{"type" => "Event"}} = object}) do
|
||||
{:ok, join_object, meta} = Builder.join(actor, object)
|
||||
|
||||
{:ok, _, _} = Pipeline.common_pipeline(join_object, Keyword.put(meta, :local, true))
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp maybe_join_own_event(_, _), do: :ok
|
||||
|
||||
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
|
||||
additional = params[:additional] || %{}
|
||||
|
@ -482,9 +502,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> where(
|
||||
[activity],
|
||||
fragment(
|
||||
"?->>'type' = ? and ?->>'context' = ?",
|
||||
"?->>'type' = 'Create' and ?->>'context' = ?",
|
||||
activity.data,
|
||||
"Create",
|
||||
activity.data,
|
||||
^context
|
||||
)
|
||||
|
@ -999,7 +1018,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp restrict_media(query, %{only_media: true}) do
|
||||
from(
|
||||
[activity, object] in query,
|
||||
where: fragment("(?)->>'type' = ?", activity.data, "Create"),
|
||||
where: fragment("(?)->>'type' = 'Create'", activity.data),
|
||||
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
||||
)
|
||||
end
|
||||
|
@ -1282,6 +1301,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_unauthenticated(query, _), do: query
|
||||
|
||||
defp restrict_object(query, %{object: object}) do
|
||||
from(activity in query, where: fragment("?->>'object' = ?", activity.data, ^object))
|
||||
end
|
||||
|
||||
defp restrict_object(query, _), do: query
|
||||
|
||||
defp restrict_join_state(query, %{state: state}) when is_binary(state) do
|
||||
from(
|
||||
[activity] in query,
|
||||
where: fragment("(?)->>'state' = ?", activity.data, ^state)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_join_state(query, _), do: query
|
||||
|
||||
defp restrict_quote_url(query, %{quote_url: quote_url}) do
|
||||
from([_activity, object] in query,
|
||||
where: fragment("(?)->'quoteUrl' = ?", object.data, ^quote_url)
|
||||
|
@ -1304,7 +1338,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp exclude_poll_votes(query, _) do
|
||||
if has_named_binding?(query, :object) do
|
||||
from([activity, object: o] in query,
|
||||
where: fragment("not(?->>'type' = ?)", o.data, "Answer")
|
||||
where: fragment("not(?->>'type' = 'Answer')", o.data)
|
||||
)
|
||||
else
|
||||
query
|
||||
|
@ -1316,7 +1350,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp exclude_chat_messages(query, _) do
|
||||
if has_named_binding?(query, :object) do
|
||||
from([activity, object: o] in query,
|
||||
where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
|
||||
where: fragment("not(?->>'type' = 'ChatMessage')", o.data)
|
||||
)
|
||||
else
|
||||
query
|
||||
|
@ -1462,6 +1496,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> restrict_announce_object_actor(opts)
|
||||
|> restrict_filtered(opts)
|
||||
|> restrict_rule(opts)
|
||||
|> restrict_object(opts)
|
||||
|> restrict_quote_url(opts)
|
||||
|> maybe_restrict_deactivated_users(opts)
|
||||
|> exclude_poll_votes(opts)
|
||||
|
@ -1896,4 +1931,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp maybe_restrict_deactivated_users(activity, _opts),
|
||||
do: Activity.restrict_deactivated_users(activity)
|
||||
|
||||
def fetch_joined_events(user, params \\ %{}, pagination \\ :keyset) do
|
||||
user.ap_id
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_type("Join")
|
||||
|> Activity.with_joined_object()
|
||||
|> Object.with_joined_activity()
|
||||
|> select([join, object, activity], %{activity | object: object, pagination_id: join.id})
|
||||
|> order_by([join, _, _], desc_nulls_last: join.id)
|
||||
|> restrict_join_state(params)
|
||||
|> Pagination.fetch_paginated(
|
||||
Map.merge(params, %{skip_order: true}),
|
||||
pagination
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,10 +21,10 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
def accept_or_reject(actor, activity, type) do
|
||||
def accept_or_reject(%User{ap_id: ap_id}, activity, type) do
|
||||
data = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"actor" => actor.ap_id,
|
||||
"actor" => ap_id,
|
||||
"type" => type,
|
||||
"object" => activity.data["id"],
|
||||
"to" => [activity.actor]
|
||||
|
@ -33,14 +33,26 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
{:ok, data, []}
|
||||
end
|
||||
|
||||
@spec reject(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def reject(actor, rejected_activity) do
|
||||
accept_or_reject(actor, rejected_activity, "Reject")
|
||||
def accept_or_reject(%Object{data: %{"actor" => actor}}, activity, type) do
|
||||
data = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"actor" => actor,
|
||||
"type" => type,
|
||||
"object" => activity.data["id"],
|
||||
"to" => [activity.actor]
|
||||
}
|
||||
|
||||
{:ok, data, []}
|
||||
end
|
||||
|
||||
@spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def accept(actor, accepted_activity) do
|
||||
accept_or_reject(actor, accepted_activity, "Accept")
|
||||
@spec reject(User.t() | Object.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def reject(object, rejected_activity) do
|
||||
accept_or_reject(object, rejected_activity, "Reject")
|
||||
end
|
||||
|
||||
@spec accept(User.t() | Object.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def accept(object, accepted_activity) do
|
||||
accept_or_reject(object, accepted_activity, "Accept")
|
||||
end
|
||||
|
||||
@spec follow(User.t(), User.t()) :: {:ok, map(), keyword()}
|
||||
|
@ -307,13 +319,13 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
|
||||
@spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||
def update(actor, object) do
|
||||
{to, cc} =
|
||||
{to, cc, bcc} =
|
||||
if object["type"] in Pleroma.Constants.actor_types() do
|
||||
# User updates, always public
|
||||
{[Pleroma.Constants.as_public(), actor.follower_address], []}
|
||||
{[Pleroma.Constants.as_public(), actor.follower_address], [], []}
|
||||
else
|
||||
# Status updates, follow the recipients in the object
|
||||
{object["to"] || [], object["cc"] || []}
|
||||
{object["to"] || [], object["cc"] || [], object["participations"] || []}
|
||||
end
|
||||
|
||||
{:ok,
|
||||
|
@ -323,7 +335,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
"actor" => actor.ap_id,
|
||||
"object" => object,
|
||||
"to" => to,
|
||||
"cc" => cc
|
||||
"cc" => cc,
|
||||
"bcc" => bcc
|
||||
}, []}
|
||||
end
|
||||
|
||||
|
@ -431,4 +444,38 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
defp pinned_url(nickname) when is_binary(nickname) do
|
||||
Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)
|
||||
end
|
||||
|
||||
def join(actor, object, participation_message \\ nil) do
|
||||
with {:ok, data, meta} <- object_action(actor, object) do
|
||||
data =
|
||||
data
|
||||
|> Map.put("type", "Join")
|
||||
|> Map.put("participationMessage", participation_message)
|
||||
|
||||
{:ok, data, meta}
|
||||
end
|
||||
end
|
||||
|
||||
@spec event(ActivityDraft.t()) :: {:ok, map(), keyword()}
|
||||
def event(%ActivityDraft{} = draft) do
|
||||
data = %{
|
||||
"type" => "Event",
|
||||
"to" => draft.to,
|
||||
"cc" => draft.cc,
|
||||
"name" => draft.params[:name],
|
||||
"content" => draft.content_html,
|
||||
"context" => draft.context,
|
||||
"attachment" => draft.attachments,
|
||||
"actor" => draft.user.ap_id,
|
||||
"tag" => Keyword.values(draft.tags) |> Enum.uniq(),
|
||||
"joinMode" => draft.params[:join_mode] || "free",
|
||||
"location" => draft.location,
|
||||
"location_id" => draft.location_id,
|
||||
"location_provider" => draft.location_provider,
|
||||
"startTime" => draft.start_time,
|
||||
"endTime" => draft.end_time
|
||||
}
|
||||
|
||||
{:ok, data, []}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,6 +33,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.JoinValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LeaveValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
||||
|
@ -201,7 +203,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
|
||||
def validate(%{"type" => type} = object, meta)
|
||||
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
|
||||
ChatMessage Answer] do
|
||||
ChatMessage Answer Join Leave] do
|
||||
validator =
|
||||
case type do
|
||||
"Accept" -> AcceptRejectValidator
|
||||
|
@ -213,6 +215,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
"Announce" -> AnnounceValidator
|
||||
"ChatMessage" -> ChatMessageValidator
|
||||
"Answer" -> AnswerValidator
|
||||
"Join" -> JoinValidator
|
||||
"Leave" -> LeaveValidator
|
||||
end
|
||||
|
||||
cast_func =
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
@ -32,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
|
|||
|> validate_required([:id, :type, :actor, :to, :object])
|
||||
|> validate_inclusion(:type, ["Accept", "Reject"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Follow"])
|
||||
|> validate_object_presence(allowed_types: ["Follow", "Join"])
|
||||
|> validate_accept_reject_rights()
|
||||
end
|
||||
|
||||
|
@ -44,8 +45,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
|
|||
|
||||
def validate_accept_reject_rights(cng) do
|
||||
with object_id when is_binary(object_id) <- get_field(cng, :object),
|
||||
%Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id),
|
||||
true <- followed_actor == get_field(cng, :actor) do
|
||||
%Activity{} = activity <- Activity.get_by_ap_id(object_id),
|
||||
true <- validate_actor(activity, get_field(cng, :actor)) do
|
||||
cng
|
||||
else
|
||||
_e ->
|
||||
|
@ -53,4 +54,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
|
|||
|> add_error(:actor, "can't accept or reject the given activity")
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Follow", "object" => followed_actor}}, actor) do
|
||||
followed_actor == actor
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Join", "object" => joined_event}}, actor) do
|
||||
%Object{data: %{"actor" => event_author}} = Object.get_cached_by_ap_id(joined_event)
|
||||
event_author == actor
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.PlaceValidator
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Ecto.Changeset
|
||||
|
@ -24,6 +26,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
|||
status_object_fields()
|
||||
end
|
||||
end
|
||||
|
||||
field(:startTime, ObjectValidators.DateTime)
|
||||
field(:endTime, ObjectValidators.DateTime)
|
||||
|
||||
field(:joinMode, :string, default: "free")
|
||||
|
||||
embeds_one(:location, PlaceValidator)
|
||||
|
||||
field(:participation_count, :integer, default: 0)
|
||||
field(:participations, {:array, ObjectValidators.ObjectID}, default: [])
|
||||
field(:participation_request_count, :integer, default: 0)
|
||||
end
|
||||
|
||||
def cast_and_apply(data) do
|
||||
|
@ -58,14 +71,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
|||
data = fix(data)
|
||||
|
||||
struct
|
||||
|> cast(data, __schema__(:fields) -- [:attachment, :tag])
|
||||
|> cast(data, __schema__(:fields) -- [:attachment, :tag, :location])
|
||||
|> cast_embed(:attachment)
|
||||
|> cast_embed(:tag)
|
||||
|> cast_embed(:location)
|
||||
end
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Event"])
|
||||
|> validate_inclusion(:joinMode, ~w[free restricted invite external])
|
||||
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|
||||
|> CommonValidations.validate_any_presence([:cc, :to])
|
||||
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.JoinValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
quote do
|
||||
unquote do
|
||||
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
||||
message_fields()
|
||||
activity_fields()
|
||||
end
|
||||
end
|
||||
|
||||
field(:state, :string, default: "pending")
|
||||
|
||||
field(:participationMessage, :string)
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Join"])
|
||||
|> validate_inclusion(:state, ~w{pending reject accept})
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Event"])
|
||||
|> validate_existing_join()
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data()
|
||||
|> validate_data()
|
||||
end
|
||||
|
||||
defp validate_existing_join(%{changes: %{actor: actor, object: object}} = cng) do
|
||||
if Utils.get_existing_join(actor, object) do
|
||||
cng
|
||||
|> add_error(:actor, "already joined this event")
|
||||
|> add_error(:object, "already joined by this actor")
|
||||
else
|
||||
cng
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_existing_join(cng), do: cng
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.LeaveValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
quote do
|
||||
unquote do
|
||||
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
||||
message_fields()
|
||||
activity_fields()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Leave"])
|
||||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Event"])
|
||||
|> validate_existing_join()
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data()
|
||||
|> validate_data()
|
||||
end
|
||||
|
||||
defp validate_existing_join(%{changes: %{actor: actor, object: object}} = cng) do
|
||||
if !Utils.get_existing_join(actor, object) do
|
||||
cng
|
||||
|> add_error(:actor, "not joined this event")
|
||||
|> add_error(:object, "not joined by this actor")
|
||||
else
|
||||
cng
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_existing_join(cng), do: cng
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.PlaceValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key false
|
||||
embedded_schema do
|
||||
field(:type, :string)
|
||||
field(:name, :string)
|
||||
field(:longitude, :float)
|
||||
field(:latitude, :float)
|
||||
field(:accuracy, :float)
|
||||
field(:altitude, :float)
|
||||
field(:radius, :float)
|
||||
field(:units, :string)
|
||||
|
||||
embeds_one :address, Address do
|
||||
field(:type, :string)
|
||||
field(:postalCode, :string)
|
||||
field(:addressRegion, :string)
|
||||
field(:streetAddress, :string)
|
||||
field(:addressCountry, :string)
|
||||
field(:addressLocality, :string)
|
||||
end
|
||||
end
|
||||
|
||||
def changeset(struct, data) do
|
||||
data = fix(data)
|
||||
|
||||
struct
|
||||
|> cast(data, [:type, :name, :longitude, :latitude, :accuracy, :altitude, :radius, :units])
|
||||
|> cast_embed(:address, with: &address_changeset/2)
|
||||
|> validate_inclusion(:type, ["Place"])
|
||||
|> validate_inclusion(:radius, ~w[cm feet inches km m miles])
|
||||
|> validate_number(:accuracy, greater_than_or_equal_to: 0, less_than_or_equal_to: 100)
|
||||
|> validate_number(:radius, greater_than_or_equal_to: 0)
|
||||
|> validate_required([:type, :name])
|
||||
end
|
||||
|
||||
def address_changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, [
|
||||
:type,
|
||||
:postalCode,
|
||||
:addressRegion,
|
||||
:streetAddress,
|
||||
:addressCountry,
|
||||
:addressLocality
|
||||
])
|
||||
|> validate_inclusion(:type, ["PostalAddress"])
|
||||
end
|
||||
|
||||
defp fix(data) do
|
||||
data
|
||||
|> fix_address()
|
||||
end
|
||||
|
||||
defp fix_address(%{"address" => address} = data) when is_binary(address) do
|
||||
data
|
||||
|> Map.put("address", %{
|
||||
"type" => "PostalAddress",
|
||||
"streetAddress" => address
|
||||
})
|
||||
end
|
||||
|
||||
defp fix_address(data), do: data
|
||||
end
|
|
@ -22,6 +22,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Streamer
|
||||
alias Pleroma.Workers.EventReminderWorker
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
require Pleroma.Constants
|
||||
|
@ -42,23 +43,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
# - Sends a notification
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Accept",
|
||||
"object" => follow_activity_id
|
||||
}
|
||||
} = object,
|
||||
%{data: %{"actor" => actor, "type" => "Accept", "object" => activity_id}} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_accepted(activity, actor)
|
||||
|
||||
if activity.data["type"] === "Join" do
|
||||
Notification.create_notifications(object)
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -74,18 +68,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Reject",
|
||||
"object" => follow_activity_id
|
||||
"object" => activity_id
|
||||
}
|
||||
} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do
|
||||
FollowingRelationship.update(follower, followed, :follow_reject)
|
||||
Notification.dismiss(follow_activity)
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_rejected(activity, actor)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -427,6 +417,43 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
end
|
||||
end
|
||||
|
||||
# Tasks this handles:
|
||||
# - accepts join if event is local and public
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Join"}} = object, meta) do
|
||||
joined_event = Object.get_by_ap_id(object.data["object"])
|
||||
|
||||
if Object.local?(joined_event) and
|
||||
(joined_event.data["joinMode"] == "free" or
|
||||
object.data["actor"] == joined_event.data["actor"]) do
|
||||
{:ok, accept_data, _} = Builder.accept(joined_event, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true)
|
||||
end
|
||||
|
||||
if Object.local?(joined_event) and joined_event.data["joinMode"] != "free" and
|
||||
object.data["actor"] != joined_event.data["actor"] do
|
||||
Utils.update_participation_request_count_in_object(joined_event)
|
||||
end
|
||||
|
||||
Notification.create_notifications(object)
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle(%{actor: actor_id, data: %{"type" => "Leave", "object" => event_id}} = object, meta) do
|
||||
with undone_object <- Utils.get_existing_join(actor_id, event_id),
|
||||
:ok <- handle_undoing(undone_object) do
|
||||
event = Object.get_by_ap_id(event_id)
|
||||
|
||||
if Object.local?(event) and event.data["joinMode"] != "free" do
|
||||
Utils.update_participation_request_count_in_object(event)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
end
|
||||
|
||||
# Nothing to do
|
||||
@impl true
|
||||
def handle(object, meta) do
|
||||
|
@ -472,6 +499,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
{:ok, _, updated} =
|
||||
Object.Updater.do_update_and_invalidate_cache(orig_object, updated_object)
|
||||
|
||||
EventReminderWorker.schedule_event_reminder(object)
|
||||
|
||||
if updated do
|
||||
object
|
||||
|> Activity.normalize()
|
||||
|
@ -482,6 +511,52 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
defp handle_accepted(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_accepted(
|
||||
%Activity{data: %{"type" => "Join", "object" => event_id}} = join_activity,
|
||||
actor
|
||||
) do
|
||||
with %Object{data: %{"actor" => ^actor}} = joined_event <- Object.get_by_ap_id(event_id),
|
||||
{:ok, join_activity} <- Utils.update_join_state(join_activity, "accept") do
|
||||
Utils.add_participation_to_object(join_activity, joined_event)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_rejected(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do
|
||||
FollowingRelationship.update(follower, followed, :follow_reject)
|
||||
Notification.dismiss(follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_rejected(
|
||||
%Activity{data: %{"type" => "Join", "object" => event_id}} = join_activity,
|
||||
actor
|
||||
) do
|
||||
with %Object{data: %{"actor" => ^actor}} = joined_event <- Object.get_by_ap_id(event_id),
|
||||
{:o, join_activity} <- Utils.update_join_state(join_activity, "reject") do
|
||||
Utils.remove_participation_from_object(join_activity, joined_event)
|
||||
Notification.dismiss(join_activity)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do
|
||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||
actor = User.get_cached_by_ap_id(object.data["actor"])
|
||||
|
@ -536,8 +611,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => "Event"} = object, activity, meta) do
|
||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||
EventReminderWorker.schedule_event_reminder(activity)
|
||||
{:ok, object, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => objtype} = object, _activity, meta)
|
||||
when objtype in ~w[Audio Video Image Event Article Note Page] do
|
||||
when objtype in ~w[Audio Video Image Article Note Page] do
|
||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
@ -589,6 +671,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_undoing(
|
||||
%{data: %{"type" => "Join", "actor" => _actor_id, "object" => event_id}} = object
|
||||
) do
|
||||
with %Object{} = event_object <- Object.get_by_ap_id(event_id),
|
||||
{:ok, _} <- Utils.remove_participation_from_object(object, event_object),
|
||||
{:ok, _} <- Repo.delete(object) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def handle_undoing(object), do: {:error, ["don't know how to handle", object]}
|
||||
|
||||
@spec delete_object(Activity.t()) :: :ok | {:error, Ecto.Changeset.t()}
|
||||
|
|
|
@ -559,7 +559,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
%{"type" => type} = data,
|
||||
_options
|
||||
)
|
||||
when type in ~w{Update Block Follow Accept Reject} do
|
||||
when type in ~w{Update Block Follow Accept Reject Join Leave} do
|
||||
fixed_obj = maybe_fix_object(data["object"])
|
||||
data = if fixed_obj != nil, do: %{data | "object" => fixed_obj}, else: data
|
||||
|
||||
|
@ -622,7 +622,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
} = data,
|
||||
_options
|
||||
)
|
||||
when type in ["Like", "EmojiReact", "Announce", "Block"] do
|
||||
when type in ["Like", "EmojiReact", "Announce", "Block", "Join"] do
|
||||
with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
|
|
@ -448,6 +448,48 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
end
|
||||
end
|
||||
|
||||
def add_participation_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
[actor | fetch_participations(object)]
|
||||
|> Enum.uniq()
|
||||
|> update_participations_in_object(object)
|
||||
end
|
||||
|
||||
def remove_participation_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
List.delete(fetch_participations(object), actor)
|
||||
|> update_participations_in_object(object)
|
||||
end
|
||||
|
||||
defp update_participations_in_object(participations, object) do
|
||||
update_element_in_object("participation", participations, object)
|
||||
end
|
||||
|
||||
def update_participation_request_count_in_object(object) do
|
||||
params = %{
|
||||
type: "Join",
|
||||
object: object.data["id"],
|
||||
state: "pending"
|
||||
}
|
||||
|
||||
count =
|
||||
[]
|
||||
|> ActivityPub.fetch_activities_query(params)
|
||||
|> Repo.aggregate(:count)
|
||||
|
||||
data = Map.put(object.data, "participation_request_count", count)
|
||||
|
||||
object
|
||||
|> Changeset.change(data: data)
|
||||
|> Object.update_and_set_cache()
|
||||
end
|
||||
|
||||
defp fetch_participations(object) do
|
||||
if is_list(object.data["participations"]) do
|
||||
object.data["participations"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
#### Follow-related helpers
|
||||
|
||||
@doc """
|
||||
|
@ -956,4 +998,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> Enum.reject(&User.blocks?(&1, poster))
|
||||
|> Enum.each(&Pleroma.Web.CommonAPI.repeat(activity.id, &1))
|
||||
end
|
||||
|
||||
def get_existing_join(actor, id) do
|
||||
actor
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_object_id(id)
|
||||
|> Activity.Queries.by_type("Join")
|
||||
|> order_by([activity], fragment("? desc nulls last", activity.id))
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def update_join_state(
|
||||
%Activity{} = activity,
|
||||
state
|
||||
) do
|
||||
new_data = Map.put(activity.data, "state", state)
|
||||
changeset = Changeset.change(activity, data: new_data)
|
||||
|
||||
with {:ok, activity} <- Repo.update(changeset) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -140,7 +140,8 @@ defmodule Pleroma.Web.ApiSpec do
|
|||
"Status actions",
|
||||
"Media attachments",
|
||||
"Bookmark folders",
|
||||
"Tags"
|
||||
"Tags",
|
||||
"Event actions"
|
||||
]
|
||||
},
|
||||
%{
|
||||
|
|
|
@ -136,6 +136,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
BooleanLike.schema(),
|
||||
"Include only statuses with media attached"
|
||||
),
|
||||
Operation.parameter(
|
||||
:only_events,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Include only objects with Event type"
|
||||
),
|
||||
Operation.parameter(
|
||||
:with_muted,
|
||||
:query,
|
||||
|
|
|
@ -174,6 +174,11 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
"Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.",
|
||||
nullable: true
|
||||
},
|
||||
participation_message: %Schema{
|
||||
type: :string,
|
||||
description: "Description of event participation request",
|
||||
nullable: true
|
||||
},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
|
@ -211,12 +216,17 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
"status",
|
||||
"update",
|
||||
"admin.sign_up",
|
||||
"admin.report"
|
||||
"admin.report",
|
||||
"pleroma:participation_accepted",
|
||||
"pleroma:participation_request",
|
||||
"pleroma:event_reminder",
|
||||
"pleroma:event_update"
|
||||
],
|
||||
description: """
|
||||
The type of event that resulted in the notification.
|
||||
|
||||
- `follow` - Someone followed you
|
||||
- `follow_request` - Someone wants to follow you
|
||||
- `mention` - Someone mentioned you in their status
|
||||
- `reblog` - Someone boosted one of your statuses
|
||||
- `favourite` - Someone favourited one of your statuses
|
||||
|
@ -229,6 +239,10 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
- `update` - A status you boosted has been edited
|
||||
- `admin.sign_up` - Someone signed up (optionally sent to admins)
|
||||
- `admin.report` - A new report has been filed
|
||||
- `pleroma:event_reminder` – An event you are participating in or created is taking place soon
|
||||
- `pleroma:event_update` – An event you are participating in was edited
|
||||
- `pleroma:participation_request - Someone wants to participate in your event
|
||||
- `pleroma:participation_accepted - Your event participation request was accepted
|
||||
"""
|
||||
}
|
||||
end
|
||||
|
|
341
lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex
Normal file
341
lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex
Normal file
|
@ -0,0 +1,341 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaEventOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ParticipationRequest
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
alias Pleroma.Web.ApiSpec.StatusOperation
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Publish new status",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Create a new event",
|
||||
operationId: "PleromaAPI.EventController.create",
|
||||
requestBody: request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
422 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Update event",
|
||||
description: "Change the content of an event",
|
||||
operationId: "PleromaAPI.EventController.update",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
parameters: [id_param()],
|
||||
requestBody: request_body("Parameters", update_request(), required: true),
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def participations_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Participants list",
|
||||
description: "View who joined a given event",
|
||||
operationId: "EventController.participations",
|
||||
security: [%{"oAuth" => ["read"]}],
|
||||
parameters: [id_param() | pagination_params()],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Array of Accounts",
|
||||
"application/json",
|
||||
AccountOperation.array_of_accounts()
|
||||
),
|
||||
403 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def participation_requests_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Participation requests list",
|
||||
description: "View who wants to join the event",
|
||||
operationId: "EventController.participations",
|
||||
security: [%{"oAuth" => ["read"]}],
|
||||
parameters: [id_param() | pagination_params()],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Array of participation requests",
|
||||
"application/json",
|
||||
array_of_participation_requests()
|
||||
),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def join_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Participate",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Participate in an event",
|
||||
operationId: "PleromaAPI.EventController.join",
|
||||
parameters: [id_param()],
|
||||
requestBody:
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
account: Account,
|
||||
participation_message: %Schema{
|
||||
type: :string,
|
||||
description: "Why the user wants to participate"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: false
|
||||
),
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def leave_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Unparticipate",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Delete event participation",
|
||||
operationId: "PleromaAPI.EventController.leave",
|
||||
parameters: [id_param()],
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def authorize_participation_request_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Accept participation",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Accept event participation request",
|
||||
operationId: "PleromaAPI.EventController.authorize_participation_request",
|
||||
parameters: [id_param(), participant_id_param()],
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def reject_participation_request_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Reject participation",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Reject event participation request",
|
||||
operationId: "PleromaAPI.EventController.reject_participation_request",
|
||||
parameters: [id_param(), participant_id_param()],
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def export_ics_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Export status",
|
||||
description: "Export event to .ics",
|
||||
operationId: "PleromaAPI.EventController.export_ics",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [id_param()],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Event", "text/calendar; charset=utf-8", %Schema{type: :string}),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def joined_events_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Joined events",
|
||||
description: "Get your joined events",
|
||||
operationId: "PleromaAPI.EventController.joined_events",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:state,
|
||||
:query,
|
||||
%Schema{type: :string, enum: ["pending", "reject", "accept"]},
|
||||
"Filter by join state"
|
||||
)
|
||||
| pagination_params()
|
||||
],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Array of Statuses",
|
||||
"application/json",
|
||||
StatusOperation.array_of_statuses()
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_request do
|
||||
%Schema{
|
||||
title: "EventCreateRequest",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Name of the event."
|
||||
},
|
||||
status: %Schema{
|
||||
type: :string,
|
||||
nullable: true,
|
||||
description: "Text description of the event."
|
||||
},
|
||||
banner_id: %Schema{
|
||||
nullable: true,
|
||||
type: :string,
|
||||
description: "Attachment id to be attached as banner."
|
||||
},
|
||||
start_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "Start time."
|
||||
},
|
||||
end_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "End time."
|
||||
},
|
||||
join_mode: %Schema{
|
||||
type: :string,
|
||||
enum: ["free", "restricted"]
|
||||
},
|
||||
location_id: %Schema{
|
||||
type: :string,
|
||||
description: "Location ID from geospatial provider",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"name" => "Example event",
|
||||
"status" => "No information for now.",
|
||||
"start_time" => "2022-02-21T22:00:00.000Z",
|
||||
"end_time" => "2022-02-21T23:00:00.000Z"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp update_request do
|
||||
%Schema{
|
||||
title: "EventUpdateRequest",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Name of the event."
|
||||
},
|
||||
status: %Schema{
|
||||
type: :string,
|
||||
nullable: true,
|
||||
description: "Text description of the event."
|
||||
},
|
||||
banner_id: %Schema{
|
||||
nullable: true,
|
||||
type: :string,
|
||||
description: "Attachment id to be attached as banner."
|
||||
},
|
||||
start_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "Start time."
|
||||
},
|
||||
end_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "End time."
|
||||
},
|
||||
location_id: %Schema{
|
||||
type: :string,
|
||||
description: "Location ID from geospatial provider",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"name" => "Updated event",
|
||||
"status" => "We had to reschedule the event.",
|
||||
"start_time" => "2022-02-22T22:00:00.000Z",
|
||||
"end_time" => "2022-02-22T23:00:00.000Z"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp event_response do
|
||||
Operation.response(
|
||||
"Status",
|
||||
"application/json",
|
||||
Status
|
||||
)
|
||||
end
|
||||
|
||||
defp id_param do
|
||||
Operation.parameter(:id, :path, FlakeID, "Event ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
|
||||
defp participant_id_param do
|
||||
Operation.parameter(:participant_id, :path, FlakeID, "Event participant ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
|
||||
def array_of_participation_requests do
|
||||
%Schema{
|
||||
title: "ArrayOfParticipationRequests",
|
||||
type: :array,
|
||||
items: ParticipationRequest,
|
||||
example: [ParticipationRequest.schema().example]
|
||||
}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaSearchOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.LocationResult
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def location_operation do
|
||||
%Operation{
|
||||
tags: ["Search"],
|
||||
summary: "Search locations",
|
||||
security: [%{"oAuth" => []}],
|
||||
operationId: "PleromaAPI.SearchController.location",
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:q,
|
||||
:query,
|
||||
%Schema{type: :string},
|
||||
"What to search for",
|
||||
required: true
|
||||
),
|
||||
Operation.parameter(
|
||||
:locale,
|
||||
:query,
|
||||
%Schema{type: :string},
|
||||
"The user's locale. Geocoding backends will make use of this value"
|
||||
),
|
||||
Operation.parameter(
|
||||
:type,
|
||||
:query,
|
||||
%Schema{type: :string, enum: ["ADMINISTRATIVE"]},
|
||||
"Filter by type of results"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Results", "application/json", location_results())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def location_results do
|
||||
%Schema{
|
||||
type: :array,
|
||||
items: LocationResult,
|
||||
description: "Locations which match the given query",
|
||||
example: [LocationResult.schema().example]
|
||||
}
|
||||
end
|
||||
end
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.LocationResult
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
|
@ -544,7 +545,8 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
responses: %{
|
||||
200 => status_response(),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
404 => Operation.response("Not Found", "application/json", ApiError),
|
||||
422 => Operation.response("Unprocessable Entity", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -828,6 +830,11 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
content_type: %Schema{
|
||||
type: :string,
|
||||
description: "The content type of the source"
|
||||
},
|
||||
location: %Schema{
|
||||
allOf: [LocationResult],
|
||||
description: "Location result for an event",
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
local_param(),
|
||||
remote_param(),
|
||||
only_media_param(),
|
||||
only_events_param(),
|
||||
with_muted_param(),
|
||||
exclude_visibilities_param(),
|
||||
reply_visibility_param() | pagination_params()
|
||||
|
@ -62,6 +63,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
local_param(),
|
||||
instance_param(),
|
||||
only_media_param(),
|
||||
only_events_param(),
|
||||
remote_param(),
|
||||
with_muted_param(),
|
||||
exclude_visibilities_param(),
|
||||
|
@ -109,6 +111,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
),
|
||||
local_param(),
|
||||
only_media_param(),
|
||||
only_events_param(),
|
||||
remote_param(),
|
||||
with_muted_param(),
|
||||
exclude_visibilities_param() | pagination_params()
|
||||
|
@ -139,6 +142,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
local_param(),
|
||||
remote_param(),
|
||||
only_media_param(),
|
||||
only_events_param(),
|
||||
exclude_visibilities_param() | pagination_params()
|
||||
],
|
||||
operationId: "TimelineController.list",
|
||||
|
@ -211,6 +215,15 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp only_events_param do
|
||||
Operation.parameter(
|
||||
:only_events,
|
||||
:query,
|
||||
%Schema{allOf: [BooleanLike], default: false},
|
||||
"Include only objects with Event type"
|
||||
)
|
||||
end
|
||||
|
||||
defp remote_param do
|
||||
Operation.parameter(
|
||||
:remote,
|
||||
|
|
112
lib/pleroma/web/api_spec/schemas/event.ex
Normal file
112
lib/pleroma/web/api_spec/schemas/event.ex
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.Event do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Event",
|
||||
description: "Represents an event attached to a status",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Name of the event"
|
||||
},
|
||||
start_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "Start time",
|
||||
nullable: true
|
||||
},
|
||||
end_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "End time",
|
||||
nullable: true
|
||||
},
|
||||
join_mode: %Schema{
|
||||
type: :string,
|
||||
description: "Who can join the event"
|
||||
},
|
||||
participants_count: %Schema{
|
||||
type: :integer,
|
||||
description: "Event participants count",
|
||||
nullable: true
|
||||
},
|
||||
participation_request_count: %Schema{
|
||||
type: :integer,
|
||||
description: "Event participation requests count",
|
||||
nullable: true
|
||||
},
|
||||
location: %Schema{
|
||||
type: :object,
|
||||
description: "Location where the event takes part",
|
||||
properties: %{
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Object name",
|
||||
nullable: true
|
||||
},
|
||||
url: %Schema{
|
||||
type: :string,
|
||||
description: "Object URL",
|
||||
nullable: true
|
||||
},
|
||||
longitude: %Schema{
|
||||
type: :number,
|
||||
description: "Object vertical coordinate",
|
||||
nullable: true
|
||||
},
|
||||
latitude: %Schema{
|
||||
type: :number,
|
||||
description: "Object horizontal coordinate",
|
||||
nullable: true
|
||||
},
|
||||
street: %Schema{
|
||||
type: :string,
|
||||
description: "Object street",
|
||||
nullable: true
|
||||
},
|
||||
postal_code: %Schema{
|
||||
type: :string,
|
||||
description: "Object postal code",
|
||||
nullable: true
|
||||
},
|
||||
locality: %Schema{
|
||||
type: :string,
|
||||
description: "Object locality",
|
||||
nullable: true
|
||||
},
|
||||
region: %Schema{
|
||||
type: :string,
|
||||
description: "Object region",
|
||||
nullable: true
|
||||
},
|
||||
country: %Schema{
|
||||
type: :string,
|
||||
description: "Object country",
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
nullable: true
|
||||
},
|
||||
join_state: %Schema{
|
||||
type: :string,
|
||||
description: "Have you joined the event?",
|
||||
enum: ["pending", "reject", "accept"],
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
name: "Example event"
|
||||
# start_time: "2022-02-21T22:00:00.000Z",
|
||||
# end_time: "2022-02-21T23:00:00.000Z",
|
||||
# join_mode: "free",
|
||||
# participants_count: 0
|
||||
}
|
||||
})
|
||||
end
|
80
lib/pleroma/web/api_spec/schemas/location_result.ex
Normal file
80
lib/pleroma/web/api_spec/schemas/location_result.ex
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.LocationResult do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "LocationResult",
|
||||
description: "Represents a location lookup result",
|
||||
type: :object,
|
||||
properties: %{
|
||||
country: %Schema{type: :string, description: "The address's country", nullable: true},
|
||||
description: %Schema{
|
||||
type: :string,
|
||||
description: "The address's description",
|
||||
nullable: true
|
||||
},
|
||||
locality: %Schema{type: :string, description: "The address's locality", nullable: true},
|
||||
origin_id: %Schema{
|
||||
type: :string,
|
||||
description: "The address's original ID from the provider",
|
||||
nullable: true
|
||||
},
|
||||
origin_provider: %Schema{
|
||||
type: :string,
|
||||
description: "The provider used by instance",
|
||||
nullable: true
|
||||
},
|
||||
postal_code: %Schema{
|
||||
type: :string,
|
||||
description: "The address's postal code",
|
||||
nullable: true
|
||||
},
|
||||
region: %Schema{type: :string, description: "The address's region", nullable: true},
|
||||
street: %Schema{
|
||||
type: :string,
|
||||
description: "The address's street name (with number)",
|
||||
nullable: true
|
||||
},
|
||||
timezone: %Schema{
|
||||
type: :string,
|
||||
description: "The (estimated) timezone of the location",
|
||||
nullable: true
|
||||
},
|
||||
type: %Schema{type: :string, description: "The address's type", nullable: true},
|
||||
url: %Schema{type: :string, description: "The address's URL", nullable: true},
|
||||
geom: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
coordinates: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :number}
|
||||
},
|
||||
srid: %Schema{type: :integer}
|
||||
},
|
||||
nullable: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"country" => "Poland",
|
||||
"description" => "Dworek Modrzewiowy",
|
||||
"geom" => %{
|
||||
"coordinates" => [19.35267765039501, 52.233616299999994],
|
||||
"srid" => 4326
|
||||
},
|
||||
"locality" => "Kutno",
|
||||
"origin_id" => "251399743",
|
||||
"origin_provider" => "nominatim",
|
||||
"postal_code" => "80-549",
|
||||
"region" => "Łódź Voivodeship",
|
||||
"street" => "20 Gabriela Narutowicza",
|
||||
"timezone" => nil,
|
||||
"type" => "house",
|
||||
"url" => nil
|
||||
}
|
||||
})
|
||||
end
|
30
lib/pleroma/web/api_spec/schemas/participation_request.ex
Normal file
30
lib/pleroma/web/api_spec/schemas/participation_request.ex
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.ParticipationRequest do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ParticipationRequest",
|
||||
description: "Represents an event participation request",
|
||||
type: :object,
|
||||
properties: %{
|
||||
account: %Schema{
|
||||
allOf: [Account],
|
||||
description: "The account that wants to participate in the event."
|
||||
},
|
||||
participation_message: %Schema{
|
||||
type: :string,
|
||||
description: "Why the user wants to participate"
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"account" => Account.schema().example,
|
||||
"participation_message" => "I'm interested in this event"
|
||||
}
|
||||
})
|
||||
end
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Attachment
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Event
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Tag
|
||||
|
@ -255,6 +256,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
nullable: true,
|
||||
description:
|
||||
"The ID of the list the post is addressed to (if any, only returned to author)"
|
||||
},
|
||||
event: %Schema{
|
||||
allOf: [Event],
|
||||
nullable: true,
|
||||
description: "The event attached to the status"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -362,6 +362,99 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def join(%User{} = user, event_id, params \\ %{}) do
|
||||
participation_message = Map.get(params, :participation_message)
|
||||
|
||||
case join_helper(user, event_id, participation_message) do
|
||||
{:ok, _} = res ->
|
||||
res
|
||||
|
||||
{:error, :not_found} = res ->
|
||||
res
|
||||
|
||||
{:error, :external_joins} ->
|
||||
{:error, dgettext("errors", "Joins are managed by external system")}
|
||||
|
||||
{:error, :not_an_event} ->
|
||||
{:error, dgettext("errors", "Not an event")}
|
||||
|
||||
{:error, e} ->
|
||||
Logger.error("Could not join #{event_id}. Error: #{inspect(e, pretty: true)}")
|
||||
{:error, dgettext("errors", "Could not join")}
|
||||
end
|
||||
end
|
||||
|
||||
defp join_helper(user, id, participation_message) do
|
||||
with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
|
||||
{_, true} <- {:object_type, object.data["type"] == "Event"},
|
||||
{_, true} <- {:managed_joins, object.data["joinMode"] != "external"},
|
||||
{_, {:ok, join_object, meta}} <-
|
||||
{:build_object, Builder.join(user, object, participation_message)},
|
||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
Pipeline.common_pipeline(join_object, Keyword.put(meta, :local, true))} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:find_object, _} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:object_type, false} ->
|
||||
{:error, :not_an_event}
|
||||
|
||||
{:managed_joins, false} ->
|
||||
{:error, :external_joins}
|
||||
|
||||
{:common_pipeline, {:error, {:validate, {:error, changeset}}}} = e ->
|
||||
if {:object, {"already joined by this actor", []}} in changeset.errors do
|
||||
{:ok, :already_joined}
|
||||
else
|
||||
{:error, e}
|
||||
end
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def leave(%User{ap_id: participant_ap_id} = user, event_id) do
|
||||
with %Activity{data: %{"object" => event_ap_id}} <- Activity.get_by_id(event_id),
|
||||
%Activity{} = join_activity <- Utils.get_existing_join(participant_ap_id, event_ap_id),
|
||||
{:ok, undo, _} <- Builder.undo(user, join_activity),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
|
||||
{:ok, activity}
|
||||
else
|
||||
nil ->
|
||||
{:error, dgettext("errors", "Not participating in the event")}
|
||||
|
||||
_ ->
|
||||
{:error, dgettext("errors", "Could not remove join activity")}
|
||||
end
|
||||
end
|
||||
|
||||
def accept_join_request(%User{} = user, %User{ap_id: participant_ap_id} = participant, event_id) do
|
||||
with %Activity{} = join_activity <- Utils.get_existing_join(participant_ap_id, event_id),
|
||||
{:ok, accept_data, _} <- Builder.accept(user, join_activity),
|
||||
{:ok, _activity, _} <- Pipeline.common_pipeline(accept_data, local: true),
|
||||
event <- Object.get_by_ap_id(event_id) do
|
||||
if Object.local?(event) and event.data["joinMode"] != "free" and
|
||||
join_activity.data["actor"] == event.data["actor"] do
|
||||
Utils.update_participation_request_count_in_object(event)
|
||||
end
|
||||
|
||||
{:ok, participant}
|
||||
end
|
||||
end
|
||||
|
||||
def reject_join_request(%User{} = user, %User{ap_id: participant_ap_id} = participant, event_id) do
|
||||
with %Activity{} = join_activity <- Utils.get_existing_join(participant_ap_id, event_id),
|
||||
{:ok, reject_data, _} <- Builder.reject(user, join_activity),
|
||||
{:ok, _activity, _} <- Pipeline.common_pipeline(reject_data, local: true),
|
||||
event <- Object.get_by_ap_id(event_id),
|
||||
{:ok, _} <- Utils.update_participation_request_count_in_object(event) do
|
||||
{:ok, participant}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_not_author(%{data: %{"actor" => ap_id}}, %{ap_id: ap_id}),
|
||||
do: {:error, dgettext("errors", "Poll's author can't vote")}
|
||||
|
||||
|
@ -720,6 +813,47 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def event(user, data, location \\ nil) do
|
||||
with {:ok, draft} <- ActivityDraft.event(user, data, location) do
|
||||
ActivityPub.create(draft.changes)
|
||||
end
|
||||
end
|
||||
|
||||
def update_event(user, orig_activity, changes, location \\ nil) do
|
||||
with orig_object <- Object.normalize(orig_activity),
|
||||
{:ok, new_object} <- make_update_event_data(user, orig_object, changes, location),
|
||||
{:ok, update_data, _} <- Builder.update(user, new_object),
|
||||
{:ok, update, _} <- Pipeline.common_pipeline(update_data, local: true) do
|
||||
{:ok, update}
|
||||
else
|
||||
_ -> {:error, nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp make_update_event_data(user, orig_object, changes, location) do
|
||||
kept_params = %{
|
||||
visibility: Visibility.get_visibility(orig_object),
|
||||
in_reply_to_id:
|
||||
with replied_id when is_binary(replied_id) <- orig_object.data["inReplyTo"],
|
||||
%Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(replied_id) do
|
||||
activity_id
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
}
|
||||
|
||||
params = Map.merge(changes, kept_params)
|
||||
|
||||
with {:ok, draft} <- ActivityDraft.event(user, params, location) do
|
||||
change =
|
||||
Object.Updater.make_update_object_data(orig_object.data, draft.object, Utils.make_date())
|
||||
|
||||
{:ok, change}
|
||||
else
|
||||
_ -> {:error, nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_cancel_jobs(%Activity{id: activity_id}) do
|
||||
Oban.Job
|
||||
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Language.LanguageDetector
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
@ -45,7 +46,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
language: nil,
|
||||
object: nil,
|
||||
preview?: false,
|
||||
changes: %{}
|
||||
changes: %{},
|
||||
location: nil,
|
||||
start_time: nil,
|
||||
end_time: nil,
|
||||
location_id: nil,
|
||||
location_provider: nil
|
||||
|
||||
def new(user, params) do
|
||||
%__MODULE__{user: user}
|
||||
|
@ -101,6 +107,41 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
@spec event(any, map) :: {:error, any} | {:ok, %{:valid? => true, optional(any) => any}}
|
||||
def event(user, params, location \\ nil) do
|
||||
user
|
||||
|> new(params)
|
||||
|> status()
|
||||
|> visibility()
|
||||
|> content()
|
||||
|> to_and_cc()
|
||||
|> context()
|
||||
|> with_valid(&event_banner/1)
|
||||
|> event_location(location)
|
||||
|> with_valid(&event_date/1)
|
||||
|> event_object()
|
||||
|> with_valid(&changes/1)
|
||||
|> validate()
|
||||
end
|
||||
|
||||
defp event_object(draft) do
|
||||
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
||||
|
||||
{:ok, event_data, _meta} = Builder.event(draft)
|
||||
|
||||
object =
|
||||
event_data
|
||||
|> Map.put("emoji", emoji)
|
||||
|> Map.put("source", %{
|
||||
"content" => draft.status,
|
||||
"mediaType" => Utils.get_content_type(draft.params[:content_type])
|
||||
})
|
||||
|> Map.put("generator", draft.params[:generator])
|
||||
|> Map.put("content_type", draft.params[:content_type])
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
defp put_params(draft, params) do
|
||||
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||
%__MODULE__{draft | params: params}
|
||||
|
@ -339,6 +380,78 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
%__MODULE__{draft | changes: changes}
|
||||
end
|
||||
|
||||
defp event_date(draft) do
|
||||
case draft.params[:start_time] do
|
||||
%DateTime{} = start_time ->
|
||||
case draft.params[:end_time] do
|
||||
%DateTime{} = end_time ->
|
||||
if DateTime.compare(end_time, start_time) == :lt do
|
||||
add_error(draft, dgettext("errors", "Event can't end before its start"))
|
||||
else
|
||||
start_time = start_time |> DateTime.to_iso8601()
|
||||
end_time = end_time |> DateTime.to_iso8601()
|
||||
|
||||
%__MODULE__{draft | start_time: start_time, end_time: end_time}
|
||||
end
|
||||
|
||||
_ ->
|
||||
start_time = start_time |> DateTime.to_iso8601()
|
||||
|
||||
%__MODULE__{draft | start_time: start_time}
|
||||
end
|
||||
|
||||
_ ->
|
||||
add_error(draft, dgettext("errors", "Start date is required"))
|
||||
end
|
||||
end
|
||||
|
||||
defp event_location(draft, %Geospatial.Address{} = address) do
|
||||
location = %{
|
||||
"type" => "Place",
|
||||
"name" => address.description,
|
||||
"id" => address.url,
|
||||
"address" => %{
|
||||
"type" => "PostalAddress",
|
||||
"streetAddress" => address.street,
|
||||
"postalCode" => address.postal_code,
|
||||
"addressLocality" => address.locality,
|
||||
"addressRegion" => address.region,
|
||||
"addressCountry" => address.country
|
||||
}
|
||||
}
|
||||
|
||||
location =
|
||||
if is_nil(address.geom) do
|
||||
location
|
||||
else
|
||||
{longitude, latitude} = address.geom.coordinates
|
||||
|
||||
location
|
||||
|> Map.put("longitude", longitude)
|
||||
|> Map.put("latitude", latitude)
|
||||
end
|
||||
|
||||
%__MODULE__{
|
||||
draft
|
||||
| location: location,
|
||||
location_id: address.origin_id,
|
||||
location_provider: address.origin_provider
|
||||
}
|
||||
end
|
||||
|
||||
defp event_location(draft, _), do: draft
|
||||
|
||||
defp event_banner(draft) do
|
||||
with media_id when is_binary(media_id) <- draft.params[:banner_id],
|
||||
%Object{data: data} <- Repo.get(Object, media_id) do
|
||||
banner = Map.put(data, "name", "Banner")
|
||||
|
||||
%__MODULE__{draft | attachments: [banner]}
|
||||
else
|
||||
_ -> draft
|
||||
end
|
||||
end
|
||||
|
||||
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
|
||||
defp with_valid(draft, _func), do: draft
|
||||
|
||||
|
|
|
@ -437,6 +437,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
|
||||
def maybe_notify_followers(recipients, _), do: recipients
|
||||
|
||||
def maybe_notify_participants(
|
||||
recipients,
|
||||
%Activity{data: %{"type" => "Update"}} = activity
|
||||
) do
|
||||
with %Object{data: object} <- Object.normalize(activity, fetch: false) do
|
||||
participant_ids = Map.get(object, "participations", [])
|
||||
|
||||
recipients ++ participant_ids
|
||||
else
|
||||
_e -> recipients
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_notify_participants(recipients, _), do: recipients
|
||||
|
||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||
tag
|
||||
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
||||
|
|
|
@ -35,6 +35,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
|||
poll
|
||||
update
|
||||
status
|
||||
pleroma:participation_request
|
||||
pleroma:participation_accepted
|
||||
pleroma:event_reminder
|
||||
pleroma:event_update
|
||||
}
|
||||
|
||||
# GET /api/v1/notifications
|
||||
|
|
|
@ -23,7 +23,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
|
@ -104,6 +103,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
|
||||
plug(RateLimiter, [name: :statuses_actions] when action in @rate_limited_status_actions)
|
||||
|
||||
plug(Pleroma.Web.Plugs.SetApplicationPlug, [] when action in [:create, :update])
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.StatusOperation
|
||||
|
@ -279,6 +280,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
{_, true} <- {:is_create, activity.data["type"] == "Create"},
|
||||
actor <- Activity.user_actor(activity),
|
||||
{_, true} <- {:own_status, actor.id == user.id},
|
||||
{_, true} <- {:not_event, activity.object.data["type"] != "Event"},
|
||||
changes <- body_params |> put_application(conn),
|
||||
{_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(activity, user, changes)},
|
||||
{_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do
|
||||
|
@ -290,6 +292,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
)
|
||||
else
|
||||
{:own_status, _} -> {:error, :forbidden}
|
||||
{:not_event, _} -> {:error, :unprocessable_entity, "Use event update route"}
|
||||
{:pipeline, _} -> {:error, :internal_server_error}
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
|
@ -626,13 +629,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
)
|
||||
end
|
||||
|
||||
defp put_application(params, %{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do
|
||||
if user.disclose_client do
|
||||
%{client_name: client_name, website: website} = Repo.preload(token, :app).app
|
||||
Map.put(params, :generator, %{type: "Application", name: client_name, url: website})
|
||||
else
|
||||
Map.put(params, :generator, nil)
|
||||
end
|
||||
defp put_application(params, %{assigns: %{application: application}} = _conn) do
|
||||
Map.put(params, :generator, application)
|
||||
end
|
||||
|
||||
defp put_application(params, _), do: Map.put(params, :generator, nil)
|
||||
|
|
|
@ -157,7 +157,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
"pleroma:bookmark_folders",
|
||||
if Pleroma.Language.LanguageDetector.configured?() do
|
||||
"pleroma:language_detection"
|
||||
end
|
||||
end,
|
||||
"pleroma:events"
|
||||
]
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
}
|
||||
|
||||
case notification.type do
|
||||
"mention" ->
|
||||
type when type in ["mention", "poll", "pleroma:event_reminder"] ->
|
||||
put_status(response, activity, reading_user, status_render_opts)
|
||||
|
||||
"status" ->
|
||||
|
@ -118,15 +118,12 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
"reblog" ->
|
||||
put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
|
||||
|
||||
"update" ->
|
||||
type when type in ["update", "pleroma:event_update"] ->
|
||||
put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
|
||||
|
||||
"move" ->
|
||||
put_target(response, activity, reading_user, %{})
|
||||
|
||||
"poll" ->
|
||||
put_status(response, activity, reading_user, status_render_opts)
|
||||
|
||||
"pleroma:emoji_reaction" ->
|
||||
response
|
||||
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|
||||
|
@ -138,6 +135,21 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
"pleroma:report" ->
|
||||
put_report(response, activity)
|
||||
|
||||
"pleroma:participation_accepted" ->
|
||||
request_activity = Activity.get_by_ap_id(activity.data["object"])
|
||||
create_activity = Activity.get_create_by_object_ap_id(request_activity.data["object"])
|
||||
|
||||
response
|
||||
|> put_status(create_activity, reading_user, status_render_opts)
|
||||
|> put_participation_request(request_activity)
|
||||
|
||||
"pleroma:participation_request" ->
|
||||
create_activity = Activity.get_create_by_object_ap_id(activity.data["object"])
|
||||
|
||||
response
|
||||
|> put_status(create_activity, reading_user, status_render_opts)
|
||||
|> put_participation_request(activity)
|
||||
|
||||
type when type in ["follow", "follow_request"] ->
|
||||
response
|
||||
end
|
||||
|
@ -180,4 +192,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
|
||||
Map.put(response, :target, target_render)
|
||||
end
|
||||
|
||||
defp put_participation_request(response, activity) do
|
||||
Map.put(response, :participation_message, activity.data["participationMessage"])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -466,7 +466,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
pinned_at: pinned_at,
|
||||
quotes_count: object.data["quotesCount"] || 0,
|
||||
bookmark_folder: bookmark_folder,
|
||||
list_id: get_list_id(object, client_posted_this_activity)
|
||||
list_id: get_list_id(object, client_posted_this_activity),
|
||||
event: build_event(object.data, opts[:for])
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -565,7 +566,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
id: activity.id,
|
||||
text: get_source_text(Map.get(object.data, "source", "")),
|
||||
spoiler_text: Map.get(object.data, "summary", ""),
|
||||
content_type: get_source_content_type(object.data["source"])
|
||||
content_type: get_source_content_type(object.data["source"]),
|
||||
location: build_source_location(object.data)
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -602,7 +604,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
def render("attachment.json", %{attachment: attachment}) do
|
||||
[attachment_url | _] = attachment["url"]
|
||||
media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
|
||||
href = attachment_url["href"] |> MediaProxy.url()
|
||||
href_remote = attachment_url["href"]
|
||||
href = href_remote |> MediaProxy.url()
|
||||
href_preview = attachment_url["href"] |> MediaProxy.preview_url()
|
||||
meta = render("attachment_meta.json", %{attachment: attachment})
|
||||
|
||||
|
@ -641,7 +644,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
%{
|
||||
id: attachment_id,
|
||||
url: href,
|
||||
remote_url: href,
|
||||
remote_url: href_remote,
|
||||
preview_url: href_preview,
|
||||
text_url: href,
|
||||
type: type,
|
||||
|
@ -727,7 +730,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end
|
||||
end
|
||||
|
||||
def render_content(%{data: %{"name" => name}} = object) when not is_nil(name) and name != "" do
|
||||
def render_content(%{data: %{"name" => name, "type" => type}} = object)
|
||||
when not is_nil(name) and name != "" and type != "Event" do
|
||||
url = object.data["url"] || object.data["id"]
|
||||
|
||||
"<p><a href=\"#{url}\">#{name}</a></p>#{object.data["content"]}"
|
||||
|
@ -784,6 +788,61 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
end)
|
||||
end
|
||||
|
||||
defp build_event(%{"type" => "Event"} = data, for_user) do
|
||||
%{
|
||||
name: data["name"],
|
||||
start_time: data["startTime"],
|
||||
end_time: data["endTime"],
|
||||
join_mode: data["joinMode"],
|
||||
participants_count: data["participation_count"],
|
||||
location: build_event_location(data["location"]),
|
||||
join_state: build_event_join_state(for_user, data["id"]),
|
||||
participation_request_count: maybe_put_participation_request_count(data, for_user)
|
||||
}
|
||||
end
|
||||
|
||||
defp build_event(_, _), do: nil
|
||||
|
||||
defp build_event_location(%{"type" => "Place"} = location) do
|
||||
%{
|
||||
name: location["name"],
|
||||
url: location["url"],
|
||||
longitude: location["longitude"],
|
||||
latitude: location["latitude"]
|
||||
}
|
||||
|> maybe_put_address(location["address"])
|
||||
end
|
||||
|
||||
defp build_event_location(_), do: nil
|
||||
|
||||
defp maybe_put_address(location, %{"type" => "PostalAddress"} = address) do
|
||||
Map.merge(location, %{
|
||||
street: address["streetAddress"],
|
||||
postal_code: address["postalCode"],
|
||||
locality: address["addressLocality"],
|
||||
region: address["addressRegion"],
|
||||
country: address["addressCountry"]
|
||||
})
|
||||
end
|
||||
|
||||
defp maybe_put_address(location, _), do: location
|
||||
|
||||
defp build_event_join_state(%{ap_id: actor}, id) do
|
||||
latest_join = Pleroma.Web.ActivityPub.Utils.get_existing_join(actor, id)
|
||||
|
||||
if latest_join do
|
||||
latest_join.data["state"]
|
||||
end
|
||||
end
|
||||
|
||||
defp build_event_join_state(_, _), do: nil
|
||||
|
||||
defp maybe_put_participation_request_count(%{"actor" => actor} = data, %{ap_id: actor}) do
|
||||
data["participation_request_count"]
|
||||
end
|
||||
|
||||
defp maybe_put_participation_request_count(_, _), do: nil
|
||||
|
||||
defp present?(nil), do: false
|
||||
defp present?(false), do: false
|
||||
defp present?(_), do: true
|
||||
|
@ -841,6 +900,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
defp get_language(object), do: object.data["language"]
|
||||
|
||||
def build_source_location(%{"location_id" => location_id}) when is_binary(location_id) do
|
||||
location = Geospatial.Service.service().get_by_id(location_id) |> List.first()
|
||||
|
||||
if location do
|
||||
Pleroma.Web.PleromaAPI.SearchView.render("show_location.json", %{location: location})
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def build_source_location(_), do: nil
|
||||
|
||||
defp proxied_url(url, page_url_data) do
|
||||
if is_binary(url) do
|
||||
build_image_url(URI.parse(url), page_url_data) |> MediaProxy.url()
|
||||
|
|
310
lib/pleroma/web/pleroma_api/controllers/event_controller.ex
Normal file
310
lib/pleroma/web/pleroma_api/controllers/event_controller.ex
Normal file
|
@ -0,0 +1,310 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EventController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [add_link_headers: 2, json_response: 3, try_render: 3]
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.PleromaAPI.EventView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
:assign_participant
|
||||
when action in [:authorize_participation_request, :reject_participation_request]
|
||||
)
|
||||
|
||||
plug(
|
||||
:assign_event_activity
|
||||
when action in [
|
||||
:participations,
|
||||
:participation_requests,
|
||||
:authorize_participation_request,
|
||||
:reject_participation_request,
|
||||
:join,
|
||||
:leave,
|
||||
:export_ics
|
||||
]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
when action in [
|
||||
:create,
|
||||
:update,
|
||||
:authorize_participation_request,
|
||||
:reject_participation_request,
|
||||
:join,
|
||||
:leave
|
||||
]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"]}
|
||||
when action in [:participations, :participation_requests, :joined_events]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]}
|
||||
when action in [:export_ics]
|
||||
)
|
||||
|
||||
@rate_limited_event_actions ~w(create update join leave)a
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
[name: :status_id_action, bucket_name: "status_id_action:join_leave", params: [:id]]
|
||||
when action in ~w(join leave)a
|
||||
)
|
||||
|
||||
plug(RateLimiter, [name: :events_actions] when action in @rate_limited_event_actions)
|
||||
|
||||
plug(Pleroma.Web.Plugs.SetApplicationPlug, [] when action in [:create, :update])
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEventOperation
|
||||
|
||||
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||
params =
|
||||
params
|
||||
|> Map.put(:status, Map.get(params, :status, ""))
|
||||
|
||||
with location <- get_location(params),
|
||||
{:ok, activity} <- CommonAPI.event(user, params, location) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
as: :activity
|
||||
)
|
||||
else
|
||||
{:error, {:reject, message}} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "PUT /api/v1/pleroma/events/:id"
|
||||
def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do
|
||||
with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)},
|
||||
{_, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
{_, true} <- {:is_create, activity.data["type"] == "Create"},
|
||||
actor <- Activity.user_actor(activity),
|
||||
{_, true} <- {:own_status, actor.id == user.id},
|
||||
changes <- body_params |> Map.put(:generator, conn.assigns.application),
|
||||
location <- get_location(body_params),
|
||||
{_, {:ok, _update_activity}} <-
|
||||
{:pipeline, CommonAPI.update_event(user, activity, changes, location)},
|
||||
{_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
with_direct_conversation_id: true,
|
||||
with_muted: Map.get(params, :with_muted, false)
|
||||
)
|
||||
else
|
||||
{:own_status, _} -> {:error, :forbidden}
|
||||
{:pipeline, _} -> {:error, :internal_server_error}
|
||||
_ -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_location(%{location_id: location_id}) when is_binary(location_id) do
|
||||
result = Geospatial.Service.service().get_by_id(location_id)
|
||||
|
||||
result |> List.first()
|
||||
end
|
||||
|
||||
defp get_location(_), do: nil
|
||||
|
||||
def participations(%{assigns: %{user: user, event_activity: activity}} = conn, params) do
|
||||
with %Object{data: %{"participations" => participations}} <-
|
||||
Object.normalize(activity, fetch: false) do
|
||||
users =
|
||||
User
|
||||
|> Ecto.Query.where([u], u.ap_id in ^participations)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|> Enum.filter(&(not User.blocks?(user, &1)))
|
||||
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("index.json", for: user, users: users, as: :user)
|
||||
else
|
||||
{:visible, false} -> {:error, :not_found}
|
||||
_ -> json(conn, [])
|
||||
end
|
||||
end
|
||||
|
||||
def participation_requests(
|
||||
%{assigns: %{user: %{ap_id: user_ap_id} = for_user, event_activity: activity}} = conn,
|
||||
params
|
||||
) do
|
||||
case activity do
|
||||
%Activity{actor: ^user_ap_id, data: %{"object" => ap_id}} ->
|
||||
params =
|
||||
Map.merge(params, %{
|
||||
type: "Join",
|
||||
object: ap_id,
|
||||
state: "pending",
|
||||
skip_preload: true
|
||||
})
|
||||
|
||||
activities =
|
||||
[]
|
||||
|> ActivityPub.fetch_activities_query(params)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(EventView)
|
||||
|> render("participation_requests.json",
|
||||
activities: activities,
|
||||
for: for_user,
|
||||
as: :activity
|
||||
)
|
||||
|
||||
%Activity{} ->
|
||||
render_error(conn, :forbidden, "Can't get participation requests")
|
||||
|
||||
{:error, error} ->
|
||||
json_response(conn, :bad_request, %{error: error})
|
||||
end
|
||||
end
|
||||
|
||||
def join(%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn, _) do
|
||||
render_error(conn, :bad_request, "Can't join your own event")
|
||||
end
|
||||
|
||||
def join(
|
||||
%{assigns: %{user: user, event_activity: activity}, body_params: params} = conn,
|
||||
_
|
||||
) do
|
||||
with {:ok, _} <- CommonAPI.join(user, activity.id, params) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
||||
def leave(
|
||||
%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn,
|
||||
_
|
||||
) do
|
||||
render_error(conn, :bad_request, "Can't leave your own event")
|
||||
end
|
||||
|
||||
def leave(%{assigns: %{user: user, event_activity: activity}} = conn, _) do
|
||||
with {:ok, _} <- CommonAPI.leave(user, activity.id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json", activity: activity, for: user, as: :activity)
|
||||
else
|
||||
{:error, error} ->
|
||||
json_response(conn, :bad_request, %{error: error})
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_participation_request(
|
||||
%{
|
||||
assigns: %{
|
||||
user: for_user,
|
||||
participant: participant,
|
||||
event_activity: %Activity{data: %{"object" => ap_id}} = activity
|
||||
}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
with actor <- Activity.user_actor(activity),
|
||||
{_, true} <- {:own_event, actor.id == for_user.id},
|
||||
{:ok, _} <- CommonAPI.accept_join_request(for_user, participant, ap_id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
|
||||
else
|
||||
{:own_event, _} -> {:error, :forbidden}
|
||||
end
|
||||
end
|
||||
|
||||
def reject_participation_request(
|
||||
%{
|
||||
assigns: %{
|
||||
user: for_user,
|
||||
participant: participant,
|
||||
event_activity: %Activity{data: %{"object" => ap_id}} = activity
|
||||
}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
with actor <- Activity.user_actor(activity),
|
||||
{_, true} <- {:own_event, actor.id == for_user.id},
|
||||
{:ok, _} <- CommonAPI.reject_join_request(for_user, participant, ap_id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
|
||||
else
|
||||
{:own_event, _} -> {:error, :forbidden}
|
||||
end
|
||||
end
|
||||
|
||||
def export_ics(%{assigns: %{event_activity: activity}} = conn, _) do
|
||||
render(conn, "show.ics", activity: activity)
|
||||
end
|
||||
|
||||
defp assign_participant(%{params: %{participant_id: id}} = conn, _) do
|
||||
case User.get_cached_by_id(id) do
|
||||
%User{} = participant -> assign(conn, :participant, participant)
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_event_activity(%{assigns: %{user: user}, params: %{id: event_id}} = conn, _) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(event_id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)} do
|
||||
assign(conn, :event_activity, activity)
|
||||
else
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def joined_events(%{assigns: %{user: %User{} = user}} = conn, params) do
|
||||
activities = ActivityPub.fetch_joined_events(user, params)
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json",
|
||||
activities: activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
)
|
||||
end
|
||||
end
|
23
lib/pleroma/web/pleroma_api/controllers/search_controller.ex
Normal file
23
lib/pleroma/web/pleroma_api/controllers/search_controller.ex
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.SearchController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: [], op: :&} when action in [:location])
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaSearchOperation
|
||||
|
||||
def location(conn, %{q: query} = params) do
|
||||
result = Geospatial.Service.service().search(query, params |> Map.to_list())
|
||||
|
||||
render(conn, "index_locations.json", locations: result)
|
||||
end
|
||||
end
|
86
lib/pleroma/web/pleroma_api/views/event_view.ex
Normal file
86
lib/pleroma/web/pleroma_api/views/event_view.ex
Normal file
|
@ -0,0 +1,86 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EventView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
def render("participation_requests.json", %{activities: activities} = opts) do
|
||||
render_many(
|
||||
activities,
|
||||
__MODULE__,
|
||||
"participation_request.json",
|
||||
Map.delete(opts, :activities)
|
||||
)
|
||||
end
|
||||
|
||||
def render("participation_request.json", %{activity: activity} = opts) do
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
|
||||
%{
|
||||
account:
|
||||
AccountView.render("show.json", %{
|
||||
user: user,
|
||||
for: opts[:for]
|
||||
}),
|
||||
participation_message: activity.data["participationMessage"]
|
||||
}
|
||||
end
|
||||
|
||||
def render("show.ics", %{activity: %Activity{actor: actor_ap_id} = activity}) do
|
||||
with %Object{} = object <- Object.normalize(activity, fetch: false),
|
||||
%User{} = user <- User.get_cached_by_ap_id(actor_ap_id) do
|
||||
event = %ICalendar.Event{
|
||||
summary: object.data["name"],
|
||||
dtstart: object.data["startTime"] |> get_date,
|
||||
dtend: object.data["endTime"] |> get_date,
|
||||
description: Pleroma.HTML.strip_tags(object.data["content"]),
|
||||
uid: object.id,
|
||||
url: object.data["url"] || object.data["id"],
|
||||
geo: get_coords(object),
|
||||
location: get_location(object),
|
||||
organizer: Pleroma.HTML.strip_tags(user.name || user.nickname)
|
||||
}
|
||||
|
||||
%ICalendar{events: [event]}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_coords(%Object{
|
||||
data: %{"location" => %{"longitude" => longitude, "latitude" => latitude}}
|
||||
}) do
|
||||
{latitude, longitude}
|
||||
end
|
||||
|
||||
defp get_coords(_) do
|
||||
nil
|
||||
end
|
||||
|
||||
defp get_location(%Object{
|
||||
data: %{"location" => %{"name" => description, "address" => %{} = address}}
|
||||
}) do
|
||||
String.trim(
|
||||
"#{description} #{address["streetAddress"]} #{address["postalCode"]} #{address["addressLocality"]} #{address["addressRegion"]} #{address["addressCountry"]}"
|
||||
)
|
||||
end
|
||||
|
||||
defp get_location(_) do
|
||||
nil
|
||||
end
|
||||
|
||||
defp get_date(date) when is_binary(date) do
|
||||
{:ok, date, _} = DateTime.from_iso8601(date)
|
||||
|
||||
date
|
||||
end
|
||||
|
||||
defp get_date(_) do
|
||||
nil
|
||||
end
|
||||
end
|
36
lib/pleroma/web/pleroma_api/views/search_view.ex
Normal file
36
lib/pleroma/web/pleroma_api/views/search_view.ex
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.SearchView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
def render("index_locations.json", %{locations: locations}) do
|
||||
render_many(locations, __MODULE__, "show_location.json", as: :location)
|
||||
end
|
||||
|
||||
def render("show_location.json", %{location: location}) do
|
||||
%{
|
||||
url: location.url,
|
||||
description: location.description,
|
||||
geom: render("geom.json", %{geom: location.geom}),
|
||||
country: location.country,
|
||||
locality: location.locality,
|
||||
region: location.region,
|
||||
postal_code: location.postal_code,
|
||||
street: location.street,
|
||||
origin_id: "#{location.origin_id}",
|
||||
origin_provider: location.origin_provider,
|
||||
type: location.type,
|
||||
timezone: location.timezone
|
||||
}
|
||||
end
|
||||
|
||||
def render("geom.json", %{
|
||||
geom: %Geo.Point{coordinates: {longitude, latitude}, properties: _properties, srid: srid}
|
||||
}) do
|
||||
%{coordinates: [longitude, latitude], srid: srid}
|
||||
end
|
||||
|
||||
def render("geom.json", %{geom: _}), do: nil
|
||||
end
|
28
lib/pleroma/web/plugs/set_application_plug.ex
Normal file
28
lib/pleroma/web/plugs/set_application_plug.ex
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.SetApplicationPlug do
|
||||
import Plug.Conn, only: [assign: 3]
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
def init(_), do: nil
|
||||
|
||||
def call(conn, _) do
|
||||
assign(conn, :application, get_application(conn))
|
||||
end
|
||||
|
||||
defp get_application(%{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do
|
||||
if user.disclose_client do
|
||||
%{client_name: client_name, website: website} = Repo.preload(token, :app).app
|
||||
%{type: "Application", name: client_name, url: website}
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp get_application(_), do: nil
|
||||
end
|
|
@ -602,6 +602,30 @@ defmodule Pleroma.Web.Router do
|
|||
post("/bookmark_folders", BookmarkFolderController, :create)
|
||||
patch("/bookmark_folders/:id", BookmarkFolderController, :update)
|
||||
delete("/bookmark_folders/:id", BookmarkFolderController, :delete)
|
||||
|
||||
post("/events", EventController, :create)
|
||||
get("/events/joined_events", EventController, :joined_events)
|
||||
put("/events/:id", EventController, :update)
|
||||
get("/events/:id/participations", EventController, :participations)
|
||||
get("/events/:id/participation_requests", EventController, :participation_requests)
|
||||
|
||||
post(
|
||||
"/events/:id/participation_requests/:participant_id/authorize",
|
||||
EventController,
|
||||
:authorize_participation_request
|
||||
)
|
||||
|
||||
post(
|
||||
"/events/:id/participation_requests/:participant_id/reject",
|
||||
EventController,
|
||||
:reject_participation_request
|
||||
)
|
||||
|
||||
post("/events/:id/join", EventController, :join)
|
||||
post("/events/:id/leave", EventController, :leave)
|
||||
get("/events/:id/ics", EventController, :export_ics)
|
||||
|
||||
get("/search/location", SearchController, :location)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
74
lib/pleroma/workers/event_reminder_worker.ex
Normal file
74
lib/pleroma/workers/event_reminder_worker.ex
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.EventReminderWorker do
|
||||
@moduledoc """
|
||||
Generates notifications for upcoming events.
|
||||
"""
|
||||
use Oban.Worker, queue: :background
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Job{args: %{"op" => "event_reminder", "activity_id" => activity_id}}) do
|
||||
with %Activity{} = activity <- find_event_activity(activity_id) do
|
||||
Notification.create_event_notifications(activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp find_event_activity(activity_id) do
|
||||
with nil <- Activity.get_by_id(activity_id) do
|
||||
{:error, :event_activity_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_event_reminder(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
|
||||
with %Object{data: %{"type" => "Event", "startTime" => start_time}} <-
|
||||
Object.normalize(activity),
|
||||
{:ok, start_time, _} <- DateTime.from_iso8601(start_time),
|
||||
:gt <-
|
||||
DateTime.compare(
|
||||
start_time |> DateTime.add(60 * 60 * -2, :second),
|
||||
DateTime.utc_now()
|
||||
) do
|
||||
%{
|
||||
op: "event_reminder",
|
||||
activity_id: activity_id
|
||||
}
|
||||
|> new(scheduled_at: start_time |> DateTime.add(60 * 60 * -2, :second))
|
||||
|> Oban.insert()
|
||||
else
|
||||
_ -> {:error, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_event_reminder(
|
||||
%Activity{data: %{"type" => "Update", "object" => %{"id" => ap_id}}} = activity
|
||||
) do
|
||||
with %Activity{id: activity_id} = create_activity <-
|
||||
Activity.get_create_by_object_ap_id(ap_id),
|
||||
{:ok, _} <- remove_event_reminders(activity_id) do
|
||||
schedule_event_reminder(create_activity)
|
||||
else
|
||||
_ -> {:error, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_event_reminder(activity) do
|
||||
{:error, activity}
|
||||
end
|
||||
|
||||
defp remove_event_reminders(activity_id) do
|
||||
from(j in Oban.Job,
|
||||
where: j.state == "scheduled",
|
||||
where: j.queue == "event_reminders",
|
||||
where: fragment("?->>'activity_id' = ?", j.args, ^activity_id)
|
||||
)
|
||||
|> Oban.cancel_all_jobs()
|
||||
end
|
||||
end
|
2
mix.exs
2
mix.exs
|
@ -207,6 +207,8 @@ defmodule Pleroma.Mixfile do
|
|||
{:oban_live_dashboard, "~> 0.1.1"},
|
||||
{:multipart, "~> 0.4.0", optional: true},
|
||||
{:argon2_elixir, "~> 4.0"},
|
||||
{:icalendar, "~> 1.1"},
|
||||
{:geospatial, "~> 0.3.1"},
|
||||
|
||||
## dev & test
|
||||
{:phoenix_live_reload, "~> 1.3.3", only: :dev},
|
||||
|
|
8
mix.lock
8
mix.lock
|
@ -57,6 +57,8 @@
|
|||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
|
||||
"floki": {:hex, :floki, "0.35.2", "87f8c75ed8654b9635b311774308b2760b47e9a579dabf2e4d5f1e1d42c39e0b", [:mix], [], "hexpm", "6b05289a8e9eac475f644f09c2e4ba7e19201fd002b89c28c1293e7bd16773d9"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||
"geo": {:hex, :geo, "3.6.0", "00c9c6338579f67e91cd5950af4ae2eb25cdce0c3398718c232539f61625d0bd", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "1dbdebf617183b54bc3c8ad7a36531a9a76ada8ca93f75f573b0ae94006168da"},
|
||||
"geospatial": {:hex, :geospatial, "0.3.1", "0c8ca9746e44382a43eddd7b353af9aad7c0c5acd0edc78c765ec2566ab7b891", [:mix], [{:geo, "~> 3.6.0", [hex: :geo, repo: "hexpm", optional: false]}, {:hackney, "~> 1.20.1", [hex: :hackney, repo: "hexpm", optional: false]}, {:tesla, "~> 1.11.0", [hex: :tesla, repo: "hexpm", optional: false]}, {:tz_world, "~> 1.3.2", [hex: :tz_world, repo: "hexpm", optional: false]}], "hexpm", "ef5725a7f39551eb43986790077d10065720c020c32f16f60a86f8278c7697e3"},
|
||||
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
|
||||
"gun": {:hex, :gun, "2.0.1", "160a9a5394800fcba41bc7e6d421295cf9a7894c2252c0678244948e3336ad73", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "a10bc8d6096b9502205022334f719cc9a08d9adcfbfc0dbee9ef31b56274a20b"},
|
||||
"hackney": {:hex, :hackney, "1.18.2", "d7ff544ddae5e1cb49e9cf7fa4e356d7f41b283989a1c304bfc47a8cc1cf966f", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "af94d5c9f97857db257090a4a10e5426ecb6f4918aa5cc666798566ae14b65fd"},
|
||||
|
@ -64,6 +66,7 @@
|
|||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||
"http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"},
|
||||
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
|
||||
"icalendar": {:hex, :icalendar, "1.1.2", "5d0afff5d0143c5bd43f18ae32a777bf0fb9a724543ab05229a460d368f0a5e7", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2060f8e353fdf3047e95a3f012583dc3c0bbd7ca1010e32ed9e9fc5760ad4292"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"},
|
||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||
|
@ -81,7 +84,7 @@
|
|||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
||||
"mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"},
|
||||
"mint": {:hex, :mint, "1.6.1", "065e8a5bc9bbd46a41099dfea3e0656436c5cbcb6e741c80bd2bad5cd872446f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4fc518dcc191d02f433393a72a7ba3f6f94b101d094cb6bf532ea54c89423780"},
|
||||
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
|
||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
||||
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
|
||||
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
|
||||
|
@ -139,11 +142,12 @@
|
|||
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"},
|
||||
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.0", "b583c3f18508f5c5561b674d16cf5d9afd2ea3c04505b7d92baaeac93c1b8260", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "9cba950e1c4733468efbe3f821841f34ac05d28e7af7798622f88ecdbbe63ea3"},
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||
"tesla": {:hex, :tesla, "1.11.0", "81b2b10213dddb27105ec6102d9eb0cc93d7097a918a0b1594f2dfd1a4601190", [: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", "b83ab5d4c2d202e1ea2b7e17a49f788d49a699513d7c4f08f2aef2c281be69db"},
|
||||
"tesla": {:hex, :tesla, "1.11.2", "24707ac48b52f72f88fc05d242b1c59a85d1ee6f16f19c312d7d3419665c9cd5", [: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", "c549cd03aec6a7196a641689dd378b799e635eb393f689b4bd756f750c7a4014"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
|
||||
"timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},
|
||||
"toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
"tz_world": {:hex, :tz_world, "1.3.3", "6d847a8f24d84f091d3385769dad96a27170e8e9a03f5ded9fd86299a99c67b1", [: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", "dae9f255954c767fa4e36fa68b2927310a7192b525e10f860a6f4656aab23746"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.10.7", "5a31cbe11e7ce5c7484d745dc9e1f11948e89662f8510d03c616de03df581ebd", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "0bccf73e2ffd6337971340832947ba232877aa8122dba4c95be9f729c8987377"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
|
|
|
@ -36,8 +36,7 @@ defmodule Pleroma.Repo.Migrations.AddStatusToNotificationsEnum do
|
|||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report',
|
||||
'poll',
|
||||
'update'
|
||||
'poll'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
|
|
@ -37,7 +37,8 @@ defmodule Pleroma.Repo.Migrations.AddUpdateToNotificationsEnum do
|
|||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report',
|
||||
'poll'
|
||||
'poll',
|
||||
'status'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddPleromaParticipationAcceptedToNotificationsEnum do
|
||||
use Ecto.Migration
|
||||
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def up do
|
||||
"""
|
||||
alter type notification_type add value 'pleroma:participation_accepted'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter type notification_type add value 'pleroma:participation_request'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter type notification_type add value 'pleroma:event_reminder'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter type notification_type add value 'pleroma:event_update'
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
delete from notifications where type in ('pleroma:participation_accepted', 'pleroma:participation_request', 'pleroma:event_reminder', 'pleroma:event_update')
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
drop type if exists notification_type
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
create type notification_type as enum (
|
||||
'follow',
|
||||
'follow_request',
|
||||
'mention',
|
||||
'move',
|
||||
'pleroma:emoji_reaction',
|
||||
'pleroma:chat_mention',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report',
|
||||
'poll',
|
||||
'status',
|
||||
'update'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
|
@ -43,7 +43,29 @@
|
|||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"formerRepresentations": "litepub:formerRepresentations",
|
||||
"sm": "http://smithereen.software/ns#",
|
||||
"nonAnonymous": "sm:nonAnonymous"
|
||||
"nonAnonymous": "sm:nonAnonymous",
|
||||
"mz": "https://joinmobilizon.org/ns#",
|
||||
"joinMode": {
|
||||
"@id": "mz:joinMode",
|
||||
"@type": "mz:joinModeType"
|
||||
},
|
||||
"joinModeType": {
|
||||
"@id": "mz:joinModeType",
|
||||
"@type": "rdfs:Class"
|
||||
},
|
||||
"participationMessage": {
|
||||
"@id": "mz:participationMessage",
|
||||
"@type": "schema:Text"
|
||||
},
|
||||
"streetAddress": "schema:streetAddress",
|
||||
"postalCode": "schema:postalCode",
|
||||
"addressLocality": "schema:addressLocality",
|
||||
"addressRegion": "schema:addressRegion",
|
||||
"addressCountry": "schema:addressCountry",
|
||||
"location": {
|
||||
"@id": "schema:location",
|
||||
"@type": "schema:Place"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
1
test/fixtures/tesla_mock/gancio-event.json
vendored
Normal file
1
test/fixtures/tesla_mock/gancio-event.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"id":"https://demo.gancio.org/federation/m/1","name":"Demo event","url":"https://demo.gancio.org/event/demo-event","type":"Event","startTime":"2021-07-14T17:30:57.000+02:00","endTime":"2021-07-14T18:30:57.000+02:00","location":{"type":"Place","name":"Colosseo","address":"Piazza del Colosseo, Rome","latitude":null,"longitude":null},"attachment":[],"tag":[{"type":"Hashtag","name":"#test","href":"https://demo.gancio.org/tag/test"}],"published":"2021-07-01T22:33:36.543Z","attributedTo":"https://demo.gancio.org/federation/u/customized","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://demo.gancio.org/federation/u/customized/followers"],"content":"","summary":"Colosseo, Wednesday, 14 July (17:30)","@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"toot":"http://joinmastodon.org/ns#","schema":"http://schema.org#","ProperyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","Hashtag":"https://www.w3.org/ns/activitystreams#Hashtag","manuallyApprovesFollowers":"as:manuallyApprovesFollowers","focalPoint":{"@container":"@list","@id":"toot:focalPoint"}}]}
|
1
test/fixtures/tesla_mock/gancio-user.json
vendored
Normal file
1
test/fixtures/tesla_mock/gancio-user.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"toot":"http://joinmastodon.org/ns#","schema":"http://schema.org#","ProperyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","indexable":"toot:indexable"}],"id":"https://demo.gancio.org/federation/u/customized","type":"Application","summary":"Demo instance, you can login with admin@gancio.org / password","name":"customized","preferredUsername":"customized","inbox":"https://demo.gancio.org/federation/u/customized/inbox","outbox":"https://demo.gancio.org/federation/u/customized/outbox","manuallyApprovesFollowers":false,"endpoints":{"sharedInbox":"https://demo.gancio.org/federation/u/customized/inbox"},"discoverable":true,"indexable":true,"attachment":[{"type":"PropertyValue","name":"Website","value":"<a href='https://demo.gancio.org'>https://demo.gancio.org</a>"},{"type":"PropertyValue","name":"Place","value":"Demo"}],"icon":{"type":"Image","mediaType":"image/png","url":"https://demo.gancio.org/logo.png"},"publicKey":{"id":"https://demo.gancio.org/federation/u/customized#main-key","owner":"https://demo.gancio.org/federation/u/customized","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxp9BQ8TvVqu+0xXk7VuZ\nnuO42cHxVI+z/3TQ80AfX5aoUnK/uP7lIPy+NiIgRRu0L4hsjEs+HP6Ny9NAKFtC\nddS3pUrgIDz/AUyKeYRsCycw4XyeX7gaqIan4vCx+ANPDVTc3twDenynHhaXbPsP\nzGeKiAsGIFKRUxc5I5xnQBk6Fy6LZvGwfif07AcECER+nzffSOMPYFVbhlRuBwOg\n/tJcut77KOEpJIQSwqzT0FOw4oFtkvJt/nhpQMkXwOjEuiMOVpPoXUIpWjnbvNmy\nIPXdnKN4QqHi0fAE+FvKGbNmr18vqApT/D4Yen6W1ZWCRdUR1jjl8LNFBkPH/Tad\nkOj+UyRRJjRRqY5mXCI72Bmhwmi/YdS4gt9K73okOZ3atM+9Kfj3azZm8pP7fRkK\n/lwRP8RZFSSpz4w9JtzYmR7P8qTaxwMuq8VrxtFmf1IBChFpyNHUDtmC9MzLBRE7\n+fnpr1bARR3OwO83/xtT+vKNE+2SBvsf7zeFRXa+p5dGaih90rQOwL8EsUItiG61\nm4y9n3Q7BM7XwrZ7sGe3Hey5SWveOEgemfP4ANJBiMQpU69LKM9dGW1FcEX4FlwW\nZx/135nzMXE2cF+y+q/yY2FlacXPqJXMY32mIc+rHMzvFY/ZDzjRY/7Gg2ekjXuN\n1o7Ag7a+5k+r+XkWBNKIHp8CAwEAAQ==\n-----END PUBLIC KEY-----\n"},"url":"https://demo.gancio.org"}
|
80
test/fixtures/tesla_mock/mobilizon-event-join-accept.json
vendored
Normal file
80
test/fixtures/tesla_mock/mobilizon-event-join-accept.json
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"addressRegion": "sc:addressRegion",
|
||||
"timezone": {"@id": "mz:timezone", "@type": "sc:Text"},
|
||||
"isOnline": {"@id": "mz:isOnline", "@type": "sc:Boolean"},
|
||||
"pt": "https://joinpeertube.org/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"inLanguage": "sc:inLanguage",
|
||||
"address": {"@id": "sc:address", "@type": "sc:PostalAddress"},
|
||||
"discoverable": "toot:discoverable",
|
||||
"repliesModerationOption": {
|
||||
"@id": "mz:repliesModerationOption",
|
||||
"@type": "mz:repliesModerationOptionType"
|
||||
},
|
||||
"sc": "http://schema.org#",
|
||||
"mz": "https://joinmobilizon.org/ns#",
|
||||
"category": "sc:category",
|
||||
"joinModeType": {"@id": "mz:joinModeType", "@type": "rdfs:Class"},
|
||||
"Hashtag": "as:Hashtag",
|
||||
"propertyID": "sc:propertyID",
|
||||
"PostalAddress": "sc:PostalAddress",
|
||||
"discussions": {"@id": "mz:discussions", "@type": "@id"},
|
||||
"remainingAttendeeCapacity": "sc:remainingAttendeeCapacity",
|
||||
"streetAddress": "sc:streetAddress",
|
||||
"anonymousParticipationEnabled": {
|
||||
"@id": "mz:anonymousParticipationEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"addressLocality": "sc:addressLocality",
|
||||
"joinMode": {"@id": "mz:joinMode", "@type": "mz:joinModeType"},
|
||||
"location": {"@id": "sc:location", "@type": "sc:Place"},
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"participantCount": {
|
||||
"@id": "mz:participantCount",
|
||||
"@type": "sc:Integer"
|
||||
},
|
||||
"uuid": "sc:identifier",
|
||||
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
|
||||
"participationMessage": {
|
||||
"@id": "mz:participationMessage",
|
||||
"@type": "sc:Text"
|
||||
},
|
||||
"openness": {"@id": "mz:openness", "@type": "@id"},
|
||||
"members": {"@id": "mz:members", "@type": "@id"},
|
||||
"events": {"@id": "mz:events", "@type": "@id"},
|
||||
"resources": {"@id": "mz:resources", "@type": "@id"},
|
||||
"addressCountry": "sc:addressCountry",
|
||||
"posts": {"@id": "mz:posts", "@type": "@id"},
|
||||
"commentsEnabled": {
|
||||
"@id": "pt:commentsEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"value": "sc:value",
|
||||
"PropertyValue": "sc:PropertyValue",
|
||||
"repliesModerationOptionType": {
|
||||
"@id": "mz:repliesModerationOptionType",
|
||||
"@type": "rdfs:Class"
|
||||
},
|
||||
"todos": {"@id": "mz:todos", "@type": "@id"},
|
||||
"ical": "http://www.w3.org/2002/12/cal/ical#",
|
||||
"postalCode": "sc:postalCode",
|
||||
"memberCount": {"@id": "mz:memberCount", "@type": "sc:Integer"},
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"actor": "https://mobilizon.org/@tcit",
|
||||
"id": "https://mobilizon.mkljczk.pl/accept/join/fef2a925-cce5-4b8e-b12f-20afe01e5a0f",
|
||||
"object": {
|
||||
"actor": "https://pleroma.mkljczk.pl/users/mkljczk",
|
||||
"id": "https://pleroma.mkljczk.pl/activities/7d1f3986-8b2c-48c2-b89e-d27ba8459777",
|
||||
"object": "https://mobilizon.mkljczk.pl/events/d9d08e46-81af-4ee9-91e5-1298f49beea9",
|
||||
"participationMessage": null,
|
||||
"published": "2022-10-07T18:53:53Z",
|
||||
"type": "Join"
|
||||
},
|
||||
"type": "Accept"
|
||||
}
|
74
test/fixtures/tesla_mock/mobilizon-event-join.json
vendored
Normal file
74
test/fixtures/tesla_mock/mobilizon-event-join.json
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"addressRegion": "sc:addressRegion",
|
||||
"timezone": { "@id": "mz:timezone", "@type": "sc:Text" },
|
||||
"isOnline": { "@id": "mz:isOnline", "@type": "sc:Boolean" },
|
||||
"pt": "https://joinpeertube.org/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"inLanguage": "sc:inLanguage",
|
||||
"address": { "@id": "sc:address", "@type": "sc:PostalAddress" },
|
||||
"discoverable": "toot:discoverable",
|
||||
"repliesModerationOption": {
|
||||
"@id": "mz:repliesModerationOption",
|
||||
"@type": "mz:repliesModerationOptionType"
|
||||
},
|
||||
"sc": "http://schema.org#",
|
||||
"mz": "https://joinmobilizon.org/ns#",
|
||||
"category": "sc:category",
|
||||
"joinModeType": { "@id": "mz:joinModeType", "@type": "rdfs:Class" },
|
||||
"Hashtag": "as:Hashtag",
|
||||
"propertyID": "sc:propertyID",
|
||||
"PostalAddress": "sc:PostalAddress",
|
||||
"discussions": { "@id": "mz:discussions", "@type": "@id" },
|
||||
"remainingAttendeeCapacity": "sc:remainingAttendeeCapacity",
|
||||
"streetAddress": "sc:streetAddress",
|
||||
"anonymousParticipationEnabled": {
|
||||
"@id": "mz:anonymousParticipationEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"addressLocality": "sc:addressLocality",
|
||||
"joinMode": { "@id": "mz:joinMode", "@type": "mz:joinModeType" },
|
||||
"location": { "@id": "sc:location", "@type": "sc:Place" },
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"participantCount": {
|
||||
"@id": "mz:participantCount",
|
||||
"@type": "sc:Integer"
|
||||
},
|
||||
"uuid": "sc:identifier",
|
||||
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
|
||||
"participationMessage": {
|
||||
"@id": "mz:participationMessage",
|
||||
"@type": "sc:Text"
|
||||
},
|
||||
"openness": { "@id": "mz:openness", "@type": "@id" },
|
||||
"members": { "@id": "mz:members", "@type": "@id" },
|
||||
"events": { "@id": "mz:events", "@type": "@id" },
|
||||
"resources": { "@id": "mz:resources", "@type": "@id" },
|
||||
"addressCountry": "sc:addressCountry",
|
||||
"posts": { "@id": "mz:posts", "@type": "@id" },
|
||||
"commentsEnabled": {
|
||||
"@id": "pt:commentsEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"value": "sc:value",
|
||||
"PropertyValue": "sc:PropertyValue",
|
||||
"repliesModerationOptionType": {
|
||||
"@id": "mz:repliesModerationOptionType",
|
||||
"@type": "rdfs:Class"
|
||||
},
|
||||
"todos": { "@id": "mz:todos", "@type": "@id" },
|
||||
"ical": "http://www.w3.org/2002/12/cal/ical#",
|
||||
"postalCode": "sc:postalCode",
|
||||
"memberCount": { "@id": "mz:memberCount", "@type": "sc:Integer" },
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"actor": "https://mobilizon.org/@tcit",
|
||||
"id": "https://mobilizon.mkljczk.pl/join/event/d1828aac-b57f-43c9-ac87-17388fab2bc8",
|
||||
"object": "https://pleroma.mkljczk.pl/objects/b800bdb9-2cbb-40b6-9b29-196cfbd42398",
|
||||
"participationMessage": null,
|
||||
"type": "Join"
|
||||
}
|
78
test/fixtures/tesla_mock/mobilizon-event-leave.json
vendored
Normal file
78
test/fixtures/tesla_mock/mobilizon-event-leave.json
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"addressRegion": "sc:addressRegion",
|
||||
"timezone": { "@id": "mz:timezone", "@type": "sc:Text" },
|
||||
"isOnline": { "@id": "mz:isOnline", "@type": "sc:Boolean" },
|
||||
"pt": "https://joinpeertube.org/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"inLanguage": "sc:inLanguage",
|
||||
"address": { "@id": "sc:address", "@type": "sc:PostalAddress" },
|
||||
"status": { "@id": "ical:status", "@type": "ical:status" },
|
||||
"discoverable": "toot:discoverable",
|
||||
"repliesModerationOption": {
|
||||
"@id": "mz:repliesModerationOption",
|
||||
"@type": "mz:repliesModerationOptionType"
|
||||
},
|
||||
"sc": "http://schema.org#",
|
||||
"mz": "https://joinmobilizon.org/ns#",
|
||||
"category": "sc:category",
|
||||
"joinModeType": { "@id": "mz:joinModeType", "@type": "rdfs:Class" },
|
||||
"Hashtag": "as:Hashtag",
|
||||
"propertyID": "sc:propertyID",
|
||||
"PostalAddress": "sc:PostalAddress",
|
||||
"discussions": { "@id": "mz:discussions", "@type": "@id" },
|
||||
"remainingAttendeeCapacity": "sc:remainingAttendeeCapacity",
|
||||
"streetAddress": "sc:streetAddress",
|
||||
"anonymousParticipationEnabled": {
|
||||
"@id": "mz:anonymousParticipationEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"externalParticipationUrl": {
|
||||
"@id": "mz:externalParticipationUrl",
|
||||
"@type": "sc:URL"
|
||||
},
|
||||
"addressLocality": "sc:addressLocality",
|
||||
"joinMode": { "@id": "mz:joinMode", "@type": "mz:joinModeType" },
|
||||
"location": { "@id": "sc:location", "@type": "sc:Place" },
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"participantCount": {
|
||||
"@id": "mz:participantCount",
|
||||
"@type": "sc:Integer"
|
||||
},
|
||||
"uuid": "sc:identifier",
|
||||
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
|
||||
"participationMessage": {
|
||||
"@id": "mz:participationMessage",
|
||||
"@type": "sc:Text"
|
||||
},
|
||||
"openness": { "@id": "mz:openness", "@type": "@id" },
|
||||
"members": { "@id": "mz:members", "@type": "@id" },
|
||||
"events": { "@id": "mz:events", "@type": "@id" },
|
||||
"resources": { "@id": "mz:resources", "@type": "@id" },
|
||||
"addressCountry": "sc:addressCountry",
|
||||
"posts": { "@id": "mz:posts", "@type": "@id" },
|
||||
"commentsEnabled": {
|
||||
"@id": "pt:commentsEnabled",
|
||||
"@type": "sc:Boolean"
|
||||
},
|
||||
"value": "sc:value",
|
||||
"PropertyValue": "sc:PropertyValue",
|
||||
"repliesModerationOptionType": {
|
||||
"@id": "mz:repliesModerationOptionType",
|
||||
"@type": "rdfs:Class"
|
||||
},
|
||||
"todos": { "@id": "mz:todos", "@type": "@id" },
|
||||
"ical": "http://www.w3.org/2002/12/cal/ical#",
|
||||
"postalCode": "sc:postalCode",
|
||||
"memberCount": { "@id": "mz:memberCount", "@type": "sc:Integer" },
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"actor": "https://mobilizon.org/@tcit",
|
||||
"id": "https://mobilizon.mkljczk.pl/leave/event/d1828aac-b57f-43c9-ac87-17388fab2bc8",
|
||||
"object": "https://pleroma.mkljczk.pl/objects/b800bdb9-2cbb-40b6-9b29-196cfbd42398",
|
||||
"type": "Leave"
|
||||
}
|
1
test/fixtures/tesla_mock/nominatim_search_results.json
vendored
Normal file
1
test/fixtures/tesla_mock/nominatim_search_results.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"type":"FeatureCollection","geocoding":{"version":"0.1.0","attribution":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","licence":"ODbL","query":"Benis"},"features":[{"type":"Feature","properties":{"geocoding":{"place_id":45392146,"osm_type":"node","osm_id":3726208425,"osm_key":"place","osm_value":"village","type":"city","label":"Benis, بخش مرکزی, Shabestar County, East Azerbaijan Province, Iran","name":"Benis","country":"Iran","state":"East Azerbaijan Province","county":"Shabestar County","city":"بخش مرکزی","admin":{"level4":"East Azerbaijan Province","level5":"Shabestar County","level6":"بخش مرکزی"}}},"geometry":{"type":"Point","coordinates":[45.7285348,38.212263]}},{"type":"Feature","properties":{"geocoding":{"place_id":298521494,"osm_type":"relation","osm_id":8841590,"osm_key":"boundary","osm_value":"administrative","type":"district","label":"Bénis, Castelsarrasin, Tarn-et-Garonne, Occitania, Metropolitan France, 82100, France","name":"Bénis","country":"France","postcode":"82100","state":"Occitania","county":"Tarn-et-Garonne","city":"Castelsarrasin","admin":{"level3":"Metropolitan France","level4":"Occitania","level6":"Tarn-et-Garonne","level7":"Castelsarrasin","level8":"Castelsarrasin","level10":"Bénis"}}},"geometry":{"type":"Point","coordinates":[1.1304739,44.0099561]}},{"type":"Feature","properties":{"geocoding":{"place_id":46542003,"osm_type":"node","osm_id":3839840986,"osm_key":"place","osm_value":"locality","type":"locality","label":"Beniš, Jablonov nad Turňou, District of Rožňava, Region of Košice, Eastern Slovakia, 049 43, Slovakia","name":"Beniš","country":"Slovakia","postcode":"049 43","state":"Region of Košice","county":"District of Rožňava","city":"Jablonov nad Turňou","district":"Jablonov nad Turňou","admin":{"level3":"Eastern Slovakia","level4":"Region of Košice","level8":"District of Rožňava","level9":"Jablonov nad Turňou","level10":"Jablonov nad Turňou"}}},"geometry":{"type":"Point","coordinates":[20.6856788,48.5855164]}},{"type":"Feature","properties":{"geocoding":{"place_id":49659169,"osm_type":"node","osm_id":4261667102,"osm_key":"shop","osm_value":"supermarket","type":"house","label":"Benis, Carrera 18, Centro, Comuna El Cafetero, Perímetro Urbano Armenia, Armenia, Capital, Quindío, 630004, Colombia","name":"Benis","country":"Colombia","postcode":"630004","state":"Quindío","county":"Capital","city":"Armenia","district":"Comuna El Cafetero","locality":"Centro","street":"Carrera 18","admin":{"level4":"Quindío","level5":"Capital","level6":"Armenia","level7":"Perímetro Urbano Armenia","level8":"Comuna El Cafetero"}}},"geometry":{"type":"Point","coordinates":[-75.6734958,4.5362242]}},{"type":"Feature","properties":{"geocoding":{"place_id":65944064,"osm_type":"node","osm_id":6087846386,"osm_key":"amenity","osm_value":"cafe","type":"house","label":"Bénis, شارع الحرية, الحدائق, معتمدية باب بحر, Tunis, 1017, Tunisia","name":"Bénis","country":"Tunisia","postcode":"1017","state":"Tunis","city":"Tunis","district":"الحدائق","street":"شارع الحرية","admin":{"level4":"Tunis","level5":"معتمدية باب بحر","level6":"الحدائق"}}},"geometry":{"type":"Point","coordinates":[10.1796633,36.8103671]}},{"type":"Feature","properties":{"geocoding":{"place_id":19666304,"osm_type":"node","osm_id":2250812267,"osm_key":"shop","osm_value":"bakery","type":"house","label":"Bénis, Rue du Fqih al Abbas, batiment hay dakhla, Houbous District, Mechouar, Pachalik de Méchouar de Casablanca, Prefecture of Casablanca, Casablanca-Settat, 20504, Morocco","name":"Bénis","country":"Morocco","postcode":"20504","state":"Casablanca-Settat","county":"Pachalik de Méchouar de Casablanca","city":"Mechouar","district":"Houbous District","locality":"batiment hay dakhla","street":"Rue du Fqih al Abbas","admin":{"level4":"Casablanca-Settat","level5":"Prefecture of Casablanca","level6":"Pachalik de Méchouar de Casablanca","level8":"Mechouar"}}},"geometry":{"type":"Point","coordinates":[-7.6054449,33.5779601]}}]}
|
1
test/fixtures/tesla_mock/nominatim_single_result.json
vendored
Normal file
1
test/fixtures/tesla_mock/nominatim_single_result.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"type":"FeatureCollection","geocoding":{"version":"0.1.0","attribution":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","licence":"ODbL","query":"N3726208425,R3726208425,W3726208425"},"features":[{"type":"Feature","properties":{"geocoding":{"place_id":45392146,"osm_type":"node","osm_id":3726208425,"osm_key":"place","osm_value":"village","type":"city","label":"Benis, بخش مرکزی, Shabestar County, East Azerbaijan Province, Iran","name":"Benis","country":"Iran","state":"East Azerbaijan Province","county":"Shabestar County","city":"بخش مرکزی","admin":{"level4":"East Azerbaijan Province","level5":"Shabestar County","level6":"بخش مرکزی"}}},"geometry":{"type":"Point","coordinates":[45.7285348,38.212263]}}]}
|
1
test/fixtures/tesla_mock/wordpress-event.json
vendored
Normal file
1
test/fixtures/tesla_mock/wordpress-event.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"@context":["https:\/\/schema.org\/","https:\/\/www.w3.org\/ns\/activitystreams",{"pt":"https:\/\/joinpeertube.org\/ns#","mz":"https:\/\/joinmobilizon.org\/ns#","status":"http:\/\/www.w3.org\/2002\/12\/cal\/ical#status","commentsEnabled":"pt:commentsEnabled","isOnline":"mz:isOnline","timezone":"mz:timezone","participantCount":"mz:participantCount","anonymousParticipationEnabled":"mz:anonymousParticipationEnabled","joinMode":{"@id":"mz:joinMode","@type":"mz:joinModeType"},"externalParticipationUrl":{"@id":"mz:externalParticipationUrl","@type":"schema:URL"},"repliesModerationOption":{"@id":"mz:repliesModerationOption","@type":"@vocab"},"contacts":{"@id":"mz:contacts","@type":"@id"}}],"id":"https:\/\/wp-test.event-federation.eu\/?p=227","type":"Event","attachment":[{"type":"Image","url":"https:\/\/wp-test.event-federation.eu\/wp-content\/uploads\/2024\/10\/christmas-trees-seamless-pattern-1698986532LKN-1024x1024.jpg","mediaType":"image\/jpeg","name":"Alt!"}],"attributedTo":"https:\/\/wp-test.event-federation.eu\/@test","content":"\u003Cp\u003EFantastic description!\u003C\/p\u003E","contentMap":{"en":"\u003Cp\u003EFantastic description!\u003C\/p\u003E"},"name":"Test Event","nameMap":{"en":"Test Event"},"endTime":"2025-02-27T17:00:00+01:00","icon":{"type":"Image","url":"https:\/\/wp-test.event-federation.eu\/wp-content\/uploads\/2024\/10\/christmas-trees-seamless-pattern-1698986532LKN-150x150.jpg","mediaType":"image\/jpeg","name":"Alt!"},"image":{"type":"Image","url":"https:\/\/wp-test.event-federation.eu\/wp-content\/uploads\/2024\/10\/christmas-trees-seamless-pattern-1698986532LKN-1024x1024.jpg","mediaType":"image\/jpeg","name":"Alt!"},"location":{"id":"https:\/\/wp-test.event-federation.eu\/venue\/morelli\/","type":"Place","attributedTo":"https:\/\/wp-test.event-federation.eu\/@test","name":"Morelli","nameMap":{"en":"Morelli"},"published":"2024-09-29T07:46:54Z","updated":"2024-10-22T15:01:56Z","url":"https:\/\/wp-test.event-federation.eu\/venue\/morelli\/","likes":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/30\/likes","type":"Collection","totalItems":0},"shares":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/30\/shares","type":"Collection","totalItems":0},"sensitive":false,"address":{"type":"PostalAddress","addressCountry":"Austria","addressLocality":"Graz","postalCode":"8010","streetAddress":"Morelli"}},"published":"2025-02-05T17:39:51Z","startTime":"2025-02-27T08:00:00+01:00","summary":"\u003Cp\u003E\ud83d\uddd3\ufe0f Start: February 27, 2025 8:00 am\u003Cbr \/\u003E\u23f3 End: February 27, 2025 5:00 pm\u003Cbr \/\u003E\ud83d\udccd Location: Morelli, Morelli, 8010, Graz, Austria\u003C\/p\u003E\u003Cp\u003EFantastic description!\u003C\/p\u003E","summaryMap":{"en":"\u003Cp\u003E\ud83d\uddd3\ufe0f Start: February 27, 2025 8:00 am\u003Cbr \/\u003E\u23f3 End: February 27, 2025 5:00 pm\u003Cbr \/\u003E\ud83d\udccd Location: Morelli, Morelli, 8010, Graz, Austria\u003C\/p\u003E\u003Cp\u003EFantastic description!\u003C\/p\u003E"},"tag":[{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/musik\/","name":"#Musik"}],"updated":"2025-02-05T17:40:20Z","url":"https:\/\/wp-test.event-federation.eu\/event\/test-event\/","to":["https:\/\/www.w3.org\/ns\/activitystreams#Public","https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/followers"],"mediaType":"text\/html","replies":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/227\/replies","type":"Collection","first":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/227\/replies?page=1","type":"CollectionPage","partOf":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/227\/replies","items":[]}},"likes":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/227\/likes","type":"Collection","totalItems":0},"shares":{"id":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/posts\/227\/shares","type":"Collection","totalItems":0},"sensitive":false,"commentsEnabled":true,"timezone":"Europe\/Vienna","repliesModerationOption":"allow_all","category":"LGBTQ","isOnline":false,"status":"CONFIRMED","externalParticipationUrl":"https:\/\/wp-test.event-federation.eu\/event\/test-event\/","joinMode":"external"}
|
1
test/fixtures/tesla_mock/wordpress-user.json
vendored
Normal file
1
test/fixtures/tesla_mock/wordpress-user.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"@context":["https:\/\/www.w3.org\/ns\/activitystreams","https:\/\/w3id.org\/security\/v1","https:\/\/purl.archive.org\/socialweb\/webfinger",{"schema":"http:\/\/schema.org#","toot":"http:\/\/joinmastodon.org\/ns#","lemmy":"https:\/\/join-lemmy.org\/ns#","manuallyApprovesFollowers":"as:manuallyApprovesFollowers","PropertyValue":"schema:PropertyValue","value":"schema:value","Hashtag":"as:Hashtag","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"moderators":{"@id":"lemmy:moderators","@type":"@id"},"attributionDomains":{"@id":"toot:attributionDomains","@type":"@id"},"postingRestrictedToMods":"lemmy:postingRestrictedToMods","discoverable":"toot:discoverable","indexable":"toot:indexable"}],"id":"https:\/\/wp-test.event-federation.eu\/@test","type":"Person","attachment":[{"type":"PropertyValue","name":"Blog","value":"<p><a href=\"https:\/\/wp-test.event-federation.eu\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"><span class=\"invisible\">https:\/\/<\/span><span class=\"\">wp-test.event-federation.eu\/<\/span><span class=\"invisible\"><\/span><\/a><\/p>"},{"type":"Link","name":"Blog","href":"https:\/\/wp-test.event-federation.eu\/","rel":["nofollow","noopener","noreferrer","me"]}],"name":"WP Test","icon":{"type":"Image","url":"https:\/\/wp-test.event-federation.eu\/wp-content\/plugins\/activitypub\/assets\/img\/wp-logo.png"},"image":{"type":"Image","url":"https:\/\/wp-test.event-federation.eu\/wp-content\/uploads\/2025\/02\/cropped-1a14b2a8eebccd6995543a3925034f4b.jpg"},"published":"2024-04-15T10:25:30Z","summary":"","tag":[{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/musik\/","name":"#Musik"},{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/gathering\/","name":"#Gathering"},{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/forest\/","name":"#forest"},{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/party\/","name":"#party"},{"type":"Hashtag","href":"https:\/\/wp-test.event-federation.eu\/tag\/testing\/","name":"#testing"}],"url":"https:\/\/wp-test.event-federation.eu\/@test","inbox":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/inbox","outbox":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/outbox","following":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/following","followers":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/followers","streams":[],"preferredUsername":"test","publicKey":{"id":"https:\/\/wp-test.event-federation.eu\/@test#main-key","owner":"https:\/\/wp-test.event-federation.eu\/@test","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyClGfGBcTPXYn2J6lASE\nYVGR0JWS7gefeZ47QmuMzdrdgvfbackWGv5jnAdD\/AcXejYM\/NBioM9aoiRsGkSk\nvpan\/yrFeHdfGY\/L8NAMHwepjnQlFcfEPg0izYduJYhthDyVJPAorLXO16BTf2t7\nN64qOnNQKuMiIgBkk+R+3dhgrYK01V9F1r7IqLpu9gVmY3YuRDNHUH6SWfeE798f\niZ3BhqOjeegslhrMr7vR+ptbvbwFeIbmrNOKt4dkLmYcMuV7CaeBndJfrRpPpABu\nJ4Nzl7sJ6PvqUal0FlkpjTQBwg1Y4ogVx6G1UqXD5bNgGS4\/mndWnfiY3bHgKAOU\nFQIDAQAB\n-----END PUBLIC KEY-----\n"},"manuallyApprovesFollowers":false,"attributionDomains":["wp-test.event-federation.eu"],"featured":"https:\/\/wp-test.event-federation.eu\/wp-json\/activitypub\/1.0\/actors\/0\/collections\/featured","indexable":true,"webfinger":"test@wp-test.event-federation.eu","discoverable":true}
|
|
@ -173,6 +173,128 @@ defmodule Pleroma.NotificationTest do
|
|||
assert [%{type: "update"}] = Notification.for_user(repeated_user)
|
||||
assert [%{type: "mention"}] = Notification.for_user(other_user)
|
||||
end
|
||||
|
||||
test "creates notification for event join request" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
[notification] = user_notifications = Notification.for_user(user)
|
||||
assert length(user_notifications) == 1
|
||||
|
||||
assert notification.type == "pleroma:participation_request"
|
||||
end
|
||||
|
||||
test "creates notification for event join approval" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
CommonAPI.accept_join_request(user, other_user, activity.data["object"])
|
||||
|
||||
[notification] = user_notifications = Notification.for_user(other_user)
|
||||
assert length(user_notifications) == 1
|
||||
|
||||
assert notification.type == "pleroma:participation_accepted"
|
||||
end
|
||||
|
||||
test "doesn't create notification for events without participation approval" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
user_notifications = Notification.for_user(user)
|
||||
assert length(user_notifications) == 0
|
||||
|
||||
user_notifications = Notification.for_user(other_user)
|
||||
assert length(user_notifications) == 0
|
||||
end
|
||||
end
|
||||
|
||||
test "creates notifications for edited events for participants" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "test evnet",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
{:ok, _edit_activity} =
|
||||
CommonAPI.update_event(user, activity, %{
|
||||
name: "test event",
|
||||
status: "test event",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
Pleroma.Tests.ObanHelpers.perform_all()
|
||||
|
||||
[notification] = user_notifications = Notification.for_user(other_user)
|
||||
assert length(user_notifications) == 1
|
||||
|
||||
assert notification.type == "pleroma:event_update"
|
||||
end
|
||||
|
||||
test "doesn't create multiple edit notifications for events" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "test evnet",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
{:ok, _edit_activity} =
|
||||
CommonAPI.update_event(user, activity, %{
|
||||
name: "test event",
|
||||
status: "test event",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
Pleroma.Tests.ObanHelpers.perform_all()
|
||||
|
||||
user_notifications = Notification.for_user(other_user)
|
||||
assert length(user_notifications) == 1
|
||||
end
|
||||
|
||||
test "create_poll_notifications/1" do
|
||||
|
|
|
@ -491,6 +491,33 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "Event objects" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
event = build(:event, user: user)
|
||||
event_activity = build(:event_activity, event: event)
|
||||
activity_data = Map.put(event_activity.data, "object", event.data["id"])
|
||||
meta = [object_data: event.data, local: false]
|
||||
|
||||
{:ok, activity, meta} = ActivityPub.persist(activity_data, meta)
|
||||
|
||||
%{activity: activity, meta: meta}
|
||||
end
|
||||
|
||||
test "enqueues event reminder notification worker", %{activity: activity, meta: meta} do
|
||||
{:ok, activity, meta} = SideEffects.handle(activity, meta)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Pleroma.Workers.EventReminderWorker,
|
||||
args: %{op: "event_reminder", activity_id: activity.id},
|
||||
scheduled_at:
|
||||
DateTime.from_iso8601(meta[:object_data]["startTime"])
|
||||
|> elem(1)
|
||||
|> DateTime.add(60 * 60 * -2, :second)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete users with confirmation pending" do
|
||||
setup do
|
||||
user = insert(:user, is_confirmed: false)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
@ -88,4 +90,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do
|
|||
|
||||
refute User.following?(follower, followed) == true
|
||||
end
|
||||
|
||||
test "it works for incoming Mobilizon join accepts" do
|
||||
event_author = insert(:user)
|
||||
participant = insert(:user)
|
||||
|
||||
event = insert(:event, %{user: event_author, data: %{"joinMode" => "restricted"}})
|
||||
event_activity = insert(:event_activity, event: event)
|
||||
|
||||
{:ok, join_activity} = CommonAPI.join(participant, event_activity.id)
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/tesla_mock/mobilizon-event-join-accept.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", event_author.ap_id)
|
||||
|> Map.put("object", join_activity.data["id"])
|
||||
|
||||
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(accept_data)
|
||||
|
||||
event = Object.get_by_id(event.id)
|
||||
|
||||
assert event.data["participations"] == [participant.ap_id]
|
||||
|
||||
join_activity = Repo.get(Activity, join_activity.id)
|
||||
assert join_activity.data["state"] == "accept"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,5 +38,57 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do
|
|||
|
||||
assert object.data["published"] == "2019-12-17T11:33:56Z"
|
||||
assert object.data["name"] == "Mobilizon Launching Party"
|
||||
assert object.data["startTime"] == "2019-12-18T13:00:00Z"
|
||||
assert object.data["endTime"] == "2019-12-18T14:00:00Z"
|
||||
|
||||
assert object.data["location"] == %{
|
||||
"address" => %{
|
||||
"addressCountry" => "France",
|
||||
"addressLocality" => "Nantes",
|
||||
"addressRegion" => "Pays de la Loire",
|
||||
"type" => "PostalAddress"
|
||||
},
|
||||
"name" => "Cour du Château des Ducs de Bretagne",
|
||||
"type" => "Place"
|
||||
}
|
||||
end
|
||||
|
||||
test "Gancio Event object" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://demo.gancio.org/federation/m/1"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/gancio-event.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{url: "https://demo.gancio.org/federation/u/customized"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/gancio-user.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
assert {:ok, object} = Fetcher.fetch_object_from_id("https://demo.gancio.org/federation/m/1")
|
||||
|
||||
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
# assert object.data["cc"] == ["https://demo.gancio.org/federation/u/customized/followers"]
|
||||
|
||||
assert object.data["url"] == "https://demo.gancio.org/event/demo-event"
|
||||
|
||||
assert object.data["published"] == "2021-07-01T22:33:36.543Z"
|
||||
assert object.data["name"] == "Demo event"
|
||||
assert object.data["startTime"] == "2021-07-14T15:30:57.000Z"
|
||||
assert object.data["endTime"] == "2021-07-14T16:30:57.000Z"
|
||||
|
||||
assert object.data["location"] == %{
|
||||
"address" => %{
|
||||
"streetAddress" => "Piazza del Colosseo, Rome",
|
||||
"type" => "PostalAddress"
|
||||
},
|
||||
"name" => "Colosseo",
|
||||
"type" => "Place"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.JoinHandlingTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Pleroma.Factory
|
||||
import Ecto.Query
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it works for incoming Mobilizon joins" do
|
||||
user = insert(:user)
|
||||
|
||||
event = insert(:event)
|
||||
|
||||
join_data =
|
||||
File.read!("test/fixtures/tesla_mock/mobilizon-event-join.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("object", event.data["id"])
|
||||
|
||||
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(join_data)
|
||||
|
||||
event = Object.get_by_id(event.id)
|
||||
|
||||
assert event.data["participations"] == [join_data["actor"]]
|
||||
|
||||
activity = Repo.get(Activity, activity.id)
|
||||
assert activity.data["state"] == "accept"
|
||||
end
|
||||
|
||||
test "with restricted events, it does create a Join, but not an Accept" do
|
||||
[participant, event_author] = insert_pair(:user)
|
||||
|
||||
event = insert(:event, %{user: event_author, data: %{"joinMode" => "restricted"}})
|
||||
|
||||
join_data =
|
||||
File.read!("test/fixtures/tesla_mock/mobilizon-event-join.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", participant.ap_id)
|
||||
|> Map.put("object", event.data["id"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(join_data)
|
||||
|
||||
event = Object.get_by_id(event.id)
|
||||
|
||||
assert event.data["participations"] == nil
|
||||
|
||||
assert data["state"] == "pending"
|
||||
|
||||
accepts =
|
||||
from(
|
||||
a in Activity,
|
||||
where: fragment("?->>'type' = ?", a.data, "Accept")
|
||||
)
|
||||
|> Repo.all()
|
||||
|
||||
assert Enum.empty?(accepts)
|
||||
|
||||
[notification] = Notification.for_user(event_author)
|
||||
assert notification.type == "pleroma:participation_request"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.LeaveHandlingTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.SideEffects
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "handle_incoming" do
|
||||
test "it works for incoming Mobilizon leaves" do
|
||||
user = insert(:user)
|
||||
|
||||
event = insert(:event)
|
||||
|
||||
{:ok, join_activity, _} = Builder.join(user, event)
|
||||
{:ok, join_activity, _} = Pipeline.common_pipeline(join_activity, local: true)
|
||||
{:ok, _, _} = SideEffects.handle(join_activity, local: true)
|
||||
|
||||
event = Object.get_by_id(event.id)
|
||||
|
||||
assert length(event.data["participations"]) === 1
|
||||
|
||||
leave_data =
|
||||
File.read!("test/fixtures/tesla_mock/mobilizon-event-leave.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|> Map.put("object", event.data["id"])
|
||||
|
||||
{:ok, %Activity{local: false} = _activity} = Transmogrifier.handle_incoming(leave_data)
|
||||
|
||||
event = Object.get_by_id(event.id)
|
||||
|
||||
assert length(event.data["participations"]) === 0
|
||||
refute Repo.get(Activity, join_activity.id)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -670,4 +670,51 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "add_participation_to_object/2" do
|
||||
test "add actor to participations" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
object = insert(:event)
|
||||
|
||||
assert {:ok, updated_object} =
|
||||
Utils.add_participation_to_object(
|
||||
%Activity{data: %{"actor" => user.ap_id}},
|
||||
object
|
||||
)
|
||||
|
||||
assert updated_object.data["participations"] == [user.ap_id]
|
||||
assert updated_object.data["participation_count"] == 1
|
||||
|
||||
assert {:ok, updated_object2} =
|
||||
Utils.add_participation_to_object(
|
||||
%Activity{data: %{"actor" => user2.ap_id}},
|
||||
updated_object
|
||||
)
|
||||
|
||||
assert updated_object2.data["participations"] == [user2.ap_id, user.ap_id]
|
||||
assert updated_object2.data["participation_count"] == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "remove_participation_from_object/2" do
|
||||
test "removes ap_id from participations" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
object =
|
||||
insert(:event,
|
||||
data: %{"participations" => [user.ap_id, user2.ap_id], "participation_count" => 2}
|
||||
)
|
||||
|
||||
assert {:ok, updated_object} =
|
||||
Utils.remove_participation_from_object(
|
||||
%Activity{data: %{"actor" => user.ap_id}},
|
||||
object
|
||||
)
|
||||
|
||||
assert updated_object.data["participations"] == [user2.ap_id]
|
||||
assert updated_object.data["participation_count"] == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2464,6 +2464,23 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
|> json_response_and_validate_schema(:forbidden)
|
||||
end
|
||||
|
||||
test "it refuses to update an event", %{conn: conn, user: user} do
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "I'm not a regular status",
|
||||
status: "",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "edited"
|
||||
})
|
||||
|> json_response_and_validate_schema(:unprocessable_entity)
|
||||
end
|
||||
|
||||
test "it returns 404 if the user cannot see the post", %{conn: conn} do
|
||||
another_user = insert(:user)
|
||||
|
||||
|
|
|
@ -343,7 +343,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
pinned_at: nil,
|
||||
quotes_count: 0,
|
||||
bookmark_folder: nil,
|
||||
list_id: nil
|
||||
list_id: nil,
|
||||
event: nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,7 +743,28 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
||||
|
||||
assert represented[:content] ==
|
||||
"<p><a href=\"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39\">Mobilizon Launching Party</a></p><p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>"
|
||||
"<p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>"
|
||||
|
||||
assert represented.pleroma.event == %{
|
||||
name: "Mobilizon Launching Party",
|
||||
start_time: "2019-12-18T13:00:00Z",
|
||||
end_time: "2019-12-18T14:00:00Z",
|
||||
join_mode: "free",
|
||||
participants_count: 0,
|
||||
location: %{
|
||||
country: "France",
|
||||
latitude: nil,
|
||||
locality: "Nantes",
|
||||
longitude: nil,
|
||||
name: "Cour du Château des Ducs de Bretagne",
|
||||
postal_code: nil,
|
||||
region: "Pays de la Loire",
|
||||
street: nil,
|
||||
url: nil
|
||||
},
|
||||
join_state: nil,
|
||||
participation_request_count: nil
|
||||
}
|
||||
end
|
||||
|
||||
describe "build_tags/1" do
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EventControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "POST /api/v1/pleroma/events" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["write"], user: user)
|
||||
[user: user, conn: conn]
|
||||
end
|
||||
|
||||
test "creates an event", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/pleroma/events", %{
|
||||
"name" => "Event name",
|
||||
"start_time" => "2023-01-01T01:00:00.000Z",
|
||||
"end_time" => "2023-01-01T04:00:00.000Z",
|
||||
"join_mode" => "free"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"pleroma" => %{
|
||||
"event" => %{
|
||||
"name" => "Event name",
|
||||
"join_mode" => "free"
|
||||
}
|
||||
}
|
||||
} = json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "can't create event that ends before its start", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/pleroma/events", %{
|
||||
"name" => "Event name",
|
||||
"start_time" => "2023-01-01T04:00:00.000Z",
|
||||
"end_time" => "2022-12-31T04:00:00.000Z",
|
||||
"join_mode" => "free"
|
||||
})
|
||||
|
||||
assert json_response_and_validate_schema(conn, 422) == %{
|
||||
"error" => "Event can't end before its start"
|
||||
}
|
||||
end
|
||||
|
||||
test "assigns location from location id", %{conn: conn} do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/pleroma/events", %{
|
||||
"name" => "Event name",
|
||||
"start_time" => "2023-01-01T01:00:00.000Z",
|
||||
"end_time" => "2023-01-01T04:00:00.000Z",
|
||||
"join_mode" => "free",
|
||||
"location_id" => "3726208425"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"pleroma" => %{
|
||||
"event" => %{
|
||||
"location" => %{
|
||||
"name" => "Benis",
|
||||
"longitude" => 45.7285348,
|
||||
"latitude" => 38.212263,
|
||||
"street" => " ",
|
||||
"locality" => "بخش مرکزی",
|
||||
"region" => "East Azerbaijan Province",
|
||||
"country" => "Iran"
|
||||
}
|
||||
}
|
||||
}
|
||||
} = json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
|
||||
test "GET /api/v1/pleroma/events/:id/participations" do
|
||||
%{conn: conn} = oauth_access(["read"])
|
||||
|
||||
user_one = insert(:user)
|
||||
user_two = insert(:user)
|
||||
user_three = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user_one, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(user_two, activity.id)
|
||||
CommonAPI.join(user_three, activity.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/events/#{activity.id}/participations")
|
||||
|
||||
assert response = json_response_and_validate_schema(conn, 200)
|
||||
assert length(response) == 3
|
||||
end
|
||||
|
||||
describe "GET /api/v1/pleroma/events/:id/participation_requests" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["read"], user: user)
|
||||
[user: user, conn: conn]
|
||||
end
|
||||
|
||||
test "show participation requests", %{conn: conn, user: user} do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id, %{
|
||||
participation_message: "I'm interested in this event"
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/events/#{activity.id}/participation_requests")
|
||||
|
||||
assert [
|
||||
%{
|
||||
"participation_message" => "I'm interested in this event"
|
||||
}
|
||||
] = response = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert length(response) == 1
|
||||
end
|
||||
|
||||
test "don't display requests if not an author", %{conn: conn} do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(other_user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/events/#{activity.id}/participation_requests")
|
||||
|
||||
assert %{"error" => "Can't get participation requests"} =
|
||||
json_response_and_validate_schema(conn, 403)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/v1/pleroma/events/:id/join" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["write"], user: user)
|
||||
[user: user, conn: conn]
|
||||
end
|
||||
|
||||
test "joins an event", %{conn: conn} do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(other_user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/api/v1/pleroma/events/#{activity.id}/join")
|
||||
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert %{
|
||||
data: %{
|
||||
"participation_count" => 2
|
||||
}
|
||||
} = Object.get_by_ap_id(activity.data["object"])
|
||||
end
|
||||
|
||||
test "can't join your own event", %{conn: conn, user: user} do
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/api/v1/pleroma/events/#{activity.id}/join")
|
||||
|
||||
assert json_response_and_validate_schema(conn, :bad_request) == %{
|
||||
"error" => "Can't join your own event"
|
||||
}
|
||||
end
|
||||
|
||||
test "can't join externally managed event", %{conn: conn} do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
||||
{:ok, object} =
|
||||
Pleroma.Object.Fetcher.fetch_object_from_id(
|
||||
"https://wp-test.event-federation.eu/event/test-event/"
|
||||
)
|
||||
|
||||
activity = Pleroma.Activity.get_create_by_object_ap_id(object.data["id"])
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/api/v1/pleroma/events/#{activity.id}/join")
|
||||
|
||||
assert json_response_and_validate_schema(conn, :bad_request) == %{
|
||||
"error" => "Joins are managed by external system"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/v1/pleroma/events/:id/leave" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["write"], user: user)
|
||||
[user: user, conn: conn]
|
||||
end
|
||||
|
||||
test "leaves an event", %{conn: conn, user: user} do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(other_user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(user, activity.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/api/v1/pleroma/events/#{activity.id}/leave")
|
||||
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert %{
|
||||
data: %{
|
||||
"participation_count" => 1
|
||||
}
|
||||
} = Object.get_by_ap_id(activity.data["object"])
|
||||
end
|
||||
|
||||
test "can't leave event you are not participating in", %{conn: conn} do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(other_user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/api/v1/pleroma/events/#{activity.id}/leave")
|
||||
|
||||
assert json_response_and_validate_schema(conn, :bad_request) == %{
|
||||
"error" => "Not participating in the event"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/v1/pleroma/events/:id/participation_requests/:participant_id/authorize" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["write"], user: user)
|
||||
[user: user, conn: conn]
|
||||
end
|
||||
|
||||
test "accepts a participation request", %{user: user, conn: conn} do
|
||||
%{ap_id: ap_id} = other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post(
|
||||
"/api/v1/pleroma/events/#{activity.id}/participation_requests/#{other_user.id}/authorize"
|
||||
)
|
||||
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert %{
|
||||
data: %{
|
||||
"participations" => [^ap_id, _],
|
||||
"participation_count" => 2
|
||||
}
|
||||
} = Object.get_by_ap_id(activity.data["object"])
|
||||
|
||||
assert %{data: %{"state" => "accept"}} =
|
||||
Utils.get_existing_join(other_user.ap_id, activity.data["object"])
|
||||
end
|
||||
|
||||
test "it refuses to accept a request when event is not by the user", %{conn: conn} do
|
||||
[second_user, third_user] = insert_pair(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(second_user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(third_user, activity.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post(
|
||||
"/api/v1/pleroma/events/#{activity.id}/participation_requests/#{third_user.id}/authorize"
|
||||
)
|
||||
|
||||
assert json_response_and_validate_schema(conn, :forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
test "POST /api/v1/pleroma/events/:id/participation_requests/:participant_id/reject" do
|
||||
[user, other_user] = insert_pair(:user)
|
||||
%{user: user, conn: conn} = oauth_access(["write"], user: user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "restricted",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
CommonAPI.join(other_user, activity.id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post(
|
||||
"/api/v1/pleroma/events/#{activity.id}/participation_requests/#{other_user.id}/reject"
|
||||
)
|
||||
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert %{data: %{"state" => "reject"}} =
|
||||
Utils.get_existing_join(other_user.ap_id, activity.data["object"])
|
||||
end
|
||||
|
||||
test "GET /api/v1/pleroma/events/:id/ics" do
|
||||
%{conn: conn} = oauth_access(["read"])
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.event(user, %{
|
||||
name: "test event",
|
||||
status: "",
|
||||
join_mode: "free",
|
||||
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
|
||||
})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/events/#{activity.id}/ics")
|
||||
|
||||
assert conn.status == 200
|
||||
|
||||
assert conn.resp_body == """
|
||||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
PRODID:-//Elixir ICalendar//Elixir ICalendar//EN
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:
|
||||
DTSTART:20230101T010000Z
|
||||
ORGANIZER:#{Pleroma.HTML.strip_tags(user.name || user.nickname)}
|
||||
SUMMARY:test event
|
||||
UID:#{activity.object.id}
|
||||
URL:#{activity.object.data["id"]}
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
"""
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.SearchControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
test "GET /api/v1/pleroma/search/location" do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
||||
user = insert(:user)
|
||||
%{conn: conn} = oauth_access([], user: user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/search/location?q=Benis")
|
||||
|
||||
assert [result | _] = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert result == %{
|
||||
"country" => "Iran",
|
||||
"description" => "Benis",
|
||||
"geom" => %{"coordinates" => [45.7285348, 38.212263], "srid" => 4326},
|
||||
"locality" => "بخش مرکزی",
|
||||
"origin_id" => "3726208425",
|
||||
"origin_provider" => "nominatim",
|
||||
"postal_code" => nil,
|
||||
"region" => "East Azerbaijan Province",
|
||||
"street" => " ",
|
||||
"timezone" => nil,
|
||||
"type" => "city",
|
||||
"url" => nil
|
||||
}
|
||||
end
|
||||
end
|
|
@ -675,4 +675,55 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
|> Map.merge(params)
|
||||
end
|
||||
|
||||
def event_factory(attrs \\ %{}) do
|
||||
user = attrs[:user] || insert(:user)
|
||||
|
||||
data = %{
|
||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
|
||||
"type" => "Event",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"name" => "Event",
|
||||
"content" => "",
|
||||
"attachment" => [],
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [user.follower_address],
|
||||
"context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(),
|
||||
"startTime" => DateTime.utc_now() |> DateTime.add(14_400) |> DateTime.to_iso8601(),
|
||||
"endTime" => DateTime.utc_now() |> DateTime.add(18_000) |> DateTime.to_iso8601(),
|
||||
"joinMode" => "free"
|
||||
}
|
||||
|
||||
%Pleroma.Object{
|
||||
data: merge_attributes(data, Map.get(attrs, :data, %{}))
|
||||
}
|
||||
end
|
||||
|
||||
def event_activity_factory(attrs \\ %{}) do
|
||||
user = attrs[:user] || insert(:user)
|
||||
event = attrs[:event] || insert(:event, user: user)
|
||||
|
||||
data_attrs = attrs[:data_attrs] || %{}
|
||||
attrs = Map.drop(attrs, [:user, :event, :data_attrs])
|
||||
|
||||
data =
|
||||
%{
|
||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||
"type" => "Create",
|
||||
"actor" => event.data["actor"],
|
||||
"to" => event.data["to"],
|
||||
"object" => event.data["id"],
|
||||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||
"context" => event.data["context"]
|
||||
}
|
||||
|> Map.merge(data_attrs)
|
||||
|
||||
%Pleroma.Activity{
|
||||
data: data,
|
||||
actor: data["actor"],
|
||||
recipients: data["to"]
|
||||
}
|
||||
|> Map.merge(attrs)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1508,6 +1508,34 @@ defmodule HttpRequestMock do
|
|||
{:ok, %Tesla.Env{status: 200, body: "hello"}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://nominatim.openstreetmap.org/search?format=geocodejson&q=Benis&limit=10&accept-language=en&addressdetails=1&namedetails=1",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/nominatim_search_results.json"),
|
||||
headers: [{"content-type", "application/json"}]
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://nominatim.openstreetmap.org/lookup?format=geocodejson&osm_ids=N3726208425,R3726208425,W3726208425&accept-language=en&addressdetails=1&namedetails=1",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/nominatim_single_result.json"),
|
||||
headers: [{"content-type", "application/json"}]
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://friends.grishka.me/posts/54642", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
|
@ -1640,6 +1668,41 @@ defmodule HttpRequestMock do
|
|||
}}
|
||||
end
|
||||
|
||||
def get("https://wp-test.event-federation.eu/event/test-event/", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/wordpress-event.json"),
|
||||
headers: activitypub_object_headers()
|
||||
}}
|
||||
end
|
||||
|
||||
def get("https://wp-test.event-federation.eu/@test", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/wordpress-user.json"),
|
||||
headers: activitypub_object_headers()
|
||||
}}
|
||||
end
|
||||
|
||||
def get(
|
||||
"https://wp-test.event-federation.eu/wp-json/activitypub/1.0/actors/0/collections/featured",
|
||||
_,
|
||||
_,
|
||||
_
|
||||
) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
File.read!("test/fixtures/users_mock/masto_featured.json")
|
||||
|> String.replace("{{domain}}", "wp-test.event-federation.eu")
|
||||
|> String.replace("{{nickname}}", "test"),
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}}
|
||||
end
|
||||
|
||||
def get(url, query, body, headers) do
|
||||
{:error,
|
||||
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
|
||||
|
|
Loading…
Reference in a new issue