Commit Graph

2896 Commits

Author SHA1 Message Date
evazion
ef4c6f61d4 Merge pull request #5257 from eltociear/patch-1
Fix typo in session_loader.rb
2022-10-04 18:26:47 -05:00
evazion
0cfd0ff436 emails: add fix script to renormalize email addresses.
Whenever the email address normalization procedure changes, the
`normalized_address` column of the email address table must be updated.
This is normally when the list of canonical domain mappings changes.

Renormalizing addresses may also require deleting duplicates.
2022-10-03 02:55:30 -05:00
nonamethanks
775326dc37 Tumblr: fix crash when uploading image links from custom domains 2022-10-01 00:26:29 +02:00
nonamethanks
1d7caf703c Lofter: support another theme and rewrite tests 2022-09-30 22:04:40 +02:00
evazion
559bf1ae0a modqueue: fix the disapproved: metatag showing posts outside the queue.
Fix a bug where filtering the modqueue by the `disapproved:<reason>` tag
would return posts outside the modqueue.
2022-09-30 02:03:15 -05:00
nonamethanks
d51cc17eaf Nicoseiga: rewrite tests and fix several bugs
* Fixed a bug where manga posts with a single tag would raise an error
* Fixed a bug where dic.nicovideo.jp/oekaki posts weren't uploadable due
  to SSL issues
* Added support for more manga corner cases
2022-09-29 14:37:46 +02:00
evazion
d629c81aa1 Fix #5267: order[custom] no longer works when only a single ID is being searched 2022-09-29 04:36:12 -05:00
evazion
530d8cf762 searchable: fix searching for invalid IP addresses.
Fix an ArgumentError exception when searching for an invalid IP address.

Also allow searching for multiple subnets at once.
2022-09-29 04:36:12 -05:00
evazion
c5b215ffcb searchable: fix searching for polymorphic attributes.
Fix not being able to use the full set of search operators on polymorphic `model_id` and
`model_type` attributes. Before things like `search[model_type]=Post` worked, but
`search[model_type_not_eq]=Post` or other `model_type_*` operators didn't.
2022-09-29 04:36:12 -05:00
evazion
09eb763e3c searchable: fix searching by json attribute.
Fix accidentally hardcoding the attribute name to `metadata`.
2022-09-29 04:36:12 -05:00
evazion
b94cb7d824 emails: include logging information in email headers.
Log the following information in email headers:

* X-Danbooru-User: the user's name and ID.
* X-Danbooru-IP: the user's IP.
* X-Danbooru-Session: the users' session ID.
* X-Danbooru-URL: the page that triggered the email.
* X-Danbooru-Job-Id: the ID of the background job that sent the email.
* X-Danbooru-Enqueued-At: when the email was queued as a background job.
* X-Danbooru-Dmail: for Dmail notifications, the link to the Dmail.
* X-Request-Id: the request ID of the HTTP request that triggered the email.

Also make it so we log an event in the APM when we send an email.
2022-09-29 04:36:11 -05:00
nonamethanks
5051c6649d Tumblr: parse new dashboard links 2022-09-28 17:00:08 +02:00
evazion
4c03ea5be3 Fix #5132: Modqueue displays active posts when excluding any search term
Fix a bug where searching for a negated tag inside the modqueue would show
active posts.

The bug was that in a search like this:

    Post.in_modqueue.user_tag_match("-solo")

The `in_modqueue` condition would get sucked inside the tag search and negated
when we tried to apply the negation operator to the "solo" tag. So effectively
the `in_modqueue` condition would get negated and we would end up searching for
everything not in the modqueue.
2022-09-28 00:29:50 -05:00
evazion
59f166a637 Fix #5057: Modqueue: filtering by tag breaks ordering
Fix the order dropdown box on the modqueue page not working when filtering by tag.

This happened because when you do a tag search, the default order is set to `ORDER BY posts.id DESC`.
When you applied another order with the dropdown box, the new order would be tacked on to the old
ordering as a tiebreaker instead of replacing it, producing e.g. `ORDER BY posts.id DESC, queued_at DESC`
instead of `ORDER BY queued_at DESC`. The default order would always win because `posts.id` is
unique and doesn't have ties.

