Previously, locale bundles were using `require()` directly, and were
very sensitive to load order. This made it very hard to refactor things,
especially for our upcoming move to Vite.
This commit updates the four types of locale bundle to contain only very
simple JS code, with no dependencies at the top level. They simply add
POJO and functions to the `window._discourse_locale_data` global. When
`discourse-i18n` is loaded, it checks that global, loads up the data,
and executes the functions (passing in any dependencies like
messageformat/runtime where required).
Changes in the HTML files are to bring the locale bundles up to the
first position, since they no longer have any dependencies, and we need
to be 100% sure they're loaded before discourse-i18n.
Tests are updated for the new bundle format, and the most complex ones
have been converted to system specs, so that we no longer need to create
a mock environment for executing messageformat/discourse-i18n/etc.
`lib/deprecated` changes (and associated pretty-text changes) are to
make the dependencies more formalized, because `require()` is subject to
race conditions, which started being hit following the locale refactor.
---------
Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Sometimes, HTML entities can be escaped twice, typically when getting
sanitized data from our `Onebox::OpenGraph` class then providing that
value to a template. We’re using the Mustache gem to process the Onebox
templates, and it will automatically escape HTML entities. This is
usually not a problem, but it is for things like ampersands. For
example, if the value we provide to the template is `&`, then
Mustache will convert it to `&amp;`.
This patch fixes that behavior by decoding the result of the sanitization
we apply in `Onebox::OpenGraph`. That way, templates will get `&`
instead of `&`, thus there won’t be any double escaping.
Followup fc98ce9a72
When we added the "Leave channel" option to the chat sidebar
context menu, for DM and group DM channels we mistakenly
used the leaveChannel API call instead of unfollowChannel.
unfollowChannel is non-destructive and simply removes
the channel from the user's sidebar, while leaveChannel
is destructive and removes the user from the channel, which
makes it impossible to find group channels again.
c.f.
https://meta.discourse.org/t/access-common-chat-channel-actions-from-the-sidebar-context-menu/392667
This corrects it so on edit/create we ALWAYS junk old drafts and never
return old drafts to the client
---------
Co-authored-by: Régis Hanol <regis@hanol.fr>
Previously to triage with an ai tool you would need to first call a
persona
this could be inefficient since the persona would issue a call to the
llm
just to trigger the tool
This new automation bypasses that
also
- Adds the ability to set custom fields in the API
- Live refresh UI when setting tags
We ran into trouble with MethodProfiler referencing excon without having loaded
it as it was relying on an initialiser to load it and depending on the side
effect.
If the excon gem is going to be loaded anyways, it doesn't make sense to have
it not loaded by default; this will be more robust.
With the change to Email::Cleaner:
* FIX: Email::Cleaner must clear the encoding of replaced bodies
the `incoming_emails:truncate_long` task will cause the encoding on replaced
records to change to the minimally-necessary encoding, causing a failure in
`tasks/incoming_emails_spec:14`.
quoted-printable is not necessary for the content, 7bit is enough.
If this is *not* done, then the decoded body will already be considered to be
encoded, e.g.:
```
pry(main)> puts part
Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: base64
PGh0bWw+PGhlYWQ+PC9oZWFkPjxib2R5Pjxicj48YnI+PGRpdiBjbGFzcz0i
cHJvdG9ubWFpbF9zaWduYXR1cmVfYmxvY2stdXNlciI+PC9kaXY+PGJyPjxi
cj5TZW50IGZyb20gPGEgdGFyZ2V0PSJfYmxhbmsiIGhyZWY9Imh0dHBzOi8v
cHJvdG9uLm1lL21haWwvaG9tZSIgcmVsPSJub3JlZmVycmVyIj5Qcm90b24g
TWFpbDwvYT4gZm9yIEFuZHJvaWQuPC9ib2R5PjwvaHRtbD4=
pry(main)> part.body = part.body.decoded
=> "<html><head></head><body><br><br><div class=\"protonmail_signature_block-user\"></div><br><br>Sent from <a target=\"_blank\" href=\"https://proton.me/mail/home\" rel=\"noreferrer\">Proton Mail</a> for Android.</body></html>"
pry(main)> puts part
Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: base64
htmlhead/headbodybrbrdivclassprotonmailsignatureblockuser/di
vbrbrSentfromatargetblankhrefhttps//protonme/mail/homerelnor
eferrerProtonMail/aforAndroid/body/htmk=
```
Clearing the CTE indicates to the Mail gem that the content needs to be encoded
if necessary.
The Mail gem sorts MIME parts by content type on serialisation. The default
sort can lead to post-script content incorrectly appearing at the top after
sort.
A concrete example found in the wild is:
* multipart/signed
* text/plain (actual content)
* application/pgp-signature
* text/plain (mailing list footer)
After reordering it looks like:
* text/plain (mailing list footer)
* multipart/signed
* text/plain (actual content)
* application/pgp-signature
which causes the Mail gem to detect the mailing list footer as the main content
of the message, which is incorrect.
Removed this positioning in
https://github.com/discourse/discourse/pull/37066, but the container can
get in the way of themes using a grid layout. Fixed positioning here
ensures the container doesn't impact grid flow.
This was failing sometimes when locales were different because `Light
(default)` is translated... so while we were always checking for `Light
(default)`... sometimes it was `ライト (デフォルト)` or `Clair (par défaut)`
Prevents taking the dimensions from oEmbed when using rich data.
It also makes a small change to how embedded html in
`AllowlistedGenericOnebox` works, previously it would not render when
the height value was not present. I have made the height value optional,
which should be fine since the default dimensions set by the browser
(typically 150 pixels high) will be used as a fallback.
What is the problem?
The `ReviewablePerformResultSerializer` includes `transition_to` and
`transition_to_id` attributes in API responses, but these values are
not consumed by any frontend code. The frontend only uses
`remove_reviewable_ids`, `reviewable_count`, `unseen_reviewable_count`,
and `completed_message` from the perform result.
What is the solution?
Remove the unused `transition_to` and `transition_to_id` attributes
from `ReviewablePerformResultSerializer`, along with the
`transition_to_id` method. Update corresponding test expectations.
What is the problem?
`TopicLink` records can reference deleted topics or posts. When a topic
or the target post being linked to is deleted, these orphaned links
continue to appear in topic maps and post link counts.
Note that `TopicLink` records are already deleted when the source post
containing the link is trashed (see `Post#trash!`), so we only need to
filter for deleted target topics/posts.
Additionally, internal links stored with http:// are not normalized to
https:// when `force_https` site setting is enabled.
---
What is the solution?
Extend filtering in `TopicLink.topic_map` and `TopicLink.counts_for` to
exclude links where the target topic or target post is deleted. Extract
common visibility filters into `TopicLink.apply_link_visibility_filters`
helper method.
Normalize internal http:// URLs to https:// in `TopicLinkSerializer#url`
when `force_https` is enabled.
What is the problem?
The review queue throws a JavaScript error when processing MessageBus
updates:
```
MESSAGE BUS FAIL: callback /reviewable_action caused exception
TypeError: Cannot read properties of undefined (reading 'includes')
```
The bug flow is:
1. `Reviewable::PerformResult#initialize` only sets
`@remove_reviewable_ids`
when `success?` is true, leaving it nil otherwise
2. `ReviewablePerformResultSerializer` serializes nil as JSON null
3. MessageBus publishes this to `/reviewable_action` channel
4. The JS `_updateStatus` callback in `ReviewableItem` receives data
with
`remove_reviewable_ids` as null/undefined
5. Calling `.includes()` on null throws TypeError
What is the solution?
Always initialize `@remove_reviewable_ids` as an array in
`Reviewable::PerformResult#initialize` (empty for failures, containing
the reviewable id for successes). Also add defensive optional chaining
in the JS `ReviewableItem#_updateStatus` callback and update the truthy
check in `ReviewableItem#_performResult` to verify array length since
`[]` is truthy in JavaScript.
What is the problem?
The category calendar component supports color mapping for events
based on tags and categories via the `map_events_to_color` site
setting, but there were no system tests to verify this functionality.
What is the solution?
Add system tests that verify events display with the correct
border color when tag or category color mappings are configured.
This sets width and height attributes for our SVG icons. This helps
avoid a flash of unstyled SVG icons, which makes them appear gigantic in
some situations (live reload in dev environments may be the most
obvious).
The width/height here will be overridden by existing CSS, because inline
presentation attributes have lower priority than other CSS.
We already set the same `1em` width/height on `.svg-icon` in our
`svg.scss` file... so there shouldn't be any significant impact beyond
making some loading situations more pleasant!
Some reference on this situation:
https://www.sarasoueidan.com/blog/svg-style-inheritance-and-fousvg/
Before:
<img width="1364" height="1422" alt="image"
src="https://github.com/user-attachments/assets/023e8a9b-718f-425e-8e76-eff07e91389b"
/>
After:
<img width="1320" height="1360" alt="image"
src="https://github.com/user-attachments/assets/4754b092-c62c-4fd3-bde9-c952b6020d2b"
/>
When the embedding API fails (e.g., OpenAI quota exceeded), the error
was bubbling up as a 500 to end users.
This catches `Net::HTTPBadResponse` in both `search` and `quick_search`
actions, logs the error, and returns empty results instead.