This is to try fix this error by sending the keys
on the element itself:
```
Failure/Error: page.send_keys([PLATFORM_KEY_MODIFIER, "v"])
Selenium::WebDriver::Error::StaleElementReferenceError:
stale element reference: stale element not found in the current frame
(Session info: chrome=135.0.7049.84); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
```
Avoid auto-linking URLs within code marks
Avoid auto-linking URLs when there's a `` ` `` just before the matched
URL, assuming the user is about to type a closing `` ` `` to create a
code mark instead.
This PR allows for other code to force the sidebar, but making the
language not admin-specific. There is a general problem to solve of "how
can I force the sidebar to appear, while keeping the hamburger menu in
the header as-is". This problem is solved for admin interface, but the
variable naming made it seem like the logic was directly tied to the
admin UI.
It is easy with this change, to have another sidebar manager force the
sidebar to be open for a route. Technically it was easy before, but it
would look like you were doing awful hacks.
---------
Co-authored-by: Isaac Janzen <isaac.janzen@discourse.org>
We don't want "ghost" `content` within mention/hashtag, as they're
already rendering their non-editable content on `toDOM`.
`atom: true` is not necessary for content-less nodes
Re-use existing heading node spec instead of re-creating.
A UX improvement from these changes is a better Cmd-Left/Home navigation
when these nodes begin a paragraph and the caret is after them.
Without the `useCapture` as `true` on this `ComposerUpload`'s
`addEventListener`, if the clipboard contained both a Files array and a
`text/html` item, the paste event would be captured by ProseMirror
before being captured by the `ComposerUpload` setup and its
`preventDefault` call.
* Hides the welcome banner search in mobile, it's not necessary
since there is the header search
* Fix an issue where the positioning of the search input for
the header on mobile was affecting welcome banner search
* Make the welcome font a bit smaller and with less padding so
it takes up less room on mobile
---------
Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
This eliminates the `/mobile` and `/desktop` `header.scss` files and
moves the relevant CSS under `common/header.scss` using `.mobile-view`
and breakpoints.
I was also able to eliminate some redundant and unused styles in the
process and move a couple more general styles to `discourse.scss`
Adds support for a tag-chooser in form templates. It supports single tag
and multi tags. The source of the displayed tags has to be a tag_group
name.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
### Changes made
#### Styling
1. added `4px` spacing between search input and cancel button
2. increased cancel button size (with padding) to match input height
3. decreased search panel padding by 1/2 from 16px to 8px
4. animation show/hide changed (from vertical to horizontal) and made it
quicker (300ms->200ms)
5. set html unscrollable when search panel is open
#### Dev
1. changed `data-test-` value to be consistent with other element's
attrs values
2. clear search action tag changed from `<a>` to `<DButton>`
This commit adds a `:sidekiq_job_error` event which is
triggered when an error is encountered while running a Sidekiq job. The
intent of this change is to complement the `:sidekq_job_ran` event which
can be used with the `:sidekiq_job_error` event to calculate the error
rate for each job.
When a reviewable is auto-claimed, this actually claims all reviewables in a given topic, rather than just the specific post that the reviewable is about. Particularly for sites that require all posts to be reviewed, this can quickly result in many reviewables being unintentionally claimed by the first moderator to review a post in a given topic.
Unfortunately, due to `ReviewableClaimedTopic` being originally built to claim by topic, we can't fully fix this without significant re-architecture. In the mean time, however, we can ensure that reviewables are unclaimed once they've been handled, so that the next reviewable from a topic won't be automatically claimed.
When a flagged chat message has already been deleted, we offer an option
in the review queue to agree with the flag and keep the message deleted.
However, this option is currently broken due to a missing implementation
for the option.
Internal topic: t/152203.
Allow admins to edit user custom flags. Because changing
name/description will update name/description for old reviewables,
warning has to be displayed.
Still, system flags can never be edited or deleted (only disabled).
The test being fixed in this commit was flaking with the following
error:
```
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching visible css ".stream-topic-title .title a[href*='/2']"
```
The regexp match on the `href` attribute has the potential to match
multiple elements if both urls contain the `/#{topic.id}` substring. For
example, both `/t/-/2/2` and `/t/-/1/2` will satisfy the regexp.
Followup 735bef9ea0d497ae7e04bd4a104bd86e928fc472
Remove the tip in search for users to press Ctrl+F
again to do browser search, as this is no longer relevant
This update adds randomized SVG placeholder designs for themes without
screenshots. Each placeholder pulls from the theme’s color palette to
give a better visual hint of what the theme might feel like.
There are a few different styles:
1. Envelope-style gradient – uses `tertiary`, `quaternary`, and
`highlight`
2. Soft blur gradient – uses `tertiary`, `quaternary`, and `highlight`
3. Mesh – uses `tertiary` and `secondary`
4. Layered waves – uses `tertiary`, `quaternary`, `highlight`, `danger`,
and `success`
The style is randomly picked per theme, so the grid looks more varied by
default.
<img width="1109" alt="image"
src="https://github.com/user-attachments/assets/5ede0ecf-a1e4-46c0-9fd2-bee4b6c672c2"
/>
Internal: /t/149935
---------
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This is already included in nginx's default set, and is currently
causing this message to be printed:
> nginx: [warn] duplicate extension "wasm", content type:
"application/wasm", previous content type: "application/wasm" in
/etc/nginx/conf.d/discourse.conf:4
It was using `<<` that caused the SiteSetting to get modified
Updated the spec to use the same pattern as other modifiers and used
`next` to return the correct value of the modifer
State seems to be leaking into other specs. Minimal repro:
```
bin/rspec --order defined spec/helpers/application_helper_spec.rb:783 spec/lib/wizard/wizard_builder_spec.rb:38
```
```
Failures:
1) Wizard::Builder introduction should not prefill default site setting values
Failure/Error: expect(description_field.value).to eq("")
expected: ""
got: " - modified by plugin"
```
Don't require password to create users with previous authentication in
the session, regardless of email/email verification during user creation
Before my changes we were calling `user.password_required!` in
`UserAuthenticator.authenticated?` based on whether authentication
session data contained a matching email address and email verified
externally. I believe `authenticated?` is intended for our own email
validation during the activation process, but shouldn't be a gate for
password requirement.
Even when we were setting `user.password_required`, then during user
creation we were creating random passwords to get around the blank
password restriction when creating accounts with external auth.
After my change we can remove the random password creation because we no
longer require a password when the session has previous authentication
info at all.
Looks like this extra password requirement may have been introduced as a
side effect during a previous refactor of `UserController` here:
51eff92170
Before that change, password requirement was simply based on whether
session[:authentication] existed, but after that change it was based on
the email/email_valid fields as well.
Building the Discourse JS app is very resource-intensive. This commit
introduces an `assemble_ember_build` script which will check the
existing content of the `dist/` directory and re-use the core build if
possible. Plugins will always be rebuilt.
For now, this functionality is only useful for multi-stage (i.e.
non-standard) Discourse deployments. But in future, this script may be
extended to pull the contents of the `dist/` directory from a remote
location.
This commits changes the language for components that aren't used on any themes to be "used/unused" instead of "active/inactive" throughout the new components listing page.
When an installed component doesn't have a description in its locale
files, we should fallback to the description that core has for the
component if it's one of the popular components that core features.
On sites with many components, serializing and rendering all components
in one-go on a page can take quite a bit of time. The new components
listing page that was introduced in
https://github.com/discourse/discourse/pull/32164 currently loads all
components in one-go, so this commit implements infinite-scrolling
pagination for the page to address this performance issue for sites with
many components.
Theme/component description is fetched via the `locale_fields` and
`theme_translation_overrides` associations on the `Theme` model. We're
currently not preloading these associations when serializing
themes/components, which causes an N+1 performance issue when accessing
the themes/components listing pages.
The `DMultiSelect` component provides a customizable multi-select
dropdown with support for both mouse and keyboard interactions.

