Commit Graph
63386 Commits
Author SHA1 Message Date
Discourse Translator Bot 03c45bbdab I18N: Update translations (#37631) 2026-02-09 09:34:22 +01:00
Alan Guo Xiang Tan c48f18dbed FIX: Resolve tag synonyms in search and filter queries (#37628)
What is the problem?

Discourse allows admins to mark a tag as a synonym of another tag. For
example, "brunch" can be made a synonym of "lunch". When this happens,
all topics tagged with "brunch" are automatically retagged with "lunch",
and the `tags.target_tag_id` column on the synonym tag record is set to
point to the target tag.

However, several code paths did not account for synonyms:

1. **Search:** `Search#search_tags` and the hashtag advanced filter did
   not resolve synonyms. When a user searched using a synonym name (e.g.
   `tags:brunch`, `tags:brunch+eggs`, or `#brunch`), no results were
   returned because:
   - The `tags:` comma path queries `topic_tags` joined with `tags` by
     name, but topics are tagged with the target tag "lunch", not the
     synonym "brunch".
   - The `tags:` plus path aggregates tag names per topic into a
     tsvector and matches against the searched name, but the aggregated
     names are target tag names, so "brunch" never matches.
   - The `#` hashtag path picks the synonym tag's own `id` and queries
     `topic_tags` by that ID, but topics store the target tag's ID.

2. **Filter route:** `TopicsFilter#tag_ids_from_tag_names` concatenated
   both the synonym's own ID and the target tag ID. For match-all
   queries (e.g. `tag:brunch`), this required a topic to have both IDs
   in `topic_tags`, which never happens since only the target tag ID is
   stored. For negation queries (e.g. `-tag:brunch`), the exclusion
   targeted the synonym ID rather than the target, so no topics were
   excluded.

What is the solution?

In `Search#search_tags`, add a synonym resolution step before the
existing comma/plus branching logic. It splits the match string into
individual tag names, queries for any that are synonyms via
`Tag.where_name(tag_names).where.not(target_tag_id: nil)`, builds a
name mapping, and replaces synonym names with their target tag names.
The replacement operates on the split array elements rather than using
substring replacement to avoid corrupting tag names that may contain
other tag names as substrings.

In the hashtag advanced filter, pick both `:id` and `:target_tag_id`
from the tag lookup and prefer `target_tag_id` when present, so the
query uses the target tag's ID instead of the synonym's own ID.

In `TopicsFilter#tag_ids_from_tag_names`, replace the transpose/concat
approach with `.map { |id, target_id| target_id || id }` to resolve
each tag to its canonical ID — the target for synonyms, or the tag's
own ID otherwise.

A partial index on `tags.target_tag_id` is added (scoped to
`WHERE target_tag_id IS NOT NULL`) to support efficient synonym lookups.
2026-02-09 15:57:52 +08:00
Natalie Tay 6cae7a3a7e DEV: Remove tag_id param in favour of tag param for topic creation with tags to allow tag creation (#37597)
In https://github.com/discourse/discourse/pull/36678 we introduced the
`tag_ids` param for topic creation and updating to add tags to a topic,
however, this does not cater for new tags which are allowed based on the
UI and previous API.

Furthermore, the frontend wasn't even using the new param, making the
new `tag_ids` implementation on the controller moot.

This commit 
- removes this `tag_id` param
- updates the deprecation note to the `tags` param to require `[{ id: 1,
name: "old1" }, { name: "new1" }]` rather than the old `["old1",
"new1"]`.
- including IDs here eliminates the case where John renames `"old1"` to
`"old2"`, but Mary sends `["old1", "new1"]`, thus creating the `"old1"`
tag again when it was already renamed.
  - keeps us consistent with other endpoints that take in `tags`
  
Reviewer note: The `tag_topic_by_names` method is [extremely
complicated](https://github.com/discourse/discourse/blob/2b24fc91c1f46e57913d6acb7aee0c05041f64be/lib/discourse_tagging.rb#L19-L248).
Ideally, we would like to have a `tag_topics` method that does not incur
that many `Tag` queries, but to prevent a large refactor here, we are
re-using `tag_topic_by_names`.
2026-02-09 14:48:58 +08:00
Kris 26f3e2aa87 DEV: remove unused translation (#37617)
Left over from prototyping
2026-02-06 17:47:45 -05:00
Régis Hanol ce9abeb50d FIX: Handle Nokogiri HTML parser limits in PrettyText.excerpt (#37610)
The /drafts endpoint returns a 500 error when any draft contains HTML
with excessive nesting depth or too many attributes per element.

Nokogiri::HTML5.fragment raises ArgumentError when these limits are
exceeded, and PrettyText.excerpt had no error handling for this. A
previous fix in PostItemExcerpt only caught the tree depth variant,
leaving the attributes limit unhandled, and only protecting one of the
13+ callers.

Rescue ArgumentError around the Nokogiri::HTML5.fragment call in
PrettyText.excerpt and return "" on failure. This is consistent with the
existing blank-input guard and protects all callers at once. The
now-redundant rescue in PostItemExcerpt is removed.

Ref - t/173858
2026-02-06 22:00:54 +01:00
Jordan Vidrine 70c086c68e FIX: drop down menu item height (#37611) 2026-02-06 14:27:56 -06:00
Jordan Vidrine a08278eaef UX: Upcoming foundation changes (#37162) 2026-02-06 11:50:57 -06:00
Jarek Radosz 1226e226b0 DEV: ChatThreadTitler persona can be missing in specs (#37608)
A followup to 6c7bda7cea
2026-02-06 18:41:05 +01:00
Jake Goldsborough 7e9110f52a FIX: Use safe navigation for MIME lookup in ExternalUploadManager
MiniMime.lookup_by_filename can return nil if the file extension is not
recognized. The move_to_final_destination method was calling .content_type
directly on the result without safe navigation, causing NoMethodError when
processing backup files with unrecognized extensions.

This is the same bug pattern that was fixed in e817cf6f47 for
BackupRestore::Creator. The fallback to "application/gzip" is appropriate
since this method is used for backup uploads which are .tar.gz or .sql.gz.
2026-02-06 16:49:10 +00:00
Bannon Tanner 521bd4b9c5 FIX: broken image controls in form template composer preview (#37275)
Background: When using form-templates, the controls on any uploaded
images (edit title/delete image) were not working.

Details:
- Enabled modifier forwarding in the preview component so initialization
hooks run correctly when the preview is rendered directly.
- Hardened preview initialization logic to support cases where the
preview wrapper itself is the root element, ensuring image action
listeners attach reliably.
- Kept composer markdown in sync during preview updates, allowing image
edit/delete actions to correctly locate and modify the underlying
markdown.
- Moved image replacement handling to the input components themselves,
ensuring the correct field updates when preview actions occur.
- Resolved a race condition during preview refreshes by deferring
updates until after the UI has fully rendered.
- Synchronized upload state with preview deletions, keeping the uploaded
files list consistent when images are removed.

Together, these changes ensure image controls in Form Template previews
behave consistently with the standard composer and no longer fail or
revert unexpectedly.
2026-02-06 10:15:48 -06:00
方觉 6813ed60db Fix grid in topic.scss of horizon theme on mobile (#37604)
Bug report:
https://meta.discourse.org/t/topic-content-does-not-take-up-full-width-for-very-short-text/395371

With Foundation theme, when screen width <= 924px, .container.posts has
`grid-template-columns: auto` (see
app/assets/stylesheets/common/base/topic.scss). But in Horizon theme, it
is overriden with `grid-template-columns: auto auto`. However there is
not another column here and it makes the child `.row` fail to take up
full width if its content is smaller than screen width.
2026-02-06 10:48:59 -05:00
Kris 5775d5d4de DEV: animated splash SVGs should be limited to CSS (#37606)
Turns out SMIL animations in SVGs get paused by javascript execution,
and therefore can appear very stuttery! (thanks @davidtaylorhq!)

So this removes the various pieces that allowed SMIL animation, and
switches animated SVGs to be directly part of the DOM. This way CSS
transformation and opacity animations end up in the compositor layer and
aren't blocked by JS, making them less prone to stutter.
2026-02-06 10:24:20 -05:00
Renato Atilio 5bfc9764f7 FIX: Handle empty table bodies in rich editor paste (#37603)
https://github.com/discourse/discourse/commit/c35e7366 improved handling
of pasted tables in the ProseMirror rich editor by normalizing column
counts and supporting nested tables. However, it introduced a potential
crash when pasting tables with empty tbody elements or rows with zero
cells.

This PR fixes this edge case.
2026-02-06 12:12:51 -03:00
Renato Atilio 59a174f206 FIX: rich editor auto-linking (#37605)
This was broken during a [`@ts-check`
refactor](https://github.com/discourse/discourse/commit/2db74b305b216cf2b2627fbaa5043a2d7603d386):
we can't rely on class names on a production environment.

The list is unlikely to change, so the fix is to use simple `instanceof`
checks.
2026-02-06 12:12:40 -03:00
Renato Atilio 1a86043ddf FIX: rich editor base64 replacement position (#37602)
On the rich editor automatic download of base64 image data, image
positions retrieved from `dataURIMap` are now sorted in descending order
before replacement. This prevents issues when replacing multiple images
with the same data URI by ensuring that later positions are replaced
first, avoiding offset errors.
2026-02-06 12:12:25 -03:00
Renato Atilio 6a8701c677 DEV: remove invalid href attribute from p element (#37600)
Removes `href` attribute from a `<p>` element – the same `href` is
already present on a parent `<a>`.
2026-02-06 12:12:13 -03:00
Discourse Translator Bot 58e7b15d51 I18N: Update translations (#37596) 2026-02-06 16:08:58 +01:00
David Taylor 04d28fd34a FIX: GitUtils preventing boot (#37599)
GitUtils is called very early, even before `Rails.root` is available.
Use `__dir__` to determine the directory instead.
2026-02-06 13:07:28 +00:00
Kris d779aeb37b UX: improve back button styles, add optional models (#37588)
Updates our back button styles and adds the ability to pass models. 

Before:
<img width="240" alt="image"
src="https://github.com/user-attachments/assets/c00354d2-b632-4232-9568-adf74f3bd67e"
/>


After:
<img width="240" alt="image"
src="https://github.com/user-attachments/assets/f162cf84-64d5-4f49-b319-124d37c68c00"
/>

Applies globally, so impacts existing uses (mostly admin) 

Before:

<img width="240" alt="image"
src="https://github.com/user-attachments/assets/49d7eaeb-dda4-4380-a01b-d81242a9665a"
/>


After:
<img width="240" alt="image"
src="https://github.com/user-attachments/assets/aed9ab7e-268f-4b21-9cfb-d749a6210b54"
/>
2026-02-06 08:00:01 -05:00
David Taylor 339746a413 DEV: GitUtils overriding via config/git-utils-overrides.json (#37571)
`GitUtils` determines the Discourse version information which is shown
in the user interface. This commit introduces an optional
`config/git-utils-overrides.json` file, which hosting providers can use
to override this information. For example, if the hosting provider is
needs to apply hosting-platform-specific patches or embargoed security
fixes on top of a normal release branch.

Previously there were no specs for this file. So this commit introduces
specs for the default behaviour, and the new overrides feature.
2026-02-06 11:45:33 +00:00
Natalie Tay 2474fc87e7 FIX: Preserve case when bulk creating tags (#37593)
Bulk tags feature introduced in
https://github.com/discourse/discourse/pull/36645 was lower-casing tags
being sent, regardless of the `force_lowercase_tags` setting.

This commit ensures the lowercase is done only when that setting is enabled.
2026-02-06 17:12:07 +08:00
David Battersby 2b24fc91c1 FIX: prevent chat reactions from non channel members (#37078)
This is a follow up to the removal of chat action buttons for non
channel members in #37060

Previously we allowed non channel members to react to posts, now the
user can still remove reactions they left in the past (if they were once
a channel member and then left), but attempting to add new reactions via
the API or by clicking other user reactions to increment them would
result in an error message stating that you need to join the channel.

From the API request to add a reaction they would get a 403 response
code with a message `You must be a member of this channel`.

If clicking an existing reaction from the UI they will get an error
dialog:

<img width="550" alt="When non members click existing reactions"
src="https://github.com/user-attachments/assets/57b7a205-e5b7-426d-a64d-19dd04803686"
/>


Internal ref - /t/160400
2026-02-06 10:43:00 +04:00
Alan Guo Xiang Tan 8750ae2e18 FIX: allow new tags to be created from the Tag Group editor (#37594)
What is the problem?

When creating or editing a tag group, users can type new tag names into
the `TagChooser` (which has `allowAny=true`). The select-kit component
assigns a string-based ID to these new tags. `TagGroupsController#tag_groups_params`
blindly extracts `id` from each tag object, producing the string name
instead of a database ID for new tags. `TagGroup#tag_ids=` then silently
ignores these invalid values, so the new tags are dropped without error.
The same issue affects parent tags.

This was introduced in 9e99066b07 which changed tags from string arrays
to object arrays but did not handle the case where new tags have no
numeric ID.

What is the solution?

On the frontend, the `TagGroupsForm#save` action now strips non-numeric
IDs from tags and parent tags before sending to the backend via a private
`#serializeTag` method. Tags with a numeric `id` (existing) keep both
`id` and `name`; tags with a string `id` (new) are sent with only `name`.

On the backend, `TagGroupsController#tag_groups_params` now splits the
tags array into existing (have `id`) and new (no `id`). New tag names are
passed to `DiscourseTagging.find_or_create_tags!` which validates names via
`tags_for_saving` and creates them. The same logic applies to parent
tags.
2026-02-06 14:27:01 +08:00
Martin BrennanandLoïc Guitaut 43ccc4def0 FIX: Logging hole in group user histories and convert group create to service (#37054)
When creating groups, we weren't logging the group owner
as being the owner in group histories, nor were we logging
them being added to the group. So it was not clear in the user
logs where this user ever came from. This PR fixes the issue,
and also converts the admin group create endpoint to use a
service.

Now the logs look like this when a group is created:

<img width="830" height="378" alt="image"
src="https://github.com/user-attachments/assets/871e319a-5512-411e-8565-3b63803e688c"
/>


c.f.
https://meta.discourse.org/t/logging-hole-for-group-histories/392942

---------

Co-authored-by: Loïc Guitaut <loic@discourse.org>
2026-02-06 13:34:58 +10:00
Kris e89b618aa2 FIX: prevent category data from carrying over when creating a new one (#37577)
After editing a category and then attempting to create a new one,
sometimes you'd end up back to the one you were editing when clicking
tabs in the navigation. This resets the data to avoid the issue.
2026-02-05 17:03:41 -05:00
Jarek Radosz 6c7bda7cea DEV: Fix persona name collision in chat_thread_titler_spec (#37584) 2026-02-05 22:44:47 +01:00
Jordan Vidrine 0fd684ba52 DEV: Fix spec (#37585) 2026-02-05 15:19:41 -06:00
Jordan Vidrine 69ae38b53c UX: Remove air theme as suggestion in theme picker (#37581) 2026-02-05 14:10:52 -06:00
chapoi 641115bc7d UX: undo catch-all for RTL icons and add reply (#37575)
Guess I wasn't as clever as I thought ( #37568 ) and not all icons
should get flipped.

Added reply-icon to the list.
2026-02-05 20:06:22 +01:00
Kris fb26fc66a4 FIX: support nested descriptions in object settings (#37538)
Reported here:
https://meta.discourse.org/t/labels-and-descriptions-missing-from-nested-object-settings/394685

Descriptions for nested object settings were not being displayed due to
a mismatch in locale key formatting. Stripping `.schema.properties.` so
that the locale keys from the serializer match the keys expected in the
template fixes it

Before: 
<img width="600" alt="image"
src="https://github.com/user-attachments/assets/1e0ab4c6-e6eb-478d-97bc-76f16739135b"
/>


After: 
<img width="600" alt="image"
src="https://github.com/user-attachments/assets/a9a9e64c-4efa-4807-94a1-dd853ffd9f04"
/>
2026-02-05 14:01:58 -05:00
Kris ae2ab5a2d0 FIX: more specific name for rewind blink animation (#37576)
this was leaking into another animation and causing interesting results
😆

<img width="350" alt="image"
src="https://github.com/user-attachments/assets/a4738717-37c2-4f9f-b0dc-d8937d00037e"
/>
2026-02-05 14:00:15 -05:00
Régis Hanol e09cf0b1f2 DEV: code improvements to upsert category (#37572)
Follow-up to #37540. This continues the code cleanup work for the
upsert-category components.

Changes:
- consolidate 8 upload actions into a single pair using fn partial
application to reduce repetition
- move appearance-related properties (views, sorts, periods, filters,
subcategory list styles) from settings to appearance where they actually
belong
- use site.periods as the single source of truth for top periods instead
of hardcoding values
- add none option to ComboBoxes so values can be cleared back to default
- remove enable_simplified_category_creation guard from text_color and
slug fields since they should always be available
- remove unnecessary placementStrategy="absolute" from ComboBoxes
- add missing "hot" to available default views
- pass form to category-email-in plugin outlet so plugins can use it
- rename properties for consistency (eg. availableSorts → sortOrders,
parentIsRestricted → isParentRestricted)

Ref - t/173336
2026-02-05 19:41:53 +01:00
Discourse Translator Bot e2dc31b2cf I18N: Update translations (#37563) 2026-02-05 19:29:53 +01:00
Régis Hanol 6cd48ccd23 FIX: Simplified category creation validation and state reset (#37567)
Two issues addressed:

1. Require icon selection when style type is "icon" - previously the new
category interface allowed creating categories without an icon, unlike
the old interface which required one.

   https://meta.discourse.org/t/395223

2. Reset security settings when navigating from edit to new category -
the categoryVisibilityState was being reused across different
categories, causing the security tab to show the previous category's
settings.

   https://meta.discourse.org/t/395224
2026-02-05 19:21:06 +01:00
Kris d7309510d4 UX: better centering and slightly larger experimental splash (#37574)
Centering for logos is improved here, we were too low previously. Also
allowed images to be slightly larger. If admins want smaller images,
it's possible to constrain the size by leaving some space around the
viewbox... but if we start off too small you just can't make it larger.

Before (guidelines added to show center):
<img width="1720" height="1406" alt="image"
src="https://github.com/user-attachments/assets/03964b0e-c15e-4cf2-b870-b602d5606123"
/>


After:
<img width="1720" height="1406" alt="image"
src="https://github.com/user-attachments/assets/e107f8ee-bfd5-4ff4-9c14-bb5fcb5c8504"
/>

Slightly larger dimensions overall

Before:
<img width="2108" height="1728" alt="image"
src="https://github.com/user-attachments/assets/b82a49b2-ab82-4a80-8233-63164a23e39c"
/>


After: 
<img width="2106" height="1724" alt="image"
src="https://github.com/user-attachments/assets/d5a22e07-2361-4592-a7c8-863e243cabfc"
/>

Default loading dots are unchanged.
2026-02-05 12:58:22 -05:00
Kris b39866eb88 UX: fix post-notice max-width, remove radius (#37541)
These post notices are wider than posts, and the border radius doesn't
make much sense because they're generally sandwiched between borders

Before:
<img width="1000" alt="image"
src="https://github.com/user-attachments/assets/5aee15d4-d8b5-4ee3-967e-555977eb91c4"
/>


After:
<img width="800" alt="image"
src="https://github.com/user-attachments/assets/29f25fca-c177-4cca-8654-275b99452b7b"
/>
2026-02-05 10:44:12 -05:00
Rafael dos Santos Silva 55e56fb092 DEV: Add missing features to sidebar href links (#37537)
The sidebar plugin API documents `hoverType`, `hoverValue`,
`hoverAction`, and `hoverTitle` on `BaseCustomSidebarSectionLink`, but
these only work when using `route`. Links using `href` silently ignore
these properties because the template's href branch was never updated to
include them.

This affects plugins that need sidebar links which don't navigate (e.g.,
triggering actions on click) but still want hover menus like chat has.

### Why href?

Some sidebar links can't use `route` because clicking them doesn't
navigate anywhere - they trigger actions (like joining a voice room,
toggling a state, etc). These use `href="#"` with a click handler
intercept. The API suggests hover features should work, but they don't.

### The lint-disable

The `route` branch already has a button inside `LinkTo` (which renders
as `<a>`). The linter doesn't flag this because it only sees the
component, not the rendered output. Adding the same pattern to the
`href` branch requires the disable comment for parity.

An alternative would be moving the hover button outside both link
elements, but that changes the DOM structure and breaks existing styles.
2026-02-05 10:49:05 -03:00
chapoi 5d8c184e83 UX: use normal class (#37569)
No idea why it didn't originally – brainfreeze
2026-02-05 13:21:05 +01:00
chapoi a520de2e4f UX: apply a catch-all for icons in RTL (#37568)
I think the original intent was to only target directional icons, but
things fall through the cracks (see
https://meta.discourse.org/t/reverse-reply-arrow-icon-for-rtl/385565) so
this commit aims to:

* apply a catch-all to icons
* use `transform: scaleX(-1);` which provides a more correct horizontal
flip

As a downside, the transform now gets applied to all icons, but this
should come at a negligible performance cost.
2026-02-05 13:06:17 +01:00
Joffrey JAFFEUX c4905cdafe DEV: code improvements to upsert category (#37540)
- don't rely on cached, it's almost never necessary
- rely on default form-kit behavior where possible
- prefer concatClass to getters when possible to have the css directly
in the template
2026-02-05 11:30:33 +01:00
Osama Sayegh 47ca25d56e FIX: Eager load base_scheme color_scheme_colors in themes controller (#37550)
When a color scheme has a base_scheme, the base scheme's
color_scheme_colors were not being eager loaded, causing strict loading
violations.

This updates the includes statement to properly load the nested
association `base_scheme: :color_scheme_colors` instead of just
`:base_scheme`.

Also adds a test that creates color schemes with base_scheme
relationships to verify the associations are properly loaded without
strict loading errors.
2026-02-05 13:17:56 +03:00
chapoi cad95d7380 UX: grid overflow fix (#37566)
addition for #37564
2026-02-05 11:16:07 +01:00
chapoi 405603408d UX: add MQ to change grid layout for main-outlet-wrapper (#37564)
The sidebar becomes a floating-overlay element at size = md, so by
updating the grid layout we avoid an invisible element interfering with
grid gap properties.

This shouldn't change anything visually and have no theme-impact afaict.
2026-02-05 10:58:32 +01:00
Natalie Tay 73e3286356 FIX: Cache banners per locale (#37561)
Currently topic banners are localizable, but unfortunately there is a
global cache that is not scoped per locale.

This commit fixes that.
2026-02-05 17:21:12 +08:00
Loïc Guitaut 02b3407b5a FIX: Complete protection for potentially illegal reviewables on post deletion (#37536)
This is a follow-up to fd04f690.

The original commit only protected potentially illegal reviewables from
being auto-approved in one code path (direct post deletion). However,
when deleting a post through a reviewable action with
`notify_users_after_responses_deleted_on_flagged_post` enabled, the code
would still call `#ignore` on any flagged reply posts without checking
`#potentially_illegal?`.
2026-02-05 09:42:52 +01:00
Alan Guo Xiang Tan 6db95a02c7 DEV: Add system test for tag-list site setting (#37559) 2026-02-05 15:47:47 +08:00
Sam b5838d7d42 DEV: Remove redundant double-assignment in admin search scoring (#37544) 2026-02-05 18:05:23 +11:00
Sam 2e3d01ae0b FIX: incorrect Breton plural rule logic operator (#37545)
Technically there appear to also be a two and many... but this at least
removes some of the bug.

```
def wrong(n)
  n != 13 || n != 14
end

def right(n)
  n != 13 && n != 14
end

[13, 14, 15].each do |n|
  puts "#{n}: wrong=#{wrong(n)}, right=#{right(n)}"
end
```

15 is (not 13) OR (not 14)
15 is not (not 13) AND (not 14)

apologies for the headache.
2026-02-05 18:04:59 +11:00
Sam 6095ed3d0c DEV: Improved error message when not allowed to edit posts with AI tool (#37547)
Previously the PostRevisor handled the check, but this change both adds
a test and ensures the consumer gets a nicer error message
2026-02-05 18:04:12 +11:00
Sam 7fa0e65f5a FEATURE: add persona support for chat thread title generation (#37557)
Introduce a dedicated ChatThreadTitler persona to power automatic chat
thread title generation, replacing the hardcoded prompt approach.
2026-02-05 18:03:44 +11:00