Commit Graph

9861 Commits

Author SHA1 Message Date
Krzysztof Kotlarek
c0c525056f
UX: number site setting validation message (#24303)
Format big numbers validation to easy to read format like 1,000,000
2023-11-10 14:27:35 +11:00
Martin Brennan
731dffdf92
DEV: Align S3 transfer acceleration global settings (#24302)
Followup to fe05fdae24

For consistency with other S3 settings, make the global setting
the same name as the site setting and use SiteSetting.Upload
too so it reads from the correct place.
2023-11-10 09:50:23 +10:00
Andrei Prigorshnev
d91456fd53
DEV: Ability to collect stats without exposing them via API (#23933)
This adds the ability to collect stats without exposing them 
among other stats via API.

The most important thing I wanted to achieve is to provide 
an API where stats are not exposed by default, and a developer 
has to explicitly specify that they should be 
exposed (`expose_via_api: true`). Implementing an opposite 
solution would be simpler, but that's less safe in terms of 
potential security issues. 

When working on this, I had to refactor the current solution. 
I would go even further with the refactoring, but the next steps 
seem to be going too far in changing the solution we have, 
and that would also take more time. Two things that can be 
improved in the future:
1. Data structures for holding stats can be further improved
2. Core stats are hard-coded in the About template (it's hard 
to fix it without correcting data structures first, see point 1):
    63a0700d45/app/views/about/index.html.erb (L61-L101)

The most significant refactorings are:
1. Introducing the `Stat` model
2. Aligning the way the core and the plugin stats' are registered
2023-11-10 00:44:05 +04:00
Bianca Nenciu
bdb81b5346
DEV: Use a single registry for preloaded category custom fields (#24272)
There was a registry for preloaded site categories and a new one has
been introduced recently for categories serialized through a
CategoryList.

Having two registries created a lot of friction for developers and this
commit merges them into a single one, providing a unified API.
2023-11-09 18:23:24 +02:00
Martin Brennan
e772bb9fce
DEV: Allow new_features URL to be configurable (#24306)
This is so the new features plugin can be tested
easier locally.
2023-11-09 13:50:21 +10:00
Krzysztof Kotlarek
e5e7dd78c2 Bump version to v3.2.0.beta4-dev 2023-11-09 14:25:44 +11:00
Krzysztof Kotlarek
95b7cbd8a1 Bump version to v3.2.0.beta3 2023-11-09 14:25:44 +11:00
Ted Johansson
95a82d608d SECURITY: Prevent Onebox cache overflow by limiting downloads and URL lengths 2023-11-09 13:39:18 +11:00
Martin Brennan
3c5fb871c0 SECURITY: Filter unread bookmark reminders the user cannot see
There is an edge case where the following occurs:

1. The user sets a bookmark reminder on a post/topic
2. The post/topic is changed to a PM before or after the reminder
   fires, and the notification remains unread by the user
3. The user opens their bookmark reminder notification list
   and they can still see the notification even though they cannot
   access the topic anymore

There is a very low chance for information leaking here, since
the only thing that could be exposed is the topic title if it
changes to something sensitive.

This commit filters the bookmark unread notifications by using
the bookmarkable can_see? methods and also prevents sending
reminder notifications for bookmarks the user can no longer see.
2023-11-09 13:39:16 +11:00
Roman Rizzi
d78357917c SECURITY: Onebox templates' HTML injections.
The use of triple-curlies on Mustache templates opens the possibility for HTML injections.
2023-11-09 13:39:11 +11:00
Régis Hanol
7d484864fe SECURITY: escape display names
Ensure we escape the display names before passing it to the regexp used to update
quotes whenever a user change their display name.
2023-11-09 13:39:06 +11:00
Martin Brennan
67ac4c5616
DEV: Move min_trust_level_for_here_mention to group setting (#24263)
c.f. https://meta.discourse.org/t/-/283408
2023-11-09 12:07:01 +10:00
Martin Brennan
fe5383dbff
FIX: Prevent invalid tos and privacy URLs in cache (#24291)
Followup to 5fc1586abf

There are certain cases where the tos_url and privacy_policy_url
can end up with a "nil" value in the Discourse.urls_cache.

The cause of this is unclear, but it seems to behave differently
between doing this caching in the rails console and the running
server.

To avoid this we can just not store anything that looks like nil
in the cache; we can delete the cache keys entirely if we don't
need them anymore.
2023-11-09 10:33:38 +10:00
Andrei Prigorshnev
be2eb3df44
FIX: user got notified about a mention inside a chat message quote (#24229)
When quoting a chat message in a post, if that message contains a mention, 
that mention should be ignored. But we've been detecting them and sending 
notifications to users. This PR fixes the problem. Since this fix is for 
the chat plugin, I had to introduce a new API for plugins:

    # We strip posts before detecting mentions, oneboxes, attachments etc. 
    # We strip those elements that shouldn't be detected. For example, 
    # a mention inside a quote should be ignored, so we strip it off. 
    # Using this API plugins can register their own post strippers. 
    def register_post_stripper(&block) 
    end
2023-11-08 23:13:25 +04:00
Bianca Nenciu
277496b6e0
FIX: Replace watched words with wildcards (#24279)
These have been broken since fd07c943ad
because watched words were not correctly transformed to regexps.
This partially reverts the changes.
2023-11-08 18:51:11 +02:00
Mark VanLandingham
e3f8e9c0fb
DEV: Email notification filter plugin API (#24271) 2023-11-08 10:29:00 -06:00
David Taylor
0889f22a3b
FIX: Correctly map fingerprinted vendor.js file in production (#24267)
Followup to a0b94dca16
2023-11-07 11:34:19 +00:00
David Taylor
a0b94dca16
DEV: Use WebPack stats plugin to map entrypoints to chunks (#24239)
Previously, we were parsing webpack JS chunk filenames from the HTML files which ember-cli generates. This worked ok for simple entrypoints, but falls apart once we start using async imports(), which are not included in the HTML.

This commit uses the stats plugin to generate an assets.json file, and updates Rails to parse it instead of the HTML. Caching on the Rails side is also improved to avoid reading from the filesystem multiple times per request in develoment.

Co-authored-by: Godfrey Chan <godfreykfc@gmail.com>
2023-11-07 10:24:49 +00:00
Martin Brennan
b90b7ac705
DEV: Move shared_drafts_min_trust_level to group setting (#24257)
No plugins or themes rely on shared_drafts_min_trust_level so we
can just switch straight over to shared_drafts_allowed_groups

c.f. https://meta.discourse.org/t/changes-coming-to-settings-for-giving-access-to-features-from-trust-levels-to-groups/283408
2023-11-07 14:03:25 +10:00
Martin Brennan
fe05fdae24
DEV: Introduce S3 transfer acceleration for uploads behind hidden setting (#24238)
This commit adds an `enable_s3_transfer_acceleration` site setting,
which is hidden to begin with. We are adding this because in certain
regions, using https://aws.amazon.com/s3/transfer-acceleration/ can
drastically speed up uploads, sometimes as much as 70% in certain
regions depending on the target bucket region. This is important for
us because we have direct S3 multipart uploads enabled everywhere
on our hosting.

To start, we only want this on the uploads bucket, not the backup one.
Also, this will accelerate both uploads **and** downloads, depending
on whether a presigned URL is used for downloading. This is the case
when secure uploads is enabled, not anywhere else at this time. To
enable the S3 acceleration on downloads more generally would be a
more in-depth change, since we currently store S3 Upload record URLs
like this:

```
 url: "//test.s3.dualstack.us-east-2.amazonaws.com/original/2X/6/123456.png"
```

For acceleration, `s3.dualstack` would need to be changed to `s3-accelerate.dualstack`
here.

Note that for this to have any effect, Transfer Acceleration must be enabled
on the S3 bucket used for uploads per https://docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration-examples.html.
2023-11-07 11:50:40 +10:00
Godfrey Chan
9a1695ccc1
DEV: remove markdown-it-bundle and custom build code (#23859)
With Embroider, we can rely on async `import()` to do the splitting
for us.

This commit extracts from `pretty-text` all the parts that are
meant to be loaded async into a new `discourse-markdown-it` package
that is also a V2 addon (meaning that all files are presumed unused
until they are imported, aka "static").

Mostly I tried to keep the very discourse specific stuff (accessing
site settings and loading plugin features) inside discourse proper,
while the new package aims to have some resembalance of a general
purpose library, a MarkdownIt++ if you will. It is far from perfect
because of how all the "options" stuff work but I think it's a good
start for more refactorings (clearing up the interfaces) to happen
later.

With this, pretty-text and app/lib/text are mostly a kitchen sink
of loosely related text processing utilities.

After the refactor, a lot more code related to setting up the
engine are now loaded lazily, which should be a pretty nice win. I
also noticed that we are currently pulling in the `xss` library at
initial load to power the "sanitize" stuff, but I suspect with a
similar refactoring effort those usages can be removed too. (See
also #23790).

This PR does not attempt to fix the sanitize issue, but I think it
sets things up on the right trajectory for that to happen later.

Co-authored-by: David Taylor <david@taylorhq.com>
2023-11-06 16:59:49 +00:00
David Taylor
c5e6e271a5
DEV: Remove legacy /brotli_asset workaround (#24243)
When Discourse first introduced brotli support, reverse-proxy/CDN support for passing through the accept-encoding header to our NGINX server was very poor. Therefore, a separate `/brotli_assets/...` path was introduced to serve the brotli assets. This worked well, but introduces additional complexity and inconsistencies.

Nowadays, Brotli encoding is well supported, so we don't need the separate paths any more. Requests can be routed to the asset `.js` URLs, and NGINX will serve the brotli/gzip version of the asset automatically.
2023-11-06 15:57:00 +00:00
David Taylor
82d6d691ee
DEV: Refactor discovery routes to remove use of 'named outlets' (#22622)
The motivation of this PR is to remove our dependence on Ember's 'named outlets', which are removed in Ember 4+.

At a high-level, the changes can be summarized as:

- The top-level `discovery` route is totally emptied of all logic. The HTML structure of the template is moved into the `<Discovery::Layout />` component for use by child routes.

- `AbstractTopicRoute` and `AbstractCategoryRoute` routes now both lean on the `DiscoverySortableController` and associated template. This controller is where most of the logic from the old top-level `discovery` controller has ended up.

- All navigation controllers/templates have been replaced with components. `navigation/categories`, `navigation/category` and `navigation/default` were very similar, and so they've all been combined into `<Navigation::Default>`. `navigation/filter` gets its own component.

- The `discovery/topics` controller/template have been moved into a new `<Discovery::Topics>` component.

Various other parts of the app have been tweaked to support these changes, but I've tried to keep that to a minimum.

Anything from `<TopicList>` down is untouched, which should hopefully mean that a large proportion of topic-list-customizing themes are unaffected.

For more information, see https://meta.discourse.org/t/282816
2023-11-06 10:39:31 +00:00
Martin Brennan
baeac8f105
FIX: Do not log client site setting deprecations (#24237)
For deprecated site settings, we log out a warning when
the old setting is used. However when we convert all the client
settings to JSON, we are creating a lot of log noise like this:

> Deprecation notice: `SiteSetting.anonymous_posting_min_trust_level` has been deprecated.

We don't need to do this because we are just dumping the JSON.
2023-11-06 16:36:20 +10:00
Ted Johansson
c3708c4276
DEV: Add support for custom retries for scheduled admin checks (#24224)
We updated scheduled admin checks to run concurrently in their own jobs. The main reason for this was so that we can implement re-check functionality for especially flaky checks (e.g. group e-mail credentials check.)

This works in the following way:

1. The check declares its retry policy using class methods.
2. A block can be yielded to if there are problems, but before they are committed to Redis.
3. The job uses this block to either a) schedule a retry if there are any remaining or b) do nothing and let the check commit.
2023-11-06 08:57:02 +08:00
Osama Sayegh
3cadd6769e
FEATURE: Theme settings migrations (#24071)
This commit introduces a new feature that allows theme developers to manage the transformation of theme settings over time. Similar to Rails migrations, the theme settings migration system enables developers to write and execute migrations for theme settings, ensuring a smooth transition when changes are required in the format or structure of setting values.

Example use cases for the theme settings migration system:

1. Renaming a theme setting.

2. Changing the data type of a theme setting (e.g., transforming a string setting containing comma-separated values into a proper list setting).

3. Altering the format of data stored in a theme setting.

All of these use cases and more are now possible while preserving theme setting values for sites that have already modified their theme settings.

Usage:

1. Create a top-level directory called `migrations` in your theme/component, and then within the `migrations` directory create another directory called `settings`.

2. Inside the `migrations/settings` directory, create a JavaScript file using the format `XXXX-some-name.js`, where `XXXX` is a unique 4-digit number, and `some-name` is a descriptor of your choice that describes the migration.

3. Within the JavaScript file, define and export (as the default) a function called `migrate`. This function will receive a `Map` object and must also return a `Map` object (it's acceptable to return the same `Map` object that the function received).

4. The `Map` object received by the `migrate` function will include settings that have been overridden or changed by site administrators. Settings that have never been changed from the default will not be included.

5. The keys and values contained in the `Map` object that the `migrate` function returns will replace all the currently changed settings of the theme.

6. Migrations are executed in numerical order based on the XXXX segment in the migration filenames. For instance, `0001-some-migration.js` will be executed before `0002-another-migration.js`.

Here's a complete example migration script that renames a setting from `setting_with_old_name` to `setting_with_new_name`:

```js
// File name: 0001-rename-setting.js

export default function migrate(settings) {
  if (settings.has("setting_with_old_name")) {
    settings.set("setting_with_new_name", settings.get("setting_with_old_name"));
  }
  return settings;
}
```

Internal topic: t/109980
2023-11-02 08:10:15 +03:00
Martin Brennan
d50fccfcaf
DEV: Do not auto-generate plugin routes for admin experimental sidebar (#24211)
Followup to b53449eac9, we cannot
generate the links to plugin admin pages in this way because it
depends on which plugins are installed; we would need to somehow
do it at runtime. Leaving it out for now, for people who need to
find these admin routes the Ember Inspector extension for Chrome
can be used in the meantime.
2023-11-02 12:13:20 +10:00
Martin Brennan
b53449eac9
DEV: Automatically generate all admin links for app for new sidebar (#24175)
NOTE: Most of this is experimental and will be removed at a later
time, which is why things like translations have not been added.

The new /admin-revamp UI uses a sidebar for admin nav. This initial
step adds a script to generate a map of all the current admin nav
into a format the sidebar to read. Then, people can experiment
with different changes to this structure.

The structure can then be edited from `/admin-revamp/config/sidebar-experiment`,
and it is saved to local storage so people can visually experiment with different ways
of showing the admin sidebar links.
2023-11-02 10:34:37 +10:00
Daniel Waterworth
5c92d7da22
FIX: Increase defer queue length (#24200)
It's important that there is a limit, but the current limit is too
restrictive.
2023-11-01 14:02:53 -05:00
David Taylor
85c2cc2f9e
FIX: Correctly handle materialized views during db restore (#24193) 2023-11-01 16:46:13 +00:00
Bianca Nenciu
fd07c943ad
DEV: Refactor watched words (#24163)
- Ignore only invalid words, not all words if one of them is invalid

- The naming scheme for methods was inconsistent

- Optimize regular expressions
2023-11-01 16:41:10 +02:00
Neil Lalonde
5dae0fdfb6
DEV: add a plugin modifier to change hidden site settings (#24160)
Plugins can use a new modifier to change which site settings are hidden using the :hidden_site_settings modifier. For example:

```
register_modifier(:hidden_site_settings) do |hidden|
  (hidden + [:invite_only, :login_required]).uniq
end
```
2023-10-31 10:09:51 -04:00
Martin Brennan
0a4b1b655d
FIX: Alter "Take Action" default behaviour to hide post (#24088)
This commit fixes an issue where clicking the default
"Take Action" option on a flag for a post doesn't always
end up with the post hidden.

This is because the "take_action" score bonus doesn’t take into account
the final score required to hide the post.

Especially with the `hide_post_sensitivity` site setting set to `low`
sensitivity, there is a likelihood the score needed to hide the post
won’t be reached.

Now, the default "Take Action" button has been changed to "Hide Post"
to reflect what is actually happening and the description has been
improved, and if "Take Action" is clicked we _always_ hide the post
regardless of score and sensitivity settings. This way the action reflects
expectations of the user.
2023-10-30 10:24:35 +10:00
Blake Erickson
1a78e8ec1b
FEATURE: Add keywords support for site_settings search (#24146)
* FEATURE: Add keywords support for site_settings search

This change allows for a new `keywords` field that can be added to site
settings in order to help with searching. Keywords are not visible in
the UI, but site settings matching one of the contained keywords will
appear when searching for that keyword.

Keywords can be added for site settings inside of the
`config/locales/server.en.yml` file under the new `keywords` key.

```
site_settings
  example_1: "fancy description"
  example_2: "another description"

  keywords:
    example_1: "capybara"
```

* Add keywords entry for a recently changed site setting and add system specs

* Use page.visit now that we have our own visit
2023-10-27 15:42:57 -06:00
David Taylor
983fd04f4b
FIX: Memoization in EmberCli ruby helper class (#24139)
Previously we were memoizing based on `defined?`, but the `clear_cache!` method was doing `@blah = nil`. That meant that after the cache was cleared, future calls to the memoized method would return `nil` instead of triggering a recalculation.
2023-10-27 13:35:33 +01:00
David Taylor
e2bb84757e
FIX: Ensure JS transpiler is available for multisite-migrate (#24136)
Previously done for the normal `db:migrate` command in 1e59e18ad2
2023-10-27 09:59:41 +01:00
Ted Johansson
f9f9cf0bf4
DEV: Remove unreachable IP address validation message (#24131)
The message: :signup_not_allowed option to the IP address validator does nothing, because the AllowedIpAddressValidator chooses one of either:

- ip_address.blocked or
- ip_address.max_new_accounts_per_registration_ip

internally. This means that the translation for this was also never used.

This PR removes the ineffectual option and the unused translation. It also moves the translated error messages for blocked and max_new_accounts_per_registration_ip into the correct location so we can pass a symbol to ActiveModel::Errors#add.

There is no actual change in behaviour.
2023-10-27 15:22:38 +08:00
Ted Johansson
9acdafe87c
DEV: Remove deprecated EmailValidator.email_regex (#24129)
The EmailValidator.email_regex method was moved to EmailAddressValidator.email_regex and marked for removal in 2.9.0. The method was proxied for backwards compatibility in plugins. This PR removes the method.
2023-10-27 15:11:10 +08:00
Ted Johansson
ac70ef929f
DEV: Remove deprecated #pluck_first freedom patch (#24130)
The #pluck_first method got a replacement in ActiveRecord core named #pick. After a bunch of replacements in core and plugins, we are now ready to retire this freedom patch.
2023-10-27 14:52:23 +08:00
Neil Lalonde
bd7cea7dd7
Revert "DEV: add a plugin modifier to change hidden site settings (#24104)" (#24117)
This reverts commit ddcee84e45.
2023-10-26 15:56:08 -04:00
Neil Lalonde
ddcee84e45
DEV: add a plugin modifier to change hidden site settings (#24104)
Plugins can use a new modifier to change which site settings are
hidden using the :hidden_site_settings modifier. For example:

register_modifier(:hidden_site_settings) do |hidden|
  (hidden + [:invite_only, :login_required]).uniq
end
2023-10-26 15:28:39 -04:00
David Taylor
c124c69833
DEV: Simplify sprockets configuration (#24111)
- Remove the wildcard crawler. This was already excluding almost all file types, but the exclude list was missing '.gjs' which meant those files were unnecessarily being hoisted into the `public/` directory during precompile

- Automatically include all ember-cli-generated assets without needing them to be listed. The main motivation for this change is to allow us to start using async imports via Embroider/Webpack. The filenames for those new async bundles will not be known in advance.

- Skips sprockets fingerprinting on Embroider/Webpack chunk JS files. Their filenames already include a fingerprint, and having sprockets change the filenames will cause problems for the async import feature (where filenames are included deep inside js bundles)

This commit also updates our ember-cli build so that it skips building plugin tests in the production environment. This should provide a slight build speed improvement.
2023-10-26 17:29:53 +01:00
Angus McLeod
2a75656ff2
DEV: Add category custom field preloading to CategoryList (#23969)
This commit also introduced a plugin API for preloading category custom
fields.
2023-10-26 16:34:23 +03:00
David Taylor
c88303bb27
DEV: Relax auth provider registration restrictions for plugins (#24095)
In the past we would build the stack of Omniauth providers at boot, which meant that plugins had to register any authenticators in the root of their plugin.rb (i.e. not in an `after_initialize` block). This could be frustrating because many features are not available that early in boot (e.g. Zeitwerk autoloading).

Now that we build the omniauth strategy stack 'just in time', it is safe for plugins to register their auth methods in an `after_initialize` block. This commit relaxes the old restrictions so that plugin authors have the option to move things around.
2023-10-26 10:54:30 +01:00
David Taylor
5c38e55dc9
DEV: Only run omniauth strategies for enabled authenticators (#24094)
Previously, we would build the stack of omniauth authenticators once on boot. That meant that all strategies had to be included, even if they were disabled. We then used the `before_request_phase` to ensure disabled strategies could not be used. This works well, but it means that omniauth is often doing unnecessary work running logic in disabled strategies.

This commit refactors things so that we build the stack of strategies on each request. That means we only need to include the enabled strategies in the stack - disabled strategies are totally ignored. Building the stack on-demand like this does add some overhead to auth requests, but on the majority of sites that will be significantly outweighed by the fact we're now skipping logic for disabled authenticators.

As well as the slight performance improvement, this new approach means that:

- Broken (i.e. exception-raising) strategies cannot cause issues on a site if they're disabled

- `other_phase` of disabled strategies will never appear in the backtrace of other authentication errors
2023-10-25 13:52:33 +01:00
Martin Brennan
9db4eaa870
DEV: Change anonymous_posting_min_trust_level to a group-based setting (#24072)
No plugins or themes rely on anonymous_posting_min_trust_level so we
can just switch straight over to anonymous_posting_allowed_groups

This also adds an AUTO_GROUPS const which can be imported in JS
tests which is analogous to the one defined in group.rb. This can be used
to set the current user's groups where JS tests call for checking these groups
against site settings.

Finally a AtLeastOneGroupValidator validator is added for group_list site
settings which ensures that at least one group is always selected, since if
you want to allow all users to use a feature in this way you can just use
the everyone group.
2023-10-25 11:45:10 +10:00
Neil Lalonde
8eda55e639
DEV: add a remove_step method to Wizard (#24063)
Using Wizard.exclude_steps applies to all sites in a multisite cluster.
In order to exclude steps for individual sites at run-time, a new
instance method `remove_step` is being added.
2023-10-24 13:22:55 -04:00
Blake Erickson
07360a96aa
DEV: Remove core plugins from plugin metadata file (#24070)
* DEV: Remove core plugins from plugin metadata file

* Add new plugin names
2023-10-23 15:26:59 -06:00
Jeff Wong
a2000a3559
FIX: missing quote (#24049) 2023-10-22 19:23:55 -07:00
Jeff Wong
441a330f1e
DEV: refactor rake asset precompile tasks (#24045)
* DEV: refactor rake asset precompile tasks

add a separate ember build task that does not depend on rails env
allowing us to compile assets without db+redis connections

rename EMBER_CLI_COMPILE_DONE to SKIP_EMBER_CLI_COMPILE
better semantics in build steps
2023-10-22 18:55:09 -07:00
David Taylor
c06b308895
DEV: Support RUN_SYSTEM_TESTS and SINGLE_PLUGIN in docker test task (#24040)
Previously, RUN_SYSTEM_TESTS would run system tests of all plugins. This commit makes it respect the SINGLE_PLUGIN env if it's set.
2023-10-23 07:41:21 +08:00
Sérgio Saquetim
0cfc42e0e6
FEATURE: Add dark mode option for category backgrounds (#24003)
Adds a new upload field for a dark mode category background that will be used as an alternative when Discourse is using a dark mode theme.
2023-10-20 12:48:06 +00:00
Ty Correll
700c636d29
UX: unify ai representing icon (#24029)
This PR addresses the push to unify the icon representing AI throughout Discourse, by using the discourse-sparkles icon.

The icon is being moved to core to make changes with dependencies included in core that were using the "magic" icon instead.

In 2 places "magic" -> "discourse-sparkles,
1. topic summaries
2. (unreleased) chat summaries example
2023-10-19 17:30:27 -05:00
Rafael dos Santos Silva
0604dc7d3e
FIX: Remove invalid chars from feed XMLs (#24001)
* FIX: Remove invalid chars from feed XMLs

See https://meta.discourse.org/t/rss-subscription-broken-by-post-content/282415?u=falco

* Adjust filter condition
2023-10-19 14:37:37 -03:00
Ted Johansson
47b2667099
DEV: Remove deprecated UrlHelper#escape_uri helper (#24002)
The UrlHelper#escape_uri helper has been deprecated and replaced by UrlHelper#normalized_encode, and was marked for removal in 3.0. This PR removes the method.
2023-10-19 10:37:14 +08:00
Martin Brennan
5dc45b5dcf
FIX: Secure upload post processing race condition (#23968)
* FIX: Secure upload post processing race condition

This commit fixes a couple of issues.

A little background -- when uploads are created in the composer
for posts, regardless of whether the upload will eventually be
marked secure or not, if secure_uploads is enabled we always mark
the upload secure at first. This is so the upload is by default
protected, regardless of post type (regular or PM) or category.

This was causing issues in some rare occasions though because
of the order of operations of our post creation and processing
pipeline. When creating a post, we enqueue a sidekiq job to
post-process the post which does various things including
converting images to lightboxes. We were also enqueuing a job
to update the secure status for all uploads in that post.

Sometimes the secure status job would run before the post process
job, marking uploads as _not secure_ in the background and changing
their ACL before the post processor ran, which meant the users
would see a broken image in their posts. This commit fixes that issue
by always running the upload security changes inline _within_ the
cooked_post_processor job.

The other issue was that the lightbox wrapper link for images in
the post would end up with a URL like this:

```
href="/secure-uploads/original/2X/4/4e1f00a40b6c952198bbdacae383ba77932fc542.jpeg"
```

Since we weren't actually using the `upload.url` to pass to
`UrlHelper.cook_url` here, we weren't converting this href to the CDN
URL if the post was not in a secure context (the UrlHelper does not
know how to convert a secure-uploads URL to a CDN one). Now we
always end up with the correct lightbox href. This was less of an issue
than the other one, since the secure-uploads URL works even when the
upload has become non-secure, but it was a good inconsistency to fix
anyway.
2023-10-18 23:48:01 +00:00
Jarek Radosz
16d16c8969
DEV: Precompile the transpiler before themes:update (#23997) 2023-10-19 01:00:23 +02:00
Jarek Radosz
75c9635d8b
DEV: Remove the transpilation message (#23998) 2023-10-19 01:00:15 +02:00
Godfrey Chan
c34f8b65cb
DEV: Rename I18n imports to discourse-i18n (#23915)
As of #23867 this is now a real package, so updating the imports to
use the real package name, rather than relying on the alias. The
name change in the package name is because `I18n` is not a valid
name as NPM packages must be all lowercase.

This commit also introduces an eslint rule to prevent importing from
the old I18n path.

For themes/plugins, the old 'i18n' name remains functional.
2023-10-18 11:07:09 +01:00
Martin Brennan
61c87fb59f
FIX: Properly attach secure images to email for non-secure uploads (#23865)
There are cases where a user can copy image markdown from a public
post (such as via the discourse-templates plugin) into a PM which
is then sent via an email. Since a PM is a secure context (via the
.with_secure_uploads? check on Post), the image will get a secure
URL in the PM post even though the backing upload is not secure.

This fixes the bug in that case where the image would be stripped
from the email (since it had a /secure-uploads/ URL) but not re-attached
further down the line using the secure_uploads_allow_embed_images_in_emails
setting because the upload itself was not secure.

The flow in Email::Sender for doing this is still not ideal, but
there are chicken and egg problems around when to strip the images,
how to fit in with other attachments and email size limits, and
when to apply the images inline via Email::Styles. It's convoluted,
but at least this fixes the Template use case for now.
2023-10-17 14:08:21 +10:00
Penar Musaraj
1400d4a8fd
Bump version to v3.2.0.beta3-dev 2023-10-16 11:20:22 -04:00
Penar Musaraj
be04154838
Bump version to v3.2.0.beta2 2023-10-16 11:20:20 -04:00
Alan Guo Xiang Tan
cbbe3a808b
SECURITY: Add a default limit as to when logs should be truncated
Why this change?

This ensures that malicious requests cannot end up causing the logs to
quickly fill up. The default chosen is sufficient for most legitimate
requests to the Discourse application.

When truncation happens, parsing of logs in supported format like
lograge may break down.
2023-10-16 10:34:38 -04:00
Alan Guo Xiang Tan
4cb7472376
SECURITY: Prevent arbitrary topic custom fields from being set
Why this change?

The `PostsController#create` action allows arbitrary topic custom fields
to be set by any user that can create a topic. Without any restrictions,
this opens us up to potential security issues where plugins may be using
topic custom fields in security sensitive areas.

What does this change do?

1. This change introduces the `register_editable_topic_custom_field` plugin
API which allows plugins to register topic custom fields that are
editable either by staff users only or all users. The registered
editable topic custom fields are stored in `DiscoursePluginRegistry` and
is called by a new method `Topic#editable_custom_fields` which is then
used in the `PostsController#create` controller action. When an unpermitted custom fields is present in the `meta_data` params,
a 400 response code is returned.

2. Removes all reference to `meta_data` on a topic as it is confusing
   since we actually mean topic custom fields instead.
2023-10-16 10:34:35 -04:00
Bianca Nenciu
76bdea5ce2
SECURITY: Hide user profiles from public
User profiles, including the summary, should be private to anonymous
users if hide_user_profiles_from_public is enabled.
2023-10-16 10:34:32 -04:00
Blake Erickson
2443446e62
DEV: Prevent videos from preloading metadata (#23807)
Preloading just metadata is not always respected by browsers, and
sometimes the whole video will be downloaded. This switches to using a
placeholder image for the video and only loads the video when the play
button is clicked.
2023-10-12 13:47:48 -06:00
Godfrey Chan
2e00482ac4
DEV: convert I18n pseudo package into real package (discourse-i18n) (#23867)
Currently, `window.I18n` is defined in an old school hand written
script, inlined into locale/*.js by the Rails asset pipeline, and
then the global variable is shimmed into a pseudo AMD module later
in `module-shims.js`.

This approach has some problems – for one thing, when we add a new
V2 addon (e.g. in #23859), Embroider/Webpack is stricter about its
dependencies and won't let you `import from "I18n";` when `"I18n"`
isn't listed as one of its `dependencies` or `peerDependencies`.

This moves `I18n` into a real package – `discourse-i18n`. (I was
originally planning to keep the `I18n` name since it's a private
package anyway, but NPM packages are supposed to have lower case
names and that may cause problems with other tools.)

This package defines and exports a regular class, but also defines
the default global instance for backwards compatibility. We should
use the exported class in tests to make one-off instances without
mutating the global instance and having to clean it up after the
test run. However, I did not attempt that refactor in this PR.

Since `discourse-i18n` is now included by the app, the locale
scripts needs to be loaded after the app chunks. Since no "real"
work happens until later on when we kick things off in the boot
script, the order in which the script tags appear shouldn't be a
problem. Alternatively, we can rework the locale bundles to be more
lazy like everything else, and require/import them into the app.

I avoided renaming the imports in this commit since that would be
quite noisy and drowns out the actual changes here. Instead, I used
a Webpack alias to redirect the current `"I18n"` import to the new
package for the time being. In a separate commit later on, I'll
rename all the imports in oneshot and remove the alias. As always,
plugins and the legacy bundles (admin/wizard) still relies on the
runtime AMD shims regardless.

For the most part, I avoided refactoring the actual I18n code too
much other than making it a class, and some light stuff like `var`
into `let`.

However, now that it is in a reasonable format to work with (no
longer inside the global script context!) it may also be a good
opportunity to refactor and make clear what is intended to be
public API vs internal implementation details.

Speaking of, I took the librety to make `PLACEHOLDER`, `SEPARATOR`
and `I18nMissingInterpolationArgument` actual constants since it
seemed pretty clear to me those were just previously stashed on to
the `I18n` global to avoid polluting the global namespace, rather
than something we expect the consumers to set/replace.
2023-10-12 14:44:01 +01:00
Penar Musaraj
e3e73a3091
DEV: Add routes and controller actions for passkeys (2/3) (#23587)
This is part 2 (of 3) for passkeys support.

This adds a hidden site setting plus routes and controller actions.

1. registering passkeys

Passkeys are registered in a two-step process. First, `create_passkey`
returns details for the browser to create a passkey. This includes
- a challenge
- the relying party ID and Origin
- the user's secure identifier
- the supported algorithms
- the user's existing passkeys (if any)

Then the browser creates a key with this information, and submits it to
the server via `register_passkey`.

2. authenticating passkeys

A similar process happens here as well. First, a challenge is created
and sent to the browser. Then the browser makes a public key credential
and submits it to the server via `passkey_auth_perform`.

3. renaming/deleting passkeys

These routes allow changing the name of a key and deleting it.

4. checking if session is trusted for sensitive actions

Since a passkey is a password replacement, we want to make sure to confirm the user's identity before allowing adding/deleting passkeys. The u/trusted-session GET route returns success if user has confirmed their session (and failed if user hasn't). In the frontend (in the next PR), we're using these routes to show the password confirmation screen. 

The `/u/confirm-session` route allows the user to confirm their session with a password. The latter route's functionality already existed in core, under the 2FA flow, but it has been abstracted into its own here so it can be used independently.


Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
2023-10-11 14:36:54 -04:00
Mark VanLandingham
4c584f6e03
FIX: List parent/child tags correctly for categories restricted to tag groups (#23708)
Co-authored-by: Sérgio Saquetim <saquetim@discourse.org>
2023-10-10 17:30:24 +00:00
Mark VanLandingham
f29c476521
DEV: Add hooks to allow overriding notify_user behavior (#23850)
Adds new plugin registry `:post_action_notify_user_handlers` and more!
2023-10-10 12:21:57 -05:00
David Taylor
3f8a85ed49
DEV: Write ember exam execution file for plugin qunit in CI (#23877) 2023-10-10 16:29:28 +01:00
Martin Brennan
b58f660cd2
DEV: Add meta_topic_id plugin metadata (#23838)
For the admin plugin list we want to be able to link to
a meta topic for plugins, but we have no standard way to
do this at the moment. This adds support for meta_topic_id
alongside other plugin metadata like authors, URL etc,
that gets built into a Meta topic URL in the serializer.
2023-10-10 10:16:13 +10:00
David Taylor
93c96cf6fa
DEV: Filter files included by theme DirectoryImporter (#23842)
To match discourse_theme CLI behavior, we should skip hidden files/directories (e.g. `.git`), and two regular directories: `node_modules/` and `src/`.

Without these excludes, it's very easy for a theme to hit the file count limit. e.g. when trying this with discourse-kanban-board, I got:

> The number of files (20366) in the theme has exceeded the maximum allowed number of files (1024)
2023-10-09 12:03:02 +01:00
Ted Johansson
b2a5f5802a
DEV: Replace custom Onebox symbolize_keys implementation with ActiveSupport (#23828)
We have a custom implementation of #symbolize_keys in our Onebox helpers. This is likely a legacy from when Onebox was a standalone gem. This change replaces all usages with either #deep_symbolize_keys from ActiveSupport, or appropriate option to the JSON parser gem used.
2023-10-09 09:32:09 +02:00
Krzysztof Kotlarek
c468110929
FEATURE: granular webhooks (#23070)
Before this change, webhooks could be only configured for specific groups like for example, all topic events.

We would like to have more granular control like for example topic_created or topic_destroyed.

Test are failing because plugins changed has to be merged as well:
discourse/discourse-assign#498
discourse/discourse-solved#248
discourse/discourse-topic-voting#159
2023-10-09 03:35:31 +00:00
Ted Johansson
60e624e768
DEV: Replace custom Onebox blank implementation with ActiveSupport (#23827)
We have a custom implementation of #blank? in our Onebox helpers. This is likely a legacy from when Onebox was a standalone gem. This change replaces all usages with respective incarnations of #blank?, #present?, and #presence from ActiveSupport. It changes a bunch of "unless blank" to "if present" as well.
2023-10-07 19:54:26 +02:00
Sam
f21a4a6cb3
Revert "FIX: Allow category moderators to move topics to their categories" (#23810)
This reverts commit 70be873b9c.
2023-10-06 09:00:22 +08:00
Penar Musaraj
921f1279b9
DEV: Update webauthn authentication documentation (#23787)
Also adds a `userHandle` check for first factor verification, though this is not yet implemented in Rails controllers and UI.
2023-10-05 15:22:43 -04:00
Jarek Radosz
fbf92a2212
DEV: Build theme transpiler in custom envs (#23785)
(i.e. non-production, non-test, non-development environments :P like `RAILS_ENV="profile"`)
2023-10-05 02:06:58 +02:00
Renato Atilio
1d70cf455e
FEATURE: support a description attribute on form template fields (#23744)
* FEATURE: support a description attribute on form template fields
2023-10-04 17:51:53 -03:00
Penar Musaraj
0af6c5efdc
DEV: Refactor webauthn to support passkeys (1/3) (#23586)
This is part 1 of 3, split up of PR #23529. This PR refactors the
webauthn code to support passkey authentication/registration.

Passkeys aren't used yet, that is coming in PRs 2 and 3.

Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
2023-10-03 14:59:28 -04:00
David Taylor
99e9e3c75b
DEV: Enable USE_TURBO flag for plugin specs in docker.rake (#23761)
We run plugin specs in parallel in GitHub actions, so it makes sense to (optionally) do the same in the docker-based tests
2023-10-03 17:45:35 +01:00
Natalie Tay
70be873b9c
FIX: Allow category moderators to move topics to their categories (#20896) 2023-10-03 17:59:16 +08:00
arturo-seijas
d7b64b121b
DEV: Add task to anonymize user data (#20522) 2023-10-03 16:59:43 +08:00
Jarek Radosz
5a904949b2
DEV: Add gjs support for themes (#23473) 2023-10-02 12:36:06 +02:00
Matt Marjanović
619d43ea47
FEATURE: Add prompt=none functionality to SSO Provider protocol (#22393)
This commit adds support for an optional `prompt` parameter in the
payload of the /session/sso_provider endpoint.  If an SSO Consumer
adds a `prompt=none` parameter to the encoded/signed `sso` payload,
then Discourse will avoid trying to login a not-logged-in user:

 * If the user is already logged in, Discourse will immediately
   redirect back to the Consumer with the user's credentials in a
   signed payload, as usual.

 * If the user is not logged in, Discourse will immediately redirect
   back to the Consumer with a signed payload bearing the parameter
   `failed=true`.

This allows the SSO Consumer to simply test whether or not a user is
logged in, without forcing the user to try to log in.  This is useful
when the SSO Consumer allows both anonymous and authenticated access.
(E.g., users that are already logged-in to Discourse can be seamlessly
logged-in to the Consumer site, and anonymous users can remain
anonymous until they explicitly ask to log in.)

This feature is similar to the `prompt=none` functionality in an
OpenID Connect Authentication Request; see
https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
2023-09-28 12:53:28 +01:00
Sam
a2da2e02e7
FEATURE: improve error message when double liking (#23698)
If a user somehow is looking at an old version of the page and attempts
to like a post they already like. Display a more reasonable error message.

Previously we would display:

> You are not permitted to view the requested resource.

New error message is:

> Oops! You already performed this action. Can you try refreshing the page?

Triggering this error condition is very tricky, you need to stop the
message bus. A possible reason for it could be bad network connectivity.
2023-09-28 16:53:48 +10:00
Godfrey Chan
b5ccf89914
DEV: Cleanup unused wizard illustrations (#23659)
These were defunct since #19487
2023-09-26 10:34:38 +08:00
Jarek Radosz
0fb2812414
DEV: Rescue another http error in qunit task (#23651)
Should take care of a flake issue. Also removes an extraneous `/` character in the used URL.
2023-09-25 19:27:49 +02:00
David Taylor
e0daacf3ef
DEV: Drop /theme-qunit from smoke test (#23562)
We will soon be dropping support for `/theme-qunit` in production, so this will start failing if we don't remove it. Plus, we now have system specs which verify the end-to-end functionality of the Theme QUnit system.

This was the last thing which was using the legacy `run-qunit` script, so that can also be dropped.
2023-09-13 16:14:27 +01:00
David Battersby
6e2b484f12
FIX: prevent lightbox images from double escaping titles (#23458)
This change fixes an issue where lightbox images are showing escaped text in the link title and lightbox image description area.
2023-09-13 14:33:08 +08:00
Sam
267e8ebaa6
FIX: min_personal_message_post_length not applying to first post (#23531)
* FIX: min_personal_message_post_length not applying to first post

Due to the way PostCreator is wired, we were not applying min_personal_message_post_length
to the first post.

This meant that admins could not configure it so PMs have different
limits.

The code was already pretending that this works, but had no reliable way
of figuring out if we were dealing with a private message
2023-09-13 15:43:54 +10:00
Penar Musaraj
f6326d03f0
DEV: Bump max theme sprite size to 1MB (#23556)
There is one repo with very large sprites, and it's causing missing
icons on some instances.
2023-09-13 15:00:26 +10:00
Roman Rizzi
75e8a6bf90
Bump version to v3.2.0.beta2-dev 2023-09-12 15:41:11 -03:00
Roman Rizzi
a9cc379121
Bump version to v3.2.0.beta1 2023-09-12 15:41:11 -03:00
Bianca Nenciu
6f782d8e45
SECURITY: Add limits for themes and theme assets
This commit adds limits to themes and theme components on the:

- file size of about.json and .discourse-compatibility
- file size of theme assets
- number of files in a theme
2023-09-12 15:31:31 -03:00
Daniel Waterworth
290306a932
SECURITY: Reduce maximum size of SVG sprite cache to prevent DoS
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
2023-09-12 15:31:28 -03:00
OsamaSayegh
c1b5faa5fd
SECURITY: Limit name field length of TOTP authenticators and security keys 2023-09-12 15:31:17 -03:00
David Taylor
8b51a89919
DEV: Do not squash commits in version_bump:stage_security_fixes (#23547)
Sometimes fixes will deliberately keep commits separate, and we don't want to undo that
2023-09-12 18:00:42 +01:00
Loïc Guitaut
b7d7099d08 DEV: Add link to PR when generating release notes 2023-09-12 09:26:46 +02:00
Sam
f25849501d
FEATURE: allow consumers to parse a search string (#23528)
This extends search so it can have consumers that:

1. Can split off "term" from various advanced filters and orders
2. Can build a relation of either order or filter

It also moves a lot of stuff around in the search class for clarity.

Two new APIs are exposed:

`.apply_filter` to apply all the special filters to a posts/topics relation
`.apply_order` to force a particular order (eg: order:latest)

This can then be used by semantic search in Discourse AI
2023-09-12 16:21:01 +10:00
Alan Guo Xiang Tan
07c29f3066
Revert "DEV: Run core system tests by default in docker test image (#23517)" (#23525)
This reverts commit 40acb9a111.

Reverting because test runs are breaking due to this change
2023-09-12 11:45:49 +10:00
Alan Guo Xiang Tan
d2e4b32c87
DEV: Add support for uploading a theme from a directory in system tests (#23402)
Why this change?

Currently, we do not have an easy way to test themes and theme components
using Rails system tests. While we support QUnit acceptance tests for
themes and theme components, QUnit acceptance tests stubs out the server
and setting up the fixtures for server responses is difficult and can lead to a
frustrating experience. System tests on the other hand allow authors to
set up the test fixtures using our fabricator system which is much
easier to use.

What does this change do?

In order for us to allow authors to run system tests with their themes
installed, we are adding a `upload_theme` helper that is made available
when writing system tests. The `upload_theme` helper requires a single
`directory` parameter where `directory` is the directory of the theme
locally and returns a `Theme` record.
2023-09-12 07:38:47 +08:00
Daniel Waterworth
40acb9a111
DEV: Run core system tests by default in docker test image (#23517) 2023-09-11 16:04:33 -05:00
David Taylor
055d29d898
DEV: Correct sourceMappingURL regex (#23504)
This comment isn't necessarily on a line by itself, so we need to remove the `^` from the regex. This will fix `EMBER_ENV=development bin/rake assets:precompile`
2023-09-11 11:39:55 +01:00
David Taylor
b07445ced8
DEV: Disable Webpack parallelization for low-memory environments (#23487)
This reduces memory usage for Embroider-based builds on low-memory servers (e.g. entry-level Digital Ocean droplets)
2023-09-11 09:32:37 +01:00
David Taylor
9667485951
DEV: Stop building test assets in production under Embroider (#23388)
Until now, we have allowed testing themes in production environments via `/theme-qunit`. This was made possible by hacking the ember-cli build so that it would create the `tests.js` bundle in production. However, this is fundamentally problematic because a number of test-specific things are still optimized out of the Ember build in production mode. It also makes asset compilation significantly slower, and makes it more difficult for us to update our build pipeline (e.g. to introduce Embroider).

This commit removes the ability to run qunit tests in production builds of the JS app when the Embdroider flag is enabled. If a production instance of Discourse exists exclusively for the development of themes (e.g. discourse.theme-creator.io) then they can add `EMBER_ENV: development` to their `app.yml` file. This will build the entire app in development mode, and has a significant performance impact. This must not be used for real production sites.

This commit also refactors many of the request specs into system specs. This means that the tests are guaranteed to have Ember assets built, and is also a better end-to-end test than simply checking for the presence of certain `<script>` tags in the HTML.
2023-09-11 09:12:37 +01:00
David Taylor
231ea8faa2
DEV: Correctly identify Embroider chunks (#23488)
This method is used by assets:precompile to decide whether to apply `terser` to a file. Embroider chunks do not necessarily start with `chunk.`, and so they were incorrectly being re-terser'd by our assets:precompile task. This is inefficient, and also led to broken sourcemaps on some assets.
2023-09-10 10:18:43 +01:00
Bianca Nenciu
4db5310135
DEV: Remove unused topic_create_allowed_category_ids (#23463) 2023-09-08 12:03:22 +03:00
Alan Guo Xiang Tan
dc76d82f24
DEV: Fix broken conditional in docker:test Rake task (#23477)
Broke in ef73d20832
2023-09-08 12:16:02 +08:00
Alan Guo Xiang Tan
ef73d20832
DEV: Fix docker:test:setup Rake task that was missing the DB (#23474)
This is a follow up to 9caba30d5c

In that commit, we were migrating the database but we didn't actually
ensure that the database was created and that plugins were updated
before the databases were migrated.
2023-09-08 10:16:23 +08:00
David Taylor
75ce01a69b
DEV: Ensure Embroider sourcemaps are collected by Sprockets (#23468)
Names of sourcemaps are not necessarily equal to the js file names. Instead, we can check the `sourceMappingURL` comment to find the map's filename.
2023-09-07 22:20:52 +01:00
Alan Guo Xiang Tan
1f0a78fb82
DEV: Remove accidentally hardcoded Redis port in docker.rake (#23455)
Follow up to 9caba30d5c
2023-09-07 14:22:14 +08:00
Alan Guo Xiang Tan
9caba30d5c
DEV: Add docker:test:setup Rake task (#23430)
## What is the context here?

The `docker.rake` Rakefile contains Rake tasks that are meant to be run
in the `discourse/discourse_test:release` Docker image. For example, we
have the `docker:test` Rake task that makes it easier to run the test
suite for a particular Discourse commit.

Why are we introducing a `docker:test:setup` Rake task?

While we have the `docker:test` Rake task, it is very limited in the
test commands that can be executed. It is very useful for automated
testing but not very useful for running tests in the development
environment. Therefore, we are introducing a `docker:test:setup` rake
task that can be used to set up the test environment for running tests.

The envisioned example usage is something like this:

```
docker run -d --name=discourse_test --entrypoint=/sbin/boot discourse/discourse_test:release
docker exec -u discourse:discourse discourse_test ruby script/docker_test.rb --no-tests
docker exec -u discourse:discourse discourse_test bundle exec rake docker:test:setup
docker exec -u discourse:discourse discourse_test bundle exec rspec <path to file>
```
2023-09-07 13:46:23 +08:00
Ted Johansson
ad58b6d604
DEV: Validate before and bumped_before options in TopicQuery (#23451) 2023-09-07 14:38:03 +10:00
Ted Johansson
e74560f062
FIX: Don't mix up action labels between different reviewables (#23365)
Currently, if the review queue has both a flagged post and a flagged chat message, one of the two will have some of the labels of their actions replaced by those of the other. In other words, the labels are getting mixed up. For example, a flagged chat message might show up with an action labelled "Delete post".

This is happening because when using bundles, we are sending along the actions in a separate part of the response, so they can be shared by many reviewables. The bundles then index into this bag of actions by their ID, which is something generic describing the server action, e.g. "agree_and_delete".

The problem here is the same action can have different labels depending on the type of reviewable. Now that the bag of actions contains multiple actions with the same ID, which one is chosen is arbitrary. I.e. it doesn't distinguish based on the type of the reviewable.

This change adds an additional field to the actions, server_action, which now contains what used to be the ID. Meanwhile, the ID has been turned into a concatenation of the reviewable type and the server action, e.g. post-agree_and_delete.

This still provides the upside of denormalizing the actions while allowing for different reviewable types to have different labels and descriptions.

At first I thought I would prepend the reviewable type to the ID, but this doesn't work well because the ID is used on the server-side to determine which actions are possible, and these need to be shared between different reviewables. Hence the introduction of server_action, which now serves that purpose.

I also thought about changing the way that the bundle indexes into the bag of actions, but this is happening through some EmberJS mechanism, so we don't own that code.
2023-09-06 10:57:30 +08:00
Alan Guo Xiang Tan
7cc5501cfd
DEV: Remove outdated ENV in docker.rake that is no longer being used (#23428)
We now have `USE_TURBO` which relis on `turbo_rspec`.
2023-09-06 09:07:57 +08:00
Jarek Radosz
ee3ac739f3
DEV: Don't run dev-only code in specs (#23427)
This was causing the following notice to be printed out when running system specs:

```
I did no detect a custom `config/dev.yml` file, creating one for you where you can amend defaults.
```

(since 61571bee43)
2023-09-06 02:14:57 +02:00
Martin Brennan
c532f6eb3d
FEATURE: Secure uploads in PMs only (#23398)
This adds a new secure_uploads_pm_only site setting. When secure_uploads
is true with this setting, only uploads created in PMs will be marked
secure; no uploads in secure categories will be marked as secure, and
the login_required site setting has no bearing on upload security
either.

This is meant to be a stopgap solution to prevent secure uploads
in a single place (private messages) for sensitive admin data exports.
Ideally we would want a more comprehensive way of saying that certain
upload types get secured which is a hybrid/mixed mode secure uploads,
but for now this will do the trick.
2023-09-06 09:39:09 +10:00
Martin Brennan
de9b567c19
FIX: Admins not able to convert topics if they aren't in personal_message_enabled_groups (#23399)
Admins are always able to send PMs, so it doesn't make
sense that they shouldn't be able to convert topics just
because they aren't in personal_message_enabled_groups.
2023-09-06 09:17:40 +10:00
David Taylor
534f62cf5b
FIX: Respect default category sort when filter=default (#23411)
Previously we would respect it if the filter was `nil`, but if `default` was explicitly passed then it would ignore the category order settings. This explicit passing of `filter=default` happens for some types of navigations in the JS app.

This extends the fix from 92bc61b4be
2023-09-05 19:05:30 +01:00
Alan Guo Xiang Tan
738343d4d2
DEV: Remove unused line of comment (#23401)
Wizard tests have been merged into core so the env isn't being used
anymore.
2023-09-05 14:28:23 +08:00
David Taylor
082ccdbd66
DEV: Reduce theme-qunit smoke test timeout (#23394)
The theme tests we use for the smoke-test typically take 3-4 seconds to complete. This commit reduces the timeout from 10 minutes to 20 seconds, so that failures are detected more quickl
2023-09-04 23:10:40 +01:00
David Taylor
b59f1ad4ee
DEV: Improve strategy for identifying ember-cli JS chunks (#23382)
Our Ember build compiles assets into multiple chunks. In the past, we used the output from ember-auto-import-chunks-json-generator to give Rails a map of those chunks. However, that addon is specific to ember-auto-import, and is not compatible with Embroider.

Instead, we can switch to parsing the html files which are output by ember-cli. These are guaranteed to have the correct JS files in the correct place. A <discourse-chunked-script> will allow us to easily identify which chunks belong to which entrypoint.

In future, as we update more entrypoints to be compiled by Embroider/Webpack, we can easily introduce new wrappers.

Previously applied in 2c58d45 and reverted in 24d46fd. This version has been updated for subfolder support.
2023-09-04 13:56:34 +01:00
Chris Rendle-Short
7ea3079e3e
FIX: Update DIRECT_UPLOAD CORS ruleset to include new Amazon signing headers (#23379)
New headers were added to upload PUT requests as part of a MinIO update (cf42466). This change updates the asset bucket CORS ruleset to allow the new headers in the preflight request.

See https://dev.discourse.org/t/111136



Co-authored-by: Sam Saffron <sam.saffron@gmail.com>
2023-09-04 16:09:52 +10:00
Joffrey JAFFEUX
f1d8cd529e
Revert "Revert "PERF: Cache each theme field value once (#23192)" (#23354)" (#23356)
This reverts commit 9821ca9413.
2023-08-31 14:12:03 -05:00
Joffrey JAFFEUX
24d46fd981
Revert "DEV: Improve strategy for identifying ember-cli JS chunks (#23336)" (#23355)
This reverts commit 2c58d456dd.
2023-08-31 19:28:48 +02:00
Joffrey JAFFEUX
9821ca9413
Revert "PERF: Cache each theme field value once (#23192)" (#23354)
This reverts commit 82a56334a3.
2023-08-31 19:04:43 +02:00
Daniel Waterworth
82a56334a3
PERF: Cache each theme field value once (#23192)
Previously, theme fields from components would be cached for each of
their parent themes.
2023-08-31 11:24:02 -05:00
Penar Musaraj
006a5166e5
DEV: Refactor rp_id and rp_name (#23339)
They're both constant per-instance values, there is no need to store them
in the session. This also makes the code a bit more readable by moving
the `session_challenge_key` method up to the `DiscourseWebauthn` module.
2023-08-31 09:11:23 -04:00
David Taylor
2c58d456dd
DEV: Improve strategy for identifying ember-cli JS chunks (#23336)
Our Ember build compiles assets into multiple chunks. In the past, we used the output from `ember-auto-import-chunks-json-generator` to give Rails a map of those chunks. However, that addon is specific to ember-auto-import, and is not compatible with Embroider.

Instead, we can switch to parsing the html files which are output by ember-cli. These are guaranteed to have the correct JS files in the correct place. A `<discourse-chunked-script>` will allow us to easily identify which chunks belong to which entrypoint.

In future, as we update more entrypoints to be compiled by Embroider/Webpack, we can easily introduce new wrappers.
2023-08-30 18:47:06 +01:00
Martin Brennan
2965519c76
DEV: Add auto _map extensions for list site settings with no type (#23331)
Followup to eea74e0e32. Site settings
which are a list without a list_type should also have the _map
extension added which returns an array based on split("|").

For example:

```
SiteSetting.post_menu_map
=> ["read", "like"]
```
2023-08-30 16:14:06 +10:00
Renato Atilio
58b49bce41
FEATURE: support to initial values for form templates through /new-topic (#23313)
* FEATURE: adds support for initial values through /new-topic to form templates
2023-08-29 18:41:33 -03:00
Blake Erickson
5d438f805c
DEV: Add rake command to help detect dead settings (#23300)
* DEV: Add rake command to help detect dead settings

Some Site Settings may still exist but are no longer being used in the
core discourse code or in related plugins. This rake task will help
identify any unused (aka: dead) settings by using the `rg` command to
search for them.

You can execute the rake task by using this command:

`LOAD_PLUGINS=1 bin/rails "site_settings:find_dead"`

* Add env variable, apply feedback
2023-08-29 09:42:52 -06:00
Jarek Radosz
09446baf10
DEV: Fix redis.sadd warnings (#23244)
```
Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead
```
2023-08-28 12:58:47 +08:00
David Taylor
92bc61b4be
FIX: Respect default category sort order when navigating within app (#23270)
When navigating around, we make ajax requests with a parameter like `?filter=latest`. This results in the TopicQuery being set up with `filter: "latest"` as a string. The logic introduced in fd9a5bc0 checks for equality with `:latest` and `:unseen` symbols, which didn't work correctly in this situation

This commit makes the logic detect both strings and symbols, and adds a spec for the behaviour.
2023-08-25 16:49:49 +01:00
Jarek Radosz
3ce3c2ff81
DEV: Add a spec for assets:precompile:js_processor (#23220) 2023-08-25 11:44:30 +02:00
Kelv
93c2de3ba5
DEV: add geoblocking and tag-by-group plugins (#23255) 2023-08-25 16:22:54 +08:00
Jarek Radosz
e60d227c8f
DEV: Avoid constant redefinition warnings in specs (#23241)
Specs sometimes do `Discourse::Application.load_tasks` which re-loads rake task files, causing constant redefinition.
2023-08-24 23:16:32 +02:00
Rafael dos Santos Silva
d10e9a6c1d
FEATURE: Onebox and Download for WEBP and AVIF (#23235)
This adds support for oneboxing WEBP and AVIF images in posts and fixing
oneboxing fixes download remote images for those formats too.

Reported in https://meta.discourse.org/t/-/276433?u=falco
2023-08-24 16:44:06 -03:00
Jarek Radosz
1e59e18ad2
FIX: Compile js-processor before db:migrate (#23229)
In production env it's possible to have migrations run before js-processor is available.
2023-08-24 19:24:43 +02:00
Jarek Radosz
70f1cc5552
DEV: Use esbuild to make DiscourseJsProcessor (#23223)
Reverts e2705df and re-lands #23187 and #23219.

The issue was incorrect order of execution of Rails' `assets:precompile` task in our own precompilation stack.

Co-authored-by: David Taylor <david@taylorhq.com>
2023-08-24 16:36:22 +02:00
David Taylor
e2705df0f4
Revert "DEV: Use esbuild to make DiscourseJsProcessor (#23187)" (#23221)
This reverts commit 4dfe25d062 and 4fdeb6281e. We are investigating an issue related to asset compilation and S3 assets
2023-08-24 13:25:44 +01:00
Jarek Radosz
4fdeb6281e
FIX: assets:precompile:js_processor task issue (#23219)
* Fix the reference
* Use mutex in non-prod only (…and don't try to build the processor in runtime in prod)
2023-08-24 13:19:57 +02:00
Jarek Radosz
4dfe25d062
DEV: Use esbuild to make DiscourseJsProcessor (#23187)
Co-authored-by: David Taylor <david@taylorhq.com>
2023-08-24 12:43:59 +02:00
Martin Brennan
cf42466dea
DEV: Add S3 upload system specs using minio (#22975)
This commit adds some system specs to test uploads with
direct to S3 single and multipart uploads via uppy. This
is done with minio as a local S3 replacement. We are doing
this to catch regressions when uppy dependencies need to
be upgraded or we change uppy upload code, since before
this there was no way to know outside manual testing whether
these changes would cause regressions.

Minio's server lifecycle and the installed binaries are managed
by the https://github.com/discourse/minio_runner gem, though the
binaries are already installed on the discourse_test image we run
GitHub CI from.

These tests will only run in CI unless you specifically use the
CI=1 or RUN_S3_SYSTEM_SPECS=1 env vars.

For a history of experimentation here see https://github.com/discourse/discourse/pull/22381

Related PRs:

* https://github.com/discourse/minio_runner/pull/1
* https://github.com/discourse/minio_runner/pull/2
* https://github.com/discourse/minio_runner/pull/3
2023-08-23 11:18:33 +10:00
Jarek Radosz
9b63ac473b
FIX: next vs return in maxmind task (#23196)
(and added the dependence on `environment` for trying the task independently of `assets:precompile`)
2023-08-22 23:00:02 +02:00
Jarek Radosz
c9de84c63d
DEV: Extract maxmind refresh logic to its own task (#23195) 2023-08-22 22:27:16 +02:00
Penar Musaraj
4c3ee1b211
DEV: Remove unused code (#23171)
This method slipped in by accident in 10c6b2a and is not used.
2023-08-21 15:01:05 -04:00
Mark VanLandingham
730f652255
DEV: Add plugin modifier locations for user search locations (#23169) 2023-08-21 12:23:42 -05:00
David Taylor
82b16f4f47
DEV: Do not manipulate theme module paths at build-time (#23148)
Manipulating theme module paths means that the paths you author are not the ones used at runtime. This can lead to some very unexpected behavior and potential module name clashes. It also meant that the refactor in 16c6ab8661 was unable to correctly match up theme connector js/templates.

While this could technically be a breaking change, I think it is reasonably safe because:

1. Themes are already forced to use relative paths when referencing their own modules (since they're namespaced based on the site-specific id). The only time this might be problematic is when theme tests reference modules in the theme's main `javascripts` directory

2. For things like components/services/controllers/etc. our custom Ember resolver works backwards from the end of the path, so adding `discourse/` in the middle will not affect resolution.
2023-08-18 18:15:23 +01:00
Selase Krakani
87ebbec9b2
FIX: Pending post deletion by creator (#23130)
`ReviewableQueuedPost` got refactored a while back to use the more
appropriate `target_created_by` for the user of the post being queued
instead of `created_by`. The change was not extended to the `DELETE
/review/:id` endpoint leading to error responses for a user attempting
to deleting their own queued post.

This fix extends the `Reviewable` lookup implementation in
`ReviewablesController#destroy` and Guardian implementation to account
for this change.
2023-08-18 15:30:59 +00:00
Penar Musaraj
10c6b2a0c2
WIP: Rename Webauthn to DiscourseWebauthn (#23077) 2023-08-18 08:39:10 -04:00
Osama Sayegh
09d3709ec9
FEATURE: New topics vs replies toggle for the new new view (#22920)
This PR adds a new toggle to switch the (new) /new list between showing topics with new replies (a.k.a unread topics), new topics, or everything mixed together.
2023-08-18 12:44:04 +08:00
Sam
e654edf844
FEATURE: do not bump topics when retroactively closing (#23115)
The category feature that automatically closes topics does it silently

This amends it so `rake topics:apply_autoclose` which does retroactive
closing will also do so silently.
2023-08-16 11:20:47 +10:00
Keegan George
61571bee43
UX: Add show more button to long post queued reviewables (#23075) 2023-08-14 10:11:30 -07:00
Roman Rizzi
7ca5ee6cd2
FEATURE: Stream topic summaries. (#23065)
When we receive the stream parameter, we'll queue a job that periodically publishes partial updates, and after the summarization finishes, a final one with the completed version, plus metadata.

`summary-box` listens to these updates via MessageBus, and updates state accordingly.
2023-08-11 15:08:49 -03:00
Jarek Radosz
8bebd8fd99
DEV: Format .thor files (#23059) 2023-08-10 13:59:13 +02:00
tshenry
5e72f2a342
DEV: Add jira plugin to official list of plugins (#22997)
https://github.com/discourse/discourse-jira
2023-08-10 03:25:22 -07:00
Jarek Radosz
94649565ce
DEV: Correct Style/RedundantReturn rubocop issues (#23052) 2023-08-10 02:03:38 +02:00
Gerhard Schlager
eabea3e8fd DEV: Create missing user profiles in "import:ensure_consistency" rake task 2023-08-09 20:56:14 +02:00
Gerhard Schlager
b2fee68b3f DEV: Add rake task for generating avatars from SSO 2023-08-09 20:56:14 +02:00
Joffrey JAFFEUX
df7dab9dce
FIX: ensures generic onebox has width/height for thumbnail (#23040)
Prior to this fix we would output an image with no width/height which would then bypass a large part of `CookedProcessorMixin` and have no aspect ratio. As a result, an image with no size would cause layout shift.

It also removes a fix for oneboxes in chat messages due to this case.
2023-08-09 20:31:11 +02:00
Angus McLeod
6801cf34cc
DEV: Enhance post action handler events (#23027) 2023-08-09 13:55:00 -04:00
Godfrey Chan
923b51ad25
DEV: add loader.js shims for packages used across bundles (#22703)
This adds a new `loaderShim()` function to ensure certain modules
are present in the `loader.js` registry and therefore runtime
`require()`-able.

Currently, the classic build pipeline puts a lot of things in the
runtime `loader.js` registry automatically. For example, all of
the ember-auto-import packages are in there.

Going forward, and especially as we switch to the Embroider build
pipeline, this will not be guarenteed. We need to keep an eye on
what modules (packages) our "external" bundles (admin, wizard,
markdown-it, plugins, etc) are expecting to be present and put
them into the registry proactively.
2023-08-09 12:04:41 +01:00
chapoi
a1899996d0
UX: Add bookmark-delete icon (#23025) 2023-08-09 10:51:09 +02:00
Roman Agilov
3eac47443f
FEATURE: Add audio.com onebox provider (#22936)
* Audio.com provider added to onebox
* added specs for audio.com onebox provider
2023-08-08 16:55:04 +10:00
Martin Brennan
09223e5ae7
DEV: Remove enable_experimental_hashtag_autocomplete logic (#22820)
This commit removes any logic in the app and in specs around
enable_experimental_hashtag_autocomplete and deletes some
old category hashtag code that is no longer necessary.

It also adds a `slug_ref` category instance method, which
will generate a reference like `parent:child` for a category,
with an optional depth, which hashtags use. Also refactors
PostRevisor which was using CategoryHashtagDataSource directly
which is a no-no.

Deletes the old hashtag markdown rule as well.
2023-08-08 11:18:55 +10:00
Alan Guo Xiang Tan
135e88cb12
PERF: Improve performance of queries when loading a topic list (#22949)
What is the context of this change?

Before 7c6a8f1c74, we were using
`preload(:tags)` on the topics relation but that was accidentally
removed in the refactor. This was discovered and fixed in
5bec894a8c but insteadl of using
`preload(:tags)` we ended up using `includes(:tags)`. The problem here
is that `includes(:tags)` can either result in `preload(:tags)` or
`eager_load(:tags)` but for some reason ActiveRecord is deciding to
`eager_load(:tags)` resulting in a joins to the `topic_tags` and `tags`
table which is not necessarily and leads to more inefficient queries.

When `includes(:tags)` is used, listing the latest topics ended up
generating the following sample queries to fetch the list of topics to display.

```
SELECT DISTINCT "topics"."pinned_at" AS alias_0, "topics"."id" FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN "topic_tags" ON "topic_tags"."topic_id" = "topics"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "topic_tags"."tag_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL)) ORDER BY "topics"."pinned_at" DESC LIMIT 30

SELECT "topics"."id" AS t0_r0, "topics"."title" AS t0_r1, "topics"."last_posted_at" AS t0_r2, "topics"."created_at" AS t0_r3, "topics"."updated_at" AS t0_r4, "topics"."views" AS t0_r5, "topics"."posts_count" AS t0_r6, "topics"."user_id" AS t0_r7, "topics"."last_post_user_id" AS t0_r8, "topics"."reply_count" AS t0_r9, "topics"."featured_user1_id" AS t0_r10, "topics"."featured_user2_id" AS t0_r11, "topics"."featured_user3_id" AS t0_r12, "topics"."deleted_at" AS t0_r13, "topics"."highest_post_number" AS t0_r14, "topics"."like_count" AS t0_r15, "topics"."incoming_link_count" AS t0_r16, "topics"."category_id" AS t0_r17, "topics"."visible" AS t0_r18, "topics"."moderator_posts_count" AS t0_r19, "topics"."closed" AS t0_r20, "topics"."archived" AS t0_r21, "topics"."bumped_at" AS t0_r22, "topics"."has_summary" AS t0_r23, "topics"."archetype" AS t0_r24, "topics"."featured_user4_id" AS t0_r25, "topics"."notify_moderators_count" AS t0_r26, "topics"."spam_count" AS t0_r27, "topics"."pinned_at" AS t0_r28, "topics"."score" AS t0_r29, "topics"."percent_rank" AS t0_r30, "topics"."subtype" AS t0_r31, "topics"."slug" AS t0_r32, "topics"."deleted_by_id" AS t0_r33, "topics"."participant_count" AS t0_r34, "topics"."word_count" AS t0_r35, "topics"."excerpt" AS t0_r36, "topics"."pinned_globally" AS t0_r37, "topics"."pinned_until" AS t0_r38, "topics"."fancy_title" AS t0_r39, "topics"."highest_staff_post_number" AS t0_r40, "topics"."featured_link" AS t0_r41, "topics"."reviewable_score" AS t0_r42, "topics"."image_upload_id" AS t0_r43, "topics"."slow_mode_seconds" AS t0_r44, "topics"."bannered_until" AS t0_r45, "topics"."external_id" AS t0_r46, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1, "categories"."color" AS t1_r2, "categories"."topic_id" AS t1_r3, "categories"."topic_count" AS t1_r4, "categories"."created_at" AS t1_r5, "categories"."updated_at" AS t1_r6, "categories"."user_id" AS t1_r7, "categories"."topics_year" AS t1_r8, "categories"."topics_month" AS t1_r9, "categories"."topics_week" AS t1_r10, "categories"."slug" AS t1_r11, "categories"."description" AS t1_r12, "categories"."text_color" AS t1_r13, "categories"."read_restricted" AS t1_r14, "categories"."auto_close_hours" AS t1_r15, "categories"."post_count" AS t1_r16, "categories"."latest_post_id" AS t1_r17, "categories"."latest_topic_id" AS t1_r18, "categories"."position" AS t1_r19, "categories"."parent_category_id" AS t1_r20, "categories"."posts_year" AS t1_r21, "categories"."posts_month" AS t1_r22, "categories"."posts_week" AS t1_r23, "categories"."email_in" AS t1_r24, "categories"."email_in_allow_strangers" AS t1_r25, "categories"."topics_day" AS t1_r26, "categories"."posts_day" AS t1_r27, "categories"."allow_badges" AS t1_r28, "categories"."name_lower" AS t1_r29, "categories"."auto_close_based_on_last_post" AS t1_r30, "categories"."topic_template" AS t1_r31, "categories"."contains_messages" AS t1_r32, "categories"."sort_order" AS t1_r33, "categories"."sort_ascending" AS t1_r34, "categories"."uploaded_logo_id" AS t1_r35, "categories"."uploaded_background_id" AS t1_r36, "categories"."topic_featured_link_allowed" AS t1_r37, "categories"."all_topics_wiki" AS t1_r38, "categories"."show_subcategory_list" AS t1_r39, "categories"."num_featured_topics" AS t1_r40, "categories"."default_view" AS t1_r41, "categories"."subcategory_list_style" AS t1_r42, "categories"."default_top_period" AS t1_r43, "categories"."mailinglist_mirror" AS t1_r44, "categories"."minimum_required_tags" AS t1_r45, "categories"."navigate_to_first_post_after_read" AS t1_r46, "categories"."search_priority" AS t1_r47, "categories"."allow_global_tags" AS t1_r48, "categories"."reviewable_by_group_id" AS t1_r49, "categories"."read_only_banner" AS t1_r50, "categories"."default_list_filter" AS t1_r51, "categories"."allow_unlimited_owner_edits_on_first_post" AS t1_r52, "categories"."default_slow_mode_seconds" AS t1_r53, "categories"."uploaded_logo_dark_id" AS t1_r54, "tags"."id" AS t2_r0, "tags"."name" AS t2_r1, "tags"."created_at" AS t2_r2, "tags"."updated_at" AS t2_r3, "tags"."pm_topic_count" AS t2_r4, "tags"."target_tag_id" AS t2_r5, "tags"."description" AS t2_r6, "tags"."public_topic_count" AS t2_r7, "tags"."staff_topic_count" AS t2_r8 FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN "topic_tags" ON "topic_tags"."topic_id" = "topics"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "topic_tags"."tag_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL)) AND "topics"."id" = 7 ORDER BY "topics"."pinned_at" DESC

SELECT DISTINCT topics.bumped_at AS alias_0, "topics"."id" FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN "topic_tags" ON "topic_tags"."topic_id" = "topics"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "topic_tags"."tag_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (NOT ( pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL) )) ORDER BY topics.bumped_at DESC LIMIT 30

SELECT "topics"."id" AS t0_r0, "topics"."title" AS t0_r1, "topics"."last_posted_at" AS t0_r2, "topics"."created_at" AS t0_r3, "topics"."updated_at" AS t0_r4, "topics"."views" AS t0_r5, "topics"."posts_count" AS t0_r6, "topics"."user_id" AS t0_r7, "topics"."last_post_user_id" AS t0_r8, "topics"."reply_count" AS t0_r9, "topics"."featured_user1_id" AS t0_r10, "topics"."featured_user2_id" AS t0_r11, "topics"."featured_user3_id" AS t0_r12, "topics"."deleted_at" AS t0_r13, "topics"."highest_post_number" AS t0_r14, "topics"."like_count" AS t0_r15, "topics"."incoming_link_count" AS t0_r16, "topics"."category_id" AS t0_r17, "topics"."visible" AS t0_r18, "topics"."moderator_posts_count" AS t0_r19, "topics"."closed" AS t0_r20, "topics"."archived" AS t0_r21, "topics"."bumped_at" AS t0_r22, "topics"."has_summary" AS t0_r23, "topics"."archetype" AS t0_r24, "topics"."featured_user4_id" AS t0_r25, "topics"."notify_moderators_count" AS t0_r26, "topics"."spam_count" AS t0_r27, "topics"."pinned_at" AS t0_r28, "topics"."score" AS t0_r29, "topics"."percent_rank" AS t0_r30, "topics"."subtype" AS t0_r31, "topics"."slug" AS t0_r32, "topics"."deleted_by_id" AS t0_r33, "topics"."participant_count" AS t0_r34, "topics"."word_count" AS t0_r35, "topics"."excerpt" AS t0_r36, "topics"."pinned_globally" AS t0_r37, "topics"."pinned_until" AS t0_r38, "topics"."fancy_title" AS t0_r39, "topics"."highest_staff_post_number" AS t0_r40, "topics"."featured_link" AS t0_r41, "topics"."reviewable_score" AS t0_r42, "topics"."image_upload_id" AS t0_r43, "topics"."slow_mode_seconds" AS t0_r44, "topics"."bannered_until" AS t0_r45, "topics"."external_id" AS t0_r46, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1, "categories"."color" AS t1_r2, "categories"."topic_id" AS t1_r3, "categories"."topic_count" AS t1_r4, "categories"."created_at" AS t1_r5, "categories"."updated_at" AS t1_r6, "categories"."user_id" AS t1_r7, "categories"."topics_year" AS t1_r8, "categories"."topics_month" AS t1_r9, "categories"."topics_week" AS t1_r10, "categories"."slug" AS t1_r11, "categories"."description" AS t1_r12, "categories"."text_color" AS t1_r13, "categories"."read_restricted" AS t1_r14, "categories"."auto_close_hours" AS t1_r15, "categories"."post_count" AS t1_r16, "categories"."latest_post_id" AS t1_r17, "categories"."latest_topic_id" AS t1_r18, "categories"."position" AS t1_r19, "categories"."parent_category_id" AS t1_r20, "categories"."posts_year" AS t1_r21, "categories"."posts_month" AS t1_r22, "categories"."posts_week" AS t1_r23, "categories"."email_in" AS t1_r24, "categories"."email_in_allow_strangers" AS t1_r25, "categories"."topics_day" AS t1_r26, "categories"."posts_day" AS t1_r27, "categories"."allow_badges" AS t1_r28, "categories"."name_lower" AS t1_r29, "categories"."auto_close_based_on_last_post" AS t1_r30, "categories"."topic_template" AS t1_r31, "categories"."contains_messages" AS t1_r32, "categories"."sort_order" AS t1_r33, "categories"."sort_ascending" AS t1_r34, "categories"."uploaded_logo_id" AS t1_r35, "categories"."uploaded_background_id" AS t1_r36, "categories"."topic_featured_link_allowed" AS t1_r37, "categories"."all_topics_wiki" AS t1_r38, "categories"."show_subcategory_list" AS t1_r39, "categories"."num_featured_topics" AS t1_r40, "categories"."default_view" AS t1_r41, "categories"."subcategory_list_style" AS t1_r42, "categories"."default_top_period" AS t1_r43, "categories"."mailinglist_mirror" AS t1_r44, "categories"."minimum_required_tags" AS t1_r45, "categories"."navigate_to_first_post_after_read" AS t1_r46, "categories"."search_priority" AS t1_r47, "categories"."allow_global_tags" AS t1_r48, "categories"."reviewable_by_group_id" AS t1_r49, "categories"."read_only_banner" AS t1_r50, "categories"."default_list_filter" AS t1_r51, "categories"."allow_unlimited_owner_edits_on_first_post" AS t1_r52, "categories"."default_slow_mode_seconds" AS t1_r53, "categories"."uploaded_logo_dark_id" AS t1_r54, "tags"."id" AS t2_r0, "tags"."name" AS t2_r1, "tags"."created_at" AS t2_r2, "tags"."updated_at" AS t2_r3, "tags"."pm_topic_count" AS t2_r4, "tags"."target_tag_id" AS t2_r5, "tags"."description" AS t2_r6, "tags"."public_topic_count" AS t2_r7, "tags"."staff_topic_count" AS t2_r8 FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN "topic_tags" ON "topic_tags"."topic_id" = "topics"."id" LEFT OUTER JOIN "tags" ON "tags"."id" = "topic_tags"."tag_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (NOT ( pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL) )) AND "topics"."id" IN (477, 481, 480, 479, 478, 467, 466, 230, 209, 183, 173, 179, 168, 139, 102, 144, 150, 118, 126, 88, 63, 46, 117, 171, 45, 77, 154, 158, 43, 79) ORDER BY topics.bumped_at DESC
```

Note how there are two extra queries which has to select `DISTINCT
topics.pinned_at` and `DISTINCT topics.bumped_at` because of the
unnecessary left joins to the `topic_tags` and `tags` table result in
duplicated rows in the topic tables. As a result, PG is not able to
use our indexes to effectively execute the query.

Comparing this to the queries being executed when `preload(:tags)` is
used.

```
SELECT "topics"."id" AS t0_r0, "topics"."title" AS t0_r1, "topics"."last_posted_at" AS t0_r2, "topics"."created_at" AS t0_r3, "topics"."updated_at" AS t0_r4, "topics"."views" AS t0_r5, "topics"."posts_count" AS t0_r6, "topics"."user_id" AS t0_r7, "topics"."last_post_user_id" AS t0_r8, "topics"."reply_count" AS t0_r9, "topics"."featured_user1_id" AS t0_r10, "topics"."featured_user2_id" AS t0_r11, "topics"."featured_user3_id" AS t0_r12, "topics"."deleted_at" AS t0_r13, "topics"."highest_post_number" AS t0_r14, "topics"."like_count" AS t0_r15, "topics"."incoming_link_count" AS t0_r16, "topics"."category_id" AS t0_r17, "topics"."visible" AS t0_r18, "topics"."moderator_posts_count" AS t0_r19, "topics"."closed" AS t0_r20, "topics"."archived" AS t0_r21, "topics"."bumped_at" AS t0_r22, "topics"."has_summary" AS t0_r23, "topics"."archetype" AS t0_r24, "topics"."featured_user4_id" AS t0_r25, "topics"."notify_moderators_count" AS t0_r26, "topics"."spam_count" AS t0_r27, "topics"."pinned_at" AS t0_r28, "topics"."score" AS t0_r29, "topics"."percent_rank" AS t0_r30, "topics"."subtype" AS t0_r31, "topics"."slug" AS t0_r32, "topics"."deleted_by_id" AS t0_r33, "topics"."participant_count" AS t0_r34, "topics"."word_count" AS t0_r35, "topics"."excerpt" AS t0_r36, "topics"."pinned_globally" AS t0_r37, "topics"."pinned_until" AS t0_r38, "topics"."fancy_title" AS t0_r39, "topics"."highest_staff_post_number" AS t0_r40, "topics"."featured_link" AS t0_r41, "topics"."reviewable_score" AS t0_r42, "topics"."image_upload_id" AS t0_r43, "topics"."slow_mode_seconds" AS t0_r44, "topics"."bannered_until" AS t0_r45, "topics"."external_id" AS t0_r46, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1, "categories"."color" AS t1_r2, "categories"."topic_id" AS t1_r3, "categories"."topic_count" AS t1_r4, "categories"."created_at" AS t1_r5, "categories"."updated_at" AS t1_r6, "categories"."user_id" AS t1_r7, "categories"."topics_year" AS t1_r8, "categories"."topics_month" AS t1_r9, "categories"."topics_week" AS t1_r10, "categories"."slug" AS t1_r11, "categories"."description" AS t1_r12, "categories"."text_color" AS t1_r13, "categories"."read_restricted" AS t1_r14, "categories"."auto_close_hours" AS t1_r15, "categories"."post_count" AS t1_r16, "categories"."latest_post_id" AS t1_r17, "categories"."latest_topic_id" AS t1_r18, "categories"."position" AS t1_r19, "categories"."parent_category_id" AS t1_r20, "categories"."posts_year" AS t1_r21, "categories"."posts_month" AS t1_r22, "categories"."posts_week" AS t1_r23, "categories"."email_in" AS t1_r24, "categories"."email_in_allow_strangers" AS t1_r25, "categories"."topics_day" AS t1_r26, "categories"."posts_day" AS t1_r27, "categories"."allow_badges" AS t1_r28, "categories"."name_lower" AS t1_r29, "categories"."auto_close_based_on_last_post" AS t1_r30, "categories"."topic_template" AS t1_r31, "categories"."contains_messages" AS t1_r32, "categories"."sort_order" AS t1_r33, "categories"."sort_ascending" AS t1_r34, "categories"."uploaded_logo_id" AS t1_r35, "categories"."uploaded_background_id" AS t1_r36, "categories"."topic_featured_link_allowed" AS t1_r37, "categories"."all_topics_wiki" AS t1_r38, "categories"."show_subcategory_list" AS t1_r39, "categories"."num_featured_topics" AS t1_r40, "categories"."default_view" AS t1_r41, "categories"."subcategory_list_style" AS t1_r42, "categories"."default_top_period" AS t1_r43, "categories"."mailinglist_mirror" AS t1_r44, "categories"."minimum_required_tags" AS t1_r45, "categories"."navigate_to_first_post_after_read" AS t1_r46, "categories"."search_priority" AS t1_r47, "categories"."allow_global_tags" AS t1_r48, "categories"."reviewable_by_group_id" AS t1_r49, "categories"."read_only_banner" AS t1_r50, "categories"."default_list_filter" AS t1_r51, "categories"."allow_unlimited_owner_edits_on_first_post" AS t1_r52, "categories"."default_slow_mode_seconds" AS t1_r53, "categories"."uploaded_logo_dark_id" AS t1_r54 FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL)) ORDER BY "topics"."pinned_at" DESC LIMIT 30

SELECT "topic_tags".* FROM "topic_tags" WHERE "topic_tags"."topic_id" = 7

SELECT "topics"."id" AS t0_r0, "topics"."title" AS t0_r1, "topics"."last_posted_at" AS t0_r2, "topics"."created_at" AS t0_r3, "topics"."updated_at" AS t0_r4, "topics"."views" AS t0_r5, "topics"."posts_count" AS t0_r6, "topics"."user_id" AS t0_r7, "topics"."last_post_user_id" AS t0_r8, "topics"."reply_count" AS t0_r9, "topics"."featured_user1_id" AS t0_r10, "topics"."featured_user2_id" AS t0_r11, "topics"."featured_user3_id" AS t0_r12, "topics"."deleted_at" AS t0_r13, "topics"."highest_post_number" AS t0_r14, "topics"."like_count" AS t0_r15, "topics"."incoming_link_count" AS t0_r16, "topics"."category_id" AS t0_r17, "topics"."visible" AS t0_r18, "topics"."moderator_posts_count" AS t0_r19, "topics"."closed" AS t0_r20, "topics"."archived" AS t0_r21, "topics"."bumped_at" AS t0_r22, "topics"."has_summary" AS t0_r23, "topics"."archetype" AS t0_r24, "topics"."featured_user4_id" AS t0_r25, "topics"."notify_moderators_count" AS t0_r26, "topics"."spam_count" AS t0_r27, "topics"."pinned_at" AS t0_r28, "topics"."score" AS t0_r29, "topics"."percent_rank" AS t0_r30, "topics"."subtype" AS t0_r31, "topics"."slug" AS t0_r32, "topics"."deleted_by_id" AS t0_r33, "topics"."participant_count" AS t0_r34, "topics"."word_count" AS t0_r35, "topics"."excerpt" AS t0_r36, "topics"."pinned_globally" AS t0_r37, "topics"."pinned_until" AS t0_r38, "topics"."fancy_title" AS t0_r39, "topics"."highest_staff_post_number" AS t0_r40, "topics"."featured_link" AS t0_r41, "topics"."reviewable_score" AS t0_r42, "topics"."image_upload_id" AS t0_r43, "topics"."slow_mode_seconds" AS t0_r44, "topics"."bannered_until" AS t0_r45, "topics"."external_id" AS t0_r46, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1, "categories"."color" AS t1_r2, "categories"."topic_id" AS t1_r3, "categories"."topic_count" AS t1_r4, "categories"."created_at" AS t1_r5, "categories"."updated_at" AS t1_r6, "categories"."user_id" AS t1_r7, "categories"."topics_year" AS t1_r8, "categories"."topics_month" AS t1_r9, "categories"."topics_week" AS t1_r10, "categories"."slug" AS t1_r11, "categories"."description" AS t1_r12, "categories"."text_color" AS t1_r13, "categories"."read_restricted" AS t1_r14, "categories"."auto_close_hours" AS t1_r15, "categories"."post_count" AS t1_r16, "categories"."latest_post_id" AS t1_r17, "categories"."latest_topic_id" AS t1_r18, "categories"."position" AS t1_r19, "categories"."parent_category_id" AS t1_r20, "categories"."posts_year" AS t1_r21, "categories"."posts_month" AS t1_r22, "categories"."posts_week" AS t1_r23, "categories"."email_in" AS t1_r24, "categories"."email_in_allow_strangers" AS t1_r25, "categories"."topics_day" AS t1_r26, "categories"."posts_day" AS t1_r27, "categories"."allow_badges" AS t1_r28, "categories"."name_lower" AS t1_r29, "categories"."auto_close_based_on_last_post" AS t1_r30, "categories"."topic_template" AS t1_r31, "categories"."contains_messages" AS t1_r32, "categories"."sort_order" AS t1_r33, "categories"."sort_ascending" AS t1_r34, "categories"."uploaded_logo_id" AS t1_r35, "categories"."uploaded_background_id" AS t1_r36, "categories"."topic_featured_link_allowed" AS t1_r37, "categories"."all_topics_wiki" AS t1_r38, "categories"."show_subcategory_list" AS t1_r39, "categories"."num_featured_topics" AS t1_r40, "categories"."default_view" AS t1_r41, "categories"."subcategory_list_style" AS t1_r42, "categories"."default_top_period" AS t1_r43, "categories"."mailinglist_mirror" AS t1_r44, "categories"."minimum_required_tags" AS t1_r45, "categories"."navigate_to_first_post_after_read" AS t1_r46, "categories"."search_priority" AS t1_r47, "categories"."allow_global_tags" AS t1_r48, "categories"."reviewable_by_group_id" AS t1_r49, "categories"."read_only_banner" AS t1_r50, "categories"."default_list_filter" AS t1_r51, "categories"."allow_unlimited_owner_edits_on_first_post" AS t1_r52, "categories"."default_slow_mode_seconds" AS t1_r53, "categories"."uploaded_logo_dark_id" AS t1_r54 FROM "topics" LEFT OUTER JOIN "categories" ON "categories"."id" = "topics"."category_id" LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = 29) LEFT JOIN category_users ON category_users.category_id = topics.category_id AND category_users.user_id = 29 WHERE "topics"."deleted_at" IS NULL AND (topics.archetype <> 'private_message') AND (COALESCE(categories.topic_id, 0) <> topics.id) AND (COALESCE(tu.notification_level,1) > 0) AND (topics.category_id = -1
                OR
                (COALESCE(category_users.notification_level, 1) <> 0 AND (topics.category_id IS NULL OR topics.category_id NOT IN(-1)))

                OR tu.notification_level > 1) AND (NOT ( pinned_globally AND  pinned_at IS NOT NULL  AND (topics.pinned_at > tu.cleared_pinned_at OR tu.cleared_pinned_at IS NULL) )) ORDER BY topics.bumped_at DESC LIMIT 30

SELECT "topic_tags".* FROM "topic_tags" WHERE "topic_tags"."topic_id" IN (477, 481, 480, 479, 478, 467, 466, 230, 209, 183, 173, 179, 168, 139, 102, 144, 150, 118, 126, 88, 63, 46, 117, 171, 45, 77, 154, 158, 43, 79)
SELECT "tags"."id", "tags"."name", "tags"."created_at", "tags"."updated_at", "tags"."pm_topic_count", "tags"."target_tag_id", "tags"."description", "tags"."public_topic_count", "tags"."staff_topic_count" FROM "tags" WHERE "tags"."id" IN (10, 20, 26, 7, 27, 28, 30, 19, 9, 4, 15, 29, 14, 18, 11, 25, 1, 21, 8, 22, 5, 32)
```

We end up with queries that are much more efficient as those queries can
effectively use the indexes.
2023-08-03 10:48:41 +08:00
Sérgio Saquetim
03690ccccf
DEV: Add :push_notification event and deprecate :post_notification_alert (#22917)
This commit introduces the :push_notification event and deprecates :post_notification_alert.

The old :post_notification_alert event was not triggered when pushing chat notifications and did not respect when the user was in "do not disturb" mode.

The new event fixes these issues.
2023-08-02 18:44:19 -03:00
Canapin
b3c722f2f7
FIX: created:@ search keyword for uppercase usernames (#22878)
The filter wasn't working if the username had uppercase letters.
2023-08-02 15:28:17 -04:00
David Taylor
13b13a758c
Remove discourse-canned-replies from official list (#22932)
This plugin is no longer supported, and so we no longer need to run its tests in CI

(removing the comment and the 'Canned Replies' value from the array caused syntax_tree to change to the `%w` syntax)
2023-08-02 12:48:20 +01:00
David Taylor
2d4be458a5
FEATURE: Bundle discourse-checklist plugin into core (#22927)
Formerly https://github.com/discourse/discourse-checklist
2023-08-02 10:17:24 +01:00
Alan Guo Xiang Tan
773b22e8d0
DEV: Seperate concerns of tracking GC stat from MethodProfiler (#22921)
Why this change?

This is a follow up to e8f7b62752.
Tracking of GC stats didn't really belong in the `MethodProfiler` class
so we want to extract that concern into its own class.

As part of this PR, the `track_gc_stat_per_request` site setting has
also been renamed to `instrument_gc_stat_per_request`.
2023-08-02 10:46:37 +08:00
Alan Guo Xiang Tan
e8f7b62752
DEV: Add site setting to allow collection of GC stats during requests (#22898)
What does this change do?

This change adds a hidden `track_gc_stat_per_request` site setting which
when enabled will track the time spent in GC, major GC count and minor
GC count during a request.

Why is this change needed?

We have plans to tune our GC in production but without any
instrumentation, we will not be able to know if our tuning is effective
or not. This commit takes the first step at instrumenting some basic GC
stats in core during a request which can then be consumed by the discourse-prometheus plugin.
2023-08-02 09:16:32 +08:00
Gerhard Schlager
ad0b8aed51
FIX: Use category hashtag instead of link in discourse_welcome_topic.body (#22875)
Linking to the #feedback category can break if the category gets renamed or a different site locale is used. By using the correct hashtag (at the time of seeding) this issues can be avoided.
2023-08-01 13:53:23 +02:00
David Taylor
20481abefc
Bump version to v3.2.0.beta1-dev 2023-08-01 09:50:46 +01:00
David Taylor
9b339bcd2c
Bump version to v3.1.0.beta8 2023-08-01 09:50:45 +01:00
David Taylor
bb217bbcc8
FIX: Ensure PresenceChannel does not raise error during readonly (#22899)
PresenceChannel configuration is cached using redis. That cache is used, and sometimes repopulated, during normal GET requests. When the primary redis server was readonly, that `redis.set` call would raise an error and cause the entire request to fail. Instead, we should ignore the failure and continue without populating the cache.
2023-08-01 09:34:57 +01:00
David Taylor
8a0d00b866
DEV: Introduce version_bump rake tasks (#22817)
This commit introduces five rake tasks to help us with version bump procedures:

- `version_bump:beta` and `version_bump:minor_stable` are for our minor releases
- `version_bump:major_stable_prepare` and `version_bump:major_stable_merge` are for our major release process
- `version_bump:stage_security_fixes` is to collate multiple security fixes from private branches into a single branch for release

The scripts will stage the necessary commits in a branch and prompt you to create a PR for review. No changes to release branches or tags will be made without the PR being approved, and explicit confirmation of prompts in the scripts.

To avoid polluting the operator's primary working tree, the scripts create a temporary git worktree in a temporary directory and perform all checkouts/commits there.
2023-07-31 16:05:13 +01:00
Blake Erickson
274c3c9396
DEV: Update code comment wording (#22861)
Follow up to: a8c504aee4

See: https://github.com/discourse/discourse/pull/22829#discussion_r1277747036
2023-07-28 11:47:22 -06:00
Selase Krakani
81cf481b16
FIX: Missing pending queued posts from topic view (#22838)
A previous change updated `ReviewableQueuedPost`'s `created_by`
to be consistent with other reviewable types. It assigns
the the creator of the post being queued to `target_created_by` and sets
the `created_by` to the creator of the reviewable itself.

This fix updates some of the `created_by` references missed during the
intial fix.
2023-07-28 16:16:23 +00:00
Blake Erickson
a8c504aee4
FIX: Internal oneboxes with github links (#22829)
Internal oneboxes to posts that contained oneboxed github links to
commits or PRs with long enough commit messages to have the `show-more`
and the `excerpt hidden` classes in their html were being stripped of
their content resulting in empty internal oneboxes.

see: https://meta.discourse.org/t/269436

This fixes a regression introduced in:

0b3cf83e3c
2023-07-28 09:07:53 -06:00
David Taylor
263afe6b6a
Bump version to v3.1.0.beta7 2023-07-28 13:05:29 +01:00
Daniel Waterworth
26e267478d
SECURITY: Don't allow a particular site to monopolize the defer queue 2023-07-28 12:53:51 +01:00
Alan Guo Xiang Tan
bfc3132bb2
SECURITY: Impose a upper bound on limit params in various controllers
What is the problem here?

In multiple controllers, we are accepting a `limit` params but do not
impose any upper bound on the values being accepted. Without an upper
bound, we may be allowing arbituary users from generating DB queries
which may end up exhausing the resources on the server.

What is the fix here?

A new `fetch_limit_from_params` helper method is introduced in
`ApplicationController` that can be used by controller actions to safely
get the limit from the params as a default limit and maximum limit has
to be set. When an invalid limit params is encountered, the server will
respond with the 400 response code.
2023-07-28 12:53:46 +01:00
OsamaSayegh
0976c8fad6
SECURITY: Don't reuse CSP nonce between anonymous requests 2023-07-28 12:53:44 +01:00
Jarek Radosz
3a11c82547
DEV: Update minitest to 5.19.0 (#22821) 2023-07-27 12:18:40 +02:00
David Taylor
6222a60335
DEV: Refactor Discourse::VERSION and add -dev support (#22807)
For the Discourse 3.2 beta series, we intend to use a `-dev` suffix while beta versions are being developed in `main`/`tests-passed`. When a beta version is ready, it will be 'released' without the `-dev` suffix.

This commit adds support for the `-dev` suffix, and also refactors `Discourse::VERSION` so that the canonical representation is a simple human-readable string. Constants for each segment are derived  from that, so the interface remains unchanged.
2023-07-26 14:48:08 +01:00
Ryan Vandersmith
44a104dff8
FIX: Update "Embed Motoko" Onebox URLs (#22198)
Embed Motoko service's primary URL is transiting from embed.smartcontracts.org to embed.motoko.org, this PR updates the Onebox logic to work for either domain.
2023-07-26 09:41:01 +08:00
David Taylor
00903f6b11
DEV: Support version operators in .discourse-compatibility (#22714)
This adds support for the `<=` and `<` version operators in `.discourse-compatibility` files. This allows for more flexibility (e.g. targeting the entire 3.1.x stable release via `< 3.2.0.beta1`), and should also make compatibility files to be more readable.

If an operator is not specified we default to `<=`, which matches the old behavior.
2023-07-25 14:04:39 +01:00
Ted Johansson
f380643528
DEV: Ensure don't feed the trolls feature considers active flags only (#22774)
We recently added a "don't feed the trolls" feature which warns you about interacting with posts that have been flagged and are pending review. The problem is the warning persists even if an admin reviews the post and rejects the flag.

After this change we only consider active flags when deciding whether to show the warning or not.
2023-07-25 15:12:22 +08:00
Ted Johansson
f1a43f2319
DEV: Handle SSL errors in push notification pusher (#22771)
We're seeing unhandled errors in production when web push notifications are failing with an SSL error. This is happening for a few users, but generating a large amount of log noise due to the sheer number of notifications.

This adds handling of SSL errors in two places:

1. In FinalDestination::HTTP, this is handled the same as a timeout error, and gives a chance to recover.
2. In PushNotificationPusher. This will cause the notification to retry a number of times, and if it keeps failing, disable push notifications for the user. (Existing behaviour.)

I wanted to wrap the SSL error in e.g. WebPush::RequestError, but the gem doesn't have request error handling, so didn't want to have the freedom patch diverge from the gem as well. Instead just propagating the raw SSL error.
2023-07-25 15:01:02 +08:00
Ted Johansson
0db98e9d86
DEV: Remove deprecated ascending param from AdminUserIndexQuery (#22741)
The parameter ascending was deprecated (replaced by asc) and marked for deletion in 2.9. This PR removes it. Since the resulting code was a simple one-liner, the method body was inlined instead.
2023-07-24 09:37:18 +08:00
Emmett Ling
978d52841a
FEATURE: Implement SiteSetting to Allow Anonymous Likes (#22131)
Allow anonymous users (logged-in, but set to anonymous posting) to like posts

---------

Co-authored-by: Emmett Ling <eling@zendesk.com>
Co-authored-by: Nat <natalie.tay@discourse.org>
2023-07-21 21:21:07 +08:00
Ted Johansson
a0ad7d0a04
DEV: Remove deprecated arguments to Plugin::Instande#add_api_parameter_route (#22736)
We deprecated the keywords method, route, and format (replaced with methods, actions, and formats respectively) as parameters to Plugin::Instance#add_api_parameter_route, marked for removal in 2.7. This PR deletes them.
2023-07-21 12:22:33 +08:00
Ted Johansson
a9a568d112
DEV: Remove warning for discontinued site_setting_saved event (#22735)
We used to have a deprecation warning here for the now defunct site_setting_saved event, marked for removal in 2.4. This PR deletes it.
2023-07-21 12:22:18 +08:00
Alan Guo Xiang Tan
1f2213414a
DEV: Remove redundant line of code (#22734)
No need for `limit(1)` when we are already calling `first`
2023-07-21 11:50:07 +08:00
Alan Guo Xiang Tan
40bedea75a
DEV: Remove duplicated line of code (#22733) 2023-07-21 11:37:36 +08:00
Ted Johansson
4ef8129bff
DEV: Remove deprecated whitelist methods (#22731)
These methods were deprecated and marked for removal in 2.6. This change deletes them.

These deprecations use raise_error: true, so the fallbacks are at this point unreachable and can't be used anyway.
2023-07-21 11:32:21 +08:00
Isaac Janzen
37942cb8bb
DEV: Convert admin-incoming-email modal to component-based API (#22701)
- Convert `admin-incoming-email` modal to component-based API
- Testing that the modal was working in local development was extremely challenging due to the need for `rejected` and `bounced` emails. Something that is not easy to stub in a local dev environment. To make this process more smooth for future developers I have added a new rake task:

```
desc "Creates sample email logs"
task "email_logs:populate" => ["db:load_config"] do |_, args|
  DiscourseDev::EmailLog.populate!
end
```

That will generate fully functional email logs in development to be toyed with.

<img width="787" alt="Screenshot 2023-07-20 at 3 27 04 PM" src="https://github.com/discourse/discourse/assets/50783505/47b3fe34-cd7e-49a5-8fe6-768c0fbd1aa2">
2023-07-20 16:31:20 -05:00
Juan David Martínez Cubillos
9e83d64723
DEV: Add Custom emoji sanitization (#22697)
* DEV: Add Custom emoji sanitization

* added tests for implemented changes
2023-07-19 14:09:26 -05:00
Blake Erickson
90f395a118
DEV: Skip srcset for onebox thumbnails (#22621)
* DEV: Skip srcset for onebox thumbnails

In an effort to preserve bandwidth especially for mobile devices this
change will prevent upscaled srcset attributes from being added to
onebox thumbnail images.

Besides checking the html for onebox classes, our database structure for
uploads does not distinguish between regular images and onebox thumbnail
images, but all upload images in discourse do have a thumbnail. By
default this thumbnail is what is used for the non-upscaled image for
onebox images, so we should only use that thumbnail. Because the
rendered onebox image size is likely smaller than the upload thumbnail
size there really shouldn't be a need to upscale.
2023-07-19 12:21:34 -06:00
Martin Brennan
3be9e6c97e
FIX: Hashtag error in PrettyText when processing email (#22680)
Followup to b583872eed
and 54001060ea

Another place where we need to filter hashtag types to
only enabled ones is PrettyText, though the latter PR
above should also already make it so the correct priority
types are passed.

This is causing errors in the email processing workflow
for some customers (presumably ones with tagging disabled).
2023-07-19 10:52:18 +10:00
Selase Krakani
3d554aa10e
FIX: Keep ReviewableQueuedPosts even with user delete reviewable actions (#22501)
Performing a `Delete User`/`Delete and Block User` reviewable actions for a
queued post reviewable from the `review.show` route results in an error
popup even if the action completes successfully.

This happens because unlike other reviewable types, a user delete action
on a queued post reviewable results in the deletion of the reviewable
itself. A subsequent attempt to reload the reviewable record results in
404. The deletion happens as part of the call to `UserDestroyer` which
includes a step for destroying reviewables created by the user being
destroyed. At the root of this is the creator of the queued post
being set as the creator of the reviewable as instead of the system
user.

This change assigns the creator of the reviewable to the system user and
uses the more approapriate `target_created_by` column for the creator of the
post being queued.
2023-07-18 11:50:31 +00:00
Martin Brennan
6459922993
DEV: Move Bookmark modal/component to use d-modal (#22532)
c.f. https://meta.discourse.org/t/converting-modals-from-legacy-controllers-to-new-dmodal-component-api/268057

This also converts the Bookmark component to a Glimmer
component.
2023-07-17 10:14:17 +10:00
Blake Erickson
9e8010df8b
DEV: Use thumbnail url for wikimedia onebox image (#22620)
Wikimedia provides a thumbnail url for its images, so we should use that
for oneboxes instead of the full-size image. Because the size of the
  onebox image we display is quite small anyways the thumbnail wikimedia
  provides should suffice and will save bandwidth.

See: https://meta.discourse.org/t/264039
2023-07-14 12:20:18 -06:00
Dax74
2d6fdf86f0
Add discourse-ai plugin (#22619) 2023-07-14 17:29:42 +02:00
Godfrey Chan
4d62c49e20
DEV: move raw handlebars to /raw-templates/ (#22574)
The primary motivation is to simplify `eagerLoadRawTemplateModules` which curently introspects the module dependencies (the `imports` at runtime). This is no longer supported in Embroider as the AMD shims do not have any dependencies (since it's managed internally with webpack).
2023-07-13 12:57:45 -05:00
David Taylor
9c915345ea
DEV: Simplify qunit target selection (#22591)
Previously we had three query parameters to control which tests would be run. The default was to run all core/plugin tests together, which would almost always lead to errors and does not match the way we run tests in CI.

This commit removes the three old parameters (skip_core, skip_plugins and single_plugin), and introduces a new 'target' parameter. This can have a value of 'core', 'plugins', 'all', or a specific plugin name. The default is 'core'. Attempting to use the old parameters will raise an error.
2023-07-13 14:20:00 +01:00
Alan Guo Xiang Tan
48c8ed49d6
FIX: Dismissing unread posts did not publish changes to other clients (#22584)
Why this change?

Prior to this change, dismissing unreads posts did not publish the
changes across clients for the same user. As a result, users can end up
seeing an unread count being present but saw no topics being loaded when
visiting the `/unread` route.
2023-07-13 18:05:56 +08:00
Joe
82c03127df
FEATURE: New Discourse Lightbox using Glimmer (#19798)
Introduces new lightbox as a step to migrate away from Magnific Popup.

Please see https://meta.discourse.org/t/migrating-away-from-magnific-popup/251505 for more details

Co-authored-by: Nat <natalie.tay@discourse.org>
Co-authored-by: David Battersby <info@davidbattersby.com>
2023-07-13 15:06:17 +08:00
Daniel Waterworth
b7404373cf
FIX: Always clear caches after committing the current transaction (#22550)
Instead of having to remember every time, just always wait until the
current transaction (if it exists) has committed before clearing any
DistributedCache.

The only exception to this is caches that aren't caching things from
postgres.

This means we have to do the test setup after setting the test
transaction, because doing the test setup involves clearing caches.

Reapplying this - it now doesn't use after_commit if skip_db is set
2023-07-12 09:49:28 -05:00
Roman Rizzi
61aeb2da90
FEATURE: Inline topic summary. Cached version accessible to everyone. (#22551)
* FEATURE:  Inline topic summary. Cached version accessible to everyone.

Anons and non-members of the `custom_summarization_allowed_groups_map` groups can see cached summaries for any accessible topic. After the first 12 hours and if the posts to summarize have changed, allowed users clicking on the button will automatically re-generate it.

* Ensure chat summaries work and prevent model hallucinations when there are no messages.
2023-07-12 11:21:51 -03:00
David Taylor
2fde58def4
DEV: Move avatar-utils into dedicated discourse-common module (#22517)
These avatar-related helper functions are used in pretty-text, which currently means we load the entire `discourse/lib/utilities` module into the mini-racer when running pretty-text on the server side. This stops us adding any logic or imports to discourse/lib/utilities which may depend on other `discourse/` namespace features.

This commit moves the avatar-related utils into a dedicated module in the `discourse-common` namespace, adds backwards-compatibility shims, and updates the pretty-text config accordingly.
2023-07-12 09:06:16 +01:00
liushuyu
8e63244e72
DEV: allow using CDN URL for all s3 uploads (#20755)
This adds an option to allow non-image s3 files to be downloaded through CDN URL.

Addresses the issues in:

* meta.discourse.org/t/s3-cdn-url-not-being-used-on-non-image-uploads/175332
* meta.discourse.org/t/s3-uploads-using-cdn-for-pdfs/213218
2023-07-12 12:06:49 +08:00
Blake Erickson
78b009aafe
Version bump to v3.1.0.beta6 (#22555) 2023-07-11 16:25:44 -06:00
Blake Erickson
eed7d86601
SECURITY: Don't reuse CSP nonce between requests (#22544)
Co-authored-by: OsamaSayegh <asooomaasoooma90@gmail.com>
2023-07-11 15:24:36 -06:00
Daniel Waterworth
cb794275a7
Revert "FIX: Always clear caches after committing the current transaction" (#22493)
This reverts commit 8310c7842c.

This was breaking precompilation.
2023-07-07 18:05:38 -05:00
Daniel Waterworth
9dd01ca2ef
FIX: Always clear caches after committing the current transaction (#22491)
Instead of having to remember every time, just always wait until the
current transaction (if it exists) has committed before clearing any
DistributedCache.

The only exception to this is caches that aren't caching things from
postgres.

This means we have to do the test setup after setting the test
transaction, because doing the test setup involves clearing caches.
2023-07-07 14:24:56 -05:00
Guhyoun Nam
b70bd4366b
FEATURE: Separated 'trusted users can edit others' setting for trust level 3 & 4 (#21493) 2023-07-07 10:48:14 -05:00
Matt Palmer
bd9c919e06
FIX: don't use etags for post-upload verification (#21923)
They don't work for server-side encryption with customer keys, and so instead we just use Content-MD5 to ensure there was no corruption in transit, which is the best we can do.

See also: https://meta.discourse.org/t/s3-uploads-incompatible-with-server-side-encryption/266853
2023-07-07 09:53:49 +02:00
Osama Sayegh
d2d6d727de
FEATURE: Add default site settings to control the defaults of navigation menu preferences (#22485)
Follow-up to b27e12445d

This commit adds 2 new site settings `default_sidebar_link_to_filtered_list` and `default_sidebar_show_count_of_new_items` to control the default values for the navigation menu preferences that were added in the linked commit (`sidebar_link_to_filtered_list` and `sidebar_show_count_of_new_items` respectively).
2023-07-07 04:52:10 +03:00
Rafael dos Santos Silva
3fd327c458
FEATURE: Basic support for threads.net onebox (#22471) 2023-07-06 16:02:49 -03:00
Alan Guo Xiang Tan
de7110d539
DEV: Add process pid to bin/turbo_tests --format documentation output (#22429)
Why this change?

The process's pid is useful when we're trying to link output from
different processes together. In this case, we want to be able to link
the Rails server logs to the right rspec process.

Before:

[2] Viewing sidebar mobile collapses the sidebar when clicking outside of it

After:

[2] (#176342) Viewing sidebar mobile collapses the sidebar when clicking outside of it
2023-07-05 11:47:35 +08:00
Krzysztof Kotlarek
134dcdd63a
FEATURE: allow user to override watched_precedence_over_muted setting (#22340)
Recently, site setting watched_precedence_over_muted was introduced - https://github.com/discourse/discourse/pull/22252

In this PR, we are allowing users to override it. The option is only displayed when the user has watched categories and muted tags, or vice versa.
2023-07-04 15:08:29 +10:00
Jan Cernik
77732cd2b4
FIX: Minor Twitter onebox improvements (#22387) 2023-07-03 19:53:12 -03:00
Martin Brennan
3f1024de76
DEV: Refactor DM channel creation into new service pattern (#22144)
This will be used when we move the channel creation for DMs
to happen when we first send a message in a DM channel to avoid
a double-request. For now we can just have a new API endpoint
for creating this that the existing frontend code can use,
that uses the new service pattern.

This also uses the new policy pattern for services where the policy
can be defined in a class so a more dynamic reason for the policy
failing can be sent to the controller.

Co-authored-by: Loïc Guitaut <loic@discourse.org>
2023-07-03 10:18:37 +10:00
David Taylor
e9387e238c
FIX: Do not follow redirects for twitter oneboxes (#22362)
Twitter is now redirecting anonymous users (with a browser-like user agent, which FinalDestination uses) to the login page. Skipping redirect-following for twitter.com will allow us to continue oneboxing tweets via the OpenGraph data and the API (when credentials are present).

https://meta.discourse.org/t/269371/17
2023-06-30 11:30:03 +01:00
Jarek Radosz
6b4762e450
DEV: Delete uglify asset codepath (#22346)
We no longer process any thrid-party assets with uglify
2023-06-29 12:23:46 +02:00
Neil Lalonde
d6a82f1034
DEV: Don't print plugin changes in details blocks (#22328) 2023-06-28 11:28:08 -04:00
Alan Guo Xiang Tan
68bb53a196
DEV: Fix failing spec after Rails upgrade to 7.0.5.1 (#22317)
Follow up to 4d3999de10
2023-06-28 08:17:11 +08:00
Alan Guo Xiang Tan
6c838c73e5
DEV: Add order:title filter to experimental /filter route (#22293)
This has been requested multiple times by users so it makes sense for us
to support this as well.

See https://meta.discourse.org/t/sort-display-of-topics-alphabetically/53911
2023-06-28 06:21:56 +08:00
Roman Rizzi
f4e7a80600
DEV: Cache summarization strategy results. (#22230)
Updates the interface for implementing summarization strategies and adds a cache layer to summarize topics once.

The cache stores the final summary and each chunk used to build it, which will be useful when we have to extend or rebuild it.
2023-06-27 11:44:34 -03:00
Krzysztof Kotlarek
9cf981f1f1
FEATURE: new watched_precedence_over_muted setting (#22252)
New setting which allow admin to define behavior when topic is in watched category and muted topic and vice versa.

If watched_precedence_over_muted setting is true, that topic is still visible in list of topics and notification is created.

If watched_precedence_over_muted setting is false, that topic is not still visible in list of topics and notification is skipped as well.
2023-06-27 14:49:34 +10:00
Rafael dos Santos Silva
3f7105e7cd
FEATURE: Try to load plugin gems platform variants (#21643) 2023-06-26 15:11:35 -03:00
Alessio Cosenza
56718504ac
FEATURE: Add hooks for email poller plugins (#21384)
While we are unable to support OAUTH2 with pop3 (due to upstream dependency ruby/net-pop#16), we are adding the support for mail pollers plugin. Doing so, it would be possible to write a plugin which then uses other ways (microsoft graph sdk for example) to poll emails from a mailbox.

The idea is that a plugin would define a class which inherits from Email::Poller and defines a poll_mailbox static method which returns an array of strings. Then the plugin could call register_mail_poller(<class_name>) to have it registered. All the configuration (oauth2 tokens, email, etc) could be managed by sitesettings defined in the plugin.
2023-06-26 13:16:03 +08:00
Vinoth Kannan
52894b9d7c
FEATURE: display commit hash for each plugin on /admin/plugins page. (#22176)
It will help to find out the current version of the plugins even without the `docker_manager` plugin.
2023-06-26 10:09:57 +05:30
Ted Johansson
a183f14d09
DEV: Update display name in new quote format - Part 2 (#22104)
This change adds support retroactively updating display names in the new quote format when the user's name is changed. It happens through a background job that is triggered by a callback when a user is saved with a new name.
2023-06-26 11:01:59 +08:00
Jan Cernik
24c90534fb
FIX: Use Twitter API v2 for oneboxes and restore OpenGraph fallback (#22187) 2023-06-22 14:39:02 -03:00
Sam
0de3b279ce
FEATURE: add db:resize:notification_id task for growing table (#20505)
Under exceptional cases people may need to resize the notification table.
This only happens on forums with a total of more than 2.5 billion notifications.

This rake task can be used to convert all the notification columns to
bigint to make more room.
2023-06-21 09:57:16 -04:00
Emmanuel Ferdman
722180edba
DEV: Typo in an output message of uploads.rake (#22209)
Signed-off-by: emmanuel-ferdman <emmanuelferdman@gmail.com>
2023-06-21 12:00:26 +08:00
Sam
9e241e82e9
DEV: use HTML5 version of loofah (#21522)
https://meta.discourse.org/t/markdown-preview-and-result-differ/263878

The result of this markdown had different results in the composer preview and the post. This is solved by updating Loofah to the latest version and using html5 fragments like our user had reported. While the change was only needed in cooked_post_processor.rb for this fix, other areas also had to be updated due to various side effects.
2023-06-20 09:49:22 +08:00
David Taylor
f8ea5b1136
DEV: Update TranslateAccelerator missing translation string (#22158)
Upstream added a capital 'T' to the 'Translation missing' message in https://github.com/ruby-i18n/i18n/commit/c5c6e753f3. This caused our translate accelerator patch to diverge, and the change in case affected a number of our specs. This commit updates the translate accelerator to match the upstream casing, and introduces a spec to detect future divergence.
2023-06-16 15:28:03 +01:00
Ted Johansson
d548231475
DEV: Remove old deprecation warnings where constants already removed (#22140)
We renamed these constants 3 years ago. This PR just removes the old deprecation notices.
2023-06-16 11:26:26 +08:00
Martin Brennan
9174716737
DEV: Remove Discourse.redis.delete_prefixed (#22103)
This method is a huge footgun in production, since it calls
the Redis KEYS command. From the Redis documentation at
https://redis.io/commands/keys/:

> Warning: consider KEYS as a command that should only be used in
production environments with extreme care. It may ruin performance when
it is executed against large databases. This command is intended for
debugging and special operations, such as changing your keyspace layout.
Don't use KEYS in your regular application code.

Since we were only using `delete_prefixed` in specs (now that we
removed the usage in production in 24ec06ff85)
we can remove this and instead rely on `use_redis_snapshotting` on the
particular tests that need this kind of clearing functionality.
2023-06-16 12:44:35 +10:00
Sérgio Saquetim
4b22e67c8b
DEV: Added modifier to change mentions extracted from cooked text (#21654)
Added a new modifier hook to allow plugins to modify the @mentions
extracted from a cooked text.

Use case: Some plugins may change how the mentions are cooked to prevent
them from being confused with user or group mentions and display the user
card.

This modifier hook allows the plugin to filter the mentions detected or add new ways
to add mentions into cooked text.
2023-06-15 10:52:52 -03:00
Krzysztof Kotlarek
2effcaa0f9
FIX: Update sidebar to be navigation menu (#22101)
Communities can use sidebar or header dropdown, therefore navigation menu is a better name settings in 2 places:

- Old user sidebar preferences;
- Site setting about default tags and categories.
2023-06-15 09:31:28 +10:00
Natalie Tay
3fe06bba9d
FIX: Allow half-merged user to be accessed in admin UI (#22105) 2023-06-14 19:19:12 +08:00
Ted Johansson
a674c6c4c2
DEV: Update username in new quote format - Part 1 (#22032)
When we introduced the new quote format with full-name display name:

```
[quote="Ted Johansson, post:1, topic:2, username:ted"]
we overlooked the code responsible for rewriting quotes when a user's name is changed.
```

The functional part of this change adds support for the new quote format in the code that updates quotes when a user's username changes. See the test case in `spec/services/username_changer_spec.rb` for the details.

In addition, this change adds a regression test for PrettyText to cover the new quote format, and extracts the code responsible for rewriting raw and cooked quotes into its own `QuoteRewriter` class. The functionality of the latter is tested through the tests in `spec/services/username_changer_spec.rb`.
2023-06-14 16:14:11 +08:00