### Parameters
#### `@loadFn` (required)
An async function that returns the data to populate the dropdown
options.
```javascript
const loadFn = async () => {
return [
{ id: 1, name: "Option 1" },
{ id: 2, name: "Option 2" },
];
};
```
#### `@compareFn`
A function used to determine equality between items. This is
particularly useful when working with complex objects. By default, `id`
will be used.
```javascript
const compareFn = (a, b) => {
return a.name === b.name;
};
```
#### `@selection`
An array of pre-selected items that will be displayed as selected when
the component renders.
```javascript
const selection = [
{ id: 1, name: "Option 1" },
{ id: 2, name: "Option 2" },
];
```
#### `@label`
Text label displayed in the trigger element when no items are selected.
```javascript
@label="Select options"
```
### Named Blocks
#### :selection
Block for customizing how selected items appear in the trigger.
```javascript
<:selection as |result|>{{result.name}}</:selection>
```
#### :result
Block for customizing how items appear in the dropdown list.
```javascript
<:result as |result|>{{result.name}}</:result>
```
#### :result
Block for customizing how errors appear in the component.
```javascript
<:error as |error|>{{error}}</:error>
```
### Example Usage
```javascript
<DMultiSelect
@loadFn={{this.loadOptions}}
@selection={{this.selectedItems}}
@compareFn={{this.compareItems}}
@label="Select options">
<:selection as |result|>{{result.name}}</:selection>
<:result as |result|>{{result.name}}</:result>
<:error as |error|>{{error}}</:error>
</DMultiSelect>
```
Co-Authored-By: Jordan Vidrine
<30537603+jordanvidrine@users.noreply.github.com>
---------
Co-authored-by: Jordan Vidrine <30537603+jordanvidrine@users.noreply.github.com>
Adding some changes to site.rb, and seeing this error:
```
Failure/Error: raise "Clearing modifiers during a plugin spec run will affect all future specs. Use unregister_modifier instead."
```
This is a standalone PR to update the usage here so that my future PR
won't be affected.
Adds a plugin outlet below the login buttons in the login-required
splash screen.
Use case: adding a custom footer to the splash screen with links to tos
and privacy policy.
This makes two tweaks to the setup wizard:
1. Move the branding (site logo) step to after styling (look & feel).
2. Remove the corporate (your organization) step.
This commit removes the Ctrl+F search shortcut, which hijacks the
browser search shortcut in Discourse. We are doing this because there
has been many years of complaints around this behaviour, and now that
we have the new header search it has become even more annoying.
We originally added this because we lazy load posts in a topic, and it
might not have been immediately clear to users that they are not
searching
all the posts in the topic when pressing Ctrl+F. However, that was 10+
years
ago, most people are very familiar with lazy loading now, it doesn't
make sense
to keep this additional hijack for this one topic use case.
Search is still accessible by pressing the `/` shortcut.
When we do replaceText in the ProseMirror TextManipulation lib,
we are creating a regex for the provided markdown. However this markdown
can have things like * (which is valid for a markdown list), which also
doubles as a regex symbol. We can escape the markdown provided to
the regex first to fix the issue.
It takes a moment to sync site settings. Therefore, it is better to pass
new values to `refreshPage` function.
Also, it was not working for some fonts like `JetBrains Mono`.
SiteSetting key is `jet_brains_mono` but font family value should be
`JetBrains Mono`.
`PreviewBase#drawPills` expects a `homepageStyle` option, but on the logo wizard page we weren't passing any. In this change we pass one of the possible values: `hot`.
Instead of using the matched string, this PR uses the actual sliced
content from the ProseMirror document range as the content when creating
the footnote node.
New lines on `alt`/`title` embedded media are not allowed, causing an
invalid serialized Markdown which is not supposed to contain new lines
for these attributes.
This PR removes them during HTML parsing – we are not creating this new
line scenario otherwise.
Previously, there was only those helpers in `crawler.html.erb` as we
added it in https://github.com/discourse/discourse/pull/32259
However, to keep it consistent, we need to add it in
`application.html.erb` as well.
Our `insertBlock` implementation was only taking the `firstChild` into
consideration.
We didn't notice this before because there's only one use of this in
core, quoting from a post, which has a single child.
This fix is necessary to support the `insert-block` call that the
https://github.com/discourse/discourse-templates plugin makes when
inserting a template.
Continues the work done on
https://github.com/discourse/discourse/pull/30815.
Adds a `footnote` node, parser, `^[inline]` input rule, toolbar button
item, and serializer.
Also adds a NodeView with an internal ProseMirror editor to edit the
footnote content.
`pos` is the exact position of the click in the entire document
`nodePos` is the position of the clicked node itself
The idea is that we want the click to be the first position **within the
node**.
The previous code was checking for the first 2 positions of the entire
document.
I'd love to add a test for this, but it's very tricky.
When using a html block within the rich editor (currently only possible
after parsing it from Markdown), it would get serialized with the
default escape=true processing.
Additionally, html blocks are a continuous block, if a `\n\n` is seen
during cooking the block is closed, so we make sure at most one
continuous `\n` is seen in the html block node on the ProseMirror doc.
EDIT: It turns out, it's not so simple. I'm merging this as is, but it's
important to link the actual behavior to close a paragraph from
markdown-it:
0fe7ccb4b7/lib/rules_block/html_block.mjs (L6-L17)
On some cases during serialization (like with headings), the previous
node is still "open" when it's the "turn" of the node we're serializing.
In this case, checking the boundaries before writing was getting the
wrong state, because the `write` call uses `flushClose()`, which makes
sure we have the newlines from closing the previous node.
This would create scenarios like (notice the space before each node
below the heading)
```markdown
# Heading
🎉
# Heading
@mention
# Heading
#hashtag
```
This PR makes sure we call flushClose() before checking boundaries, and
adds tests for that.
Repro steps:
1. Go to `/admin/customize/site_texts`
2. Click edit on any translation
3. Go back by clicking on "Back to search"
4. Click edit on another translation
5. Change the text field in any way
Expected results:
The disabled state on the "Save changes" button is removed and you're
able to click it
Actual results:
The "Save changes" button remains disabled
This happens because the computed property for the button's disabled
state doesn't get re-evaluated when navigating between translation
strings because it doesn't include on the `siteText` property in its
dependent properties, so changing the site text doesn't invalidate the
old value for the disabled state and it always stays the same.
Meta topic:
https://meta.discourse.org/t/i-cant-save-edit-on-site-texts/360990?u=osama
Followup 198dc813750d7a0de98cc94372dea1222b97743b
Indicate the shortcut in the tooltip for the rich/markdown
editor toggle, and also add the control translation for mac
in `translateModKey`
## Requirements
Initially defined in
https://meta.discourse.org/t/software-engineer-frontend-ember-js-yuriy-kurant/353612/14?u=yaran.
1. On mobile devices and tablets, users can open the search input field
by tapping the search icon
2. When opened, the search input takes over the entire header area:
- Users can tap the sliders icon to navigate to the advanced search page
(/search)
- Users can tap Cancel to close search input
3. After submitting a search, results area will take over the entire
screen:
- Users can tap the X icon to clear the search term from the input field
- Users can tap Cancel to close search results area and return to their
previous page (i.e. search area overlays content)
## Implementation
1. When opened, the search input takes over the entire header area:
- changed panel width from `90vw` to `100vw`
- on initial render (when search input is empty), search panel hovers over the header section only (doesn't cover main content below)
- quick tip and recent searches lists are not displayed on mobile view
2. Tap on the sliders icon navigates to the advanced search page
(`/search`)
3. Tap on the **Cancel** button:
- closes search menu
- if the search term is present, it is cleared
4. Tap on the left-hand side **Search** icon triggers a topics search
## Dev notes
1. Added `// TODO` questioning `search` service `noResults` default value of `false`
2. Added animation on toggling header search panel (created `delayed-destroy` custom modifier)
3. Extracted in-context search filters into a standalone component `search-menu/active-filters`:
- mobile: active filters below search input
- desktop: active filters inside search input
3. Removed unnecessary top padding when search results are empty
4. Added `data-test-` attrs for Ember tests. Benefits:
- semantically `data-test-` attrs indicate that these parts of the layout are covered with tests
- decouples selector dependency on `id/class` names for testing purposes - eliminates risk of broken tests due to `id/class` name changes
Our service worker concatenates `baseUrl` with `url` coming from the
payload:
e8f4433872/app/views/static/service-worker.js.erb (L96)
This PRs strips the base path before sending `url` to the push
notification to avoid having a URL with the base path duplicated,
causing a 404.
In https://github.com/discourse/discourse/pull/32159 we overrode the
`og:` and `twitter:` title and description but for some crawlers, we
need to override the `title` and `description` meta tag as well.
Sharing a controller seems to make query params behave more weirdly than
normal. This change lets Ember create a separate controller instance for
each route, even though they will still share the same class.
When a watched words CSV file is uploaded, we assume it's utf-8 encoded, but that's not always going to be the case. This change loads the CSV and converts it to utf-8 before processing it.
Changing the `CORE_SVG_SPRITES` constant to a method enables us to
detect SVG files that are written to the vendor directory during plugin
initialization (as done by the [FontAwesome pro
plugin](79a8d39fb4/plugin.rb (L31)))
which happens after all the ruby classes/files are read and loaded into
memory.
Internal topic: t/151476.
Relaxed restrictions:
- Automation posts are not validated for similarity. This was causing
error when PMs were created by regular user with same content and sent
to different users.
- Don't create warning logs when PM target does not exist anymore. When
for example spammer was deleted, delayed PM is not sent, but it is
correct behaviour;
- Allow tags to be applied even if an automation user is not allowed to
tag;
- Restricted category tags should be visible in configuration UI. Still,
they will be applied only when specific topic belongs to correct
category.
Ensure `last_read_post_number` is set to the latest post for imported
topic users with a `watching` notification level, even if they haven't
engaged with the topic
Added in https://github.com/discourse/discourse/pull/32086 this
prioritization did not accounted for when the user was replying to the
topic/OP.
Now when replying to the topic, the author will be prioritized in the
list.
All other cases are the same as before.
Added testing for all cases and changed `replyingToUser` to
`replyingToUserId` for clarity and consistency with API.
---------
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
The style type data attribute is missing for cooked posts prior to
#31795 which meant that older posts with category hashtags were not
displaying correctly. We already have a default style type at the
database level, but for hashtag styling it is handled based on stored
markup at the point in time when the post was last cooked.
Simply rebuilding html solves this but a good workaround is to set a
proper default when the data attr is missing.
The previous code could attempt to log a `nil` `run_time` if the block
would raise an exception. This commit adds two safeguards:
- rescue any exception to still compute `run_time`
- defaults to `0` if we still don't have any `run_time`
Add new advanced search filters for post types
- `in:bot` or `in:bots`: Filters for posts made by bot users (user_id <
0)
- `in:human` or `in:humans`: Filters for posts made by human users
(user_id >= 0)
- `in:whisper` or `in:whispers`: Filters for whisper posts (respects
permissions)
- `in:regular`: Filters for regular posts only
Currently, this is the order of i18n translate function:
1. Translation in required language;
2. Optional `defaultValue` provided;
3. Fallback to forum default language.
When admin set language as English GB, translation was not correctly
displayed as it went to step 2 and displayed `defaultValue` instead of
correct translation from default language.
discourse presence can be disabled, given it's a plugin the `composerPresenceManager` service couldn't be present anymore and would cause an error.
No test as it's testing imbrication of multiple plugins and hard to test reliably.
This fixes a bug on Safari where, for some reason, it was leaking to
affect the rich editor list item rendering. When typing, the current
list item was breaking to a next line.
With this change, it doesn't happen anymore, and the more specific `svg`
target will serve the same purpose as it's currently defined on
`InputTip`.
Added button to remove password from account if user has a linked
external account or passkey
The button only displays if the user has at least one associated account
or a passkey set up. Uses the ConfirmSession dialog in addition to a
warning about deleting the password.
Users can still reset their password via the Reset Password button
(which will now display "Set Password" if they've removed it).
Also prevent user from removing their last remaining associated account
or passkey if they have no password set.
Replaces PR #31489 from my personal repo, with some fixes for conflicts
since then.
mailgun no longer offers a 5k/3 month trial. it offers a 50k/30 day
trial. the "Basic" level for $15/mo with 10k emails has no ability to
trial it for any time.
Adjusts the rich editor onebox handling to ensure the full protocol URLs
are passed to the inline/full onebox endpoints, but when they are
resolved, we keep the original textual content as the stored URLs for
the onebox nodes, because we need to keep them as-is when serializing
them back to Markdown.
Additionally, it now re-uses the `isTopLevel` logic for inline oneboxes,
so top level domains like `example.com` are not mistakenly inline
oneboxed.
This PR refactors the scrolling mixin into a proper Ember service,
moving away from the mixin pattern. We will leave the actual removal of
the scolling mixin for a separate PR as there are still other
repositories dependent on it.
I considered refactoring to an ember-modifier but that will have to come
later if we want to go with a smaller refactor for `load-more` -
`load-more` is used in non-core repos and so we shouldn't change its
behaviour too drastically. The service approach allows us to do this in
a more gradual fashion as it can replace the `Scrolling` mixin directly
in `load-more` in this iteration.
For reference, this is the mixin we are refactoring:
38bd0b3f86/app/assets/javascripts/discourse/app/mixins/scrolling.js
Follow-up to https://github.com/discourse/discourse/pull/31887
This commit introduces a new design for the components listing page, which
is not linked from anywhere in the UI at the moment, but it can be
accessed by heading to the `/admin/config/customize/components` path
directly. We'll make this new design available from the sidebar and
remove the old page once we've tested and validated the new design
internally.
Internal topic: t/146007.
---------
Co-authored-by: Ella <ella.estigoy@gmail.com>
Using markdown-it's linkify strategy, this plugin adds marks to regular
text that happens to be identified as a URL, and removes marks from
link-marked text that happens to not be a valid URL anymore.
Additionally, it changes the onebox handling to only act if we're not in
the same word as the link to be oneboxed.
This commit fixes a bug where, if you're editing or creating a category,
selecting a parent category for the category that's being edited/created
causes the rest of the fields in the category edit/create form to be
cleared out.
Internal topic: t/151232.
When running specs I noticed this mixed-decls Sass deprecation warning:
> Deprecation Warning [mixed-decls]: Sass's behavior for declarations
that appear after nested
> rules will be changing to match the behavior specified by CSS in an
upcoming
> version. To keep the existing behavior, move the declaration above the
nested
> rule. To opt into the new behavior, wrap the declaration in & {}.
>
> More info: https://sass-lang.com/d/mixed-decls
This change address that issue.
Follow up to: 928f9175f05413289c45d3fd6424f62956ea0944
* It only imports users and emails so far
* It stores mapped IDs and usernames in a SQLite DB. In the future, we might want to copy those into the Discourse DB at the end of a migration.
* The importer is split into steps which can mostly be configured with a simple DSL
* Data that needs to be shared between steps can be stored in an instance of the `SharedData` class
* Steps are automatically sorted via their defined dependencies before they are executed
* Common logic for finding unique names (username, group name) is extracted into a helper class
* If possible, steps try to avoid loading already imported data (via `mapping.ids` table)
* And steps should select the `discourse_id` instead of the `original_id` of mapped IDs via SQL
* Splits the existing script into multiple classes
* Adds command for generating IntermediateDB schema (`migrations/bin/cli schema generate`)
* Changes the syntax of the IntermediateDB schema config
* Adds validation for the schema config
* It uses YAML schema aka JSON schema to validate the config file
* It generates the SQL schema file and Ruby classes for storing data in the IntermediateDB
* Updates GitHub Action for migrations
* Rubocop: Always `EnforcedShorthandSyntax` for hashes in the `migrations` directory
* Automatically load all available converter steps
* Enable YJIT at runtime, if available
* Progressbar shows skipped records and other small improvements
`GroupsController#search` now accepts an `include_everyone` parameter to
include the "everyone" group in the results. discourse-ai relies on this
endpoint for configuring personas' allowed groups and needs this group
to be present. You still need to be able to see the group.
New configure fonts section was added. Because now we have two sections
completed (logos and fonts), new /branding page was introduced and old
/logo and /font pages was removed.
When text size is changed, modal is displayed to ask if preferences of
existing users should be retrospectively updated.
https://github.com/user-attachments/assets/f6b0c92a-117f-4064-bd76-30fa05acc6d3
---------
Co-authored-by: Ella <ella.estigoy@gmail.com>
Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
Followup c4d971ea2c7677d893e0d6fdcec5d7d93d9c964e
There are some customizations still relying on #search-menu
for an ID, this provides a fallback to that ID if no
@searchInputId arg is provided while we work through the
customizations.
I've considered some _very_ generic, like:
```rb
DiscoursePluginRegistry.apply_modifier(:"meta_data_content_for_#{property}",
content, property, opts)
```
But, I think for now, we can use this one and rely on the plugin to do the filtering (`if property == :title`)
This change makes the cache size either 100, or the 10% of topics with
activity since the hot topic days cutoff, whatever is lower.
We observed that in sites with a small number of topics, everything is
flagged as hot, which while true, defeats the purpose of the feature.
When replying to a post, the user who is getting the reply should be
prioritized in the autocomplete
- Added in composer a getter for getting `.replyingToUser`
- Added in d-editor the reference to the user that is getting the
reply(`this.composer.replyingToUser`)
- Passed along the reference to the user that is getting the reply to
the user-search service as `replyingToUser`
- Controller `users_controller.rb` was modified to accept the `user_id`
parameter and pass it to the `UserSearch` model
- The `UserSearch` model was modified to accept the `user_id` parameter
and use it to prioritize the user that is getting the reply in the
autocomplete on the first time you call the autocomplete service and
while the username is included in the searched term
- Had to update the serializer to pass the id of the `replyingToUser`
from the post
Recent testing has shown that this config requires significantly less
memory for a successful build, which should improve the situation for
people running Discourse on low-memory servers.
Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
Bulk archiving group messages can be done from two places: a) from the user's view (/u/ted/messages/group/swedes) and b) from the group's view (/g/swedes/messages/inbox). Only the former is currently working. From the latter view, the action looks like a success, but is actually a no-op.
Adds a one-click chat reactions setting to the chat preferences page
where members can determine what one-click reactions are shown in chat.
- Frequent: This will be the default setting. (Automatically set based
on most used chat reactions)
- Custom: Members can choose up to three reactions they want to see in
their one-click chat/DM reactions menu. Defaults are `❤️`, `👍` ,
and `😄`.

This pull request is essentially the work of @dsims in
https://github.com/discourse/discourse/pull/31761
---------
Co-authored-by: dsims <1041068+dsims@users.noreply.github.com>
There are flaky system tests that have been exhausting the 10 seconds
default max wait time which we have set previously on CI. However, I
have yet to be able to figure out why and lack the tools to be able to
figure out why. The hope here is that the upcoming playwright driver
will provide us with better tooling to debug such problems.
I've attempted to use `Capybara::Session#using_wait_time` by there is
some race condition going on where the session's default max wait time
is sometimes not set properly. I can't figure out why and have spent too
much time trying to figure out why. Instead, I will just bump up the
`default_max_wait_time` to `20`. This may the build take longer when
there are test failures but it is a trade-off we will make right now.
This introduces functionality to chat transcripts. If
you select _only_ a thread's OP and press the Copy button,
we will now copy the OP plus all of the thread's replies
into the transcript.
This is being done to make it easier to copy all the context
of a thread easily. The old way of selecting only some messages
inside a thread still works too.
This commit also unskips and refactors some transcript specs.
This aligns closer to how we are passing data through to such helper
classes (for example, the UserFieldsHelperClass
https://github.com/discourse/discourse/pull/31670) and reduces coupling
with the component itself.
Fixing hashtag text glued to the icon, and updated some related properties to using better units.
---------
Co-authored-by: Martin Brennan <martin@discourse.org>
We now have 3 search UX variations:
* Header search
* Welcome banner search
* Icon search
And within each of these there is a `<SearchMenu />`
component with an input that needs to be focused based
on either Ctrl+F or `/` shortcuts. This commit makes
sure that each has a unique ID, and moves the functionality
of determining the "current" input ID and focusing the
input to the search service.
This fixes issues like where pressing Ctrl+F twice on
the header search would not reveal the regular browser
search.
Elements with position: fixed are taken out of the rendering context, so we can't easily make the banner the width of the main container using CSS. We tried a CSS hack, but that doesn't scale to larger screens, and probably breaks in some themes.
This is a simple JavaScript implementation that matches the width of the main container.
This builds onto #32013 in two major ways:
- Unsaved changes are now persisted when you browse categories inside "All site settings".
- If you're about to navigate away (and lose edits) you will be prompted if you want to save or discard changes. (This applies to individual category site setting pages as well.
This commit adds an indicator of the shortcut for admin search to the
full page version of the search.
In addition, this commit does extensive refactoring of
`AdminSearchDataSource` and
adds many more tests for it to account for all the variations of
label/url formatting.
This change forces consistency around code font in Discourse once the
new
rich-editor is enabled
- This means all code blocks in Discourse will render with this font
- Additionally the markdown mode composer will render with this font
Additionally we make 3 small adjustments:
1. We disable ligatures which conflict with typographer
2. We add support for custom ligature settings
3. We adjust down font size, cause 14px ends up matching visually with a
16px non monospace font, this change is already in place previously on
posts
Example:

Since 3e4eac0fed05daedcdea50d6275e143469d55eda, the daily
`Jobs::BadgeGrant` scheduled job enqueues one `Jobs::BackfillBadge`
job per enabled badge. This can become problematic on large sites that
have alot of enabled badges as the long running queries executed by each
`Jobs::BackfillBadge` job may end up exhausting all available
database connections in a short span of time. To combat this, we are
limiting the `cluster_concurrency` of the `Jobs::BackfillBadge` job to
`1`.
The system test relies on a MessageBus message being published and
received by the client. As we currently don't have a reliable way to
ensure that we run an assertion after the client has received all
MessageBus messages, we will just double the timeout for now.
When `Capybara.threadsafe` has been set to `true` as in our case, we
have to use `Capybara::Session#using_wait_time` instead of
`Capybara.using_wait_time`. The latter sets `default_max_wait_time` on
`Capybara.default_max_wait_time` while the former sets
`default_max_wait_time` on the session.
Some themes make use of the category background color even when using
emojis or icons as the category style.
Therefore we should always allow the color selector to be visible when
editing the category.
Currently when using `register_topic_preloader_associations`, we are
not able to specify a condition that is evaluated at runtime.
This commit allows specifying a condition and also keeps backward
compatibility.
```
register_topic_preloader_associations(:linked_topic) { true }
register_topic_preloader_associations({
first_post: [uploads]
})
```
When bulk removing tags, the tag topic count isn't updated. You have to wait for the daily "ensure consistency" job to run for it to update properly. This is causing downstream problems, like the inability to use the "remove unused tags" feature.
The counter updating is handled on destroy by the join table model TopicTag. But we are calling #delete_all to perform the bulk tag removal, which bypasses callbacks.
This changes from #delete_all to #destroy_all, which invokes callbacks and updates the counters.
- adds .d-editor-container--rich-editor-enabled to d-editor-container
- conditionally swaps textarea to monospace when
siteSettings.rich_editor is enabled
We are doing this intentionally on the old composer when the
new rich editor is enabled to further differentiate the two and
to make it clear that markdown is the "source code"
This is a follow up to 59edec46a3de105e720bd2c716ae34d636794044
Test is flaky and seems to require more time to complete on CI so we
double the max wait time for now to see if it reduces the test's
flakiness.
SASS deprecation warnings are quite noisy, especially when
running specs, here is an example:
```
Deprecation Warning [mixed-decls]: Sass's behavior for declarations that appear after nested
rules will be changing to match the behavior specified by CSS in an upcoming
version. To keep the existing behavior, move the declaration above the nested
rule. To opt into the new behavior, wrap the declaration in `& {}`.
More info: https://sass-lang.com/d/mixed-decls
╷
52 │ ┌ @media screen and (max-width: 1048px) {
53 │ │ right: 0;
54 │ │ }
│ └─── nested rule
... │
56 │ background: var(--secondary);
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ declaration
╵
theme-entrypoint/desktop.scss 56:5 @import
/home/mb/repos/discourse/desktop.scss 3:9 root stylesheet
Deprecation Warning [mixed-decls]: Sass's behavior for declarations that appear after nested
rules will be changing to match the behavior specified by CSS in an upcoming
version. To keep the existing behavior, move the declaration above the nested
rule. To opt into the new behavior, wrap the declaration in `& {}`.
```
This commit adds an env var (`QUIET_SASS_DEPRECATIONS`) to control
turning them off.
On 1st April 2025, we start showing no icons if the icon name used is a
deprecated one and therefore no longer part of the svg set.
We'll continue showing the messages with the correct icon name to aid
correction of these names.
Console logging will now be done at an error level for such icons.
We retain the behaviour of raising an error for such icons in plugins
from svg_sprite.rb in test environments, but removed this from
icon-library.js as it's harder to test the actual expected behaviour of
returning the original icon now that it's not part of the deprecation
workflow. (sinon.stub doesn't work well here for `isTesting` - the
alternative would be to override the environment.js module with
`proxyquire`) In any case, once we remove the mapping logic, we won't be
raising errors in test environment either for this scenario.
In a development environment, the server auto-restarter can't watch symlinked paths, which means that any symlinked plugins won't reload if their non-autoloaded files change.
This change does a simple resolve of the symlink for any symlinked plugins, and adds the real plugin path to those that it will auto-restart for.
These aren't technically needed in the output, since we inline all
styles. However, there's no harm in including them, and having them
visible will improve the developer experience when restyling emails.
iOS is automatically converting a double-hyphen (--) to an em-dash (–).
It may get in the way when we're trying to create an horizontal rule
with a ---, so this PR adds the "em-dash + hyphen" case to our input
rule regex.
This commit also ensures meta will resize with the textarea to avoid
meta being badly positioned.
I couldn't easily write a test working on all platforms.
Currently when using `register_topic_preloader_associations`, we are not
able to specify a condition that is evaluated at runtime.
This commit allows specifying a condition and also keeps backward
compatibility.
```
register_topic_preloader_associations({
association: :linked_topic, condition: ->(topic) { topic.custom_field.present? }
})
```
Before this fix a click on a d-menu trigger would fire a click on the
parent element, which could have undesired consequences.
This also fixes two things:
- moves float-kit/form-kit css files higher in the chain, so it's easier
to override
- increases the z-index of the notifications-tracking menu content or it
would be behind the timeline control
This property is used for the `isNew` and `isCreated` computed
properties, which are then used in templates. To make that work
correctly, we need to use `.set` or `@tracked`.
This change removes the foreground color category setting to simplify
the category creation and edit process for admins.
Instead we determine the highest contrasting color (either white or
black) based on the background color.
Contrast algorithm is based on:
https://www.w3.org/TR/AERT/#color-contrast
We also implement the value transformer as part of this change, which
allows overriding the category text color.
This commit allows the ProseMirror rich editor to display chat
transcripts copied from chat using the "Copy" button.
The BBCode usually looks something like this:
```
[chat quote="hunter;29856;2025-03-20T07:13:04Z" channel="design gems 🎉" channelId="95"]
haha **ok** _cool_
[/chat]
```
But there are several variations that must be accounted for:
* Single message from single user
* Multiple messages from a single and multiple users
* Messages inside chat threads
The rich transcript extension has to ignore many of the chat transcript
markdown
tokens because they simply aren't necessary -- none of the ProseMirror
nodes need
to be editable. So, we basically recreate the same HTML that the chat
transcript markdown
rule does in the `toDOM()` function. Maybe in future we want to make the
markdown rule
do less and have this HTML creation in one place, but for now we need to
mirror in both files.
---------
Co-authored-by: Renato Atilio <renato@discourse.org>
When a site has the `must_approve_users` setting enabled, new user data is stored on the Reviewable model, including username, email, and any other data that is entered during signup. If the user is rejected, that data is retained, without a clear path to deleting it.
In order to allow data that could be PII to be removed, without breaking Discourse's audit and logging trails, this change scrubs the PII from the relevant `ReviewableUser` and `UserHistory` objects, replacing that data with who scrubbed it, and why.
When passing our ProsemirrorEditor component an initial value,
we were sometimes passing `null`, which causes the error in
the title of this commit. The fix here is to handle `null`
gracefully as string.
We originally found this on the category edit page, since
we have a `<DEditor />` in the form template form for the
category.
7d587937 introduced css property deduplication in emails to workaround
bugs in some email clients. However, this didn't account for
admin-supplied customizations which were overriding core rules using
`!important`. This commit ensures that the deduplicator will always
prioritise `!important` rules, just like a browser.
The rules are:
- between 1 and 3 emojis: bigger emoji
- more than 3 or any text or node in the same paragraph: regular emoji
This is implemented through a prose mirror plugin, which try to be smart
and recompute only edited paragraphs. Full scan on first load.
---------
Co-authored-by: Renato Atilio <renato@discourse.org>
This is a hidden site setting which has never been publicized, and is
not recommended for use. If we decide to add a feature like this in
future as a visible site setting, it would need many more safeguards to
prevent misuse.
The `using_wait_time` DSL did not work when used in the `around` context
as the Capybara's session has not been initialized.
Follow-up to afde7cc1727a751e4d87be2ceee17f2d82edf89c
I'm not sure what is causing the tests to be flaky so I need more
information to determine where the problem is. For now, I'm doubling the
wait time to rule out any server side problem.
We are not waiting for asynchronous operations to complete before
executing our assertions.
### Reviewer notes
Example test failure:
https://github.com/discourse/discourse/actions/runs/14100970402/job/39496976787
```
Failure/Error: measurement = Benchmark.measure { example.run }
expected `Chat::Channel.count` to have changed by 1, but was changed by 0
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_chat_new_message_from_params_with_multiple_users_creates_a_dm_channel_when_none_exists_626.png
~~~~~~~ JS LOGS ~~~~~~~
~~~~~ END JS LOGS ~~~~~
./plugins/chat/spec/system/chat_new_message_spec.rb:57:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
Some site settings support backfilling if the user specified it. This works fine for singular site settings sent to the SiteSettingsController#update endpoint, but with bulk save we need to support this for a list of settings as well.
This change alters the params format for SiteSetting::Update.
It also moves the backfill logic into the service.
A system test in `system/search_spec.rb` was failing with the following
error frequently on CI:
```
Failure/Error: expect(search_page).to have_heading_text("Search")
expected `#<PageObjects::Pages::Search:0x00007fb9fcd3f028>.has_heading_text?("Search")` to be truthy, got false
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_search_when_using_full_page_search_on_mobile_works_and_clears_search_page_state_912.png
~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~
./spec/system/search_spec.rb:42:in `block (3 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
The failure screenshot shows that the "user" is on the homepage even
though we have already clicked the search icon and ensured that the user
can see the search container. I suspect there is some sort of race
condition here since Capybara executes clicks in quick sucession where
we clicked on both the homepage logo and the search icon. It may be
possible that Ember redirected the user to the search page first
before the browser was able to finish navigating the user to the `/`
href.
### Reviewer notes
Test flaked in
https://github.com/discourse/discourse/actions/runs/14085443789/job/39448197089
with the following failure screenshot:

This is to provide diagnostic information for some flaky system tests
which we are currently investigating. The previous attempt in
6f92f42eb83fea61b2712aaaefcc124e4939365d only dumped threads that
contains the `puma` gem in the backtrace but that is not enough. Since I
am now printing all thread backtraces, I added the
`dump_threads_on_failure` metadata
which will be used to determine if all the thread backtraces should be
printed out in the spec's failure output.
* Prevents tag separators (commas by default) from wrapping separately
by using absolute positioning so they don't impact the width (thus a
comma can't wrap on its own, because a tag can't influence the width)
* Removes some whitespace creating extra space between categories and
tags (to better match desktop)
* Removes a redundant `flex-wrap: wrap` as `.discourse-tags` already
carries this
* Improves vertical alignment of commas (they were too high on mobile,
which is avoided on desktop with baseline alignment in flex)
* Fixes an issue where tag and separator color could be mismatched
because of a too-broad color being applied to all links
Before:

After:

Small alignment and sizing improvements for emojis/icons added to
categories.
We are also fixing the private category locked icon to retain the grey
color.
This aligns the composer with the post when the preview is
hidden.
Once the width gets a bit too narrow it occupies the full width.
[Screen Recording 2025-03-21 at 2.50.50
PM.webm](https://github.com/user-attachments/assets/4a854642-5b20-41fd-a478-7a943dfe0dd4)
The sidebar requires this to get a little magic numbery, but I tried to
anchor to predictable values otherwise.
---------
Co-authored-by: chapoi <101828855+chapoi@users.noreply.github.com>
This patch adds a new shared example to be used as a smoke test in
plugins and themes.
A `skip_examples` argument is available to easily opt-out from a
category of tests.
Example:
```rb
RSpec.describe "Testing core features", type: :system do
it_behaves_like "having working core features", skip_examples: %i[search login]
end
```
When a Capybara helper times out after hitting
`Capybara.default_max_wait_time`, we will now
dump the puma server threads backtraces. This information will help us
diagnose test failures due to requests not completing within
`Capybara.default_max_wait_time`.
Example test failure output:
```
Failures:
1) User resetting password when desktop when user has multi-factor authentication configured when user has TOTP, security key and backup codes configured should allow a user to toggle from security key to TOTP and between TOTP and backup codes
Failure/Error: expect(page).to have_current_path("/u/#{user.username}/preferences/second-factor")
expected `#<Capybara::Session>.has_current_path?("/u/john/preferences/second-factor")` to be truthy, got false
[Screenshot Image]: /Users/tgxworld/work/discourse/tmp/capybara/failures_r_spec_example_groups_user_resetting_password_when_desktop_when_user_has_multi_factor_authentication_configured_when_user_has_totp_security_key_and_backup_codes_configured_should_allow_a_user_to_toggl_874.png
~~~~~~~ SERVER THREADS BACKTRACES ~~~~~~~
/Users/tgxworld/work/discourse/app/controllers/users_controller.rb:2216:in 'Kernel#sleep'
/Users/tgxworld/work/discourse/app/controllers/users_controller.rb:2216:in 'UsersController#confirm_secure_session'
/Users/tgxworld/work/discourse/app/controllers/users_controller.rb:1543:in 'UsersController#confirm_session'
/Users/tgxworld/work/discourse/app/controllers/application_controller.rb:427:in 'block in ApplicationController#with_resolved_locale'
/Users/tgxworld/work/discourse/app/controllers/application_controller.rb:427:in 'ApplicationController#with_resolved_locale'
/Users/tgxworld/work/discourse/lib/middleware/omniauth_bypass_middleware.rb:35:in 'Middleware::OmniauthBypassMiddleware#call'
/Users/tgxworld/work/discourse/lib/content_security_policy/middleware.rb:12:in 'ContentSecurityPolicy::Middleware#call'
/Users/tgxworld/work/discourse/lib/middleware/anonymous_cache.rb:368:in 'Middleware::AnonymousCache#call'
/Users/tgxworld/work/discourse/lib/middleware/csp_script_nonce_injector.rb:12:in 'Middleware::CspScriptNonceInjector#call'
/Users/tgxworld/work/discourse/spec/rails_helper.rb:48:in 'RspecErrorTracker#call'
/Users/tgxworld/work/discourse/config/initializers/008-rack-cors.rb:14:in 'Discourse::Cors#call'
/Users/tgxworld/work/discourse/lib/middleware/default_headers.rb:13:in 'Middleware::DefaultHeaders#call'
/Users/tgxworld/work/discourse/config/initializers/100-quiet_logger.rb:20:in 'DiscourseRackQuietAssetsLogger#call'
/Users/tgxworld/work/discourse/config/initializers/100-silence_logger.rb:29:in 'SilenceLogger#call'
/Users/tgxworld/work/discourse/lib/middleware/enforce_hostname.rb:24:in 'Middleware::EnforceHostname#call'
/Users/tgxworld/work/discourse/lib/middleware/processing_request.rb:12:in 'Middleware::ProcessingRequest#call'
/Users/tgxworld/work/discourse/lib/middleware/request_tracker.rb:385:in 'Middleware::RequestTracker#call'
/Users/tgxworld/work/discourse/config/initializers/200-first_middlewares.rb:30:in 'TestMultisiteMiddleware#call'
/Users/tgxworld/work/discourse/config/initializers/200-first_middlewares.rb:77:in 'BlockRequestsMiddleware#call'
~~~~~~~ END SERVER THREADS BACKTRACES ~~~~~~~
~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~
```
### Reviewer notes
In this [flaky test
example](https://github.com/discourse/discourse/actions/runs/14087382392/job/39454911943),
the test failed because a capybara assertion did not pass within
`Capybara.default_max_wait_time`. From the screenshot below, we see that
the submit button is still loading which indicates that the request is
still being processed by the server. However, we need to know why the
server is taking more than 10 seconds to generate a response.
```
Failure/Error: expect(page).to have_current_path("/u/#{user.username}/preferences/second-factor")
expected `#<Capybara::Session>.has_current_path?("/u/john/preferences/second-factor")` to be truthy, got false
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_user_resetting_password_when_desktop_when_user_has_multi_factor_authentication_configured_when_user_only_has_security_key_configured_should_allow_a_user_to_reset_password_with_a__75.png
~~~~~~~ JS LOGS ~~~~~~~
(no logs)
~~~~~ END JS LOGS ~~~~~
Shared Example Group: "forgot password scenarios" called from ./spec/system/forgot_password_spec.rb:213
./spec/system/page_objects/pages/user_preferences_security.rb:15:in `visit_second_factor'
./spec/system/forgot_password_spec.rb:30:in `create_user_security_key'
./spec/system/forgot_password_spec.rb:79:in `block (4 levels) in <main>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```

This change disables the save and undo buttons for site settings while the setting is being saved. This gives some visual indication that saving is underway, and prevents unnecessarily sending more than one request (which will be no-ops anyway.)
This update adds a new value transformer to the `PostTextSelection`
component. This allows for dynamically setting a `preventClose`
property. This is useful to prevent the `onSelectionChange` listener
from firing a toolbar close. In particular, we want to use this in
Discourse AI to prevent the selection change from closing the toolbar
when selecting new text inside the explain popup
(https://github.com/discourse/discourse-ai/pull/1221)
This was accidentally reverted as part of the rebasing/merging of the
gjs conversions in b29e0b6e1b6001890ce92f96448258550f80132b
Co-authored-by: Robert <35533304+merefield@users.noreply.github.com>
Follow-up to bbea7c3b11054b8e7faeee85a0336ecc45b84890
This behavior should only be applied to mobile view, otherwise there are
some scenarios (Android tablets) where the composer toolbar will become
inaccessible.
This commit improves said method to ensure that user is redirected to
the right page before returning.
### Reviewer notes
Example of test flakiness:
https://github.com/discourse/discourse/actions/runs/14081653020/job/39435797236
```
Failure/Error: raise capybara_timeout_error
CapybaraTimeoutExtension::CapybaraTimedOut:
This spec passed, but capybara waited for the full wait duration (10s) at least once. This will slow down the test suite. Beware of negating the result of selenium's RSpec matchers.
[Screenshot Image]: /__w/discourse/discourse/tmp/capybara/failures_r_spec_example_groups_user_resetting_password_when_desktop_when_user_has_multi_factor_authentication_configured_when_user_has_security_key_and_backup_codes_configured_should_allow_a_user_to_reset_pass_261.png
~~~~~~~ JS LOGS ~~~~~~~
~~~~~ END JS LOGS ~~~~~
Shared Example Group: "forgot password scenarios" called from ./spec/system/forgot_password_spec.rb:213
./spec/rails_helper.rb:426:in `block (3 levels) in <top (required)>'
./spec/rails_helper.rb:619:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.0/lib/benchmark.rb:304:in `measure'
./spec/rails_helper.rb:619:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:580:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
./spec/rails_helper.rb:570:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:527:in `block (2 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
b1924c35 switched our compiler to use `@import` internally for scss
entrypoints. This logic also applied to `.css` files, but unfortunately
sass doesn't do anything with `@import` of CSS files, so they'll be left
intact all the way to the browser. Continue using the old concatenation
approach for them in the compiler.
Followup to b1924c352487ab2c85ae50af45c5b3e098589014
Previously we would prepend extra content to developer-authored files,
which means adding `@use` in some files would throw an error because
`@use` must be at the top of any compiled file.
Instead, we can ensure any developer-authored files are on the load
path, and then `@import` them into the synthetic entrypoint.
Plugin color_definitions stylesheets are an edge case here, and will
need to be handled separately (or... wait until we move to native css
relative-color syntax, then we can drop color-definition stylesheets
altogether)
This PR moves the logic that checks if a site setting we're trying to update has been deprecated from one of the controllers into a policy of the SiteSetting::Update service.
It also gives us the opportunity to shift the error message into a locale file.
With header search we toggle the search icon when scrolling on topics,
which can feel visually jarring when the search icon is in the middle.
Switching the position of the chat icon in the header reduces the impact
of this.
This commit adds a loading state to the confirm button in
`confirm-session` dialog.
This also unskips the flaky system tests in
`spec/system/forgot_password_spec.rb` as this change will allow us to
gather more information about why the test is flaky. The screenshots
which we have gathered when the test flakes does not allow us to know if
the button has been clicked or not before the test times out.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.