Commit graph

6109 commits

Author SHA1 Message Date
Suzanne Aitchison
54eaee899e
Update tag search and suggest (#15810)
* add needed fields to tag search

* specs

* add badge to tags suggest response (#15840)

* Add badge to tags suggest response

* Update tags_spec.rb

Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>

* limit tag search badge to only badge_image

* only unpack badge image if badge exists

Co-authored-by: Dwight Scott <dwight@forem.com>
Co-authored-by: Jeremy Friesen <jeremy.n.friesen@gmail.com>
2022-01-05 07:51:17 +00:00
Arit Amana
4e8cd50952
require tag (#15955) 2022-01-04 20:51:21 -05:00
Michael Kohl
ec9a5adcb5
Remove store_attribute gem (#15923) 2022-01-04 11:15:57 +07:00
Daniel Uber
349ba97a5e
Send custom user agent string when fetching feeds (#15942)
* Send custom user agent string when fetching feeds

dev.to blocks access to the feeds for user agent "Ruby"

Use "Forem Feeds Import" as alternate user agent when fetching feeds.

fixes https://github.com/forem/forem/issues/15939

* Update app/services/feeds/import.rb

Co-authored-by: Michael Kohl <citizen428@forem.com>

Co-authored-by: Michael Kohl <citizen428@forem.com>
2022-01-03 22:09:24 -06:00
Jeremy Friesen
6269aa4108
Proposing a new feed experiment (#15789)
* Proposing a new feed experiment

This commit involves tweaking a few subtle aspects of the challenger:

1) Remove the sort by published date on the relevance score.  The
   articles will now be listed in the order of perceived relevance.
2) Disable a few levers that did little.  There really aren't any
   `articles.featured = true` articles in the database.
3) Gently increase the weight of each comment in a linear manner.
4) Give a little more weight to posts that don't have followed tags.

In addition, it involves renaming the conversions to better convey their
implementation.  This renaming helps create more descriptive labels for
the test results at `/admin/abtests`.

This commit also adds logic to repurpose the AbExperiment feed_strategy
logic; I envision adjusting the weighted feed strategy levers with some
frequency.

Per discussions, I'm also disabling the "not logged in" feed testing.
We'll use the LargeForemExperimental for this.

* Flipping attribute name to positive

Mentally "not disabled" is harder to parse than "enabled".  This change
helps with setting an optimistic attribute.

* Bump for travis

* Bump for travis

* Removing file committed by mistake

* Extracting method
2022-01-03 14:38:14 -05:00
Jeremy Friesen
72b89aa57c
Updating documentation for LiquidTagBase (#15933)
In addition adding a Rubocop auto-update.
2022-01-03 14:34:37 -05:00
Michael Kohl
7326744313
Remove Storext gem (#15907) 2022-01-03 22:21:04 +07:00
Michael Kohl
466f111712
Remove sign in/up link from navlinks (#15922)
* Remove sign in/up link from navlinks

* Remove translations

* Update spec
2022-01-03 22:20:29 +07:00
Derek Crosson
ae27d15be7
Prevent whitespace unicode characters from being used in article title and tag (#14909)
* test: whitespace unicode characters cannot be used as titles or tags

* feat: add localized error message

* refactor: use localized error message

* fix: whitespace unicode characters cannot be used as titles or tags

* chore: fix locale after merge

* refactor: fix indentation

* Fix spelling of prohibited in method names

Additionally, rubocop removed a redundant user_id validation since
Article belongs to user.

* Fix spelling

Missed one method call on the same line (fixed the first of two
instances needing changes)

* add failing test cases

The reorganization to remove let was due to a limit on nesting rspec
contexts (it inside context inside describe inside describe when
trying to use different titles in let blocks in contexts)

The first test was clarified (the "U+202D" string that looks like the
code for a unicode point is valid, but the character \u202d is
expected to be invalid

The remaining two tests are based on the feedback I'd given in the PR,
failing because we don't assert title is present after removing
unicode whitespace, and we don't actually set the title (gsub is
non-destructive, returning a value).

* Set title to sanitized title

contains_prohibitied_unicode_characters? was using == (which is not a
good match for strings to regexes), use =~ instead

Change invalid example characters in titles from \u202d (bidi override) to
\u200a (hair space)

Assert that the empty title is blank and can't be blank (even though
it contains no prohibitd characters after replacement)

* change the definition of prohibited characters

From the description, it looks like "unicode space characters" was the
desired rejection set. It was unclear what the existing regex was
matching on (I couldn't get the expected examples to match correctly).

Given the intent, I select "unicode space property, except the ascii
space character", which may _also_ be incorrect but passed the tests.

* Remove now-invalid expectation for presence of user id

Since this was redundant (belongs_to user), we no longer will validate
presence of user id, and we should not test for it.

* Only reject bidirectional text controls

The original issue pointed to the BIDI controls as problematic.

While they called out other "whitespace" characters, as we accept a
wider range of input languages, the need to handle non-printing
punctuation (for example a group separating space for some asian
languages).

It's possible a wider list of characters should be added - if that's
the case I suggest this regex and the sanitization be moved to a
standalone class, and each of the recommendations in
https://www.w3.org/TR/unicode-xml/#Charlist be checked for
applicability.

* Check for blank title after sanitizing disallowed characters

Since validate_title modifies the title, and doesn't set error
messages on the model itself, check for non-empty title _after_
potentially removing any disallowed characters.

* Remove invalid characters before validation

Remove the validate_title and contains_prohibited_characters methods,
and center all logic on the remove prohibitied unicode characters
method.

Remove unneeded and unused arguments (input string is always title,
replacement is always removal/empty string).

* handle case when validating null title

If the title's nil, we don't want to call match? since it will fail.

Title will sometimes be nil when validating.

Co-authored-by: Ben Halpern <bendhalpern@gmail.com>
Co-authored-by: Dan Uber <dan@forem.com>
2022-01-03 09:37:03 -05:00
Jeremy Friesen
5430c2e203
Refactoring and documenting class (#15849)
* Refactoring and documenting class

I came to this commit by looking for raw `where` method calls to the
Article object.  Originally, I was looking at `where.not(user_id: :id)`
but found this service class.

In this class, I saw some significant duplication and went with a bit of
refactoring and some documentation.

This refactoring removed some unnecessary guards and calculations (as
documented inline).

* Naming a constant and adding more documentation
2022-01-03 09:30:41 -05:00
Jeremy Friesen
87f7184b45
Favor repurposing existing scope (#15914)
* Favor repurposing existing scope

In addition to using the existing scope, I went ahead and added some
documentation.

* Fixing comment
2022-01-03 09:30:34 -05:00
Jeremy Friesen
4a03b4aaa8
Renaming/rearranging constants for clarity (#15911)
* Renaming/rearranging constants for clarity

As I'm writing about the Unified Embed project and creating
documentation for the upcoming Forem Fest, I realized that the naming
convention created confusion.

This commit is an effort to tidy up that confusion.

* Normalizing implementation pattern
2022-01-03 09:30:11 -05:00
Michael Kohl
f886c758bd
Add crayons_icon_tag helper (#15878) 2022-01-03 09:52:05 +07:00
Andy Zhao
89f3dbb786
Update trusted user welcome email copy with Discord link (#15903)
* Update trusted user welcome email copy with Discord link

* Only show Discord link for dev.to

* Add accidentally removed line oops
2021-12-30 12:28:15 -05:00
Daniel Uber
cd835c281f
Periodic Digest Email frequency should be in days (#15904)
* Use the most recent timestamp from sent digest messages

The original logic here had queried for up to 10 (unordered) messages
from the database for this user, perhaps leveraging the knowledge that
we purge old messages after 90 days to ensure 10 was enough.

Since the last message in the limited and unordered list could have
had any sent_at time in the past 90 days, it's possible users could
pass the "should send email" check multiple times in a day (and get
multiple digest emails, I'm not certain when the rake task runs but it
could be as frequently as once per deployment?)

Since we're only using this list of messages to find the most recently
sent message, select the maximum sent time.

* convert periodic_email_digest setting to days

Time.current - last_email_sent_at gives an integer count of elapsed
seconds

Settings::General.periodic_email_digest is an integer count of days
between digests.

Convert to days (so we're only sending digests when enough time has
elapsed) instead of defaulting to once every 2 seconds (or at least
several times per day).

* Clean up comparison

Rather than subtracting the current time from the last time, and
checking that the difference in time is greater than the periodic
setting, use days.ago to get the timestamp far enough in the past, and
ensure last email was sent before that ( `sent_at < n.days.ago` ).

This seems like it reads clearer than the original implementation.

* Prefer Time.before? to numeric comparison

Change the method name from last_email_sent_at to last_email_sent, so
that the comparison reads like English (the other callers use it as a
number)

And since the code reveals its intention clearly, there's no need for
an inline comment about the next line any more.
2021-12-30 09:23:24 -06:00
yheuhtozr
3acd4e9deb
delete and add back i18n keys (#15905) 2021-12-30 10:12:40 -05:00
Jeremy Friesen
973ff20eb8
Refactoring to consolidate logic (#15851)
* Refactoring to consolidate logic

Prior to this commit, two controllers had nearly identical chunks of
logic.  This refactor extracts the logic to a common and more canonical
location.

* Addressing rubocop's aggressive auto-fix

* Adding spat operator for pluck

* Renaming method for greater clarity
2021-12-29 11:08:04 -05:00
Daniel Uber
ba590c1750
Load article before creating a page view for an invalid one (#15898)
* Load article before creating a page view for an invalid one

This should resolve a validation error in PageView.create! when the
article does not exist (perhaps it was deleted, or the user suspended,
or it was invalid data sent from the client).

This had been happening dozens of times per day for the last 10
months.

* Use an intention revealing symbol instead of calculated id

Since we only require that find_by not find anything, pass a symbol
that's not going to be the id for any article under any conditions.

As an added benefit, this provides a clear indication of the purpose
of the symbol, without needing to mentally confirm that

```sql
  SELECT * FROM articles
  WHERE id IN (
    SELECT (1 + MAX(id)) FROM articles
  ) LIMIT 1
```

actually never gives any articles back.
2021-12-29 09:48:34 -06:00
Jeremy Friesen
f2cf46d426
Refactoring to leverage an Article scope (#15850)
* Refactoring to leverage an Article scope

I've been looking at the queries that involve filtering articles based
on scores.  There's several places that seem to be close to consistent,
but have some nuanced difference.

This refactor consolidates one of those "almost the same" cases.

* Bump for travis
2021-12-29 09:25:31 -05:00
Mac Siri
0b0902d81d
Add catch to trackFifteenSecondsOnPage() (#15886) 2021-12-28 12:44:36 -05:00
Michael Kohl
5da625cadb
Rubocop fixes (#15892)
* Add missing spaces

* Use filter_map over map + reject/compact

* Simplify FactoryBot calls

* Use to_h with block instead of map + to_h

* Use guard clause
2021-12-28 09:11:41 -06:00
Michael Kohl
06d6b68d9d
Remove webhooks and related code (#15827) 2021-12-28 10:56:37 +07:00
Daniel Uber
648062d69a
Require parameter string before matching time for youtube video ids (#15890)
We are seeing test failures when an id like "aaabbbcccdd1m" match with
"1m" as the time parameter. I think we only want to match the time
when a ?t= or ?start= (and the permissive &t= or &start=, which might
only be part of the larger REGISTRY_REGEXP and either not effective or
not needed for the video id pattern)

The goal here is these should be valid

"aaabbbcccdd"
"aaabbbcccdd?t=1"
"aaabbbcccdd?start=1h23m55s"
"aaabbbcccdd&t=1"

but not these

"aaabbbcccddt=1"
"aaabbbcccddstart=1"
"aaabbbcccdd123456h"

The same logic may be appropriate to backfill into the prior regexp as
well, my immediate concern is with randomly generated 12-15 character
strings from Fake getting matched during testing (where they were
expected to raise an error during id parsing).
2021-12-27 15:04:40 -06:00
Daniel Uber
c879c5b3d8
Remove slash characters from user name search term (#15867)
* Remove slash characters from user supplied user search input

Prevents an error when the search term includes '\'

    PG::SyntaxError: ERROR:  syntax error in tsquery

https://app.honeybadger.io/projects/66984/faults/79391397

I had originally thought to add this cleanup to Search::Username but
decided to move it as close to the generated (invalid) query as
possible to prevent alternate paths finding their way here.

* Add spec

Since there's no existing tests for the scope - I put the test code on
the caller (Search::Username) rather than the model (User), this seems reasonable.

* When the term is empty (or only slashes) just return null relation

* Handle nil input (search for nothing) correctly

One of the request specs sends a username search with no query, so we
can't call nil.delete or nil.empty?, use blank? of empty?
2021-12-27 12:30:36 -06:00
Daniel Uber
4d40ea18a2
When returning a json error, don't use a raw unescaped string (#15879)
The 400 and 403 error pages show json parsing errors in most browsers,
since "Error: Bad Request" is not a valid json body ('"Error: Bad
Request"' would be, or the object with key error and value of the
string which I've selected is _also_ valid).

Do we have frontend code that's looking for this body before it parses
for some reason, or was this just a mistaken copying from the api
controller in the initial PRs (#2293 for not_authorized, and #6248 for
the bad_request method, which may have just replicated the decision
for not_authorized)?
2021-12-27 11:39:15 -06:00
Jeremy Friesen
0b8c09851c
Favoring constant over magic string (#15852) 2021-12-27 11:31:29 -05:00
dependabot[bot]
2d98e3346b
Bump rubocop from 1.23.0 to 1.24.0 (#15872)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Kohl <me@citizen428.net>
2021-12-27 10:04:59 -05:00
Jeremy Friesen
c7902a3261
Favor destroy_by over where.first&.destroy (#15868)
* Favor `destroy_by` over `where.first&.destroy`

We can use [ActiveRecord::Relation.destroy_by][1] instead of the method
chain.

[1]:https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-destroy_by

* Restoring custom behavior

Those constants are not ActiveRecord::Base objects, so don't implement
the method.
2021-12-22 23:55:52 -05:00
Jeremy Friesen
6d37f4303c
Favoring single SQL and computation over enum (#15865)
* Favoring single SQL and computation over enum

Prior to this commit, we ran a SQL statement to generate a list of
articles then applied some minor randomization.

With this commit, I'm removing the randomization in favor of expected
values, and collapsing the result set into a single inline SQL and some
post query simple arithmatic.

Let's check my math.  For each article we sum:

* `article.score`
* `article.comments_count` * 14
* a random number between 0 and 5 (`rand(6)`) with expected value of 2.5
* the tag's (taggings_count + 1) / 2

The new SQL query sums the `article.score` and the
`article.comments_count` * 14.  I then reduce the remainder of the
equation.  From "for each article sum `(rand(5) + (taggings_count + 1) /
2)`" we have the following:

`article_count * (2.5 + (taggings_count + 1) / 2)`

Which is equivalent to:

`article_count * ((taggings_count + 1 + (2.5 * 2)) / 2)`

Which is equivalent to:

`article_count * ((taggings_count + 6) / 2)`

This change reduces computation time by favoring expected values.

* Fixing broken behavior and adding comments
2021-12-22 14:59:37 -05:00
Mac Siri
ea037a127a
Add catch to trackAdImpression() (#15864) 2021-12-22 13:12:37 -05:00
Josh Puetz
10c13078cb
User correct username in mobile mention notifications (#15861) 2021-12-22 10:31:22 -06:00
Jeremy Friesen
c75dd69d09
Favoring common method not previously available (#15855)
With this refactor, I'm adding documentation and favoring using a common
method that wasn't available at the time of implementation.

In [this commit][1] we had logic that said "if we have a singular tag
use the cache" otherwise use the join.  At that time, the implementation
of [Article.cached_tag_with][previous] was as follows, allowing only a
singular tag:

```ruby
scope :cached_tagged_with, ->(tag) { where("cached_tag_list ~* ?",
"^#{tag},| #{tag},|, #{tag}$|^#{tag}$") }
```

The [current implementation][current], as of writing this, allows for
multiple tags and is as follows:

```ruby
scope :cached_tagged_with, lambda { |tag|
  case tag
  when String, Symbol
    # In Postgres regexes, the [[:<:]] and [[:>:]] are equivalent to "start of
    # word" and "end of word", respectively. They're similar to `\b` in Perl-
    # compatible regexes (PCRE), but that matches at either end of a word.
    # They're more comparable to how vim's `\<` and `\>` work.
    where("cached_tag_list ~ ?", "[[:<:]]#{tag}[[:>:]]")
  when Array
    tag.reduce(self) { |acc, elem| acc.cached_tagged_with(elem) }
  when Tag
    cached_tagged_with(tag.name)
  else
    raise TypeError, "Cannot search tags for: #{tag.inspect}"
  end
}
```

Given that we are content to use the cached tag in the singular tag
case, it seems safe to say that we're comfortable using it in the
multiple tag case.

[1]:af5a391429 (diff-24503fd25ed68e6ebceee4951bc4f9b255b197278d4aa9d86ef9d5afe3f26bea)
[previous]:af5a391429/app/models/article.rb (L151)
[current]:98e97e7aa8/app/models/article.rb (L212-L227)
2021-12-22 11:00:54 -05:00
Jeremy Friesen
a40efc6bbd
Refactoring questions asked of user (#15762)
* Refactoring questions asked of user

In this pull request, I'm extracting and normalizing role-based
questions asked of the user.

Prior to this commit, our codebase has asked two very similar questions
of our user model:

- `user.has_role?(:admin)`
- `user.admin?`

In asking `has_role?(:admin)` we are relying on implementation details
of the rolify gem.  In addition, the `has_role?` question asked
throughout controllers or views means that it's harder to create
hieararchies of permissions.

In favoring `user.admin?` as our question, we can use that indirection
as an opportunity to discuss and decide "Should someone with the
`:super_admin` role be `user.admin? == true`?"

The details of this commit is to do three primary things:

1. Ask the `has_role?` questions in "one place" in the code (e.g. the
   `Authorizer` module)
2. Extract the role based questions that are on the `User` model and
   provde backwards compatable delegation.
3. Structure the code so that it's harder to accidentally call
   `user.has_role?` (e.g., make `User#has_role?` and `User#has_any_role?`
   private).

This is related to #15624 and the updates are informed by discussion in
PR #15691.  This commit supplants #15691.

* Refactoring the liquid tag policy tests

* Fixing typo

* Bump for travis
2021-12-21 12:45:12 -05:00
Suzanne Aitchison
5fd1f94e85
Add comment notification test to Cypress, including a11y improvements to notification page (#15842)
* changes to accessible names on notification comment box, cypress specs

* add reply to comment test, delete old specs

* tweak to seeds
2021-12-21 15:42:13 +00:00
dependabot[bot]
c2659d38df
Bump eslint-config-preact from 1.2.0 to 1.3.0 (#15834)
* Bump eslint-config-preact from 1.2.0 to 1.3.0

Bumps [eslint-config-preact](https://github.com/preactjs/eslint-config-preact) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/preactjs/eslint-config-preact/releases)
- [Commits](https://github.com/preactjs/eslint-config-preact/compare/1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: eslint-config-preact
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* apply new lint fixes after version bump

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
2021-12-21 14:42:20 +00:00
yheuhtozr
5253717797
fix character limits i18n (#15830) 2021-12-21 07:15:21 -05:00
Michael Kohl
872b007c30
Remove Doorkeeper gem (#15749) 2021-12-21 12:29:58 +07:00
yheuhtozr
021ed9708e
restore i18n key _footer.html.erb (#15831) 2021-12-20 16:33:41 -06:00
Daniel Uber
032378b3bd
Disable dash replacement filter in markdown fixer (#15839)
* Remove modify_hr_tags method definition

this breaks things that call it.

* Remove direct uses of modify_hr_tags fixer method

Remove from class METHODS lists, remove test cases about this behavior, and remove from
methods lists in test cases.

* Remove hr tag modification spec

Still don't understand what problem this was fixing, but I've removed
the test case.
2021-12-20 15:39:55 -06:00
ludwiczakpawel
d125617e13
CSS Variables cleanup (#15828)
* variables cleanup

* little fix
2021-12-20 15:42:40 +01:00
ludwiczakpawel
5e098df276
fix (#15829) 2021-12-20 13:38:29 +01:00
Anna Buianova
9f688be406
Moved setting old_username from before_validation to Users::Update (#15782)
* Moved setting old_username from before_validation to Users::Update

* Removed unused code

* Fixed specs after moving setting old_usernames
2021-12-20 11:08:09 +03:00
ludwiczakpawel
9b7503fde3
Update left sidebar links with new Crayons components (#15757)
* update

* hamburger fix

* Update app/views/shared/_hamburger.html.erb

Co-authored-by: Suzanne Aitchison <suzanne@forem.com>

* updates

* spec

* social media icons

* reading list counter

Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
2021-12-20 08:46:30 +01:00
Andy Zhao
8a7e5a5566
Escape periods and display the group name properly (#15819)
* Escape periods and display the group name properly

* Rename method to dom_safe_name and move to admin helper

* Replace beginning of string digit with underscore then digit
2021-12-17 17:35:42 -05:00
Andy Zhao
6335f9a7e9
Remove profile when banish (#15818)
* Clear profile when banishing user

* Add test for clearing profile when banishing

* Also clear any social usernames
2021-12-17 15:36:35 -05:00
Arit Amana
dee0b36981
Implement Unified Embed for CodeSandbox URLs (#15808)
* Implement embed and write specs

* fix slight regex ommision

* remove error over invalid options; add specs to cover

* account for missing options when using embed keyword

* no videos, no ns video_id 🤦🏾‍♀️
2021-12-17 12:12:54 -05:00
Daniel Uber
4b93684dab
Don't raise error when empty community emoji setting submitted (#15723)
* Don't raise NoMethodError when validating emoji settings

Cargo culted the unique cross model slug validator setup (which is why
value is called name here in the validatable).

Don't raise an error when validating a nil emoji, and assert nil and
empty string are permitted values.

This expects no non-emoji characters (so an empty string would be
permitted), and nil should be permitted.

I believe this might have been introduced by the string settings
cleaner (exchanging "" for nil in form inputs).

* Update app/validators/emoji_only_validator.rb

guard for value, and then validate value
2021-12-17 09:29:55 -06:00
Alex
51f2bf7aab
Add article and comment count to instance API (#15794) 2021-12-16 13:05:49 -05:00
Arit Amana
5c13d9d8fd
Implement embed tag and specs (#15801) 2021-12-16 12:58:43 -05:00
Jeremy Friesen
aa7712a80e
Extracting container for allowed tags & attrs (#15338)
* Extracting container for allowed tags & attrs

Prior to this commit, we had several different locations in which we
specified ALLOWED_TAGS and ALLOWED_ATTRIBUTES for HTML rendering and
sanitization.

Curious to see how these either intersected or didn't, I opted to
create a container module that allows for us to more readily normalize
these allowed tags and attributes.  It's possible that we won't do any
normalization, but this work helps make that easier.

Ideally, I'd love us to contextualize "why did we choose the
tags/attributes we chose?"  But for now, I think consolidating these
tags and attributes will help make adding a `details` and `summary` tag
easier.

This relates to forem/rfcs#296

See [Google Sheet][1] for analysis of what tags/attributes are used, the
intersection and union.

[1]:https://docs.google.com/spreadsheets/d/1yj-a1qus1o0o4cj-_gOMP5yteeg-_f3s5z7kvK0Y7RM/edit#gid=0

* Fixing misnamed constant

* Fixing misnamed constant

* Extracting additional HtmlRendering use cases

* Adding comparative documentation for HTML tags

* Fixing broken parameter signature

* Moving constants into MarkdownProcessor
2021-12-16 12:24:45 -05:00