Merge branch 'develop' into feature/digest-email
authorRoman Chvanikov <chvanikoff@pm.me>
Sat, 20 Jul 2019 13:41:58 +0000 (16:41 +0300)
committerRoman Chvanikov <chvanikoff@pm.me>
Sat, 20 Jul 2019 13:41:58 +0000 (16:41 +0300)
1  2 
CHANGELOG.md
lib/pleroma/user.ex

diff --combined CHANGELOG.md
index 653edcf3700e3f6df32941bfdeea524f6f342408,f60f3ed978d576d12a2c8e0d75b764d4569e7543..a6d98c0bc7059f2eb8a8080ec79cec0dba68dba6
@@@ -20,6 -20,7 +20,7 @@@ The format is based on [Keep a Changelo
  - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
  - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
  - ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
+ - Existing user id not being preserved on insert conflict
  
  ### Added
  - MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
@@@ -34,6 -35,7 +35,7 @@@
  - Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`). <https://github.com/tootsuite/mastodon/pull/10373>
  - Mastodon API: Add `pleroma.deactivated` to the Account entity
  - Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
+ - Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
  - Admin API: Return users' tags when querying reports
  - Admin API: Return avatar and display name when querying users
  - Admin API: Allow querying user by ID
@@@ -62,7 -64,6 +64,7 @@@
  - Rich media: Do not crawl private IP ranges
  
  ### Added
 +- Digest email for inactive users
  - Add a generic settings store for frontends / clients to use.
  - Explicit addressing option for posting.
  - Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
@@@ -89,7 -90,6 +91,7 @@@
  - Configuration: `notify_email` option
  - Configuration: Media proxy `whitelist` option
  - Configuration: `report_uri` option
 +- Configuration: `email_notifications` option
  - Configuration: `limit_to_local_content` option
  - Pleroma API: User subscriptions
  - Pleroma API: Healthcheck endpoint
diff --combined lib/pleroma/user.ex
index cf6aee54713210b381e6832d84322b09f58d9745,5ea2b518bc55c97c05b5eb730a7fa7e3f1472b6f..c0e418e2f8c9cdd3dd9ff20e064cfad734c1935c
@@@ -57,7 -57,6 +57,7 @@@ defmodule Pleroma.User d
      field(:search_type, :integer, virtual: true)
      field(:tags, {:array, :string}, default: [])
      field(:last_refreshed_at, :naive_datetime_usec)
 +    field(:last_digest_emailed_at, :naive_datetime)
      has_many(:notifications, Notification)
      has_many(:registrations, Registration)
      embeds_one(:info, User.Info)
      data
      |> Map.put(:name, blank?(data[:name]) || data[:nickname])
      |> remote_user_creation()
-     |> Repo.insert(on_conflict: :replace_all, conflict_target: :nickname)
+     |> Repo.insert(on_conflict: :replace_all_except_primary_key, conflict_target: :nickname)
      |> set_cache()
    end
  
      target.ap_id not in user.info.muted_reblogs
    end
  
 +  @doc """
 +  The function returns a query to get users with no activity for given interval of days.
 +  Inactive users are those who didn't read any notification, or had any activity where
 +  the user is the activity's actor, during `inactivity_threshold` days.
 +  Deactivated users will not appear in this list.
 +
 +  ## Examples
 +
 +      iex> Pleroma.User.list_inactive_users()
 +      %Ecto.Query{}
 +  """
 +  @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
 +  def list_inactive_users_query(inactivity_threshold \\ 7) do
 +    negative_inactivity_threshold = -inactivity_threshold
 +    now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
 +    # Subqueries are not supported in `where` clauses, join gets too complicated.
 +    has_read_notifications =
 +      from(n in Pleroma.Notification,
 +        where: n.seen == true,
 +        group_by: n.id,
 +        having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
 +        select: n.user_id
 +      )
 +      |> Pleroma.Repo.all()
 +
 +    from(u in Pleroma.User,
 +      left_join: a in Pleroma.Activity,
 +      on: u.ap_id == a.actor,
 +      where: not is_nil(u.nickname),
 +      where: fragment("not (?->'deactivated' @> 'true')", u.info),
 +      where: u.id not in ^has_read_notifications,
 +      group_by: u.id,
 +      having:
 +        max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
 +          is_nil(max(a.inserted_at))
 +    )
 +  end
 +
 +  @doc """
 +  Enable or disable email notifications for user
 +
 +  ## Examples
 +
 +      iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true)
 +      Pleroma.User{info: %{email_notifications: %{"digest" => true}}}
 +
 +      iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false)
 +      Pleroma.User{info: %{email_notifications: %{"digest" => false}}}
 +  """
 +  @spec switch_email_notifications(t(), String.t(), boolean()) ::
 +          {:ok, t()} | {:error, Ecto.Changeset.t()}
 +  def switch_email_notifications(user, type, status) do
 +    info = Pleroma.User.Info.update_email_notifications(user.info, %{type => status})
 +
 +    change(user)
 +    |> put_embed(:info, info)
 +    |> update_and_set_cache()
 +  end
 +
 +  @doc """
 +  Set `last_digest_emailed_at` value for the user to current time
 +  """
 +  @spec touch_last_digest_emailed_at(t()) :: t()
 +  def touch_last_digest_emailed_at(user) do
 +    now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
 +
 +    {:ok, updated_user} =
 +      user
 +      |> change(%{last_digest_emailed_at: now})
 +      |> update_and_set_cache()
 +
 +    updated_user
 +  end
 +
    @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
    def toggle_confirmation(%User{} = user) do
      need_confirmation? = !user.info.confirmation_pending