aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/group.ex
blob: 880cd52c6705cdf1a3d9c175063ab70e4ecfc242 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# 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.UserRelationship

  @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)

    has_many(:members, through: [:user, :group_members])

    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

  def is_member?(%{user_id: user_id}, member) do
    UserRelationship.membership_exists?(%User{id: user_id}, member)
  end

  def members(group) do
    Repo.preload(group, :members).members
  end

  def add_member(%{user_id: user_id} = group, member) do
    with {:ok, _relationship} <- UserRelationship.create_membership(%User{id: user_id}, member) do
      {:ok, group}
    end
  end

  def remove_member(%{user_id: user_id} = group, member) do
    with {:ok, _relationship} <- UserRelationship.delete_membership(%User{id: user_id}, member) do
      {:ok, group}
    end
  end

  @spec get_for_object(map()) :: t() | nil
  def get_for_object(%{"type" => "Group", "id" => id}) do
    with %User{} = user <- User.get_cached_by_ap_id(id),
         group <- Repo.preload(user, :group).group do
      group
    end
  end

  def get_for_object(%{"type" => "Create", "object" => object}), do: get_for_object(object)
  def get_for_object(_), do: nil
end