From 821b36f06ce1a12ef16efb57a44ba7781cbbf4fb Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Fri, 22 Jan 2021 15:29:43 +0100 Subject: [PATCH] Groups: Add basic groups. --- lib/pleroma/group.ex | 74 ++++++++++++++++++++++ lib/pleroma/user.ex | 6 ++ .../migrations/20210113150220_create_groups.exs | 17 +++++ test/pleroma/group_test.exs | 36 +++++++++++ 4 files changed, 133 insertions(+) create mode 100644 lib/pleroma/group.ex create mode 100644 priv/repo/migrations/20210113150220_create_groups.exs create mode 100644 test/pleroma/group_test.exs diff --git a/lib/pleroma/group.ex b/lib/pleroma/group.ex new file mode 100644 index 000000000..6cc208f15 --- /dev/null +++ b/lib/pleroma/group.ex @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Group do + use Ecto.Schema + + import Ecto.Changeset + + alias Pleroma.User + alias Pleroma.Repo + alias Pleroma.Web + alias Pleroma.EctoType.ActivityPub.ObjectValidators + + @moduledoc """ + Groups contain all the additional information about a group that's not stored + in the user table. + + Concepts: + + - Groups have an owner + - Groups have members, invited by the owner. + """ + + @type t :: %__MODULE__{} + @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} + + schema "groups" do + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + belongs_to(:owner, User, type: FlakeId.Ecto.CompatType, foreign_key: :owner_id) + + field(:members, {:array, ObjectValidators.ObjectID}) + field(:name, :string) + field(:description, :string) + field(:members_collection, :string) + + timestamps() + end + + @spec create(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} + def create(params) do + with {:ok, user} <- generate_user() do + %__MODULE__{user_id: user.id, members_collection: "#{user.ap_id}/members"} + |> changeset(params) + |> Repo.insert() + end + end + + defp generate_ap_id(id) do + "#{Web.base_url()}/groups/#{id}" + end + + defp generate_user() do + id = Ecto.UUID.generate() + ap_id = generate_ap_id(id) + + %{ + ap_id: ap_id, + name: id, + nickname: id, + follower_address: "#{ap_id}/followers", + following_address: "#{ap_id}/following", + local: true + } + |> User.group_changeset() + |> Repo.insert() + end + + def changeset(struct, params) do + struct + |> cast(params, [:user_id, :owner_id, :name, :description, :members_collection]) + |> validate_required([:user_id, :owner_id, :members_collection]) + end +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2aeacf816..3da658950 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -407,6 +407,12 @@ defmodule Pleroma.User do defp fix_follower_address(params), do: params + def group_changeset(struct \\ %User{actor_type: "Group"}, params) do + struct + |> cast(params, [:ap_id, :nickname, :name, :follower_address, :following_address, :local]) + |> validate_required([:ap_id, :nickname, :name, :follower_address, :following_address, :local]) + end + def remote_user_changeset(struct \\ %User{local: false}, params) do bio_limit = Config.get([:instance, :user_bio_length], 5000) name_limit = Config.get([:instance, :user_name_length], 100) diff --git a/priv/repo/migrations/20210113150220_create_groups.exs b/priv/repo/migrations/20210113150220_create_groups.exs new file mode 100644 index 000000000..642326d51 --- /dev/null +++ b/priv/repo/migrations/20210113150220_create_groups.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateGroups do + use Ecto.Migration + + def change do + create table(:groups, primary_key: false) do + add(:id, :uuid, primary_key: true) + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) + add(:owner_id, references(:users, type: :uuid, on_delete: :nilify_all)) + add(:members, {:array, :string}, default: []) + add(:name, :text) + add(:description, :text) + add(:members_collection, :text) + + timestamps() + end + end +end diff --git a/test/pleroma/group_test.exs b/test/pleroma/group_test.exs new file mode 100644 index 000000000..f73abd237 --- /dev/null +++ b/test/pleroma/group_test.exs @@ -0,0 +1,36 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.GroupTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Group + alias Pleroma.Repo + + import Pleroma.Factory + + test "a user can create a group" do + user = insert(:user) + {:ok, group} = Group.create(%{owner_id: user.id, name: "cofe", description: "corndog"}) + group = Repo.preload(group, :user) + + assert group.user.actor_type == "Group" + assert group.owner_id == user.id + assert group.name == "cofe" + assert group.description == "corndog" + + # Deleting the owner does not delete the group, just orphans it + Repo.delete(user) + + group = + Repo.get(Group, group.id) + |> Repo.preload(:user) + + assert group.owner_id == nil + + # Deleting the group user deletes the group + Repo.delete(group.user) + refute Repo.get(Group, group.id) + end +end