Merge branch 'develop' into issue/1383

This commit is contained in:
Maksim Pechnikov 2019-12-09 19:41:43 +03:00
commit 2b821036c4
37 changed files with 926 additions and 409 deletions

View file

@ -0,0 +1,83 @@
defmodule Mix.Tasks.Pleroma.NotificationSettings do
@shortdoc "Enable&Disable privacy option for push notifications"
@moduledoc """
Example:
> mix pleroma.notification_settings --privacy-option=false --nickname-users="parallel588" # set false only for parallel588 user
> mix pleroma.notification_settings --privacy-option=true # set true for all users
"""
use Mix.Task
import Mix.Pleroma
import Ecto.Query
def run(args) do
start_pleroma()
{options, _, _} =
OptionParser.parse(
args,
strict: [
privacy_option: :boolean,
email_users: :string,
nickname_users: :string
]
)
privacy_option = Keyword.get(options, :privacy_option)
if not is_nil(privacy_option) do
privacy_option
|> build_query(options)
|> Pleroma.Repo.update_all([])
end
shell_info("Done")
end
defp build_query(privacy_option, options) do
query =
from(u in Pleroma.User,
update: [
set: [
notification_settings:
fragment(
"jsonb_set(notification_settings, '{privacy_option}', ?)",
^privacy_option
)
]
]
)
user_emails =
options
|> Keyword.get(:email_users, "")
|> String.split(",")
|> Enum.map(&String.trim(&1))
|> Enum.reject(&(&1 == ""))
query =
if length(user_emails) > 0 do
where(query, [u], u.email in ^user_emails)
else
query
end
user_nicknames =
options
|> Keyword.get(:nickname_users, "")
|> String.split(",")
|> Enum.map(&String.trim(&1))
|> Enum.reject(&(&1 == ""))
query =
if length(user_nicknames) > 0 do
where(query, [u], u.nickname in ^user_nicknames)
else
query
end
query
end
end

View file

@ -373,9 +373,9 @@ defmodule Mix.Tasks.Pleroma.User do
users
|> Enum.each(fn user ->
shell_info(
"#{user.nickname} moderator: #{user.info.is_moderator}, admin: #{user.info.is_admin}, locked: #{
user.info.locked
}, deactivated: #{user.info.deactivated}"
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
user.locked
}, deactivated: #{user.deactivated}"
)
end)
end)

View file

@ -30,6 +30,7 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
Pleroma.HTML.compile_scrubbers()
Pleroma.Config.DeprecationWarnings.warn()
setup_instrumenters()

View file

