When editing a color palette via the new page introduced in
https://github.com/discourse/discourse/pull/31742, it should apply the
color changes for the admin making the change automatically upon save.
Internal topic: t/148628/12.
The URLs returned by DiscourseConnect for the user's avatar, profile and
card backgrounds were not always correctly handling CDN.
This make use of the `GlobalPath.full_cdn_url` helper method which has
been battle-tested.
Ref - https://meta.discourse.org/t/-/356599
`/categories` sometimes returns accompanying topics under certain site
settings. The `CategoryList` currently allows preloading for topic
custom fields via `preloaded_topic_custom_fields`, but not for topics
themselves.
This addition is required for
https://github.com/discourse/discourse-solved/pull/342.
When multiple admins are working in the review queue, it's quite easy for two people to try and handle the same reviewable at the same time. This change addresses the two major situations where this can occur.
The `ReviewableClaimedTopic` model has been extended to allow the system to mark a reviewable as claimed as soon as the first moderator starts handling the reviewable, even when the `reviewable_claiming` setting is disabled. This ensures that reviewable actions with client-site activity (for example, `agree_and_suspend`) will lock the reviewable before another moderator starts working on it.
When someone handles handles a reviewable, we now use `MessageBus` to inform other moderators that it's changed. If any of the other moderator have that reviewable open (either individually, or on the list screen), it will automatically refresh that data.
Currently, the light version of mobile logo falls back to the desktop
version if the mobile version isn't set. It makes sense to have the same
fallback rule for the dark version as well, i.e. if there's no dark
mobile logo, use the dark desktop logo.
Internal topic: t/150316.
This PR renames a couple of settings related to anonymous mode:
1. `allow_anonymous_posting` → `allow_anonymous_mode`. This setting is
used as a switch for the entire anonymous mode feature, so it makes
sense to give it a generic name that better reflects what the setting
does.
2. `allow_anonymous_likes` → `allow_likes_in_anonymous_mode`. The new
name is clearer and will match a new setting that we'll add to allow
anonymous users to post in chat.
Internal topic: t/148088.
This commit changes the way Sidekiq is restarted when memory limit is
exceeded. The HUP signal was replaced with TERM, as mentioned in the
official documentation. The stopping timeout has been set to 10 seconds
to account for the Sidekiq timeout (5 seconds) and another for it
shutdown cleanly (5 more seconds).
See https://github.com/sidekiq/sidekiq/wiki/Deployment.
When hard deleting a first post by passing force_destroy: true as an option to PostDestroyer, the post/topic is correctly deleted, and the staff record is created, but the app then errors out.
This only happens on sites with a topic_destroyed webhook setup.
After deleting the record, we pass the topic's ID to TopicView, which then raises an error because it can not load it from the DB.
TopicView supports being initialized with either an ID or an already instantiated record. Since we still have the record in memory after deleting, we can pass that to TopicView.
When performing an action in the review queue, this change makes two improvements:
- The buttons on the reviewable item are disabled, so you can't accidentally multi-click.
- A toast is displayed when the action is complete, as a success indication.
Currently we allow for 2 theme screenshots to be specified,
with a lightweight spec to allow both a light and dark
version of the screenshot. However, we were not storing this
screenshot name anywhere, so we would not be able to use it
for light/dark switching.
This commit fixes that issue, and also does some general refactoring
around theme screenshots, and adds more tests.
This is a follow up to 6820622467ab3613e824f0cb6219def2a575bc1d.
This commit addresses migrations that uses `remove_index` with the
`if_exists: true` option to drop an existing index before creating an
index using the `concurrently` option.
This commit also reruns two migration which may have caused indexes to
be left in an `invalid` state.
### Reviewers Note
Plugin tests are failing due to
https://github.com/discourse/discourse-translator/pull/251
This improves the new rake task to bulk delete posts based on feedback:
- Honour the `can_permanently_delete` site setting.
- Fix a warning about a scope order no being applied due to batching.
- Fix an issue where double delete would result in duplicate user
histories.
There are a number of minor changes in this commit :
1. Combine the "Themes" and "Components" links in the admin sidebar into
a single tab labelled "Themes and components"
2. The combined tab links to the `/admin/config/customize/themes` page
(titled as "Themes and components")
3. Add a new "Components" tab to the "Themes and components" page.
There's already an existing "Themes" tab
4. Add a "back to" link at the top of individual theme/component page to
navigate back to the respective tab in the "Themes and components" page
5. Remove the themes/components list/sidebar that currently serves for
navigating between themes/components
6. Remove the header in the theme/component page
Changes 4–6 apply only if the admin sidebar is enabled; they have no
effect otherwise.
Internal topic: t/146006.
This commit updates `Migration::SafeMigrate` to protect against unsafe
ways of adding a Postgres index concurrently.
Per postgres documentation:
If a problem arises while scanning the table, such as a deadlock or a
uniqueness violation in a unique index,
the CREATE INDEX command will fail but leave behind an "invalid" index.
This index will be ignored for querying
purposes because it might be incomplete; however it will still consume
update overhead. The recommended recovery
method in such cases is to drop the index and try again to perform
CREATE INDEX CONCURRENTLY .
Therefore, the simplest way for us to ensure that migrations that create
indexes concurrently are idempotent is to follow postgres'
recommendation of dropping the index first before trying to create the
index concurrently.
A new user joining a community via DiscourseHub and logging in via oauth
goes through this process. This would break down for two reasons.
Reason 1: in some cases, especially on Safari mobile, the redirect in
the omniauth callback was happening too early. A new user may not be
signed in yet by that point, which means the redirect to
`/user-api-key/new` triggers a redirect to `/login` which ends up in a
bit of an infinite loop. Not all browsers exhibited this behaviour, but
Safari definitely did.
Reason 2: `/user-api-key/new` is gated via group membership using the
`user_api_key_allowed_groups` site setting. By default that is set to
include `trust_level_0`, however, auto group assignment wasn't taking
place for all user `create` events (only some that go through staged
users).
Followup https://github.com/discourse/discourse/pull/31505/
When sending notification emails for system user responses for PMs,
we removed the part of the CTA where it says "to respond to xyz" in a
previous commit.
This commit takes it slightly further -- we now only show a "Visit
Topic"
or "Visit Message" button if the PM notification is from a system user,
it's a bit cleaner.
This commit also adds more in-depth tests, and refactors the message
builder a little.
According to Zlib documentation, this is not a fatal error. In this case
we attempt to deflate the asset three times before giving up.
Sprockets will be replaced in the long term and this is an acceptable fix.
Fixes the same issue that
e9387e238c
did for twitter.com, but for x.com. Since they redirect
on our first FinalDestination call with onebox, the request
fails even with `twitter_consumer_secret` and `twitter_consumer_key`
settings set.
Follow up from https://github.com/discourse/discourse/pull/31559.
We expect some standard headers to be added from
`Rails.application.config.action_dispatch.default_headers` for
responses, however these were found to be removed in some error paths.
For more detail on this behaviour, refer to https://github.com/discourse/discourse/pull/31619#issuecomment-2699644232.
This PR adds those headers back if they aren't there, with the caveats
that we don't add headers that are irrelevant for non-HTML responses,
and neither do we add X-Frame-Options which is intentionally removed for
embeddables.
We are developing our new composer, and it would be useful
if we could know how posts are being created by members.
To this end, we are going to start storing the following
on post_stats, which are created at the same time as a post
is created:
* writing_device - Based on `BrowserDetection.device`, which in
turn is based on user agent. Will store .e.g iphone, android,
mac, windows etc.
* writing_user_agent - Stores the full user agent (truncated at
400 chars) of the device/browser the member used to write the post.
* composer_version - Either `1` for our old composer, or
`2` if the new rich composer is enabled in site settings and
the user has toggled it on
This PR adds a destroy:posts rake task that can be used to hard-delete a list of posts. Useful for dealing with large amounts of spam that has been soft deleted and needs to go.
Notes:
Works on both non-deleted and soft-deleted posts. (We might want to change this to work on only soft-deleted posts?)
Works exclusively on post IDs. We can't mix topic and post IDs as they might clash, and we have no way of resolving that ambiguity.
Accepts either a rake-style array of IDs or, more conveniently, you can pipe the argument in through STDIN.
Added a confirmation step since it's a fairly destructive operation.
Currently, after creating an API key, there is no way in the UI to see what scope the key has. To do this we need to first store the selected scope mode when creating a new key.
In this PR we:
- Convert scope_mode from a transient attribute to a database backed enum.
- Ship the possible values through the javascript:update_constants rake task instead of hard coding in front-end.
In follow-up PRs we will:
- Backfill existing API keys based on their associated api_key_scopes records.
- Start showing the scope mode in the UI.
When a post is flagged due to matching watched words, it can be difficult to know what you're looking for, particularly if you have a lot of watched words built up over a long period of time.
This change stores the list of matched words, and later displays them in the review queue, listing which Watched Words were responsible for the flag. Because watched words can change, this is recorded at the time the post is flagged. For posts that were flagged prior to this feature landing, it tries to guess the relevant words based on the current Watched Words set.
Redesigned page to update site logos. `AdminBrandingLogoFormComponent`
is attached to the old logos page and the new branding page. In the next
steps, branding will replace the logos page.
A new `AdminConfigAreaCardSection` component was added hidden and less
frequently used settings.
An image placeholder was also needed because many additional logos have
a fallback to the site logo.
Finally, `twitter_summary_large_image` was renamed to
`x_summary_large_image`.
Desktop

