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>
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>
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.
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 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.
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 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)>'
```
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.
Meta:
https://meta.discourse.org/t/cant-erase-the-bookmark-search-input/357861/4
> When we fill in the bookmark search input and send the request, then
we can’t delete the input’s content.
After removing the last character, the `searchTerm` getter is called. At
this point, `_searchTerm` is empty.
However, `this._searchTerm || this.q` will treat the empty string as
_falsy_, and the `q` value is displayed instead.
It makes it impossible to clear the input.
To fix this, we check specifically on the initial state of
`_searchTerm,` which is _undefined_, to include an empty string as a
valid value.
Note: because of `@computed`, the issue is not triggered when the
content is selected and deleted.
Now we have the search input showing in a few
different configurations:
* Welcome banner
* Header field
* Header icon
And we can get to the search with both `/` and
`Ctrl+F` shortcuts. These configurations can
be used together, and we need to focus on the right
search input at the right time.
This commit fixes the shortcuts not working
or showing the wrong thing in some cases,
and adds a comprehensive system spec for all
the variants.
The change made in #31854 introduced a regression when editing groups, preventing saving when no auto membership e-mail domains are entered.
This change fixes that and adds a system test.
Before this commit,
`PageObjects::Components::SelectKit#expanded_component` might not expand
the component if `is_collapsed?` returns true which can return true if
the method was called before the select-kit component appears on the
screen. When that happens, we end up not expanding the `select-kit`
because we think it is collapsed when in fact the select-kit component
is not rendered yet. This lead to system test failures with errors like:
```
Capybara::ElementNotFound:
Unable to find css "#add-synonyms.is-expanded"
```
This commit updates `PageObjects::Components::SelectKit#is_collapsed?`
to check that the select-kit component has rendered first before
checking if the component is not expanded.
### Reviewer notes
Instances of test flakiness due to this bug:
1.
https://github.com/discourse/discourse/actions/runs/13905226478/job/38906569777
2.
https://github.com/discourse/discourse/actions/runs/13848357836/job/38751122333
This is a stripped-back version of the Search Banner
component https://meta.discourse.org/t/search-banner/122939,
which will be renamed to Advanced Search Banner,
see https://github.com/discourse/discourse-search-banner/pull/84.
This welcome banner interacts with the header search.
When `search_experience` is set to `search_field`, we only
show the header search after the welcome banner scrolls
out of view, and vice-versa.
Only new sites will get this feature turned on by default,
existing sites have a migration to disable it.
---------
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
The "Tag synonyms when visiting edit tag page allows an admin to create
a new tag as synonym when tag does not exist" system
test was flaky with the following error:
```
Failure/Error: super
Capybara::ElementNotFound:
Unable to find css "#add-synonyms.is-expanded"
```
Backtrace points to the following location:
```
...
./spec/system/page_objects/components/select_kit.rb:30:in `expanded_component'
./spec/system/page_objects/components/select_kit.rb:88:in `search'
...
```
What I noticed is that
`PageObjects::Components::SelectKit#expanded_component` has already
found the expanded element when it calls `#expand`. Therefore, there is
no need for us to search for it again.
The "Admin editing objects type theme setting when editing a theme
setting of objects type allows an admin to edit a theme setting of
objects type via the settings editor"
system test was flaky because of the way we were filling in content into
AceEditor. It is not a normal input and does not seem to function like a
normal input. Using Capybara's `fill_in` method was not reliable so we
will just use AceEditor's API directly to update its input.
When copying images from cooked, we have a `data-base62-sha1` attribute
instead of a `data-orig-src` that we were expecting.
This PR fixes it and adds a system test.
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.
When you are uploading, it just causes weird problems
when you switch between markdown/rich editors, and it's
not something we really want to support. This commit
disables the Composer::ToggleSwitch while uploading.
Continues the work done on
https://github.com/discourse/discourse/pull/30815.
Adds a `onebox` and an `onebox_inline` node spec, their serializers, and
a plugin that automatically converts links to oneboxes.
Continues the work done on
https://github.com/discourse/discourse/pull/30815.
Extends the ProseMirror-markdown `code-block` node by integrating our
existing HighlightJS pipeline for code highlighting and adding a node
view with a `<select>` to change the language of the block.
We're also adding the markdown paste extension, which handles converting
pasted text/plain to rich content if it contains Markdown.
---------
Co-authored-by: Martin Brennan <martin@discourse.org>
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

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
Adds a search field to the page header on desktop that is controlled by
a site setting (within Search).
The search field toggles back to the search icon (magnifying class) when
the header is minimized (ie. scrolling in topics) and restores to the
field again when header is no longer minimized.
On mobile the search experience is still the same.
This change adds a new `type_source` field to the `Reviewable` model, indicating whether the Reviewable type was registered by `core`, a plugin, or an `unknown` source.
When a plugin that registered a Reviewable type is disabled, this allows us to tell the user which plugin they need to re-enable to handle any orphan reviewable items.
This commit allows admins to filter the list of feature
feed items on the "What's new?" page to _only_ show experiments.
This is useful to both find existing experiments they may have
enabled, and to get a better overview of new ones they would
like to try.
This will eventually not be required when we build a dedicated
config page for experiments.
This commit makes the
[color-scheme-toggle](https://github.com/discourse/discourse-color-scheme-toggle)
theme component a core feature with improvements and bug fixes. The
theme component will be updated to become a no-op if the core feature is
enabled.
Noteworthy changes:
* the color mode selector has a new "Auto" option that makes the site
render in the same color mode as the user's system preference
* the splash screen respects the color mode selected by the user
* dark/light variants of category logos and background images are now
picked correctly based on the selected color mode
* a new `interface_color_selector` site setting to disable the selector
or choose its location between the sidebar footer or header
Internal topic: t/139465.
---------
Co-authored-by: Ella <ella.estigoy@gmail.com>
This commit fixes a bug in the "Dark Mode" checkbox in the interface user
preferences where the checkbox state doesn't appear in the disabled
state if the user disables dark mode.
This happens because when rendering the checkbox, we check the relevant
user options field within the controller's `init` method, but at that
point in the controller's life cycle, the `user_option` object isn't
available. What we should do instead is move this check to the route's
`setupController` method where the `user_option` object is available and
we can set the correct state on the controller.
https://meta.discourse.org/t/-/349976 (private topic)
This change includes the following updates:
- Rename view all to view all drafts
- Remove view all link from drop-down when all drafts are displayed in
the menu
- Different icon for draft topics and PMs (adds envelope for PMs)
- Disable drop-down when New Topic button is disabled (private
categories etc)
- Improve drafts drop-down loading (no longer disables the trigger btn
on click)
Plugins like for example AI or Akismet create reviewable items. When the
plugin is disabled, then we cannot properly handle those items.
In that situation, we should display warnings about unhandled types.
Instruct admin to reenable plugins. In addition, we should allow the
admin to delete all pending reviews from disabled plugins.
This is the first in a series of PRs to introduce a
ProseMirror-based
WYSIWYM editor experience
alongside our current textarea Markdown editor.
Behind a hidden site setting, this PR adds a toggle to the composer
toolbar, allowing users to switch between the two options.
Our implementation builds upon the excellent ProseMirror and its
non-core Markdown
module, using the
module's schema, parsing, and serialization definitions as the base for
further Discourse-specific features.
An extension API is included to enable further customizations.
The necessary extensions to support all Discourse's core and core
plugins features **will be implemented in subsequent PRs**.
---------
Co-authored-by: David Taylor <david@taylorhq.com>
This change adds a sidebar link for each plugin that fulfils the following criteria:
- Does not have an explicit admin route defined in the plugin.
- Has at least one site setting (not including enabled/disabled.)
That sidebar link leads to the automatically generated plugin show settings page.
This moves the "hide my public profile" checkbox from the
/my/preferences/interface > other section into the top of the
/my/preferences/profile section.
Internal ref - t/146570
Changing a tag name and reverting it to a previous tag name choice on
the same screen (meaning, without reloading the page) causes a 404
error.
Reproduction steps:
* Open an existing tag page https://your-discourse.com/tag/a
* Click the wrench, change the name and save
* Open edit again, revert the name to the original tag name
* 404 error
This was happening because of the way we find existing objects
in our store. Tags are using the name as the id/primary key
unlike other records in the UI. When we get the existing object
from the store we throw away the "new" ID, so we ended up with
the old tag ID on the existing object, which led to a 404. To
fix it we can just manually set the tag ID to what it is supposed
to be from params in the tag show route.
c.f.
https://meta.discourse.org/t/404-error-when-you-revert-a-tag-name-after-changing-it-without-reloading-the-page/342559
This commit narrows down the list of fonts we offer
in our setup wizard and simplifies things to only
show a single font dropdown. This selection will then
set the `base_font` and `heading_font` site setting to
the same value.
For existing sites that may have set different values,
we will still show 2 dropdowns when visiting the wizard.
We are also changing our default font to the more modern
selection Inter, replacing Arial. Arial is very dependent
on system installed fonts, whereas Inter we can package
to everyone in Discourse.
Finally, for existing sites that have not changed their default
from Arial, we will keep that value via a migration so we do
not surprise site owners with a completely new font.
This update makes some small improvements to the posts route front-end.
Specifically, it adds a title to the page, and it improves the
positioning of expand/collapse caret.
adds a hidden site setting, "prioritize_full_names_in_ux", whose effect is to prefer full names in user-menu notifications
Co-authored-by: Mark VanLandingham <markvanlan@gmail.com>
Co-authored-by: Alan Guo Xiang Tan <gxtan1990@gmail.com>
One of the big advantages is a nicer menu on mobile.
This commit also fixes a bug where the close modal action was called for any destroyed d-menu trigger, even if this specific menu was not expanding, which means it was closing a different modal than its own modal, given we can only have one modal at a time.