@ -3,6 +3,25 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTML do
# Scrubbers are compiled on boot so they can be configured in OTP releases
# @on_load :compile_scrubbers
def compile_scrubbers do
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
dir
|> File.ls!()
|> Enum.map(&Path.join(dir, &1))
|> Kernel.ParallelCompiler.compile()
|> case do
{:error, _errors, _warnings} ->
raise "Compiling scrubbers failed"
{:ok, _modules, _warnings} ->
:ok
end
end
defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
@ -99,216 +118,3 @@ defmodule Pleroma.HTML do
end)
end
end
defmodule Pleroma.HTML.Scrubber.TwitterText do
@moduledoc """
An HTML scrubbing policy which limits to twitter-style text. Only
paragraphs, breaks and links are allowed through the filter.
"""
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
require FastSanitize.Sanitizer.Meta
alias FastSanitize.Sanitizer.Meta
Meta.strip_comments()
# links
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
Meta.allow_tag_with_this_attribute_values(:a, "class", [
"hashtag",
"u-url",
"mention",
"u-url mention",
"mention u-url"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
"tag",
"nofollow",
"noopener",
"noreferrer"
])
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
# paragraphs and linebreaks
Meta.allow_tag_with_these_attributes(:br, [])
Meta.allow_tag_with_these_attributes(:p, [])
# microformats
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
Meta.allow_tag_with_these_attributes(:span, [])
# allow inline images for custom emoji
if Pleroma.Config.get([:markup, :allow_inline_images]) do
# restrict img tags to http/https only, because of MediaProxy.
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
Meta.allow_tag_with_these_attributes(:img, [
"width",
"height",
"class",
"title",
"alt"
])
end
Meta.strip_everything_not_covered()
end
defmodule Pleroma.HTML.Scrubber.Default do
@doc "The default HTML scrubbing policy: no "
require FastSanitize.Sanitizer.Meta
alias FastSanitize.Sanitizer.Meta
# credo:disable-for-previous-line
# No idea how to fix this one…
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
Meta.strip_comments()
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
Meta.allow_tag_with_this_attribute_values(:a, "class", [
"hashtag",
"u-url",
"mention",
"u-url mention",
"mention u-url"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
"tag",
"nofollow",
"noopener",
"noreferrer",
"ugc"
])
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
Meta.allow_tag_with_these_attributes(:abbr, ["title"])
Meta.allow_tag_with_these_attributes(:b, [])
Meta.allow_tag_with_these_attributes(:blockquote, [])
Meta.allow_tag_with_these_attributes(:br, [])
Meta.allow_tag_with_these_attributes(:code, [])
Meta.allow_tag_with_these_attributes(:del, [])
Meta.allow_tag_with_these_attributes(:em, [])
Meta.allow_tag_with_these_attributes(:i, [])
Meta.allow_tag_with_these_attributes(:li, [])
Meta.allow_tag_with_these_attributes(:ol, [])
Meta.allow_tag_with_these_attributes(:p, [])
Meta.allow_tag_with_these_attributes(:pre, [])
Meta.allow_tag_with_these_attributes(:strong, [])
Meta.allow_tag_with_these_attributes(:sub, [])
Meta.allow_tag_with_these_attributes(:sup, [])
Meta.allow_tag_with_these_attributes(:u, [])
Meta.allow_tag_with_these_attributes(:ul, [])
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
Meta.allow_tag_with_these_attributes(:span, [])
@allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
if @allow_inline_images do
# restrict img tags to http/https only, because of MediaProxy.
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
Meta.allow_tag_with_these_attributes(:img, [
"width",
"height",
"class",
"title",
"alt"
])
end
if Pleroma.Config.get([:markup, :allow_tables]) do
Meta.allow_tag_with_these_attributes(:table, [])
Meta.allow_tag_with_these_attributes(:tbody, [])
Meta.allow_tag_with_these_attributes(:td, [])
Meta.allow_tag_with_these_attributes(:th, [])
Meta.allow_tag_with_these_attributes(:thead, [])
Meta.allow_tag_with_these_attributes(:tr, [])
end
if Pleroma.Config.get([:markup, :allow_headings]) do
Meta.allow_tag_with_these_attributes(:h1, [])
Meta.allow_tag_with_these_attributes(:h2, [])
Meta.allow_tag_with_these_attributes(:h3, [])
Meta.allow_tag_with_these_attributes(:h4, [])
Meta.allow_tag_with_these_attributes(:h5, [])
end
if Pleroma.Config.get([:markup, :allow_fonts]) do
Meta.allow_tag_with_these_attributes(:font, ["face"])
end
Meta.strip_everything_not_covered()
end
defmodule Pleroma.HTML.Transform.MediaProxy do
@moduledoc "Transforms inline image URIs to use MediaProxy."
alias Pleroma.Web.MediaProxy
def before_scrub(html), do: html
def scrub_attribute(:img, {"src", "http" <> target}) do
media_url =
("http" <> target)
|> MediaProxy.url()
{"src", media_url}
end
def scrub_attribute(_tag, attribute), do: attribute
def scrub({:img, attributes, children}) do
attributes =
attributes
|> Enum.map(fn attr -> scrub_attribute(:img, attr) end)
|> Enum.reject(&is_nil(&1))
{:img, attributes, children}
end
def scrub({:comment, _text, _children}), do: ""
def scrub({tag, attributes, children}), do: {tag, attributes, children}
def scrub({_tag, children}), do: children
def scrub(text), do: text
end
defmodule Pleroma.HTML.Scrubber.LinksOnly do
@moduledoc """
An HTML scrubbing policy which limits to links only.
"""
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
require FastSanitize.Sanitizer.Meta
alias FastSanitize.Sanitizer.Meta
Meta.strip_comments()
# links
Meta.allow_tag_with_uri_attributes(:a, ["href"], @valid_schemes)
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
"tag",
"nofollow",
"noopener",
"noreferrer",
"me",
"ugc"
])
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
Meta.strip_everything_not_covered()
end

View file