The fix is to have orders always override the previous order instead of adding to it.

Note that now if you use an `order:`, `ordfav:`, or `ordpool:` metatag in the search box on the
modqueue page, they will always be ignored in favor of the dropdown box.
2022-09-28 00:29:50 -05:00
evazion
04b004d2a4 Fix #4586: Wrong paginator for status:unmoderated
Don't cache the page count for status:unmoderated, status:modqueue, status:pending, status:flagged,
or status:appealed searches. Before the page count was cached for 5 minutes, which could quickly
become out of date as a user went through the modqueue. After bd21b4a86, they're now fast enough
that we no longer need to cache them.
2022-09-28 00:29:50 -05:00
evazion
f49b3c439f posts: optimize modqueue page, status:modqueue, and status:unmoderated searches.
* Optimize status:modqueue and status:unmoderated searches. This brings them down from
  taking 500ms-1000ms per search to ~5ms.

* Change status:unmoderated so that it only filters out the user's disapproved posts, not
  the user's own uploads or past approvals. Now it's equivalent to `status:modqueue -disapproved:evazion`,
  whereas before it was equivalent to `status:modqueue -disapproved:evazion -approver:evazion -user:evazion`.
  Filtering out the user's own uploads and approvals was slow and usually unnecessary,
  since for most users it's rare for their own uploads or approvals to reenter the modqueue.

Before status:modqueue did this:

   SELECT * FROM posts WHERE is_pending = TRUE OR is_flagged = TRUE OR (is_deleted = TRUE AND id IN (SELECT post_id FROM post_appeals WHERE status = 0))

Now we do this:

   SELECT * FROM posts WHERE id IN (SELECT id FROM posts WHERE is_pending = TRUE UNION ALL SELECT id FROM posts WHERE is_flagged = TRUE UNION ALL SELECT id FROM posts WHERE id IN (SELECT post_id FROM post_appeals WHERE status = 0))

Postgres had a bad time with the "pending or flagged or has a pending appeal" clause because
it didn't know that posts can only be in one state at a time, so it overestimated how many
posts would be returned and chose a seq scan. Replacing the OR with a UNION avoids this.
2022-09-28 00:29:50 -05:00
evazion
331f15259a Fix #1590: id metatag: "id:A..B,C..D,E"
Support searches like the following:

* score:<0,>100 (equivalent to `score:<0 or score:>100`)
* score:5,10..15,>20 (equivalent to `score:5 or score:10..15 or score:>20`)
* id:5,10..15,20..25,30 (equivalent to `id:5 or id:10..15 or id:20..25 or id:30`)

This also works inside the `search[id]` URL parameter, and inside other numeric
URL search parameters.
2022-09-27 01:44:18 -05:00
evazion
116c1f1af8 searchable: factor out metatag value parser.
Factor out the code that parses metatag values (e.g. `score:>5`) and
search URL params (e.g. `search[score]=>5`) into a RangeParser class.

Also fix a bug where, if the `search[order]=custom` param was used
without a `search[id]` param, an exception would be raised. Fix another
bug where if an invalid `search[id]` was provided, then the custom order
would be ignored and the search would be returned with the default order
instead. Now if you use `search[order]=custom` without a valid
`search[id]` param, the search will return no results.
2022-09-26 22:50:45 -05:00
evazion
65dbd89e25 Fix #4978: -approver:username implicitly adds approver:any
Fix the approver:, parent:, and pixiv: metatags not working correctly when negated:

* Fix -approver:<name> not including posts that don't have an approver (the approver_id is NULL)
* Fix -parent:<id> not including posts that don't have a parent (the parent_id is NULL)
* Fix -pixiv:<id> not including posts that aren't from Pixiv (the pixiv_id is NULL)

The problem lies how the equality operator is negated when the column contains NULL values;
`approver_id != 52664` doesn't match posts where the `approver_id` is NULL.

The search `approver:evazion` boils down to:

    # Post.where(approver_id: 52664).to_sql
    SELECT * FROM posts WHERE approver_id = 52664;

