## Added
- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
- Added statistic gathering about instances which do/don't have signed fetches when they request from us
+- Ability to set a default post expiry time, after which the post will be deleted. If used in concert with ActivityExpiration MRF, the expiry which comes _sooner_ will be applied.
## Changed
- MastoAPI: Accept BooleanLike input on `/api/v1/accounts/:id/follow` (fixes follows with mastodon.py)
field(:is_suggested, :boolean, default: false)
field(:last_status_at, :naive_datetime)
field(:language, :string)
+ field(:status_ttl_days, :integer, default: nil)
embeds_one(
:notification_settings,
:pleroma_settings_store,
:is_discoverable,
:actor_type,
- :disclose_client
+ :disclose_client,
+ :status_ttl_days
]
)
|> unique_constraint(:nickname)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
|> validate_inclusion(:actor_type, ["Person", "Service"])
+ |> validate_number(:status_ttl_days, greater_than: 0)
|> put_fields()
|> put_emoji()
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
description:
"Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
},
- actor_type: ActorType
+ actor_type: ActorType,
+ status_ttl_days: %Schema{
+ type: :integer,
+ nullable: true,
+ description:
+ "Number of days after which statuses will be deleted. Set to -1 to disable."
+ }
},
example: %{
bot: false,
allow_following_move: false,
also_known_as: ["https://foo.bar/users/foo"],
discoverable: false,
- actor_type: "Person"
+ actor_type: "Person",
+ status_ttl_days: 30
}
}
end
}
}
},
+ akkoma: %Schema{
+ type: :object,
+ properties: %{
+ note_ttl_days: %Schema{type: :integer}
+ }
+ },
source: %Schema{
type: :object,
properties: %{
value -> {:ok, value}
end
+ status_ttl_days_value = fn
+ -1 -> {:ok, nil}
+ value -> {:ok, value}
+ end
+
user_params =
[
:no_rich_text,
# Note: param name is indeed :discoverable (not an error)
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
|> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
+ |> Maps.put_if_present(:status_ttl_days, params[:status_ttl_days], status_ttl_days_value)
+ IO.inspect(user_params)
# What happens here:
#
# We want to update the user through the pipeline, but the ActivityPub
Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
|> put_application(conn)
+ expires_in_seconds =
+ if is_nil(user.status_ttl_days),
+ do: nil,
+ else: 60 * 60 * 24 * user.status_ttl_days
+
+ params =
+ if is_nil(expires_in_seconds),
+ do: params,
+ else: Map.put(params, :expires_in, expires_in_seconds)
+
with {:ok, activity} <- CommonAPI.post(user, params) do
try_render(conn, "show.json",
activity: activity,
},
last_status_at: user.last_status_at,
akkoma: %{
- instance: render("instance.json", %{instance: instance})
+ instance: render("instance.json", %{instance: instance}),
+ status_ttl_days: user.status_ttl_days
},
# Pleroma extensions
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
# This should be removed in a future version of Pleroma. Pleroma-FE currently
# depends on this field, as well.
defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do
- use Bitwise
+ import Bitwise
:erlang.crc32(context)
|> band(bnot(0x8000_0000))
from(u in User,
where: u.local == true,
where: is_nil(u.keys),
- select: u
+ select: u.id
)
Repo.stream(query)
|> Enum.each(fn user ->
with {:ok, pem} <- Keys.generate_rsa_pem() do
- Ecto.Changeset.cast(user, %{keys: pem}, [:keys])
- |> Repo.update()
+ Ecto.Changeset.cast(%User{id: user}, %{keys: pem}, [:keys])
+ |> Repo.update(returning: false)
end
end)
end
--- /dev/null
+defmodule Pleroma.Repo.Migrations.AddPerUserPostExpiry do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add(:status_ttl_days, :integer, null: true)
+ end
+ end
+end
)
end
+ test "automatically setting a post expiry if status_ttl_days is set" do
+ user = insert(:user, status_ttl_days: 1)
+ %{user: _user, token: _token, conn: conn} = oauth_access(["write:statuses"], user: user)
+
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("api/v1/statuses", %{
+ "status" => "aa chikichiki banban"
+ })
+
+ assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
+
+ activity = Activity.get_by_id_with_object(id)
+ {:ok, expires_at, _} = DateTime.from_iso8601(activity.data["expires_at"])
+
+ assert Timex.diff(
+ expires_at,
+ DateTime.utc_now(),
+ :hours
+ ) == 23
+
+ assert_enqueued(
+ worker: Pleroma.Workers.PurgeExpiredActivity,
+ args: %{activity_id: id},
+ scheduled_at: DateTime.add(DateTime.utc_now(), 1 * 60 * 60 * 24)
+ )
+ end
+
test "it fails to create a status if `expires_in` is less or equal than an hour", %{
conn: conn
} do
assert update_activity.data["object"]["name"] == "markorepairs"
end
+ test "updates the user's default post expiry", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "1"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["akkoma"]["status_ttl_days"] == 1
+ end
+
+ test "resets the user's default post expiry", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "-1"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert is_nil(user_data["akkoma"]["status_ttl_days"])
+ end
+
+ test "does not allow negative integers other than -1 for TTL", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{"status_ttl_days" => "-2"})
+
+ assert user_data = json_response_and_validate_schema(conn, 403)
+ end
+
test "updates the user's AKAs", %{conn: conn} do
conn =
patch(conn, "/api/v1/accounts/update_credentials", %{
inserted_at: ~N[2017-08-15 15:47:06.597036],
emoji: %{"karjalanpiirakka" => "/file.png"},
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
- also_known_as: ["https://shitposter.zone/users/shp"]
+ also_known_as: ["https://shitposter.zone/users/shp"],
+ status_ttl_days: 5
})
insert(:instance, %{host: "example.com", nodeinfo: %{version: "2.1"}})
"version" => "2.1"
},
favicon: nil
- }
+ },
+ status_ttl_days: 5
},
avatar: "http://localhost:4001/images/avi.png",
avatar_static: "http://localhost:4001/images/avi.png",
name: "localhost",
favicon: "http://localhost:4001/favicon.png",
nodeinfo: %{version: "2.0"}
- }
+ },
+ status_ttl_days: nil
},
pleroma: %{
ap_id: user.ap_id,