@ -121,10 +121,28 @@ defmodule Pleroma.Notification do
when is_list(visibility) do
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
query
|> join(:left, [n, a], mutated_activity in Pleroma.Activity,
on:
fragment("?->>'context'", a.data) ==
fragment("?->>'context'", mutated_activity.data) and
fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and
fragment("?->>'type'", mutated_activity.data) == "Create",
as: :mutated_activity
)
|> where(
[n, a],
[n, a, mutated_activity: mutated_activity],
not fragment(
"activity_visibility(?, ?, ?) = ANY (?)",
"""
CASE WHEN (?->>'type') = 'Like' or (?->>'type') = 'Announce'
THEN (activity_visibility(?, ?, ?) = ANY (?))
ELSE (activity_visibility(?, ?, ?) = ANY (?)) END
""",
a.data,
a.data,
mutated_activity.actor,
mutated_activity.recipients,
mutated_activity.data,
^visibility,
a.actor,
a.recipients,
a.data,
@ -139,17 +157,7 @@ defmodule Pleroma.Notification do
defp exclude_visibility(query, %{exclude_visibilities: visibility})
when visibility in @valid_visibilities do
query
|> where(
[n, a],
not fragment(
"activity_visibility(?, ?, ?) = (?)",
a.actor,
a.recipients,
a.data,
^visibility
)
)
exclude_visibility(query, [visibility])
end
defp exclude_visibility(query, %{exclude_visibilities: visibility})
@ -347,7 +355,7 @@ defmodule Pleroma.Notification do
def skip?(
:followers,
activity,
%{notification_settings: %{"followers" => false}} = user
%{notification_settings: %{followers: false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
@ -357,14 +365,14 @@ defmodule Pleroma.Notification do
def skip?(
:non_followers,
activity,
%{notification_settings: %{"non_followers" => false}} = user
%{notification_settings: %{non_followers: false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
!User.following?(follower, user)
end
def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do
def skip?(:follows, activity, %{notification_settings: %{follows: false}} = user) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed)
@ -373,7 +381,7 @@ defmodule Pleroma.Notification do
def skip?(
:non_follows,
activity,
%{notification_settings: %{"non_follows" => false}} = user
%{notification_settings: %{non_follows: false}} = user
) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)

View file

@ -0,0 +1,21 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.Parsers do
@moduledoc "Initializes Plug.Parsers with upload limit set at boot time"
@behaviour Plug
def init(_opts) do
Plug.Parsers.init(
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason,
length: Pleroma.Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
)
end
defdelegate call(conn, opts), to: Plug.Parsers
end

View file

@ -129,13 +129,10 @@ defmodule Pleroma.User do
field(:skip_thread_containment, :boolean, default: false)
field(:also_known_as, {:array, :string}, default: [])
field(:notification_settings, :map,
default: %{
"followers" => true,
"follows" => true,
"non_follows" => true,
"non_followers" => true
}
embeds_one(
:notification_settings,
Pleroma.User.NotificationSetting,
on_replace: :update
)
has_many(:notifications, Notification)
@ -1221,20 +1218,9 @@ defmodule Pleroma.User do
end
def update_notification_settings(%User{} = user, settings) do
settings =
settings
|> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
|> Map.new()
notification_settings =
user.notification_settings
|> Map.merge(settings)
|> Map.take(["followers", "follows", "non_follows", "non_followers"])
params = %{notification_settings: notification_settings}
user
|> cast(params, [:notification_settings])
|> cast(%{notification_settings: settings}, [])
|> cast_embed(:notification_settings)
|> validate_required([:notification_settings])
|> update_and_set_cache()
end

View file

@ -0,0 +1,40 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.NotificationSetting do
use Ecto.Schema
import Ecto.Changeset
@derive Jason.Encoder
@primary_key false
embedded_schema do
field(:followers, :boolean, default: true)
field(:follows, :boolean, default: true)
field(:non_follows, :boolean, default: true)
field(:non_followers, :boolean, default: true)
field(:privacy_option, :boolean, default: false)
end
def changeset(schema, params) do
schema
|> cast(prepare_attrs(params), [
:followers,
:follows,
:non_follows,
:non_followers,
:privacy_option
])
end
defp prepare_attrs(params) do
Enum.reduce(params, %{}, fn
{k, v}, acc when is_binary(v) ->
Map.put(acc, k, String.downcase(v))
{k, v}, acc ->
Map.put(acc, k, v)
end)
end
end

View file

@ -61,14 +61,7 @@ defmodule Pleroma.Web.Endpoint do
plug(Plug.RequestId)
plug(Plug.Logger)
plug(
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason,
length: Pleroma.Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
)
plug(Pleroma.Plugs.Parsers)
plug(Plug.MethodOverride)
plug(Plug.Head)

View file

@ -22,8 +22,8 @@ defmodule Pleroma.Web.Push.Impl do
@spec perform(Notification.t()) :: list(any) | :error
def perform(
%{
activity: %{data: %{"type" => activity_type}, id: activity_id} = activity,
user_id: user_id
activity: %{data: %{"type" => activity_type}} = activity,
user: %User{id: user_id}
} = notif
)
when activity_type in @types do
@ -39,18 +39,17 @@ defmodule Pleroma.Web.Push.Impl do
for subscription <- fetch_subsriptions(user_id),
get_in(subscription.data, ["alerts", type]) do
%{
title: format_title(notif),
access_token: subscription.token.token,
body: format_body(notif, actor, object),
notification_id: notif.id,
notification_type: type,
icon: avatar_url,
preferred_locale: "en",
pleroma: %{
activity_id: activity_id,
activity_id: notif.activity.id,
direct_conversation_id: direct_conversation_id
}
}
|> Map.merge(build_content(notif, actor, object))
|> Jason.encode!()
|> push_message(build_sub(subscription), gcm_api_key, subscription)
end
@ -100,6 +99,24 @@ defmodule Pleroma.Web.Push.Impl do
}
end
def build_content(
%{
activity: %{data: %{"directMessage" => true}},
user: %{notification_settings: %{privacy_option: true}}
},
actor,
_
) do
%{title: "New Direct Message", body: "@#{actor.nickname}"}
end
def build_content(notif, actor, object) do
%{
title: format_title(notif),
body: format_body(notif, actor, object)
}
end
def format_body(
%{activity: %{data: %{"type" => "Create"}}},
actor,

View file

@ -13,7 +13,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
notification =
Notification
|> Repo.get(notification_id)
|> Repo.preload([:activity])
|> Repo.preload([:activity, :user])
Pleroma.Web.Push.Impl.perform(notification)
end