When that is negated with `-approver:evazion`, it becomes:

    # Post.where(approver_id: 52664).invert_where.to_sql
    SELECT * FROM posts WHERE approver_id != 52664;

But in SQL, `approver_id != 52664` doesn't match when the approver_id IS NULL, so the search doesn't
include posts without an approver.

We could use `a IS NOT DISTINCT FROM b` instead of `a = b`:

    # Post.where(Post.arel_table[:approver_id].is_not_distinct_from(52664)).to_sql
    SELECT * FROM posts WHERE approver_id IS NOT DISTINCT FROM 52664;

This way when it's inverted it becomes `IS DISTINCT FROM`:

    # Post.where(Post.arel_table[:approver_id].is_not_distinct_from(52664)).invert_where.to_sql
    SELECT * FROM posts WHERE approver_id IS NOT DISTINCT FROM 52664;

`approver_id IS DISTINCT FROM 52664` is like `approver_id != 52664`, except it matches when
approver_id is NULL [1].

This works correctly, however the problem is that `IS NOT DISTINCT FROM` can't use indexes because
of a long-standing Postgres limitation [2]. This makes searches too slow. So instead we do this:

    # Post.where(approver_id: 52664).where.not(approver_id: nil).to_sql
    SELECT * FROM posts WHERE approver_id = 52664 AND approver_id IS NOT NULL;

That way when negated it becomes:

    # Post.where(approver_id: 52664).where.not(approver_id: nil).invert_where.to_sql
    SELECT * FROM posts WHERE approver_id != 52664 OR approver_id IS NULL;

Which is the correct behavior.

[1] https://modern-sql.com/feature/is-distinct-from
[2] https://www.postgresql.org/message-id/6FC83909-5DB1-420F-9191-DBE533A3CEDE@excoventures.com
2022-09-26 19:28:10 -05:00
evazion
34057b25e1 mod actions: record the subject of the mod action.
Add a polymorphic `subject` field that records the subject of the mod
action. The subject is the post, user, comment, artist, etc the mod
action is for.

* The subject for the user ban and unban actions is the user, not the ban itself.
* The subject for the user feedback update and deletion actions is the user,
  not the feedback itself.
* The subject for the post undeletion action is the post, not the approval itself.
* The subject for the move favorites action is the source post where the
  favorites were moved from, not the destination post where the favorites
  were moved to.
* The subject for the post permanent delete action is nil, because the
  post itself is hard deleted.
* When a post is permanently deleted, all mod actions related to the
  post are deleted as well.
2022-09-25 04:04:28 -05:00
evazion
361af6a4cb posts: rework post events page.
* Add a global /post_events page that shows the history of all approvals,
  disapprovals, flags, appeals, and replacements on a single page.

* Redesign the /posts/:id/events page to show all approval, disapproval,
  flag, appeal, and replacement events for a single post (before it only
  showed approvals, flags, and appeals).