Mobile

As we’re currently using a namespace for Sidekiq, in order to upgrade to
the latest version, we need to drop it as it’s not supported anymore.
The recommended way is to use a different Redis DB for Sidekiq.
This patch uses a different config for Sidekiq and also takes care of
migrating existing jobs (in queues and the retry and scheduled sets).
This commit moves most of emoji logic into the discourse-emojis gem:
https://github.com/discourse/discourse-emojis/
Most notably:
- images are now symlinked from the gem
- the gem provides path to the json files
Search aliases have also been made asynchronous and memoized. When you
will search for an emoji we will now load the aliases and store the list
for future use.
---------
Co-authored-by: David Taylor <david@taylorhq.com>
In some error paths, headers that were set earlier can get overwritten
(e.g. `Cross-Origin-Opener-Policy`) by middleware such as
ActionDispatch::ShowExceptions.
This PR sets the `Cross-Origin-Opener-Policy` header to the value of the
SiteSetting `cross_origin_opener_policy_header` if it's missing and if
the response is for HTML.
In future, this DefaultHeaders middleware can be used to set other
default headers that relate to security or other purposes.
### Testing
<img width="631" alt="test"
src="https://github.com/user-attachments/assets/05106a40-2bc7-435d-91a2-4dd2a098f349"
/>
This change standardises the `User-Agent` header that Discourse will send when talking to other sites.
`Discourse.user_agent` is now the authority on what the user agent value should be. For Onebox requests, this changes the user agent from their existing value to match the new value (unless overridden).
For all other requests, `Net::HTTPHeader` is monkey-patched to add a default `User-Agent` header when one hasn't been provided.
Followup e26a1175d7c33746bddbc858ad89e68cc14beefe
Adds extra functionality and tests for the admin search modal.
* Show third level plugin config pages in search, e.g. AI Usage
* Remember last used search filters
* Allow navigating search results with keyboard, using tab or up/down
and enter to go to result
* Add a placeholder beneath search input to tell the admin what to do
* Add a full page search at `/admin/search` which can be reached from
pressing Enter on the search input
* Add specs for modal and full page search
* Change admin sidebar filter "no results found" to point to full page
search
* Add keyboard shortcut help to modal for admin search
We have a native dependency, Rinku, that's used only to make links in one place. We can get rid of this and use PrettyText instead.
This is almost a one-for-one replacement, but PrettyText adds rel="noopener nofollow ugc" to external links, which I suspect is actually what we want. It also wraps the result in a <p> tag, which we strip out for parity with Rinku.
When secure uploads are enabled, we need to send images that are rendered in the digest e-mail as attachments. Before this change, we would indiscriminately attach all images in the relevant topic's first post, whether they were rendered the e-mail body or not.
This change fixes that by only attaching images that are referenced in the e-mail body.