Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
liamwhite committed Jul 24, 2024
2 parents ca5b0ca + 392d920 commit ccdb283
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 26 deletions.
18 changes: 9 additions & 9 deletions assets/js/markdowntoolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import { $, $$ } from './utils/dom';
const markdownSyntax = {
bold: {
action: wrapSelection,
options: { prefix: '**', shortcutKey: 'b' },
options: { prefix: '**', shortcutKeyCode: 66 },
},
italics: {
action: wrapSelection,
options: { prefix: '*', shortcutKey: 'i' },
options: { prefix: '*', shortcutKeyCode: 73 },
},
under: {
action: wrapSelection,
options: { prefix: '__', shortcutKey: 'u' },
options: { prefix: '__', shortcutKeyCode: 85 },
},
spoiler: {
action: wrapSelection,
options: { prefix: '||', shortcutKey: 's' },
options: { prefix: '||', shortcutKeyCode: 83 },
},
code: {
action: wrapSelectionOrLines,
Expand All @@ -29,7 +29,7 @@ const markdownSyntax = {
prefixMultiline: '```\n',
suffixMultiline: '\n```',
singleWrap: true,
shortcutKey: 'e',
shortcutKeyCode: 69,
},
},
strike: {
Expand All @@ -50,11 +50,11 @@ const markdownSyntax = {
},
link: {
action: insertLink,
options: { shortcutKey: 'l' },
options: { shortcutKeyCode: 76 },
},
image: {
action: insertLink,
options: { image: true, shortcutKey: 'k' },
options: { image: true, shortcutKeyCode: 75 },
},
escape: {
action: escapeSelection,
Expand Down Expand Up @@ -257,10 +257,10 @@ function shortcutHandler(event) {
}

const textarea = event.target,
key = event.key.toLowerCase();
keyCode = event.keyCode;

for (const id in markdownSyntax) {
if (key === markdownSyntax[id].options.shortcutKey) {
if (keyCode === markdownSyntax[id].options.shortcutKeyCode) {
markdownSyntax[id].action(textarea, markdownSyntax[id].options);
event.preventDefault();
}
Expand Down
26 changes: 13 additions & 13 deletions assets/js/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { $ } from './utils/dom';

type ShortcutKeyMap = Record<string, () => void>;
type ShortcutKeyMap = Record<number, () => void>;

function getHover(): string | null {
const thumbBoxHover = $<HTMLDivElement>('.media-box:hover');
Expand Down Expand Up @@ -50,19 +50,19 @@ function isOK(event: KeyboardEvent): boolean {
/* eslint-disable prettier/prettier */

const keyCodes: ShortcutKeyMap = {
j() { click('.js-prev'); }, // J - go to previous image
i() { click('.js-up'); }, // I - go to index page
k() { click('.js-next'); }, // K - go to next image
r() { click('.js-rand'); }, // R - go to random image
s() { click('.js-source-link'); }, // S - go to image source
l() { click('.js-tag-sauce-toggle'); }, // L - edit tags
o() { openFullView(); }, // O - open original
v() { openFullViewNewTab(); }, // V - open original in a new tab
f() {
74() { click('.js-prev'); }, // J - go to previous image
73() { click('.js-up'); }, // I - go to index page
75() { click('.js-next'); }, // K - go to next image
82() { click('.js-rand'); }, // R - go to random image
83() { click('.js-source-link'); }, // S - go to image source
76() { click('.js-tag-sauce-toggle'); }, // L - edit tags
79() { openFullView(); }, // O - open original
86() { openFullViewNewTab(); }, // V - open original in a new tab
70() {
// F - favourite image
click(getHover() ? `a.interaction--fave[data-image-id="${getHover()}"]` : '.block__header a.interaction--fave');
},
u() {
85() {
// U - upvote image
click(getHover() ? `a.interaction--upvote[data-image-id="${getHover()}"]` : '.block__header a.interaction--upvote');
},
Expand All @@ -72,8 +72,8 @@ const keyCodes: ShortcutKeyMap = {

export function listenForKeys() {
document.addEventListener('keydown', (event: KeyboardEvent) => {
if (isOK(event) && keyCodes[event.key]) {
keyCodes[event.key]();
if (isOK(event) && keyCodes[event.keyCode]) {
keyCodes[event.keyCode]();
event.preventDefault();
}
});
Expand Down
10 changes: 10 additions & 0 deletions lib/philomena/duplicate_reports.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ defmodule Philomena.DuplicateReports do
The DuplicateReports context.
"""

import Philomena.DuplicateReports.Power
import Ecto.Query, warn: false

alias Ecto.Multi
alias Philomena.Repo

Expand Down Expand Up @@ -46,6 +48,14 @@ defmodule Philomena.DuplicateReports do
where:
i.image_aspect_ratio >= ^(aspect_ratio - aspect_dist) and
i.image_aspect_ratio <= ^(aspect_ratio + aspect_dist),
order_by: [
asc:
power(it.nw - ^intensities.nw, 2) +
power(it.ne - ^intensities.ne, 2) +
power(it.sw - ^intensities.sw, 2) +
power(it.se - ^intensities.se, 2) +
power(i.image_aspect_ratio - ^aspect_ratio, 2)
],
limit: ^limit
end

Expand Down
9 changes: 9 additions & 0 deletions lib/philomena/duplicate_reports/power.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Philomena.DuplicateReports.Power do
@moduledoc false

defmacro power(left, right) do
quote do
fragment("power(?, ?)", unquote(left), unquote(right))
end
end
end
10 changes: 10 additions & 0 deletions lib/philomena/duplicate_reports/search_query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ defmodule Philomena.DuplicateReports.SearchQuery do
:image_aspect_ratio,
:uploaded_image
])
|> validate_required([
:image_width,
:image_height,
:image_format,
:image_duration,
:image_mime_type,
:image_is_animated,
:image_aspect_ratio,
:uploaded_image
])
|> validate_number(:image_width, greater_than: 0)
|> validate_number(:image_height, greater_than: 0)
|> validate_inclusion(
Expand Down
41 changes: 41 additions & 0 deletions lib/philomena/images.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Philomena.Images do
alias Philomena.SourceChanges.SourceChange
alias Philomena.Notifications.Notification
alias Philomena.NotificationWorker
alias Philomena.TagChanges.Limits
alias Philomena.TagChanges.TagChange
alias Philomena.Tags
alias Philomena.UserStatistics
Expand Down Expand Up @@ -418,6 +419,9 @@ defmodule Philomena.Images do
error
end
end)
|> Multi.run(:check_limits, fn _repo, %{image: {image, _added, _removed}} ->
check_tag_change_limits_before_commit(image, attribution)
end)
|> Multi.run(:added_tag_changes, fn repo, %{image: {image, added_tags, _removed}} ->
tag_changes =
added_tags
Expand Down Expand Up @@ -461,6 +465,43 @@ defmodule Philomena.Images do
{:ok, count}
end)
|> Repo.transaction()
|> case do
{:ok, %{image: {image, _added, _removed}}} = res ->
update_tag_change_limits_after_commit(image, attribution)

res

err ->
err
end
end

defp check_tag_change_limits_before_commit(image, attribution) do
tag_changed_count = length(image.added_tags) + length(image.removed_tags)
rating_changed = image.ratings_changed
user = attribution[:user]
ip = attribution[:ip]

cond do
Limits.limited_for_tag_count?(user, ip, tag_changed_count) ->
{:error, :limit_exceeded}

rating_changed and Limits.limited_for_rating_count?(user, ip) ->
{:error, :limit_exceeded}

true ->
{:ok, 0}
end
end

def update_tag_change_limits_after_commit(image, attribution) do
rating_changed_count = if(image.ratings_changed, do: 1, else: 0)
tag_changed_count = length(image.added_tags) + length(image.removed_tags)
user = attribution[:user]
ip = attribution[:ip]

Limits.update_tag_count_after_update(user, ip, tag_changed_count)
Limits.update_rating_count_after_update(user, ip, rating_changed_count)
end

defp tag_change_attributes(attribution, image, tag, added, user) do
Expand Down
1 change: 1 addition & 0 deletions lib/philomena/images/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ defmodule Philomena.Images.Image do
field :added_tags, {:array, :any}, default: [], virtual: true
field :removed_sources, {:array, :any}, default: [], virtual: true
field :added_sources, {:array, :any}, default: [], virtual: true
field :ratings_changed, :boolean, default: false, virtual: true

field :uploaded_image, :string, virtual: true
field :removed_image, :string, virtual: true
Expand Down
22 changes: 21 additions & 1 deletion lib/philomena/images/tag_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ defmodule Philomena.Images.TagValidator do
def validate_tags(changeset) do
tags = changeset |> get_field(:tags)

validate_tag_input(changeset, tags)
changeset
|> validate_tag_input(tags)
|> set_rating_changed()
end

defp set_rating_changed(changeset) do
added_tags = changeset |> get_field(:added_tags) |> extract_names()
removed_tags = changeset |> get_field(:removed_tags) |> extract_names()
ratings = all_ratings()

added_ratings = MapSet.intersection(ratings, added_tags) |> MapSet.size()
removed_ratings = MapSet.intersection(ratings, removed_tags) |> MapSet.size()

put_change(changeset, :ratings_changed, added_ratings + removed_ratings > 0)
end

defp validate_tag_input(changeset, tags) do
Expand Down Expand Up @@ -108,6 +121,13 @@ defmodule Philomena.Images.TagValidator do
|> MapSet.new()
end

defp all_ratings do
safe_rating()
|> MapSet.union(sexual_ratings())
|> MapSet.union(horror_ratings())
|> MapSet.union(gross_rating())
end

defp safe_rating, do: MapSet.new(["safe"])
defp sexual_ratings, do: MapSet.new(["suggestive", "questionable", "explicit"])
defp horror_ratings, do: MapSet.new(["semi-grimdark", "grimdark"])
Expand Down
109 changes: 109 additions & 0 deletions lib/philomena/tag_changes/limits.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
defmodule Philomena.TagChanges.Limits do
@moduledoc """
Tag change limits for anonymous users.
"""

@tag_changes_per_ten_minutes 50
@rating_changes_per_ten_minutes 1
@ten_minutes_in_seconds 10 * 60

@doc """
Determine if the current user and IP can make any tag changes at all.
The user may be limited due to making more than 50 tag changes in the past 10 minutes.
Should be used in tandem with `update_tag_count_after_update/3`.
## Examples
iex> limited_for_tag_count?(%User{}, %Postgrex.INET{})
false
iex> limited_for_tag_count?(%User{}, %Postgrex.INET{}, 72)
true
"""
def limited_for_tag_count?(user, ip, additional \\ 0) do
check_limit(user, tag_count_key_for_ip(ip), @tag_changes_per_ten_minutes, additional)
end

@doc """
Determine if the current user and IP can make rating tag changes.
The user may be limited due to making more than one rating tag change in the past 10 minutes.
Should be used in tandem with `update_rating_count_after_update/3`.
## Examples
iex> limited_for_rating_count?(%User{}, %Postgrex.INET{})
false
iex> limited_for_rating_count?(%User{}, %Postgrex.INET{}, 2)
true
"""
def limited_for_rating_count?(user, ip) do
check_limit(user, rating_count_key_for_ip(ip), @rating_changes_per_ten_minutes, 0)
end

@doc """
Post-transaction update for successful tag changes.
Should be used in tandem with `limited_for_tag_count?/2`.
## Examples
iex> update_tag_count_after_update(%User{}, %Postgrex.INET{}, 25)
:ok
"""
def update_tag_count_after_update(user, ip, amount) do
increment_counter(user, tag_count_key_for_ip(ip), amount, @ten_minutes_in_seconds)
end

@doc """
Post-transaction update for successful rating tag changes.
Should be used in tandem with `limited_for_rating_count?/2`.
## Examples
iex> update_rating_count_after_update(%User{}, %Postgrex.INET{}, 1)
:ok
"""
def update_rating_count_after_update(user, ip, amount) do
increment_counter(user, rating_count_key_for_ip(ip), amount, @ten_minutes_in_seconds)
end

defp check_limit(user, key, limit, additional) do
if considered_for_limit?(user) do
amt = Redix.command!(:redix, ["GET", key]) || 0
amt + additional >= limit
else
false
end
end

defp increment_counter(user, key, amount, expiration) do
if considered_for_limit?(user) do
Redix.pipeline!(:redix, [
["INCRBY", key, amount],
["EXPIRE", key, expiration]
])
end

:ok
end

defp considered_for_limit?(user) do
is_nil(user) or not user.verified
end

defp tag_count_key_for_ip(ip) do
"rltcn:#{ip}"
end

defp rating_count_key_for_ip(ip) do
"rltcr:#{ip}"
end
end
Loading

0 comments on commit ccdb283

Please sign in to comment.