* Remove the replacement history link from the post show page. Replacements
  are now included in the post events page (closes #4948: Highlighed replacements).

* Add /post_approvals/:id and /post_replacements/:id routes (these are
  used by the "Details" link on the post events page).
2022-09-24 20:12:41 -05:00
evazion
adba70a0de api: make IP addresses in the API.
Make the following fields visible in API responses:

* ip_bans.ip_addr
* ip_geolocations.ip_addr
* ip_geolocations.network
* users.last_ip_addr (mod only)
* user_sessions.ip_addr
* api_keys.last_ip_address
* api_keys.permitted_ip_addresses

Before IP addresses were globally hidden in API responses because IPs were
present in a lot of tables and we didn't want to accidentally leak them.
Now that we've gotten rid of IPs from most tables, it's safe to unhide them.
2022-09-24 03:48:45 -05:00
Ikko Ashimine
8c339aa364 Fix typo in session_loader.rb
reseting -> resetting
2022-09-24 01:17:12 +09:00
evazion
e5edd79180 flags: fix flagger:<name> not returning self-flagged uploads
Fix the search `flagger:evazion user:evazion` not returning the user's own self-flagged uploads.

Followup to a6e0872ce.

Fixes #4690: user profile 'flags' count links to /post_flags with different search criteria
2022-09-22 23:07:53 -05:00
evazion
a442658f8a Fix #5237: Deleted comments can be viewed by other users
* Fix it so non-moderators can't search deleted comments using the
  `updater`, `body`, `score`, `do_not_bump_post`, or `is_sticky` fields.
  Searching for these fields will exclude deleted comments.

* Fix it so non-moderators can search for their own deleted comments using the
  `creator` field, but not for deleted comments belonging to other users.

* Fix it so that if a regular user searches `commenter:<username>`, they
  can only see posts with undeleted comments by that user. If a moderator or
  the commenter themselves searches `commenter:<username>`, they can see all
  posts the user has commented on, including posts with deleted comments.

* Fix it so the comment count on user profiles only counts visible
  comments. Regular users can only see the number of undeleted comments
  a user has, while moderators and the commenter themselves can see the
  total number of comments.

Known issue:

* It's still possible to order deleted comments by score, which can let
  you infer the score of deleted comments.
2022-09-22 19:17:33 -05:00
evazion
88ac91f5f3 search: refactor to pass in the current user explicitly. 2022-09-22 04:31:21 -05:00
evazion
29a4ca0818 pools: switch from search[name_matches] to search[name_contains].
The previous commit changed it so that `/pools?search[name_matches]`
does a full-text search. So for example, `search[name_matches]=smiling`
will now match pool names containing any of the words "smiling",
"smile", "smiles", or "smiled".

This commit adds a `/pools?search[name_contains]` param that does what
`name_matches` did before, and switches to it in search forms. So for
example, `search[name_contains]=smiling` will only match pool names
containing the exact substring "smiling".

This change is so that `<field>_matches` works consistently across the
site, and so that it's possible to search pool names by either an exact
substring match, or by a looser natural language match.

This is a minor breaking API change. API users can replace
`/pools?search[name_matches]` with `/pools?search[name_contains]` to get
the same behavior as before. The same applies to /favorite_groups.
2022-09-22 01:52:13 -05:00
evazion
3114ef3daf searchable: standardize the <field>_matches operator for text fields.
Standardize it so that all fields of type `text` are searchable with
`search[<field>_matches]`.

Before, the `<field>_matches` param was handled manually and some fields
were left out or handled inconsistently. Now it applies to all columns
of type `text`.

This does a full-text search on the field, so for example, searching
`/artist_commentaries?search[translated_description_matches]=smiling`
will match translated commentaries containing either the word "smiling",
"smiles", "smiled", or "smile".

Note that this only applies to columns defined as type `text`, not to
columns defined as `character varying`. The difference is that `text` is
used for fields containing free-form natural language, such as comments,
notes, forum posts, wiki pages, pool descriptions, etc, while `character
varying` is used for short strings not containing free-form language,
such as tag names, wiki page titles, urls, status fields, etc.

API changes:

* Add the `search[original_title_matches]`, `search[original_description_matches]`,
  `search[translated_title_matches]`, `search[translated_description_matches]` params
  to /artist_commentaries and /artist_commentary_versions.
* Remove the `search[name_matches]` and `search[group_name_matches]` params from /artist_versions.
* Remove the `search[title_matches]` param from /wiki_page_versions.
* Change the `search[name_matches]` param on /pools, /favorite_groups, and /pool_versions
  to do a full-text search instead of a substring match.
2022-09-22 01:52:13 -05:00
evazion
a35f49e905 searchable: add framework for defining user search permissions.
Add a `visible_for_search` method to ApplicationPolicy that lets us
define which fields a user is allowed to search for.

For example, when a normal user searches for post flags by flagger name,
they're only allowed to see their own flags, not flags by other users.
But when a mod searches for flags by flagger name, they're allowed to
see all flags, except for flags on their own uploads.

This framework lets us define these rules in the `visible_for_search`
method in the model's policy class, rather than as special cases in the
`search` method of each model.
2022-09-22 01:51:23 -05:00
evazion
6a9a679149 searchable: refactor search_attributes helper methods into class.
Move the `search_attributes` helper methods inside the Searchable
concern into a SearchContext helper class. This is so we don't have to
pass `params` and `current_user` down through a bunch of different
helper methods, and so that these private helper methods don't pollute
the object's namespace.
2022-09-22 01:48:21 -05:00
evazion
7977572865 users: fix user deletion validation. 2022-09-19 05:50:59 -05:00
evazion
aea3837f9a users: delete accounts with invalid names.
Add a fix script to delete all accounts with invalid usernames. Also
change it so the owner-level user can delete accounts belonging to other
users.

Users who have logged in in the last year and who have a valid email
address will be given a one week warning. After that all accounts with
invalid names will be deleted. Anyone who has visited the site in the
last 6 months will have already seen a warning page that their name must
be changed to keep using the site.
2022-09-19 05:09:44 -05:00
evazion
2119a8efc5 mod actions: fix messages to use consistent format.
Fix mod actions to use the same message format everywhere.

Before mod actions were formatted in various inconsistent ways:

* "deleted post #1234"
* "comment #1234 updated by <user>"
* "<user> updated forum #1234"
* "<user> level changed Member -> Builder"

Now all mod actions consistently use this format:

* "deleted post #1234"
* "updated comment #1234"
* "updated forum #1234"
* "promoted <user> from Member to Builder"

This way mod actions are formatted consistently with other actions on
the /user_actions page, where everything is written as "<user> did X".

Also add a fix script to fix existing mod actions.
2022-09-18 21:56:57 -05:00
evazion
1d2bac7b95 Remove CurrentUser.ip_addr.
Remove the `CurrentUser.ip_addr` global variable and replace it with
`request.remote_ip`. Before we had to track the current user's IP in a
global variable so that when we edited a post for example, we could pass
down the user's IP to the model and save it in the post_versions table.
Now that we now longer save IPs in version tables, we don't need a global
variable to get access to the current user's IP outside of controllers.
2022-09-18 05:02:10 -05:00
evazion
d4da8499ce models: stop saving IP addresses in version tables.
Mark various `creator_ip_addr` and `updater_ip_addr` columns as ignored
and stop updating them in preparation for dropping them.
2022-09-18 03:49:17 -05:00
evazion
553d35178c Remove IpAddress model. 2022-09-17 23:30:13 -05:00
evazion
1ac56b72a7 emails: add proton.me to domain whitelist. 2022-09-16 06:01:44 -05:00
evazion
1e3fd3b724 artist finder: add pomf.tv to site blacklist. 2022-09-16 06:01:44 -05:00
evazion
abf493794f twitter: fix misparsing of https://twitter.com/i/status/:id urls.
Fix URLs like `https://twitter.com/i/status/943446161586733056` parsing
the username as `i`. This led to the new artist page recommending the
tag name `i` when creating an artist for a source like this.

Also fix these URLs not being normalized to `https://twitter.com/:username/status/:id` after upload.
2022-09-15 19:57:12 -05:00
evazion
0a5ebcc69d uploads: refactor media asset validation logic.
Refactor the upload validation logic to not depend on the current user.
Fixes several broken upload tests.
2022-09-15 05:09:07 -05:00
evazion
e2a3265daf mod dashboard: remove ip address search.
Remove the IP address search option from the /moderator/dashboard page.
This was an obsolete way of searching for sockpuppet accounts by IP.
The /user_events page should be used instead.
2022-09-13 00:14:19 -05:00
evazion
e1fb219738 aliases: retire artist aliases after 2 years.
Automatically retire artist aliases after they're more than 2 years old.
Before the rule was that artist aliases were only retired if they didn't
have any new posts in the last 2 years.
2022-09-12 02:31:12 -05:00
evazion
bb728ecebf tags: add /tag_versions page. 2022-09-11 18:41:16 -05:00
evazion
54a45a3021 tags: track tag histories.
Track the history of the tag `category` and `is_deprecated` fields in
the `tag_versions` table.

Adds generic Versionable and VersionFor concerns that encapsulate most
of the history tracking logic. These concerns are designed to make it
easy to add history to any model.

There are a couple notable differences between tag versions and other versions:

* There is no 1 hour edit merge window. All changes to the `category`
  and `is_deprecated` fields produce a new version in the tag history.

* New versions aren't created when a tag is created. Versions are only
  created when a tag is edited for the first time. The tag's initial
  version isn't created until *after* the tag is edited for the first time.

For example, if you change the category of a tag that was last updated
10 years ago, that will create an initial version of the tag backdated
to 10 years ago, plus a new version for your edit.

This is for a few reasons:

* So that we don't have to create new tag versions every time a new tag
  is created. This would be wasteful because most tags never have their
  category or deprecation status change.
* So that if you make a typo tag, your name isn't recorded in the tag's
  history forever.
* So that we can create new tags in various places without having to know
  who created the tag (which may be unknown if the current user isn't set).
* Because we don't know the full history of most tags, so we have to
  deal with incomplete histories anyway.

This has a few important consequences:

* Most tags won't have any tag versions. They only gain tag versions if
  they're edited.
* You can't track /tag_versions to see newly created tags. It only
  shows changes to already existing tags.
* Tag version IDs won't be in strict chronological order. Higher IDs may
  have created_at timestamps before lower IDs. For example, if you
  change the category of a tag that is 10 years old, that will create an
  initial version with a high ID, but with a created_at timestamp dated
  to 10 years ago.

Fixes #4402: Track tag category changes
2022-09-11 17:47:44 -05:00
evazion
c96bdd1766 autocomplete: fix ranking of exact matches.
Fix a bug where searching for `sakana~` ranked `sakana~_(meme)` beneath
random artist tags containing the word `sakana`. Now, if the search contains
punctuation, we rank exact matches first, even for small tags. Before we
ranked exact matches for small tags lower than inexact matches for large
tags. If the search contains punctuation, it's a strong signal the user
is looking for an exact match.
2022-09-09 15:58:48 -05:00
evazion
0cc76625eb Update Ruby gems and Yarn packages. 2022-09-07 03:13:13 -05:00
evazion
d2147eca80 tumblr: fix exception when fetching data for video urls.
Fix an exception when trying to fetch source data for URLs like
https://va.media.tumblr.com/tumblr_pgohk0TjhS1u7mrsl.mp4.

For these URLs it's not possible to use the trick where we try to open
the URL as a HTML page and scrape the post id from the HTML. Instead we
get the raw video if we try to to this.
2022-09-05 16:15:47 -05:00
evazion
f55951ab58 tumblr: fix exception when parsing mangled image urls.
Fix a nil exception when trying to parse invalid URLs like `https://25.media.tumblr.com/91719d337b218681abc48cdc24e`.
2022-09-05 16:15:46 -05:00
evazion
f83af31a00 autocomplete: fix usernames not being highlighted in @mentions.
Fix usernames not being highlighted when completing @mentions.

This also changes it so the autocomplete results don't include the '@'
in front of the name.

Minor breaking change to the /autocomplete.json API. Autocomplete
results for mentions now have the type `mention` instead of `user`.
2022-09-03 20:34:26 -05:00
evazion
03c02ef78e autocomplete: fix accidental API breakage.
Fix /autocomplete.json returning results like this:

    [
      {
        "table": {
          "type": "tag-word",
          "label": "long hair",
          "value": "long_hair",
          "category": 0,
          "post_count": 2818211,
          "antecedent": null
        }
      },
    ]

instead of like this:

    [
      {
        "type": "tag-word",
        "label": "long hair",
        "value": "long_hair",
        "category": 0,
        "post_count": 2818211,
      },
    ]

Also change it so that optional attributes like `antecedent` aren't
returned if they're null. This could be a minor breaking change if users
rely on these attributes always being present.

Lastly change XML results to look like this:

    <autocomplete-service-results type="array">
      <autocomplete-service-result>
        <type>tag-word</type>
        <label>long hair</label>
        <value>long_hair</value>
        <category type="integer">0</category>
        <post-count type="integer">2801117</post-count>
      </autocomplete-service-result>
    </autocomplete-service-results>

instead of like this:

    <objects type="array">
      <object>
        <type>tag</type>
        <label>hair ornament</label>
        <value>hair_ornament</value>
        <category type="integer">0</category>
        <post-count type="integer">883340</post-count>
        <antecedent nil="true"/>
      </object>
    </objects>
2022-09-03 18:53:29 -05:00