Compare commits

..

337 Commits
beta ... main

Author SHA1 Message Date
David Battersby
e47b0996ed
FIX: enable drafts dropdown on private categories (#32370)
Reinstates the draft dropdown on private category pages.
2025-04-18 17:38:35 +04:00
Martin Brennan
6c4e9dfbc8
DEV: Try fix composer rich text transcript spec (#32364)
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
```
2025-04-18 15:52:35 +10:00
Renato Atilio
9b5ada304f
UX: avoid auto-linking clash with code marks on rich editor (#32365)
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.
2025-04-18 01:11:59 -03:00
Mark VanLandingham
1c8646b48e
DEV: Refactor forcing admin sidebar logic in hamburger nav mode (#32257)
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>
2025-04-17 22:36:38 -05:00
Renato Atilio
f06175b72d
UX: prosemirror rich editor nodes cleanup / slightly better UX (#32361)
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.
2025-04-18 00:20:55 -03:00
Renato Atilio
2b4521d0b3
FIX: pasting img on rich editor could be double-processed (#32356)
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.
2025-04-18 00:20:44 -03:00
Martin Brennan
492256e91e
UX: Welcome bannner and search tweaks for mobile (#32362)
* 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>
2025-04-18 12:26:03 +10:00
Martin Brennan
ec1f43f9ca
DEV: Delete old admin page action button component (#32347)
Followup a879bcdc356d4e691fd217abd34a9f9a2022539c
2025-04-18 09:38:12 +10:00
Renato Atilio
4c26679b2d
DEV: fix form template flaky (#32360) 2025-04-17 19:54:38 -03:00
Kris
bdca369288
UX: don't decrease composer monospace font on mobile (#32358)
I think this is causing iOS to zoom in on the composer in some
scenarios, and we don't want that:


![image](https://github.com/user-attachments/assets/b4b22d25-c3f8-4da5-91d0-1b1940150a89)
2025-04-17 17:44:52 -04:00
Renato Atilio
cd805d0d8f
DEV: Revert "FIX: Flaky test for FormTemplatesController (#32351)" (#32359)
This reverts commit 4cd7ca677f69a18ab680c4b0ecac4a0d583d533b.
2025-04-17 18:39:26 -03:00
Kris
2dac94d99f
FIX: remove mixed declarations from header.scs (#32357)
Follow-up to a0eeb83, this fixes the mixed declarations error
2025-04-17 15:43:40 -04:00
Kris
a0eeb831d3
REFACTOR: merge mobile/desktop header styles into /common (#32320)
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`
2025-04-17 14:57:28 -04:00
Jordan Vidrine
12c5392b38
UX: Header search tweaks (#32354) 2025-04-17 11:32:19 -05:00
Joffrey JAFFEUX
fd9d0d11b4
UX: auto focus emoji picker input on mobile (#32353)
Previously this behavior was only on desktop.
2025-04-17 18:03:28 +02:00
Juan David Martínez Cubillos
4cd7ca677f
FIX: Flaky test for FormTemplatesController (#32351) 2025-04-17 10:23:58 -05:00
Juan David Martínez Cubillos
72f9714ddc
FEATURE: Implement tag group selection in dropdown and multi-select for topic creation and preview when using Form Templates (#32108)
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>
2025-04-17 08:38:03 -05:00
Yuriy Kurant
ff334fde8e
UX: header search mobile support - follow up (#32306)
### 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>`
2025-04-17 20:01:47 +08:00
Alan Guo Xiang Tan
918aa0fbec
DEV: Add :sidekiq_job_error DiscourseEvent (#32307)
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.
2025-04-17 15:41:58 +08:00
Gary Pendergast
63f7755123
FIX: Unclaim reviewables when finished. (#32346)
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.
2025-04-17 16:28:27 +10:00
Osama Sayegh
530f2f13af
FIX: Make the 'Keep Message Deleted' reviewable option work (#32345)
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.
2025-04-17 07:35:08 +03:00
Krzysztof Kotlarek
a69a304f11
FEATURE: allow edit custom flags (#32344)
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).
2025-04-17 12:31:52 +08:00
Alan Guo Xiang Tan
f6eb2e083c
DEV: Resolve flaky test due to regexp match (#32343)
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.
2025-04-17 09:32:22 +08:00
Martin Brennan
6028363de2
FIX: Remove hint for browser search shortcut (#32330)
Followup 735bef9ea0d497ae7e04bd4a104bd86e928fc472

Remove the tip in search for users to press Ctrl+F
again to do browser search, as this is no longer relevant
2025-04-17 10:22:40 +10:00
Chris Alberti
b724181bdf
DEV: Update verbiage around remove password modal, swap button for a link (#32338)
Simplified the language around remove password to be more clear, swapped out the button for a link.
2025-04-16 14:37:54 -05:00
David Taylor
3d689bbbc2
DEV: Raise exception for deprecations in core stylesheets (#31894) 2025-04-16 16:36:41 +01:00
Penar Musaraj
fec1ba0d73
DEV: Fix scss deprecation warning (#32325) 2025-04-16 11:34:30 -04:00
Ella E.
cb64b37070
UX: Add visual variation to theme screenshot placeholder (#32077)
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>
2025-04-16 05:21:45 -06:00
David Taylor
2da0804ef5
DEV: Drop wasm type override in nginx config (#32332)
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
2025-04-16 10:27:36 +01:00
Alan Guo Xiang Tan
b3389a4cc3
PERF: Set JOBS=1 for low memory build environment (#32326)
This restores the fix in ae2bc240af434168aeebe73d550b1c979ca3a29b
2025-04-16 10:14:23 +01:00
Joffrey JAFFEUX
a6e35b0225
DEV: use native promise (#32331) 2025-04-16 10:17:02 +02:00
Gary Pendergast
9b351614ea
FIX: Set X-Robots-Tag header to prevent indexing of /safe-mode (#32329)
This change adds the `X-Robots-Tag` header to the `/safe-mode` response, discouraging search engines from including the page in their index.
2025-04-16 16:51:32 +10:00
Martin Brennan
8ccb66a44a
FEATURE: Release admin search for all sites (#32327)
This commit removes the experimental setting controlling
admin search and releases it for all admins. This search
can be triggered with Ctrl+/ or Cmd+/

For more information see
https://meta.discourse.org/t/introducing-comprehensive-admin-search/360157
2025-04-16 16:26:53 +10:00
Juan David Martínez Cubillos
c7d400eda2
FEATURE: Implement Form Template Preview (#32111)
![imagen](https://github.com/user-attachments/assets/db5cf334-6e92-40b6-b93a-5cfa12882e8f)
2025-04-15 23:52:02 -05:00
dependabot[bot]
a6a85a0241
Build(deps): Bump ffi from 1.17.1 to 1.17.2 (#32322)
Bumps [ffi](https://github.com/ffi/ffi) from 1.17.1 to 1.17.2.
- [Changelog](https://github.com/ffi/ffi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ffi/ffi/compare/v1.17.1...v1.17.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 00:38:34 +02:00
dependabot[bot]
30f328067c
Build(deps): Bump version_gem from 1.1.6 to 1.1.7 (#32321)
Bumps [version_gem](https://gitlab.com/oauth-xx/version_gem) from 1.1.6
to 1.1.7.
-
[Changelog](https://gitlab.com/oauth-xx/version_gem/blob/main/CHANGELOG.md)
-
[Commits](https://gitlab.com/oauth-xx/version_gem/compare/v1.1.6...v1.1.7)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 00:38:21 +02:00
Jordan Vidrine
9145acce80
UX: Remove z-index (#32319) 2025-04-15 16:27:17 -05:00
David Taylor
e9b6dbe316
DEV: Passthrough attributes on TopicList component (#32318) 2025-04-15 21:27:40 +01:00
Gabriel Grubba
09747898d1
FIX: Fix meta data content modifier state leak (#32316)
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
2025-04-15 14:32:35 -03:00
Jordan Vidrine
47a6a24372
UX: Adjust user-field labels (#32317) 2025-04-15 12:24:14 -05:00
David Taylor
3af7410264
DEV: Skip meta_data_content specs (#32315)
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"
```
2025-04-15 17:02:17 +01:00
Chris Alberti
375a36e2ef
Don't require password to create users with previous authentication (#32201)
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.
2025-04-15 10:35:49 -05:00
David Taylor
f3d3c61754
PERF: Reuse existing core JS build where possible (#32311)
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.
2025-04-15 16:18:22 +01:00
Natalie Tay
02c5dd439b
DEV: Add a post_owner_changed event (#32312)
This allows plugins to hook onto this event when post owner changes.
2025-04-15 22:26:44 +08:00
Osama Sayegh
351a295846
DEV: Fix failing test after a rename (#32314) 2025-04-15 17:05:06 +03:00
Osama Sayegh
b3bcc2c1d3
DEV: Fix github build (#32313) 2025-04-15 16:31:20 +03:00
Osama Sayegh
0b8df4b833
UX: Use 'unused' instead of 'active' for components (#32284)
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.
2025-04-15 16:12:20 +03:00
Osama Sayegh
5e8778ecf7
UX: Use core description of popular components when there's no description (#32286)
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.
2025-04-15 16:07:22 +03:00
Osama Sayegh
71b31604b5
PERF: Implement infinite scrolling for new components page (#32291)
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.
2025-04-15 16:05:10 +03:00
Osama Sayegh
996a3493fe
PERF: Prevent N+1 queries when loading theme/component descriptions (#32305)
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.
2025-04-15 16:02:41 +03:00
Joffrey JAFFEUX
59ec86933a
DEV: DMultiSelect (#32240)
The `DMultiSelect` component provides a customizable multi-select
dropdown with support for both mouse and keyboard interactions.

![Screenshot 2025-04-10 at 15 40
26](https://github.com/user-attachments/assets/277619db-6e56-4beb-8eda-f76360cd2ad8)

### 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>
2025-04-15 14:56:57 +02:00
Natalie Tay
4510d1ad46
DEV: Update site spec for unregistering modifiers (#32310)
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.
2025-04-15 20:07:48 +08:00
dependabot[bot]
c97425d22b
Build(deps): Bump prosemirror-commands from 1.7.0 to 1.7.1 in the prosemirror group (#32272)
Bumps the prosemirror group with 1 update:
[prosemirror-commands](https://github.com/prosemirror/prosemirror-commands).


Updates `prosemirror-commands` from 1.7.0 to 1.7.1
-
[Changelog](https://github.com/ProseMirror/prosemirror-commands/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/prosemirror/prosemirror-commands/compare/1.7.0...1.7.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 13:44:11 +02:00
dependabot[bot]
248e9a341f
Build(deps-dev): Bump lefthook from 1.11.8 to 1.11.10 (#32298)
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.11.8
to 1.11.10.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
-
[Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/evilmartians/lefthook/compare/v1.11.8...v1.11.10)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 13:37:40 +02:00
dependabot[bot]
59a79de00e
Build(deps-dev): Bump @swc/core from 1.11.18 to 1.11.21 (#32299)
Bumps [@swc/core](https://github.com/swc-project/swc) from 1.11.18 to
1.11.21.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/swc-project/swc/compare/v1.11.18...v1.11.21)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 12:19:31 +02:00
Alan Guo Xiang Tan
ccf8ed5462
DEV: Fix linting (#32308) 2025-04-15 16:24:21 +08:00
dependabot[bot]
9fa441cf19
Build(deps): Bump @faker-js/faker from 9.6.0 to 9.7.0 (#32275)
Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.6.0 to
9.7.0.
- [Release notes](https://github.com/faker-js/faker/releases)
- [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md)
- [Commits](https://github.com/faker-js/faker/compare/v9.6.0...v9.7.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 17:14:22 +10:00
Isaac Janzen
31877f646f
DEV: Add toggleTagInfo to discovery-list-controls-above outlet (#32302) 2025-04-15 16:43:54 +10:00
Lumi
e3c37f3f0b
Add a plugin outlet to the login splash screen (#32271)
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.
2025-04-15 13:55:23 +08:00
dependabot[bot]
cdd5fa5b1a
Build(deps): Bump csv from 3.3.3 to 3.3.4 (#32276)
Bumps [csv](https://github.com/ruby/csv) from 3.3.3 to 3.3.4.
- [Release notes](https://github.com/ruby/csv/releases)
- [Changelog](https://github.com/ruby/csv/blob/main/NEWS.md)
- [Commits](https://github.com/ruby/csv/compare/v3.3.3...v3.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 13:54:09 +08:00
dependabot[bot]
7cc46da442
Build(deps-dev): Bump rubocop-ast from 1.44.0 to 1.44.1 (#32277)
Bumps [rubocop-ast](https://github.com/rubocop/rubocop-ast) from 1.44.0
to 1.44.1.
- [Release notes](https://github.com/rubocop/rubocop-ast/releases)
-
[Changelog](https://github.com/rubocop/rubocop-ast/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop-ast/compare/v1.44.0...v1.44.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 13:53:50 +08:00
dependabot[bot]
f21b0fee14
Build(deps): Bump parallel from 1.26.3 to 1.27.0 (#32301)
Bumps [parallel](https://github.com/grosser/parallel) from 1.26.3 to
1.27.0.
-
[Commits](https://github.com/grosser/parallel/compare/v1.26.3...v1.27.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 13:53:27 +08:00
Ted Johansson
fc6aaa3e49
DEV: Wizard step tweaks (#32304)
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.
2025-04-15 13:53:01 +08:00
Martin Brennan
735bef9ea0
UX: Remove Ctrl+F search shortcut (#32281)
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.
2025-04-15 15:14:54 +10:00
Martin Brennan
7a6006f7aa
FIX: Escape regex symbols when replaceText is called for ProseMirror (#32280)
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.
2025-04-15 14:04:33 +10:00
Krzysztof Kotlarek
077649fafd
FIX: bugs with refresh page after save fonts (#32282)
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`.
2025-04-15 12:02:28 +08:00
Ted Johansson
8b9da12bf2
FIX: Wizard logo step JS error (#32303)
`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`.
2025-04-15 10:54:46 +08:00
dependabot[bot]
2cd82abbe3
Build(deps-dev): Bump parser from 3.3.7.4 to 3.3.8.0 (#32300)
Bumps [parser](https://github.com/whitequark/parser) from 3.3.7.4 to
3.3.8.0.
-
[Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/whitequark/parser/compare/v3.3.7.4...v3.3.8.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 01:17:47 +02:00
Renato Atilio
c78a79bbf5
UX: keep content on rich editor footnote inputrule (#32296)
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.
2025-04-14 19:36:18 -03:00
Jordan Vidrine
79c642a446
UX: Quick mobile fix for quote styles (#32297) 2025-04-14 17:28:03 -05:00
Renato Atilio
d8d341ebf6
FIX: remove newline from rich editor's pasted img title/alt (#32295)
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.
2025-04-14 19:24:21 -03:00
Jordan Vidrine
1da41e62cc
UX: Use mixin for quote border radius (#32294) 2025-04-14 16:33:21 -05:00
Jordan Vidrine
fd6eacd93c
UX: Border radius prose-mirror select node (#32292) 2025-04-14 15:10:23 -05:00
Gabriel Grubba
12f05181c7
FIX: Add helpers for title and content for application.html.erb and crawler.html.erb (#32290)
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.
2025-04-14 16:34:23 -03:00
Jordan Vidrine
dcef061651
UX: Remove z-index (#32289) 2025-04-14 12:53:46 -05:00
Renato Atilio
224d6f85ec
FIX: rich editor insertBlock implementation (#32288)
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.
2025-04-14 14:27:15 -03:00
Renato Atilio
fe20b52180
UX: keep the whisper editor font/color style consistent between editors (#32287)
Adds the whisper styling to the Markdown editor as well, keeping it
consistent with the preview and the rich editor.
2025-04-14 14:27:05 -03:00
Renato Atilio
29ca0ae0b1
FEATURE: add footnote (plugin) rich editor extension (#31719)
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.
2025-04-14 14:25:36 -03:00
Renato Atilio
a0a5b2889f
DEV: replace rich editor input rules regex lookbehind (#31909)
Refactors the rich editor input rules matches that rely on a lookbehind,
it breaks on iOS <= 16.3.
2025-04-14 14:25:17 -03:00
Renato Atilio
381f6e64e8
FIX: click handler position on rich editor details node (#32268)
`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.
2025-04-14 14:06:54 -03:00
Renato Atilio
b9cef22c2f
UX: rich editor html_block without escaping and avoiding \n\n (#32269)
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)
2025-04-14 13:53:26 -03:00
Jordan Vidrine
f03a6f3557
UX: Onebox & quote border radius (#32242) 2025-04-14 10:55:51 -05:00
David Taylor
f7d95a8fad
DEV: [gjs-codemod] add codemod commit to git-blame-ignore-revs 2025-04-14 15:36:37 +01:00
David Taylor
7b2b08cf89
DEV: [gjs-codemod] Convert automation/styleguide/other to gjs
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
2025-04-14 15:36:16 +01:00
David Taylor
3462113bd4
DEV: [gjs-codemod] merge js and hbs 2025-04-14 15:27:52 +01:00
David Taylor
1eb953ca57
DEV: [gjs-codemod] renamed hbs to gjs 2025-04-14 15:27:47 +01:00
David Taylor
3ca507e008
DEV: [gjs-codemod] renamed js to gjs 2025-04-14 15:27:45 +01:00
Renato Atilio
b3f0c85d82
UX: avoid leading space when serializing some nodes from rich editor (#32270)
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.
2025-04-14 10:57:34 -03:00
Natalie Tay
d1ce861a6b
DEV: Add usage example for anon cache cookie registration (#32283)
Adding an example for the plugin api `register_anonymous_cache_key`.
2025-04-14 21:03:05 +08:00
David Taylor
f0057c7353
DEV: Drop legacy topic-list and raw-handlebars compilation system (#32081) 2025-04-14 10:42:40 +01:00
Osama Sayegh
13cb472ec8
FIX: Refresh disabled state when switching between site texts (#32262)
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
2025-04-14 08:00:08 +03:00
Martin Brennan
1a10adb089
UX: Add tooltip for markdown toggle shortcut (#32278)
Followup 198dc813750d7a0de98cc94372dea1222b97743b

Indicate the shortcut in the tooltip for the rich/markdown
editor toggle, and also add the control translation for mac
in `translateModKey`
2025-04-14 13:12:52 +10:00
Krzysztof Kotlarek
ee035582e2
FIX: Rename branding to logo and fonts (#32264)
Change branding page into logo and fonts.

In addition, icon for email setting and email appearance were changed.
2025-04-14 10:49:47 +08:00
Yuriy Kurant
8b5da219d8
UX: header search mobile support (#31711)
## 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
2025-04-14 10:27:48 +08:00
Renato Atilio
198dc81375
FEATURE: Ctrl+M to toggle between rich/markdown editor (#32266) 2025-04-12 09:01:41 -03:00
Renato Atilio
fdb805d131
UX: only toggle rich editor details on caret click (#32267) 2025-04-12 09:01:20 -03:00
Renato Atilio
bc070dcbe3
FIX: avoid double base path on push notification (#32228)
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.
2025-04-12 08:16:53 +10:00
Gabriel Grubba
c484d2fd88
FEATURE: add override for crawler title and description tags (#32259)
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.
2025-04-11 09:38:11 -03:00
David Battersby
7103078fa1
FIX: switch to search icon on narrow desktop (#32215)
Ensures the search field reverts to the search icon on small desktop
screen sizes
2025-04-11 15:17:12 +04:00
David Taylor
36f364fe11
FIX: Ensure discovery queryParams do not persist invisibly (#32178)
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.
2025-04-11 09:44:32 +01:00
Gary Pendergast
05e0491902
FIX: Ensure uploaded watched word CSVs are converted to utf-8. (#32263)
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.
2025-04-11 16:12:45 +10:00
Osama Sayegh
d416b64af0
FIX: Move CORE_SVG_SPRITES constant to a method (#32261)
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.
2025-04-11 06:25:04 +03:00
Ted Johansson
414419b43d
DEV: Add Settings tab to admin Users page (#32255)
This PR adds a Settings tab to the admin Users page, grouping relevant site settings.
2025-04-11 11:10:53 +08:00
Krzysztof Kotlarek
6e654bc596
FIX: relax automation restrictions (#32238)
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.
2025-04-11 07:27:43 +08:00
Penar Musaraj
6fc5ce9688
UX: Improve associated accounts in user preferences (#32247)
Before

![CleanShot 2025-04-09 at 16 57
09@2x](https://github.com/user-attachments/assets/73609f67-355b-4e43-b4d8-eb163ce2b3cc)


After

![CleanShot 2025-04-09 at 16 56
10@2x](https://github.com/user-attachments/assets/90b05af0-649d-43ce-87c2-2a36dd8a2caa)
2025-04-10 12:01:07 -04:00
Selase Krakani
9bfb4c005d
FIX: Set last_read_post_number for imported watching topic users (#32229)
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
2025-04-10 12:35:51 +00:00
Gabriel Grubba
181606e0bd
FIX: Prioritize the author when replying to topic (#32244)
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>
2025-04-10 09:02:32 -03:00
David Battersby
7c4787256d
FIX: category badge style missing data attr (#32253)
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.
2025-04-10 13:20:50 +04:00
Joffrey JAFFEUX
01ce003b8e
FIX: logs time even when automation raises (#32254)
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`
2025-04-10 11:05:53 +02:00
Sam
da088a24c3
FEATURE: allow searching for whispers and bots (#32252)
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
2025-04-10 16:21:46 +10:00
David Battersby
7de9f79f55
FIX: hide search field on invites page (#32236)
Prevents rendering the search field on the invites page.
2025-04-10 09:54:17 +04:00
Ted Johansson
db214a38a2
DEV: Add Content admin config page (#32194)
This PR adds a dedicated page for Content related site settings. It has four different site setting tabs.
2025-04-10 11:20:22 +08:00
Martin Brennan
db68fd7046
DEV: Skip prosemirror flaky (#32231)
Example of a failure is here

https://github.com/discourse/discourse/actions/runs/14345676579/job/40214833933
2025-04-10 12:54:11 +10:00
Ted Johansson
7f851c416d
DEV: Add Dashboard settings tab to Reports page (#32235)
This change adds a Dashboard settings tab to the Reports page.
2025-04-10 10:41:12 +08:00
Ted Johansson
d32d0277fb
DEV: Move User API admin settings into API keys page (#32232)
We used to have a User API admin page, which is just a page with the site settings. This PR moves that into a Settings tab under API keys.
2025-04-10 10:40:25 +08:00
dependabot[bot]
92a9fb33e6
Build(deps-dev): Bump ember-test-selectors from 7.0.0 to 7.1.0 (#32248)
Bumps
[ember-test-selectors](https://github.com/mainmatter/ember-test-selectors)
from 7.0.0 to 7.1.0.
- [Release
notes](https://github.com/mainmatter/ember-test-selectors/releases)
-
[Changelog](https://github.com/mainmatter/ember-test-selectors/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mainmatter/ember-test-selectors/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-10 03:00:55 +02:00
Krzysztof Kotlarek
14a42bdafe
FIX: incorrect flag message when en_GB language (#32191)
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.
2025-04-10 08:06:52 +08:00
dependabot[bot]
af64c85670
Build(deps-dev): Bump puppeteer-core from 24.6.0 to 24.6.1 (#32249)
Bumps [puppeteer-core](https://github.com/puppeteer/puppeteer) from
24.6.0 to 24.6.1.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
-
[Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v24.6.0...puppeteer-core-v24.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-10 01:45:11 +02:00
Joffrey JAFFEUX
fff1ae60f1 DEV: ensures chat can work without discourse-presence
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.
2025-04-09 17:00:31 -04:00
Kris
fb162ad64d
UX: make admin code editor height more flexible (#32241)
Reported here:
https://meta.discourse.org/t/issue-with-custom-css-save-button-on-smaller-screens/358505


Allows the admin code editor to adjust to the viewport regardless of
error message and nav height in both normal and maximized modes


Before:


![image](https://github.com/user-attachments/assets/657fb938-04d2-4ad8-a0c3-f5b6177d7fa2)


![image](https://github.com/user-attachments/assets/bfbf532d-d193-4387-b8c9-546fb6653ff6)


After:


![image](https://github.com/user-attachments/assets/9c2cb0ad-2eff-4171-8912-918dd5326eae)


![image](https://github.com/user-attachments/assets/fa865a13-0891-4fa6-9af6-ed2422bf676e)
2025-04-09 16:20:48 -04:00
Renato Atilio
949953d025
UX: only-emoji consistency between rich editor and cooked (#32245) 2025-04-09 16:54:05 -03:00
Renato Atilio
5ece8112da
FIX: safari bug on rich editor's list item (#32243)
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`.
2025-04-09 15:03:52 -03:00
Renato Atilio
d096ba8cbb
UX: whisper color and font-style on rich editor (#32239)
Respects the same styling from the preview for font-style and color for
the ProseMirror editor when we're composing a whisper.
2025-04-09 12:52:22 -03:00
Chris Alberti
3106c30f16
Added button to remove password from account (#32200)
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.
2025-04-09 09:32:51 -05:00
David Battersby
752eca04a8
FIX: add category badge style to category list (#32109)
Adds support for category style headings (square, emojis and icons) to
the all categories list.
2025-04-09 18:31:47 +04:00
dependabot[bot]
70998b73db
Build(deps-dev): Bump lefthook from 1.11.7 to 1.11.8 (#32227)
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.11.7
to 1.11.8.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
-
[Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/evilmartians/lefthook/compare/v1.11.7...v1.11.8)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 13:12:55 +02:00
dependabot[bot]
4dc1ea11af
Build(deps-dev): Bump webpack from 5.99.2 to 5.99.5 (#32226)
Bumps [webpack](https://github.com/webpack/webpack) from 5.99.2 to
5.99.5.
- [Release notes](https://github.com/webpack/webpack/releases)
-
[Commits](https://github.com/webpack/webpack/compare/v5.99.2...v5.99.5)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 13:10:45 +02:00
dependabot[bot]
3f15508009
Build(deps): Bump faraday from 2.12.2 to 2.13.0 (#32225)
Bumps [faraday](https://github.com/lostisland/faraday) from 2.12.2 to
2.13.0.
- [Release notes](https://github.com/lostisland/faraday/releases)
-
[Changelog](https://github.com/lostisland/faraday/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/lostisland/faraday/compare/v2.12.2...v2.13.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 13:03:20 +02:00
Loïc Guitaut
5e9a1a64c7 DEV: Fix the error message from the deprecated icon handler
The string was written as a JS one (using ``), but in Ruby this syntax
tries to execute the string as a command on the host system.
2025-04-09 12:18:14 +02:00
Renato Atilio
5836a9a664
UX: backspace rich editor keymap improvements (#32234) 2025-04-09 06:54:15 -03:00
Neil Hanlon
90b2ca16dc
DEV: Update mailgun pricing (#32222)
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.
2025-04-09 09:56:03 +08:00
Alan Guo Xiang Tan
6e969d5cd4
DEV: Skip a flaky test (#32230)
Has been assigned for investigation soon

```
1) Admin::SiteSettingsController#update when logged in as an admin sanitizes integer values
    Failure/Error: expect(SiteSetting.suggested_topics).to eq(1000)

      expected: 1000
          got: 5

      (compared using ==)
    # ./spec/requests/admin/site_settings_controller_spec.rb:318:in `block (4 levels) in <main>'
    # ./spec/rails_helper.rb:588:in `block (3 levels) in <top (required)>'
    # ./vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
    # ./vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
    # ./spec/rails_helper.rb:578:in `block (2 levels) in <top (required)>'
    # ./spec/rails_helper.rb:535:in `block (2 levels) in <top (required)>'
    # ./vendor/bundle/ruby/3.3.0/gems/webmock-3.25.1/lib/webmock/rspec.rb:39:in `block (2 levels) in <top (required)>'
```
2025-04-09 09:49:16 +08:00
Renato Atilio
0a63c94e4d
UX: improve onebox handling on rich editor (#32221)
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.
2025-04-08 21:04:59 -03:00
Kelv
1557e425c6
DEV: refactor scrolling mixin logic into scroll-manager service (#32212)
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
2025-04-09 07:39:21 +08:00
Jordan Vidrine
9301a11896
UX: Wrap participants in pm topic item (#32223) 2025-04-08 18:03:07 -05:00
dependabot[bot]
baad2ae9a0
Build(deps-dev): Bump lefthook from 1.11.6 to 1.11.7 (#32204)
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.11.6
to 1.11.7.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
-
[Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/evilmartians/lefthook/compare/v1.11.6...v1.11.7)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 00:47:12 +02:00
Osama Sayegh
ad0966afa9
FEATURE: Introduce new components listing page (#32164)
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>
2025-04-08 17:58:29 +03:00
Renato Atilio
e84083ee36
FEATURE: prosemirror-codemark for a fake boundary cursor on rich editor (#32165)
Uses prosemirror-codemark to provide a fake boundary cursor to code marks, to easily
prepend/append text either code-marked or not.
2025-04-08 10:32:15 -03:00
Renato Atilio
6fb8f3ff7a
FEATURE: auto-link/unlink url-like words on rich editor when typing (#32163)
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.
2025-04-08 10:31:51 -03:00
Ted Johansson
b1e97d9ebd
DEV: Add e-mail site settings to e-mail admin page (#32214)
Follow-up to #32211. This PR adds a Settings tab to the Email settings admin page. The current Settings tab is renamed to Server settings.
2025-04-08 18:59:50 +08:00
Ted Johansson
a021032a35
DEV: Extract e-mail logs into their own admin page (#32211)
This is a lift-and-shift of the admin e-mail logs, moving it out of the "Server setup & logs" page and into its own dedicated admin page.
2025-04-08 17:50:12 +08:00
Loïc Guitaut
c90544ab44 DEV: Add missing specs to User::BulkDestroy 2025-04-08 11:42:51 +02:00
dependabot[bot]
c289100f60
Build(deps-dev): Bump webpack from 5.98.0 to 5.99.1 (#32205)
Bumps [webpack](https://github.com/webpack/webpack) from 5.98.0 to
5.99.1.
- [Release notes](https://github.com/webpack/webpack/releases)
-
[Commits](https://github.com/webpack/webpack/compare/v5.98.0...v5.99.1)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-04-08 11:37:33 +02:00
dependabot[bot]
c24aba7e66
Build(deps-dev): Bump @swc/core from 1.11.16 to 1.11.18 (#32208)
Bumps [@swc/core](https://github.com/swc-project/swc) from 1.11.16 to
1.11.18.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/swc-project/swc/compare/v1.11.16...v1.11.18)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 11:03:51 +02:00
dependabot[bot]
69b4c80448
Build(deps-dev): Bump ember-cli from 6.3.0 to 6.3.1 (#32207)
Bumps [ember-cli](https://github.com/ember-cli/ember-cli) from 6.3.0 to
6.3.1.
- [Release notes](https://github.com/ember-cli/ember-cli/releases)
-
[Changelog](https://github.com/ember-cli/ember-cli/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/ember-cli/ember-cli/compare/v6.3.0...v6.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 11:03:37 +02:00
Osama Sayegh
1e69d74966
FIX: Selecting a parent category shouldn't clear other form fields (#32206)
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.
2025-04-08 06:25:15 +03:00
Krzysztof Kotlarek
063d28763a
FIX: flaky admin_branding_spec fonts section (#32209)
Wait for state to be updated before submit fonts form to avoid random
failures.

Form was submitted too quickly.
2025-04-08 10:56:20 +08:00
Ted Johansson
8483d09fc4
UX: Add missing admin config page titles (#32198)
A number of admin config pages are missing a page title. This PR adds them in.
2025-04-08 10:02:11 +08:00
Ted Johansson
ec0b6affd7
DEV: Add Site admin admin config page (#32196)
This PR adds a dedicated admin config page for Site admin related site settings.
2025-04-08 09:52:42 +08:00
Ted Johansson
464726d973
DEV: Add Interface and layout admin config page (#32197)
This PR adds a dedicated admin settings page for Interface & layout related site settings.
2025-04-08 09:22:10 +08:00
Ted Johansson
5f0b186061
DEV: Add User defaults admin config page (#32195)
This PR adds a dedicated admin config page for User defaults related site settings.
2025-04-08 09:21:54 +08:00
Blake Erickson
206ea17c3f
DEV: Fix mixed-decls Sass deprecation warning (#32202)
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
2025-04-07 18:26:55 -06:00
Krzysztof Kotlarek
67d083ede0
FIX: flaky admin_branding_spec (#32193)
When remove image, ensure that state is updated before saving form.

We know that state is updated when remove button is no longer available.
2025-04-08 07:27:53 +08:00
dependabot[bot]
abfdc1fa11
Build(deps): Bump prosemirror-view from 1.38.1 to 1.39.1 in the prosemirror group (#32186)
Bumps the prosemirror group with 1 update:
[prosemirror-view](https://github.com/prosemirror/prosemirror-view).


Updates `prosemirror-view` from 1.38.1 to 1.39.1
-
[Changelog](https://github.com/ProseMirror/prosemirror-view/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/prosemirror/prosemirror-view/compare/1.38.1...1.39.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 18:30:48 +02:00
dependabot[bot]
51854ad0ed
Build(deps): Bump reline from 0.6.0 to 0.6.1 (#32181)
Bumps [reline](https://github.com/ruby/reline) from 0.6.0 to 0.6.1.
- [Release notes](https://github.com/ruby/reline/releases)
- [Commits](https://github.com/ruby/reline/compare/v0.6.0...v0.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 18:29:09 +02:00
Gerhard Schlager
251cac39af DEV: Adds a basic importer for the IntermediateDB
* 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
2025-04-07 17:22:36 +02:00
Gerhard Schlager
7c6b116dfd DEV: Adds a new converter for migrating from Discourse
It only contains a few user-related steps right now
2025-04-07 17:22:36 +02:00
Gerhard Schlager
17ba19c7ae REFACTOR: Code generator for migrations IntemerdiateDB
* 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
2025-04-07 17:22:36 +02:00
Gerhard Schlager
71a90dcba2 DEV: Refactor migrations-tooling
* 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
2025-04-07 17:22:36 +02:00
Roman Rizzi
7b5839ec44
DEV: Optionally include everyone groups when searching (#32199)
`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.
2025-04-07 11:55:27 -03:00
Joffrey JAFFEUX
6637eb2b64
DEV: discourse-emojis 1.0.39 (#32180)
d575425413
2025-04-07 10:49:42 +02:00
Ted Johansson
cfd216598d
DEV: Add Analytics and SEO admin config page (#32190)
This PR adds a dedicated page for Analytics and SEO related site settings.
2025-04-07 16:15:37 +08:00
Krzysztof Kotlarek
601fecde06
FIX: flaky admin_customize_themes_spec (#32169)
Ensure that translations are loaded before checking input value.
2025-04-07 10:39:21 +08:00
Krzysztof Kotlarek
928f9175f0
FEATURE: fonts section for branding page (#32031)
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>
2025-04-07 10:28:42 +08:00
Martin Brennan
637a221517
FIX: Fallback to #search-menu for search input (#32188)
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.
2025-04-07 11:57:27 +10:00
dependabot[bot]
c9d583be92
Build(deps-dev): Bump selenium-webdriver from 4.29.1 to 4.31.0 (#32185)
Bumps [selenium-webdriver](https://github.com/SeleniumHQ/selenium) from
4.29.1 to 4.31.0.
- [Release notes](https://github.com/SeleniumHQ/selenium/releases)
-
[Changelog](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES)
-
[Commits](https://github.com/SeleniumHQ/selenium/commits/selenium-4.31.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 09:53:23 +08:00
dependabot[bot]
ce6583d226
Build(deps-dev): Bump bullet from 8.0.2 to 8.0.3 (#32184)
Bumps [bullet](https://github.com/flyerhzm/bullet) from 8.0.2 to 8.0.3.
- [Changelog](https://github.com/flyerhzm/bullet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flyerhzm/bullet/compare/8.0.2...8.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 09:07:03 +08:00
dependabot[bot]
cc4e4eaab1
Build(deps-dev): Bump selenium-devtools from 0.133.0 to 0.135.0 (#32183)
Bumps [selenium-devtools](https://github.com/SeleniumHQ/selenium) from
0.133.0 to 0.135.0.
- [Release notes](https://github.com/SeleniumHQ/selenium/releases)
-
[Changelog](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES)
- [Commits](https://github.com/SeleniumHQ/selenium/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 09:06:45 +08:00
Jarek Radosz
231e9ca99a
DEV: Remove an unused admin component+helpers (#32179) 2025-04-04 20:36:05 +02:00
dependabot[bot]
089d34a591
Build(deps-dev): Bump express from 4.21.2 to 5.1.0 (#32089)
Bumps [express](https://github.com/expressjs/express) from 4.21.2 to
5.1.0.
- [Release notes](https://github.com/expressjs/express/releases)
-
[Changelog](https://github.com/expressjs/express/blob/master/History.md)
-
[Commits](https://github.com/expressjs/express/compare/4.21.2...v5.1.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-04 20:29:03 +02:00
Gabriel Grubba
80e6d3d8c5
DEV: Add modifier for meta_data_content in ApplicationHelper (#32159)
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`)
2025-04-04 12:28:38 -03:00
Roman Rizzi
a57925b6db
FEATURE: Dynamic size for hot topic IDs cache (#32175)
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.
2025-04-04 11:58:06 -03:00
Gabriel Grubba
a88d8a0393
FEATURE: prioritize the user who is getting the reply in the autocomplete (#32086)
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
2025-04-04 10:11:37 -03:00
David Taylor
f571c3c62e
Revert "DEV: refactor password validation helper class to take explic… (#32173)
…it args (#32149)"

This reverts commit 568ccdc19d0ab5090e21023c7f5709b02aca6102.

Need to resolve some incompatibilities with plugins
2025-04-04 14:10:35 +01:00
David Taylor
ae2bc240af
PERF: Set JOBS=1 for low-memory build environments (#32171)
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>
2025-04-04 13:48:19 +01:00
Natalie Tay
aebb1f0968
DEV: Pass full model for tag to sidebar serializer (#32170)
We are currently only passing a few attributes of a tag when serializing
sidebar tags.

This commit passes the `Tag` into the serializer instead.
2025-04-04 16:29:57 +08:00
Ted Johansson
aa5fd55e71
FIX: Archiving messages from group inbox (#32166)
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.
2025-04-04 16:05:43 +08:00
Joffrey JAFFEUX
4c8420833e
FEATURE: One-click chat reaction settings (#32150)
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 `😄`.


![image](https://github.com/user-attachments/assets/8913db0e-d6ec-4347-ad91-2329206b127c)

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>
2025-04-04 09:15:13 +02:00
Gary Pendergast
33748895a9
FEATURE: Highlight watched words found in reviewables. (#32167)
This change highlights watched words found in any reviewable posts.
2025-04-04 17:26:14 +11:00
Alan Guo Xiang Tan
0724b03fb8
DEV: Bump Capybara.default_max_wait_time to 20 on CI (#32143)
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.
2025-04-04 12:50:28 +08:00
Krzysztof Kotlarek
e3e5f20cb6
FIX: flaky admin_sidebar_navigation_spec (#32168)
Wait till all sections are expanded before checking expected links.
2025-04-04 12:38:31 +08:00
Martin Brennan
3d6b7e2574
FEATURE: Copy thread messages when copying thread OP (#32139)
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.
2025-04-04 13:45:37 +10:00
Kelv
568ccdc19d
DEV: refactor password validation helper class to take explicit args (#32149)
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.
2025-04-04 11:13:49 +08:00
chapoi
564edc52eb
UX: add flex to mentions mixin + update units (#32145)
Fixing hashtag text glued to the icon, and updated some related properties to using better units.

---------

Co-authored-by: Martin Brennan <martin@discourse.org>
2025-04-04 12:20:35 +10:00
Martin Brennan
c4d971ea2c
FIX: Consistent search shortcuts (#32099)
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.
2025-04-04 11:54:46 +10:00
dependabot[bot]
aaa648ceac
Build(deps-dev): Bump rubocop from 1.75.1 to 1.75.2 (#32155)
Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.75.1 to
1.75.2.
- [Release notes](https://github.com/rubocop/rubocop/releases)
-
[Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop/compare/v1.75.1...v1.75.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-04 01:07:39 +02:00
dependabot[bot]
57c0ac02d1
Build(deps-dev): Bump puppeteer-core from 24.5.0 to 24.6.0 (#32161)
Bumps [puppeteer-core](https://github.com/puppeteer/puppeteer) from
24.5.0 to 24.6.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
-
[Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v24.5.0...puppeteer-core-v24.6.0)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-04-04 01:07:18 +02:00
dependabot[bot]
f91be52d5f
Build(deps): Bump mini_racer from 0.18.0 to 0.18.1 (#32154)
Bumps [mini_racer](https://github.com/discourse/mini_racer) from 0.18.0
to 0.18.1.
- [Changelog](https://github.com/rubyjs/mini_racer/blob/main/CHANGELOG)
-
[Commits](https://github.com/discourse/mini_racer/compare/v0.18.0...v0.18.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 23:51:05 +02:00
dependabot[bot]
4761a7a363
Build(deps): Bump faraday-retry from 2.3.0 to 2.3.1 (#32153)
Bumps [faraday-retry](https://github.com/lostisland/faraday-retry) from
2.3.0 to 2.3.1.
- [Release notes](https://github.com/lostisland/faraday-retry/releases)
-
[Changelog](https://github.com/lostisland/faraday-retry/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/lostisland/faraday-retry/compare/v2.3.0...v2.3.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 23:50:57 +02:00
dependabot[bot]
239634e325
Build(deps): Bump irb from 1.15.1 to 1.15.2 (#32156)
Bumps [irb](https://github.com/ruby/irb) from 1.15.1 to 1.15.2.
- [Release notes](https://github.com/ruby/irb/releases)
- [Commits](https://github.com/ruby/irb/compare/v1.15.1...v1.15.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 23:50:46 +02:00
dependabot[bot]
1d94c39bef
Build(deps-dev): Bump rubocop-ast from 1.43.0 to 1.44.0 (#32157)
Bumps [rubocop-ast](https://github.com/rubocop/rubocop-ast) from 1.43.0
to 1.44.0.
- [Release notes](https://github.com/rubocop/rubocop-ast/releases)
-
[Changelog](https://github.com/rubocop/rubocop-ast/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop-ast/compare/v1.43.0...v1.44.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-03 23:50:32 +02:00
Jordan Vidrine
abeb8de7b5
UX: Button fixes (#32152) 2025-04-03 16:13:34 -05:00
Juan David Martínez Cubillos
eb7e1613a0
DEV: Update plugin outlet wrappers on user-summary-topic.gjs (#31455)
Co-authored-by: Guhyoun Nam <70915823+rngus2344@users.noreply.github.com>
Co-authored-by: Isaac Janzen <50783505+janzenisaac@users.noreply.github.com>
Co-authored-by: rngus2344 <nam@discourse.org>
2025-04-03 15:20:51 -05:00
Jarek Radosz
9140285569
DEV: Remove rotate-center css animation (#32151)
The code that introduced/used it is gone.
2025-04-03 19:12:55 +02:00
Jarek Radosz
9be51b616b
DEV: Handle the missing d-lazy-video case (#32148)
aka: lazy load lazy video

---------

Co-authored-by: David Taylor <david@taylorhq.com>
2025-04-03 18:09:02 +02:00
Kelv
0863a42ff0
DEV: refactor CanCheckEmailsHelper to directly take model id (#32129)
We only need the `model.id` input here, so this simplifies the arguments
being passed into this helper.
2025-04-03 20:33:15 +08:00
David Taylor
1b2cf92d4c
DEV: Drop backburner freedom patch (#32147)
Bugfix is now released in ember
2025-04-03 11:58:42 +01:00
David Taylor
c824a3b300
DEV: Retry chunk loading from all chunks (#32146)
Hopefully this will resolve some flaky qunit runs
2025-04-03 11:58:25 +01:00
Kelv
60d7a4f120
DEV: refactor username validation helper class to take explicit args (#32136)
This aligns closer to how we are passing data through to the
UserFieldsHelperClass https://github.com/discourse/discourse/pull/31670,
and reduces coupling with the component itself.
2025-04-03 18:57:47 +08:00
Ted Johansson
6a6c0bc179
UX: Proper width of unsaved site setting banner (#32137)
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.
2025-04-03 17:35:01 +08:00
Martin Brennan
a62c2b9a17
DEV: Revert hashtag change and skip admin search test (#32144)
- **Revert "UX: add flex to mention for consistent alignment and spacing
(#32105)"**
- **DEV: Skip test**
2025-04-03 19:20:10 +10:00
Ted Johansson
7e77b24202
DEV: Preserve unsaved site settings (#32100)
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.
2025-04-03 16:10:30 +08:00
chapoi
191ab68509
UX: add flex to mention for consistent alignment and spacing (#32105)
Fixing hashtag text glued to the icon
![CleanShot 2025-04-01 at 14 14
39](https://github.com/user-attachments/assets/237a0d54-dc08-438d-b935-94d3e670fd09)

And updated some related properties to using better units

| Before | After |
|--------|--------|
| ![CleanShot 2025-04-01 at 14 25
32@2x](https://github.com/user-attachments/assets/818d763d-900d-4a68-9115-381173b0c0a3)
| ![CleanShot 2025-04-01 at 14 15
37@2x](https://github.com/user-attachments/assets/abbe9167-6fa1-41ff-999d-e12a779f6a0e)
|
2025-04-03 09:45:41 +02:00
Martin Brennan
b40f184a70
UX: Make admin search setting visible (#32142)
We are ready to start rolling this out to more
people and announce it on Meta.
2025-04-03 17:03:53 +10:00
Alan Guo Xiang Tan
0f0a59d7ff
DEV: Move admin only constants into admin bundle (#32141)
Minor optimization so that we don't load extra data for non-admin users.
2025-04-03 14:28:36 +08:00
Martin Brennan
b112f699f3
FIX: Admin search page shortcut (#32140)
Fixes "[missing %{shortcutHTML} value]" on standalone
admin search page.
2025-04-03 16:06:00 +10:00
Martin Brennan
60607aa231
UX: Show keyboard shortcut on full admin search page (#31646)
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.
2025-04-03 14:12:31 +10:00
Discourse Translator Bot
576f513304
Update translations (#32106) 2025-04-03 00:04:57 +02:00
dependabot[bot]
a7514670b4
Build(deps-dev): Bump bullet from 8.0.1 to 8.0.2 (#32135)
Bumps [bullet](https://github.com/flyerhzm/bullet) from 8.0.1 to 8.0.2.
- [Changelog](https://github.com/flyerhzm/bullet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/flyerhzm/bullet/compare/8.0.1...8.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:47:22 +02:00
dependabot[bot]
1d72fd388d
Build(deps): Bump a11y-dialog from 8.1.2 to 8.1.3 (#32134)
Bumps [a11y-dialog](https://github.com/KittyGiraudel/a11y-dialog) from
8.1.2 to 8.1.3.
- [Release notes](https://github.com/KittyGiraudel/a11y-dialog/releases)
-
[Commits](https://github.com/KittyGiraudel/a11y-dialog/compare/8.1.2...8.1.3)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:39:01 +02:00
Ella E.
7e7062f213
UX: Align tag separator properly when viewing search in full-page mode (#32133)
Better alignment with the  `,` separator

### Before
<img width="205" alt="image"
src="https://github.com/user-attachments/assets/5d834e72-8e55-4d4b-923a-e5d088fb0102"
/>

### After
<img width="207" alt="image"
src="https://github.com/user-attachments/assets/7d85a0ff-1804-451e-aea4-3fb9844e5b8e"
/>
2025-04-02 10:04:20 -04:00
David Battersby
783072e24d
FIX: hide search field on auth pages (#32132)
This change prevents rendering the search field on login and sign up
pages.
2025-04-02 17:21:26 +04:00
Ella E.
25e5d7748a
UX: Adjust spacing between simple tags in the site settings preview (#32125)
### Before

![image](https://github.com/user-attachments/assets/27afb534-cac3-4573-9be7-a5608f1dc498)


### After
<img width="635" alt="image"
src="https://github.com/user-attachments/assets/999aca40-5488-41f0-8887-01584ce425a4"
/>
2025-04-02 07:10:12 -06:00
David Taylor
8525de7e55
DEV: [gjs-codemod] add codemod commit to git-blame-ignore-revs 2025-04-02 13:44:33 +01:00
David Taylor
999ae73c78
DEV: [gjs-codemod] apply codemod
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
2025-04-02 13:44:15 +01:00
David Taylor
dac398d6ab
DEV: [gjs-codemod] merge js and hbs 2025-04-02 13:43:33 +01:00
David Taylor
1c8019a3c7
DEV: [gjs-codemod] renamed hbs to gjs 2025-04-02 13:43:32 +01:00
David Taylor
386bb974d1
DEV: [gjs-codemod] renamed js to gjs 2025-04-02 13:43:28 +01:00
Alan Guo Xiang Tan
0cb08a3d2f
DEV: Double timeout in spec/email_change_spec.rb (#32131)
Checking of the database state is still flaky on CI. Double the timeout
to give the server more time to process as a workaround.

Example of test flake:
https://github.com/discourse/discourse/actions/runs/14216968060/job/39835831706
2025-04-02 20:10:57 +08:00
dependabot[bot]
c1f0d9aee0
Build(deps-dev): Bump @ember/test-helpers from 5.2.0 to 5.2.1 (#32115)
Bumps
[@ember/test-helpers](https://github.com/emberjs/ember-test-helpers)
from 5.2.0 to 5.2.1.
- [Release
notes](https://github.com/emberjs/ember-test-helpers/releases)
-
[Changelog](https://github.com/emberjs/ember-test-helpers/blob/master/CHANGELOG.md)
- [Commits](https://github.com/emberjs/ember-test-helpers/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 13:54:44 +02:00
dependabot[bot]
040bcf69f3
Build(deps-dev): Bump esbuild from 0.25.1 to 0.25.2 (#32067)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.1 to 0.25.2.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.1...v0.25.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 13:33:06 +02:00
Ella E.
1d65ebe3bb
UX: Adjust spacing to prevent last setting from being hidden behind save all banner (#32127)
This PR adjusts the bottom spacing in the site settings when scrolled to
the bottom and the “Save all changes” banner is visible, ensuring that
the setting controls and helper text are fully visible within the
viewport.

### Before
<img width="954" alt="image"
src="https://github.com/user-attachments/assets/69d8b1ad-e90e-4d94-9f32-4a8ae17ef725"
/>


### After
<img width="937" alt="image"
src="https://github.com/user-attachments/assets/50365613-8553-4354-a8f3-71d834898583"
/>
2025-04-02 05:31:35 -06:00
Loïc Guitaut
c96e7aa723 DEV: Fix the enable_current_plugin spec helper
Some plugins are always enabled and don’t have a related site setting.
This patch takes this into account.
2025-04-02 12:30:46 +02:00
Meghna
116a72504b
UX: rename Twitter login button to X (#32123)
Before:

<img width="234" alt="Screenshot 2025-04-02 at 1 15 25 PM"
src="https://github.com/user-attachments/assets/1f852543-38d7-45d3-96d3-df6a8a7e3623"
/>

After:

<img width="184" alt="Screenshot 2025-04-02 at 1 20 41 PM"
src="https://github.com/user-attachments/assets/6bf9428f-2152-45a1-8215-ae006ce4f356"
/>
2025-04-02 15:35:59 +05:30
Sam
c0cf898c10
FEATURE: when rich-editor is enabled use Jetbrains Mono as code font (#32122)
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:


![image](https://github.com/user-attachments/assets/eca0b544-f3a4-4172-b2af-b39a3c0208e7)
2025-04-02 16:36:52 +11:00
Alan Guo Xiang Tan
e4244cca35
PERF: Set cluster_concurrency 1 for Jobs::BackfillBadge (#32121)
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`.
2025-04-02 11:23:32 +08:00
Alan Guo Xiang Tan
cfd539a539
DEV: Fix a flaky system test by doubling timeout (#32117)
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.
2025-04-02 11:02:05 +08:00
Krzysztof Kotlarek
35850b35f4
FIX: invite_link_max_redemptions_limit min 1 (#32118)
When the feature was designed in 2020, it had two options: invite a
single user or generate a link.

https://github.com/discourse/discourse/pull/9813

With simplified interface, it makes sense to allow to send a link to
just 1 user.

Meta:
https://meta.discourse.org/t/unable-to-restrict-invite-link-to-one-person/359872
2025-04-02 10:54:46 +08:00
Alan Guo Xiang Tan
0f3d61b463
DEV: Use Capybara::Session#using_wait_time instead (#32116)
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.
2025-04-02 09:11:26 +08:00
dependabot[bot]
72c837bc98
Build(deps-dev): Bump rubocop-rails from 2.30.3 to 2.31.0 (#32112)
Bumps [rubocop-rails](https://github.com/rubocop/rubocop-rails) from
2.30.3 to 2.31.0.
- [Release notes](https://github.com/rubocop/rubocop-rails/releases)
-
[Changelog](https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop-rails/compare/v2.30.3...v2.31.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 01:33:28 +02:00
dependabot[bot]
211b1872ae
Build(deps-dev): Bump @swc/core from 1.11.13 to 1.11.16 (#32114)
Bumps [@swc/core](https://github.com/swc-project/swc) from 1.11.13 to
1.11.16.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/swc-project/swc/compare/v1.11.13...v1.11.16)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 01:27:46 +02:00
dependabot[bot]
e57ae1f386
Build(deps-dev): Bump puppeteer-core from 24.4.0 to 24.5.0 (#32113)
Bumps [puppeteer-core](https://github.com/puppeteer/puppeteer) from
24.4.0 to 24.5.0.
- [Release notes](https://github.com/puppeteer/puppeteer/releases)
-
[Changelog](https://github.com/puppeteer/puppeteer/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v24.4.0...puppeteer-core-v24.5.0)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-04-02 01:27:27 +02:00
Bryce Huhtala
e68fef0b4c
UX: Wrap edit category subcategories (#32110)
### Issue
In the **edit category settings**, if the category has a large amount of
subcategories, the layout can spill outside the normal content area.

![image](https://github.com/user-attachments/assets/388d0f47-7054-4f6e-85cb-c648df126ee0)

### Solution
Add `flex-wrap: wrap` to form kit container content within edit
category.

![image](https://github.com/user-attachments/assets/a2b51a64-cd6d-423d-9ac6-3230445ed8cf)
2025-04-01 15:37:28 -04:00
David Taylor
c61b19e244
DEV: Update final {{raw}} usages (#32082) 2025-04-01 18:39:42 +01:00
Sérgio Saquetim
083082f328
DEV: Remove the legacy widget post menu code (#31211)
https://meta.discourse.org/t/341014
2025-04-01 16:03:58 +01:00
dependabot[bot]
4fb2fae0ed
Build(deps): Bump pry from 0.14.2 to 0.15.2 (#32070)
Bumps [pry](https://github.com/pry/pry) from 0.14.2 to 0.15.2.
- [Release notes](https://github.com/pry/pry/releases)
- [Changelog](https://github.com/pry/pry/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pry/pry/compare/v0.14.2...v0.15.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 15:07:50 +02:00
Loïc Guitaut
05f533ee0e DEV: Add more granularity to the core features specs shared example 2025-04-01 14:54:11 +02:00
David Battersby
1519f3dde9
FIX: prevent duplicate category icons (#32103)
Follow up fix to #31795 to prevent duplicate icons in category chooser.

This change prevents duplicate icons or icon and emoji combinations.
2025-04-01 13:45:14 +04:00
dependabot[bot]
69e6f94aa7
Build(deps-dev): Bump lefthook from 1.11.5 to 1.11.6 (#32091)
Bumps [lefthook](https://github.com/evilmartians/lefthook) from 1.11.5
to 1.11.6.
- [Release notes](https://github.com/evilmartians/lefthook/releases)
-
[Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/evilmartians/lefthook/compare/v1.11.5...v1.11.6)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:41:50 +02:00
dependabot[bot]
c6d443b9e9
Build(deps-dev): Bump html-entities from 2.5.3 to 2.6.0 (#32066)
Bumps [html-entities](https://github.com/mdevils/html-entities) from
2.5.3 to 2.6.0.
- [Release notes](https://github.com/mdevils/html-entities/releases)
-
[Changelog](https://github.com/mdevils/html-entities/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/mdevils/html-entities/compare/v2.5.3...v2.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:40:42 +02:00
dependabot[bot]
74e8d1075e
Build(deps): Bump faraday-retry from 2.2.1 to 2.3.0 (#32072)
Bumps [faraday-retry](https://github.com/lostisland/faraday-retry) from
2.2.1 to 2.3.0.
- [Release notes](https://github.com/lostisland/faraday-retry/releases)
-
[Changelog](https://github.com/lostisland/faraday-retry/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/lostisland/faraday-retry/compare/v2.2.1...v2.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:39:12 +02:00
dependabot[bot]
e4911eb95f
Build(deps-dev): Bump byebug from 11.1.3 to 12.0.0 (#32071)
Bumps [byebug](https://github.com/deivid-rodriguez/byebug) from 11.1.3
to 12.0.0.
- [Release notes](https://github.com/deivid-rodriguez/byebug/releases)
-
[Changelog](https://github.com/deivid-rodriguez/byebug/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/deivid-rodriguez/byebug/compare/v11.1.3...v12.0.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:38:21 +02:00
dependabot[bot]
9402946781
Build(deps): Bump rdoc from 6.13.0 to 6.13.1 (#32068)
Bumps [rdoc](https://github.com/ruby/rdoc) from 6.13.0 to 6.13.1.
- [Release notes](https://github.com/ruby/rdoc/releases)
- [Changelog](https://github.com/ruby/rdoc/blob/master/History.rdoc)
- [Commits](https://github.com/ruby/rdoc/compare/v6.13.0...v6.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:37:28 +02:00
dependabot[bot]
e7d97e78ab
Build(deps): Bump nokogiri from 1.18.6 to 1.18.7 (#32087)
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.18.6
to 1.18.7.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
-
[Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/sparklemotion/nokogiri/compare/v1.18.6...v1.18.7)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:36:11 +02:00
dependabot[bot]
6eb0890f2b
Build(deps-dev): Bump parser from 3.3.7.3 to 3.3.7.4 (#32088)
Bumps [parser](https://github.com/whitequark/parser) from 3.3.7.3 to
3.3.7.4.
-
[Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/whitequark/parser/compare/v3.3.7.3...v3.3.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-01 11:35:52 +02:00
David Battersby
c58237c01e
FIX: always allow edit category bg color (#32102)
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.
2025-04-01 13:31:41 +04:00
Natalie Tay
2a26555a7a
DEV: Allow specifying a condition when preloading topics for topic list (#32101)
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]
   })
```
2025-04-01 16:50:16 +08:00
Ted Johansson
b37591632d
FIX: Update tag topic counts when bulk removing tags (#32095)
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.
2025-04-01 11:46:07 +08:00
Sam
18433c45b9
FEATURE: when rich editor is enabled markdown is in monospace (#32097)
- 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"
2025-04-01 13:41:33 +10:00
Alan Guo Xiang Tan
7f25e13222
DEV: Double default capybara max wait time for certain tests take 3 (#32096)
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.
2025-04-01 11:35:28 +08:00
Martin Brennan
b8dcabc2ac
DEV: Add env var to silence SASS deprecation warnings (#32074)
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.
2025-04-01 13:11:33 +10:00
Kelv
1c1d687283
DEV: show no icon if icon name is missing and log at error level (#31866)
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.
2025-04-01 10:54:48 +08:00
Krzysztof Kotlarek
377a6554b9
FIX: create UserHistory only when setting changed (#32075)
Do not create UserHistory when new value is same as the old one. It is
even more important now, when we have bulk save option.
2025-04-01 09:56:33 +08:00
Ted Johansson
c4502b31e4
FEATURE: Bulk save site settings (#32013)
This feature adds a bulk-save feature to site settings.
2025-04-01 09:39:19 +08:00
Joffrey JAFFEUX
b21c3e86f7
Revert "DEV: Allow specifying a condition when preloading topics for … (#32092)
…topic list (#32079)"

This reverts commit 70887351d7962947184e8351fea5745ee2b3a974.

This was causing errors:

```
NoMethodError (undefined method `call' for nil)
app/models/topic_list.rb:144:in `block in load_topics'
app/models/topic_list.rb:141:in `each'
app/models/topic_list.rb:141:in `load_topics'
app/models/topic_list.rb:75:in `topics'
app/serializers/suggested_topics_mixin.rb:15:in `include_suggested_topics?'
```
2025-04-01 09:37:47 +11:00
Gary Pendergast
4a92b0afd8
DEV: Automatically reload dev when symlinked plugins change (#32073)
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.
2025-04-01 07:06:00 +11:00
David Taylor
119295f687
DEV: Preserve ids/classes in emails (#32084)
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.
2025-03-31 20:58:45 +01:00
Joffrey JAFFEUX
8b212d95ed
DEV: skip watch word when message is created by bots (#31959)
Especially with AI we have no exact control on the words and this could
create and endless loop of errors.
2025-03-31 20:09:04 +02:00
Renato Atilio
2134925d7a
UX: convert emdash+hyphen to a horizontal rule on rich editor (#32085)
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.
2025-03-31 14:18:21 -03:00
Loïc Guitaut
ebed9cd013 DEV: Add a spec helper to upload a theme or a component 2025-03-31 17:28:09 +02:00
Joffrey JAFFEUX
51db964859
FIX: allows to resize textarea (#32076)
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.
2025-03-31 16:52:29 +02:00
Natalie Tay
70887351d7
DEV: Allow specifying a condition when preloading topics for topic list (#32079)
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? }
   })
```
2025-03-31 21:32:05 +08:00
Joffrey JAFFEUX
d12e0eb65d
FIX: prevents d-menu trigger/untrigger propagation (#32078)
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
2025-03-31 13:47:24 +02:00
moin-Jana
6b37aaf414
DEV: Fix FA icon deprecation 'smile' -> 'face-smile' (#32065)
Over the last few weeks, I have occasionally seen a deprecation warning
on my smartphone. Unfortunately, there was no corresponding entry in
/logs, and I never had the browser console available at the time.

I have now found the steps to reproduce the issue: the warning is
triggered by the emoji selection button in the chat.


https://github.com/user-attachments/assets/fbda7ee2-6967-48af-aa00-d081466e6aea

The icon used in the button was 'smile' instead of 'face-smile'. With
this fix, the warning no longer appears.

Otherwise, the icon will be missing once
https://github.com/discourse/discourse/pull/31866 is merged.

![After_1_April](https://github.com/user-attachments/assets/eed141e4-abd8-42c3-a27c-6dad7c4467f9)
2025-03-31 09:42:19 +01:00
David Taylor
b5f5c5a1ee
DEV: Add @tracked to RestModel __state (#32064)
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`.
2025-03-31 09:26:31 +01:00
David Battersby
15751c89f4
FEATURE: auto contrast text color for categories (#32015)
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.
2025-03-31 11:02:20 +04:00
Martin Brennan
d3c2bd015d
FEATURE: Implement chat transcripts in rich editor (#31819)
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>
2025-03-31 14:04:22 +10:00
Gary Pendergast
b4cdc39e51
FEATURE: Allow rejected user details to be scrubbed (#31987)
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.
2025-03-31 12:40:35 +11:00
Martin Brennan
2ac3c731de
FIX: “Input data should be a string” error in ProseMirror (#32056)
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.
2025-03-31 11:40:19 +10:00
David Taylor
3f353a726b
FIX: Prioritize !important CSS in emails (#32061)
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.
2025-03-28 16:24:03 +00:00
Joffrey JAFFEUX
12dffc5f7d
DEV: supports bigger emoji in rich composer (#31933)
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>
2025-03-28 13:10:00 -03:00
David Taylor
a4b2b57db7
DEV: Introduce new breakpoints (#31886)
Usage:

```scss
@use "lib/viewport";

@include viewport.from(sm) {
  ...
}

@include viewport.until(lg) {
  ...
}

@include viewport.between(sm, lg) {
  ...
}
```

Values are inclusive on the lower bound, and exclusive on the upper
bound.
2025-03-28 14:50:06 +00:00
Loïc Guitaut
54c38e6163 DEV: Add a helper to enable current plugin in specs 2025-03-28 14:18:03 +01:00
David Taylor
387dc8c255
DEV: Drop 'cache_onebox_response_body' feature (#32035)
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.
2025-03-28 10:55:29 +00:00
Alan Guo Xiang Tan
59edec46a3
DEV: Double default capybara wait time for certain flaky tests take 2 (#32058)
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
2025-03-28 16:09:15 +08:00
Alan Guo Xiang Tan
afde7cc172
DEV: Double default capybara wait time for certain flaky tests (#32057)
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.
2025-03-28 13:49:42 +08:00
Alan Guo Xiang Tan
016c6a6894
DEV: Fix a flaky chat system test (#32055)
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)>'
```
2025-03-28 12:54:27 +08:00
Ted Johansson
b40cbfcb76
DEV: Move backfill into SiteSetting::Update service (#32037)
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.
2025-03-28 12:01:56 +08:00
Kelv
654e858df3
DEV: remove singleton mixin (#32054)
Follow up to https://github.com/discourse/discourse/pull/30498.

Removes the singleton mixin and its test file.
2025-03-28 11:53:10 +08:00
Alan Guo Xiang Tan
1d49da467b
DEV: Attempt to fix flaky search system test (#32053)
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:


![image](https://github.com/user-attachments/assets/55a2834f-9357-460d-adeb-dc9a2fa475e8)
2025-03-28 11:16:43 +08:00
Alan Guo Xiang Tan
f1a67ecb37
DEV: Improvements to plugin API docs (#31988)
Follow-up to af03873d37decf8dba4278fc38bfcde7747a79f4
2025-03-28 09:59:25 +08:00
Alan Guo Xiang Tan
9d786ad870
DEV: Dump all threads for some flaky system tests (#32050)
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.
2025-03-28 09:54:05 +08:00
Blake Erickson
44da7b7fbf
UX: Admin Dashboard title (#32022)
This adds a title to the admin dashboard to match other admin pages.
Adds a description to the admin dashboard.
Adds a plugin outlet after the header

BEFORE:
![CleanShot 2025-03-26 at 11 16
13@2x](https://github.com/user-attachments/assets/ebac6005-61b3-44ab-a97f-581e58e89eaf)


AFTER:
![CleanShot 2025-03-27 at 08 22
13@2x](https://github.com/user-attachments/assets/e64b1dae-8403-4545-9d6d-ac898e59a8bc)

---------

Co-authored-by: Jordan Vidrine <jordan@jordanvidrine.com>
2025-03-27 13:57:56 -06:00
Kris
eae68e121e
UX: improve mobile topic list tag layout (#32048)
* 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:

![image](https://github.com/user-attachments/assets/bc4b0f52-5eff-467f-ae0d-95661bd57351)


After:

![image](https://github.com/user-attachments/assets/dcdf39a4-4f23-4b23-a7b5-5f6a60c1ca6f)
2025-03-27 15:34:52 -04:00
Jordan Vidrine
1be7b1ed90
DEV: Remove clearfix class from topic title link in topic lists (#32047) 2025-03-27 14:30:11 -05:00
dependabot[bot]
e3fcec356e
Build(deps): Bump the prosemirror group with 3 updates (#32042)
Bumps the prosemirror group with 3 updates:
[prosemirror-commands](https://github.com/prosemirror/prosemirror-commands),
[prosemirror-schema-list](https://github.com/prosemirror/prosemirror-schema-list)
and
[prosemirror-transform](https://github.com/prosemirror/prosemirror-transform).


Updates `prosemirror-commands` from 1.6.2 to 1.7.0
-
[Changelog](https://github.com/ProseMirror/prosemirror-commands/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/prosemirror/prosemirror-commands/compare/1.6.2...1.7.0)

Updates `prosemirror-schema-list` from 1.5.0 to 1.5.1
-
[Changelog](https://github.com/ProseMirror/prosemirror-schema-list/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/prosemirror/prosemirror-schema-list/compare/1.5.0...1.5.1)

Updates `prosemirror-transform` from 1.10.2 to 1.10.3
-
[Changelog](https://github.com/ProseMirror/prosemirror-transform/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/prosemirror/prosemirror-transform/compare/1.10.2...1.10.3)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:53:30 +01:00
dependabot[bot]
467bcc7d98
Build(deps): Bump a11y-dialog from 8.1.1 to 8.1.2 (#32045)
Bumps [a11y-dialog](https://github.com/KittyGiraudel/a11y-dialog) from
8.1.1 to 8.1.2.
- [Release notes](https://github.com/KittyGiraudel/a11y-dialog/releases)
-
[Commits](https://github.com/KittyGiraudel/a11y-dialog/compare/8.1.1...8.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:53:21 +01:00
dependabot[bot]
8c42be15ae
Build(deps-dev): Bump ember-qunit from 9.0.1 to 9.0.2 (#32044)
Bumps [ember-qunit](https://github.com/emberjs/ember-qunit) from 9.0.1
to 9.0.2.
- [Release notes](https://github.com/emberjs/ember-qunit/releases)
-
[Changelog](https://github.com/emberjs/ember-qunit/blob/main/.release-plan.json)
- [Commits](https://github.com/emberjs/ember-qunit/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:53:02 +01:00
dependabot[bot]
8a51726678
Build(deps): Bump logger from 1.6.6 to 1.7.0 (#32043)
Bumps [logger](https://github.com/ruby/logger) from 1.6.6 to 1.7.0.
- [Release notes](https://github.com/ruby/logger/releases)
- [Commits](https://github.com/ruby/logger/compare/v1.6.6...v1.7.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:47:08 +01:00
dependabot[bot]
e5c919ac39
Build(deps-dev): Bump lint-to-the-future-eslint from 3.0.0 to 3.1.0 (#31807)
Bumps
[lint-to-the-future-eslint](https://github.com/mansona/lint-to-the-future-eslint)
from 3.0.0 to 3.1.0.
- [Release
notes](https://github.com/mansona/lint-to-the-future-eslint/releases)
-
[Changelog](https://github.com/mansona/lint-to-the-future-eslint/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/mansona/lint-to-the-future-eslint/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:25:27 +01:00
Jarek Radosz
61b5408ab9
DEV: Group prosemirror dependabot updates (#32041) 2025-03-27 17:19:02 +01:00
dependabot[bot]
ee1ad8e19e
Build(deps): Bump babel-plugin-ember-template-compilation from 2.3.0 to 2.4.1 (#32025)
Bumps
[babel-plugin-ember-template-compilation](https://github.com/emberjs/babel-plugin-ember-template-compilation)
from 2.3.0 to 2.4.1.
- [Release
notes](https://github.com/emberjs/babel-plugin-ember-template-compilation/releases)
-
[Changelog](https://github.com/emberjs/babel-plugin-ember-template-compilation/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/emberjs/babel-plugin-ember-template-compilation/commits)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-03-27 16:33:19 +01:00
dependabot[bot]
be6315dd1f
Build(deps-dev): Bump ember-cli from 6.2.3 to 6.3.0 (#31982)
Bumps [ember-cli](https://github.com/ember-cli/ember-cli) from 6.2.3 to
6.3.0.
- [Release notes](https://github.com/ember-cli/ember-cli/releases)
-
[Changelog](https://github.com/ember-cli/ember-cli/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/ember-cli/ember-cli/compare/v6.2.3...v6.3.0)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-03-27 16:33:03 +01:00
chapoi
3c7b532245
UX: removing baseline alignment from topic title wrapper (#32040)
This is causing misalignment with displayed metadata such as category,
tags, assigned,… We don't remember why it was added in the first place.
2025-03-27 16:07:02 +01:00
David Battersby
d4fa777bfd
UX: emoji styling adjustments (#32039)
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.
2025-03-27 18:45:39 +04:00
dependabot[bot]
c3795177f2
Build(deps-dev): Bump @discourse/lint-configs from 2.11.2 to 2.12.0 (#31890)
Bumps
[@discourse/lint-configs](https://github.com/discourse/lint-configs)
from 2.11.2 to 2.12.0.
- [Commits](https://github.com/discourse/lint-configs/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 15:36:27 +01:00
Jarek Radosz
e0bb7e936b
DEV: Update content-tag from 3.1.1 to 3.1.2 (#32038) 2025-03-27 15:33:59 +01:00
Kris
b570dea85b
UX: align the previewless composer with posts (#31960)
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>
2025-03-27 10:13:03 -04:00
Amanda Alves Branquinho
9d3a4cadfa
DEV: Add outlet args to new login template (#31997)
- Add new args to outlet
2025-03-27 10:43:40 -03:00
dependabot[bot]
22f3341d68
Build(deps-dev): Bump rubocop from 1.74.0 to 1.75.1 (#32026)
Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.74.0 to
1.75.1.
- [Release notes](https://github.com/rubocop/rubocop/releases)
-
[Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop/compare/v1.74.0...v1.75.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 14:13:17 +01:00
Git'Fellow
8e24d4e7de
UI: storage stats content overflow (#31840) 2025-03-27 08:55:57 -04:00
Jarek Radosz
29cbbd6b31
DEV: Fix Lint/ShadowingOuterLocalVariable (#32036)
unblocks a rubocop update
2025-03-27 13:50:24 +01:00
Loïc Guitaut
4f82ceaf39 DEV: Introduce core features system specs for plugins
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
```
2025-03-27 12:12:01 +01:00
Alan Guo Xiang Tan
7b9976795e
UX: Display button loading state upon submit on password reset form (#32034) 2025-03-27 16:26:57 +08:00
dependabot[bot]
664f0b116c
Build(deps-dev): Bump @ember/test-helpers from 5.1.0 to 5.2.0 (#32024)
Bumps
[@ember/test-helpers](https://github.com/emberjs/ember-test-helpers)
from 5.1.0 to 5.2.0.
- [Release
notes](https://github.com/emberjs/ember-test-helpers/releases)
-
[Changelog](https://github.com/emberjs/ember-test-helpers/blob/master/CHANGELOG.md)
- [Commits](https://github.com/emberjs/ember-test-helpers/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 09:11:58 +01:00
Alan Guo Xiang Tan
6f92f42eb8
DEV: Print puma threads backtraces when Capybara's helpers time out (#32033)
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)>'
```


![image](https://github.com/user-attachments/assets/9a6b4c91-1d17-4c23-9239-f4440fe7fb07)
2025-03-27 16:05:39 +08:00
dependabot[bot]
0862c7d9b0
Build(deps-dev): Bump the embroider group with 3 updates (#31939)
Bumps the embroider group with 3 updates:
[@embroider/compat](https://github.com/embroider-build/embroider/tree/HEAD/packages/compat),
[@embroider/core](https://github.com/embroider-build/embroider/tree/HEAD/packages/core)
and
[@embroider/macros](https://github.com/embroider-build/embroider/tree/HEAD/packages/macros).


Updates `@embroider/compat` from 3.8.4 to 3.8.5
- [Release notes](https://github.com/embroider-build/embroider/releases)
-
[Changelog](https://github.com/embroider-build/embroider/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/embroider-build/embroider/commits/HEAD/packages/compat)

Updates `@embroider/core` from 3.5.4 to 3.5.5
- [Release notes](https://github.com/embroider-build/embroider/releases)
-
[Changelog](https://github.com/embroider-build/embroider/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/embroider-build/embroider/commits/HEAD/packages/core)

Updates `@embroider/macros` from 1.16.11 to 1.16.12
- [Release notes](https://github.com/embroider-build/embroider/releases)
-
[Changelog](https://github.com/embroider-build/embroider/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/embroider-build/embroider/commits/HEAD/packages/macros)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Discourse CI <ci@ci.invalid>
2025-03-27 08:34:16 +01:00
dependabot[bot]
2c54cd914b
Build(deps-dev): Bump extralite-bundle from 2.10 to 2.12 (#32029)
Bumps [extralite-bundle](https://github.com/digital-fabric/extralite)
from 2.10 to 2.12.
-
[Changelog](https://github.com/digital-fabric/extralite/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digital-fabric/extralite/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 08:18:10 +01:00
dependabot[bot]
3935cd7e0f
Build(deps): Bump google-protobuf from 4.30.1 to 4.30.2 (#32028)
Bumps [google-protobuf](https://github.com/protocolbuffers/protobuf)
from 4.30.1 to 4.30.2.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
-
[Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl)
-
[Commits](https://github.com/protocolbuffers/protobuf/compare/v4.30.1...v4.30.2)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 08:17:43 +01:00
dependabot[bot]
e7c2846efc
Build(deps-dev): Bump parser from 3.3.7.2 to 3.3.7.3 (#32027)
Bumps [parser](https://github.com/whitequark/parser) from 3.3.7.2 to
3.3.7.3.
-
[Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/whitequark/parser/compare/v3.3.7.2...v3.3.7.3)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 08:17:03 +01:00
Ted Johansson
c7abae2741
DEV: Disable site setting save/discard buttons while saving (#32030)
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.)
2025-03-27 09:43:10 +08:00
Sérgio Saquetim
c671cfff84
DEV: Upgrade the post widgets to Glimmer components (#31375)
Co-authored-by: David Taylor <david@taylorhq.com>
2025-03-26 18:53:46 -03:00
Keegan George
4e918e1d3d
DEV: Add value transformer to post-text-selection (#32023)
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)
2025-03-26 11:28:31 -07:00
chapoi
a6394f7f57
UX: change focus to focus-visible (#32021) 2025-03-26 18:51:39 +01:00
David Taylor
f277e0bce5
FIX: Reapply 94ee3554 (#32020)
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>
2025-03-26 16:31:42 +00:00
David Battersby
45029c28ce
UX: improve hashtag emoji alignment (#32019)
Improves alignment of emojis within hashtags.
2025-03-26 19:36:08 +04:00
Kris
7decb88ce0
UX: only check for toolbar-visible class in mobileView (#32018)
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.
2025-03-26 10:43:59 -04:00
dependabot[bot]
feb30c2382
Build(deps-dev): Bump sinon from 19.0.4 to 20.0.0 (#31981)
Bumps [sinon](https://github.com/sinonjs/sinon) from 19.0.4 to 20.0.0.
- [Release notes](https://github.com/sinonjs/sinon/releases)
-
[Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v19.0.4...v20.0.0)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Radosz <jarek@cvx.dev>
2025-03-26 15:11:20 +01:00
Alan Guo Xiang Tan
ac26a52c6d
DEV: Improve PageObjects::Pages::UserPreferencesSecurity#visit_second_factor (#32017)
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)>'
```
2025-03-26 22:11:03 +08:00
dependabot[bot]
adea877a45
Build(deps): Bump ace-builds from 1.39.0 to 1.39.1 (#31943)
Bumps [ace-builds](https://github.com/ajaxorg/ace-builds) from 1.39.0 to
1.39.1.
- [Release notes](https://github.com/ajaxorg/ace-builds/releases)
-
[Changelog](https://github.com/ajaxorg/ace-builds/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/ajaxorg/ace-builds/compare/v1.39.0...v1.39.1)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 15:00:26 +01:00
dependabot[bot]
5754368586
Build(deps-dev): Bump ember-cli-deprecation-workflow from 3.2.0 to 3.3.0 (#31963)
Bumps
[ember-cli-deprecation-workflow](https://github.com/ember-cli/ember-cli-deprecation-workflow)
from 3.2.0 to 3.3.0.
- [Release
notes](https://github.com/ember-cli/ember-cli-deprecation-workflow/releases)
-
[Changelog](https://github.com/ember-cli/ember-cli-deprecation-workflow/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/ember-cli/ember-cli-deprecation-workflow/commits)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 14:41:34 +01:00
dependabot[bot]
16857060df
Build(deps-dev): Bump rubocop-ast from 1.41.0 to 1.43.0 (#32004)
Bumps [rubocop-ast](https://github.com/rubocop/rubocop-ast) from 1.41.0
to 1.43.0.
- [Release notes](https://github.com/rubocop/rubocop-ast/releases)
-
[Changelog](https://github.com/rubocop/rubocop-ast/blob/master/CHANGELOG.md)
-
[Commits](https://github.com/rubocop/rubocop-ast/compare/v1.41.0...v1.43.0)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 14:21:55 +01:00
David Battersby
f13ff5088c
FIX: update chat icon position on mobile (#32016)
The chat icon was repositioned in #31951 but we should also account for
mobile, where it should appear before the hamburger icon.
2025-03-26 17:03:47 +04:00
dependabot[bot]
75dd327c75
Build(deps-dev): Bump html-entities from 2.5.2 to 2.5.3 (#31964) 2025-03-26 13:09:05 +01:00
dependabot[bot]
df69f1a628
Build(deps): Bump rdoc from 6.12.0 to 6.13.0 (#31966) 2025-03-26 13:08:22 +01:00
dependabot[bot]
2441a54e33
Build(deps-dev): Bump @swc/core from 1.11.9 to 1.11.13 (#31984) 2025-03-26 13:07:00 +01:00
dependabot[bot]
0ec5816f05
Build(deps-dev): Bump diff-lcs from 1.6.0 to 1.6.1 (#32003) 2025-03-26 13:03:58 +01:00
dependabot[bot]
c3bd2f8629
Build(deps): Bump @babel/standalone from 7.26.10 to 7.27.0 in the babel group (#31980) 2025-03-26 13:02:58 +01:00
dependabot[bot]
a302320791
Build(deps-dev): Bump lefthook from 1.11.4 to 1.11.5 (#32006) 2025-03-26 13:02:37 +01:00
dependabot[bot]
b0780a482c
Build(deps): Bump stringio from 3.1.5 to 3.1.6 (#32005) 2025-03-26 13:02:08 +01:00
David Taylor
042c480049
FIX: Do not @import .css assets for plugins (#32014)
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
2025-03-26 11:01:24 +00:00
David Taylor
b1924c3524
DEV: Allow stylesheet entrypoints to use @use (#31905)
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)
2025-03-26 09:15:32 +00:00
Ted Johansson
c8ccd4da31
DEV: Move setting deprecation check to SiteSetting::Update service (#31993)
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.
2025-03-26 15:56:18 +08:00
David Battersby
525406ad20
UX: reposition chat header icon (#31951)
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.
2025-03-26 10:59:01 +04:00
David Battersby
d06c60ca7c
FEATURE: add icons and emojis to category (#31795)
This feature allow admins to personalize their communities by
associating emojis or icons with their site categories.

There are now 3 style types for categories:
- Square (the default)
- Emoji
- Icon

### How it looks 🎨 

Adding an icon:

<img width="502" alt="Category with an icon"
src="https://github.com/user-attachments/assets/8f711340-166e-4781-a7b7-7267469dbabd"
/>

Adding an emoji:

<img width="651" alt="Category with an emoji"
src="https://github.com/user-attachments/assets/588c38ce-c719-4ed5-83f9-f1e1cb52c929"
/>

Sidebar:

<img width="248" alt="Sidebar with emojis"
src="https://github.com/user-attachments/assets/cd03d591-6170-4515-998c-0cec20118568"
/>

Category menus:

<img width="621" alt="Screenshot 2025-03-13 at 10 32 30 AM"
src="https://github.com/user-attachments/assets/7d89797a-f69f-45e5-bf64-a92d4cff8753"
/>

Within posts/topics:

<img width="382" alt="Screenshot 2025-03-13 at 10 33 41 AM"
src="https://github.com/user-attachments/assets/b7b1a951-44c6-4a4f-82ad-8ee31ddd6061"
/>

Chat messages:

<img width="392" alt="Screenshot 2025-03-13 at 10 30 20 AM"
src="https://github.com/user-attachments/assets/126f8076-0ea3-4f19-8452-1041fd2af29f"
/>

Autocomplete:

<img width="390" alt="Screenshot 2025-03-13 at 10 29 53 AM"
src="https://github.com/user-attachments/assets/cad75669-225f-4b8e-a7b5-ae5aa8f1bcad"
/>

---------

Co-authored-by: Martin Brennan <martin@discourse.org>
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
2025-03-26 09:46:17 +04:00
Alan Guo Xiang Tan
1fd553ccb0
UX: Add loading state to button when confirming password (#32012)
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.
2025-03-26 12:58:47 +08:00
Alan Guo Xiang Tan
c91d0d7790
Bump version to v3.5.0.beta3-dev 2025-03-26 10:17:59 +08:00
1474 changed files with 40254 additions and 23926 deletions

View File

@ -76,3 +76,5 @@ cb932d6ee1b3b3571e4d4d9118635e2dbf58f0ef
1d0d7ddbb5ce5dae9bdbdab3797628b6565fb4c4
a017f566a84af2e74c132c4acc89028d5f779a29
b29e0b6e1b6001890ce92f96448258550f80132b
999ae73c7836ff20a9537e582d52b861355f1ec5
7b2b08cf89bdab332d14b68cd17cdfb4aa8a7113

View File

@ -84,6 +84,9 @@ updates:
patterns:
- "@highlightjs/cdn-assets"
- "highlight.js"
prosemirror:
patterns:
- "prosemirror-*"
types:
patterns:
- "@types/*"

View File

@ -23,21 +23,16 @@ permissions:
jobs:
tests:
if: github.event_name == 'pull_request' || github.repository != 'discourse/discourse-private-mirror'
name: Tests with Ruby ${{ matrix.ruby }}
runs-on: ${{ (github.repository != 'discourse/discourse' && 'ubuntu-latest') || 'debian-12' }}
container: discourse/discourse_test:slim
name: Tests
runs-on: ${{ (github.repository_owner == 'discourse' && 'debian-12') || 'ubuntu-latest' }}
container: discourse/discourse_test:release
timeout-minutes: 20
env:
RAILS_ENV: test
PGUSER: discourse
PGPASSWORD: discourse
strategy:
fail-fast: false
matrix:
ruby: ["3.3"]
LOAD_PLUGINS: 1
steps:
- name: Set working directory owner
@ -62,23 +57,9 @@ jobs:
sudo -E -u postgres script/start_test_db.rb
sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';"
- name: Container envs
id: container-envs
- name: Symlink vendor/bundle from image
run: |
echo "ruby_version=$RUBY_VERSION" >> $GITHUB_OUTPUT
echo "debian_release=$DEBIAN_RELEASE" >> $GITHUB_OUTPUT
shell: bash
- name: Bundler cache
uses: actions/cache@v4
with:
path: vendor/bundle
key: >-
${{ runner.os }}-
${{ steps.container-envs.outputs.ruby_version }}-
${{ steps.container-envs.outputs.debian_release }}-
${{ hashFiles('**/Gemfile.lock') }}-
migrations-tooling
ln -s /var/www/discourse/vendor/bundle vendor/bundle
- name: Setup gems
run: |
@ -88,11 +69,14 @@ jobs:
bundle config --local without development
bundle config --local with migrations
bundle install --jobs $(($(nproc) - 1))
bundle clean
- name: pnpm install
run: pnpm install --frozen-lockfile
- name: Get CPU cores
id: cpu-info
run: echo "cpu-cores=$(nproc)" >> $GITHUB_OUTPUT
- name: Fetch app state cache
uses: actions/cache@v4
id: app-cache
@ -100,7 +84,8 @@ jobs:
path: tmp/app-cache
key: >-
${{ runner.os }}-
${{ hashFiles('.github/workflows/tests.yml') }}-
${{ steps.cpu-info.outputs.cpu-cores }}-
${{ hashFiles('.github/workflows/migration-tests.yml') }}-
${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}-
${{ hashFiles('config/environments/test.rb') }}
@ -126,11 +111,16 @@ jobs:
if: steps.app-cache.outputs.cache-hit != 'true'
run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads
# - name: Check core database drift
# run: |
# mkdir /tmp/intermediate_db
# ./migrations/scripts/schema_generator /tmp/intermediate_db/base_migration.sql
# diff -u migrations/common/intermediate_db_schema/000_base_schema.sql /tmp/intermediate_db/base_migration.sql
- name: Validate IntermediateDB schema
run: |
migrations/bin/cli schema generate --db=intermediate_db
if [ ! -z "$(git status --porcelain migrations/db/intermediate_db_schema/)" ]; then
echo "IntermediateDB schema is not up to date."
echo "---------------------------------------------"
git -c color.ui=always diff migrations/db/intermediate_db_schema/
exit 1
fi
- name: RSpec
run: bin/rspec --default-path migrations/spec

View File

@ -38,13 +38,14 @@ jobs:
PGUSER: discourse
PGPASSWORD: discourse
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 10
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 20
MINIO_RUNNER_LOG_LEVEL: DEBUG
DISCOURSE_TURBO_RSPEC_RETRY_AND_LOG_FLAKY_TESTS: ${{ (matrix.build_type == 'system' || matrix.build_type == 'backend') && '1' }}
CHEAP_SOURCE_MAPS: "1"
MINIO_RUNNER_INSTALL_DIR: /home/discourse/.minio_runner
TESTEM_BROWSER: ${{ (startsWith(matrix.browser, 'Firefox') && 'Firefox') || matrix.browser }}
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
EMBER_ENV: development
strategy:
fail-fast: false
@ -262,7 +263,7 @@ jobs:
- name: Ember Build for System Tests
if: matrix.build_type == 'system'
run: bin/ember-cli --build
run: script/assemble_ember_build.rb
- name: Core System Tests
if: matrix.build_type == 'system' && matrix.target == 'core'

2
.gitignore vendored
View File

@ -53,7 +53,7 @@
/spec/fixtures/plugins/my_plugin/auto_generated
/vendor/bundle/*
/vendor/bundle
/vendor/data/GeoLite2-City.mmdb
/vendor/data/GeoLite2-ASN.mmdb

View File

@ -88,10 +88,10 @@ GEM
bootsnap (1.18.4)
msgpack (~> 1.2)
builder (3.3.0)
bullet (8.0.1)
bullet (8.0.3)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.1.3)
byebug (12.0.0)
capybara (3.40.0)
addressable
matrix
@ -119,15 +119,15 @@ GEM
crass (1.0.6)
css_parser (1.21.1)
addressable
csv (3.3.3)
csv (3.3.4)
date (3.4.1)
debug_inspector (1.2.0)
diff-lcs (1.6.0)
diff-lcs (1.6.1)
diffy (3.4.3)
digest (3.2.0)
digest-xxhash (0.2.9)
discourse-emojis (1.0.38)
discourse-fonts (0.0.18)
discourse-emojis (1.0.39)
discourse-fonts (0.0.19)
discourse-seed-fu (2.3.12)
activerecord (>= 3.1)
activesupport (>= 3.1)
@ -143,45 +143,45 @@ GEM
logger
execjs (2.10.0)
exifr (1.4.1)
extralite-bundle (2.10)
extralite-bundle (2.12)
fabrication (2.31.0)
faker (3.5.1)
i18n (>= 1.8.11, < 2)
fakeweb (1.3.0)
faraday (2.12.2)
faraday (2.13.0)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
faraday-retry (2.2.1)
faraday-retry (2.3.1)
faraday (~> 2.0)
fast_blank (1.0.1)
fastimage (2.3.1)
ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-aarch64-linux-musl)
ffi (1.17.1-arm-linux-gnu)
ffi (1.17.1-arm-linux-musl)
ffi (1.17.1-arm64-darwin)
ffi (1.17.1-x86_64-darwin)
ffi (1.17.1-x86_64-linux-gnu)
ffi (1.17.1-x86_64-linux-musl)
ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-aarch64-linux-musl)
ffi (1.17.2-arm-linux-gnu)
ffi (1.17.2-arm-linux-musl)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
ffi (1.17.2-x86_64-linux-musl)
fspath (3.1.2)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (4.30.1)
google-protobuf (4.30.2)
bigdecimal
rake (>= 13)
google-protobuf (4.30.1-aarch64-linux)
google-protobuf (4.30.2-aarch64-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.30.1-arm64-darwin)
google-protobuf (4.30.2-arm64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.30.1-x86_64-darwin)
google-protobuf (4.30.2-x86_64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.30.1-x86_64-linux)
google-protobuf (4.30.2-x86_64-linux)
bigdecimal
rake (>= 13)
guess_html_encoding (0.0.11)
@ -203,7 +203,7 @@ GEM
image_size (3.4.0)
in_threads (1.6.0)
io-console (0.8.0)
irb (1.15.1)
irb (1.15.2)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
@ -233,7 +233,7 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
literate_randomizer (0.4.0)
logger (1.6.6)
logger (1.7.0)
lograge (0.14.0)
actionpack (>= 4)
activesupport (>= 4)
@ -260,7 +260,7 @@ GEM
mini_racer (>= 0.6.3)
method_source (1.1.0)
mini_mime (1.1.5)
mini_racer (0.18.0)
mini_racer (0.18.1)
libv8-node (~> 23.6.1.0)
mini_scheduler (0.18.0)
sidekiq (>= 6.5, < 8.0)
@ -288,21 +288,21 @@ GEM
net-smtp (0.5.1)
net-protocol
nio4r (2.7.4)
nokogiri (1.18.6-aarch64-linux-gnu)
nokogiri (1.18.7-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.6-aarch64-linux-musl)
nokogiri (1.18.7-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.6-arm-linux-gnu)
nokogiri (1.18.7-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.6-arm-linux-musl)
nokogiri (1.18.7-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.6-arm64-darwin)
nokogiri (1.18.7-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.6-x86_64-darwin)
nokogiri (1.18.7-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.6-x86_64-linux-gnu)
nokogiri (1.18.7-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.6-x86_64-linux-musl)
nokogiri (1.18.7-x86_64-linux-musl)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
@ -348,10 +348,10 @@ GEM
openssl (> 2.0)
optimist (3.2.1)
ostruct (0.6.1)
parallel (1.26.3)
parallel (1.27.0)
parallel_tests (5.1.0)
parallel
parser (3.3.7.2)
parser (3.3.8.0)
ast (~> 2.4.1)
racc
pg (1.5.9)
@ -359,13 +359,14 @@ GEM
prettyprint
prettier_print (1.2.1)
prettyprint (0.2.0)
prism (1.4.0)
progress (3.6.0)
pry (0.14.2)
pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
pry-byebug (3.11.0)
byebug (~> 12.0)
pry (>= 0.13, < 0.16)
pry-rails (0.3.11)
pry (>= 0.13.0)
pry-stack_explorer (0.6.1)
@ -424,7 +425,7 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rchardet (1.9.0)
rdoc (6.12.0)
rdoc (6.13.1)
psych (>= 4.0.0)
redcarpet (3.6.1)
redis (5.4.0)
@ -434,7 +435,7 @@ GEM
redis-namespace (1.11.0)
redis (>= 4)
regexp_parser (2.10.0)
reline (0.6.0)
reline (0.6.1)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
@ -483,7 +484,7 @@ GEM
rspec-core (>= 2.14)
rtlcss (0.2.1)
mini_racer (>= 0.6.3)
rubocop (1.74.0)
rubocop (1.75.2)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@ -491,11 +492,12 @@ GEM
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.41.0)
rubocop-ast (1.44.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
@ -511,11 +513,11 @@ GEM
rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.30.3)
rubocop-rails (2.31.0)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.72.1, < 2.0)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.5.0)
lint_roller (~> 1.1)
@ -553,9 +555,9 @@ GEM
sassc-embedded (1.80.2)
sass-embedded (~> 1.80)
securerandom (0.4.1)
selenium-devtools (0.133.0)
selenium-devtools (0.135.0)
selenium-webdriver (~> 4.2)
selenium-webdriver (4.29.1)
selenium-webdriver (4.31.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
@ -597,7 +599,7 @@ GEM
sqlite3 (2.6.0-x86_64-linux-musl)
sshkey (3.0.0)
stackprof (0.2.27)
stringio (3.1.5)
stringio (3.1.6)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
test-prof (1.4.4)
@ -620,7 +622,7 @@ GEM
uniform_notifier (1.16.0)
uri (1.0.3)
useragent (0.16.11)
version_gem (1.1.6)
version_gem (1.1.7)
web-push (3.0.1)
jwt (~> 2.0)
openssl (~> 3.0)
@ -825,8 +827,8 @@ CHECKSUMS
binding_of_caller (1.0.1) sha256=2b2902abff4246ddcfbc4da9b69bc4a019e22aeb300c2ff6289a173d4b90b29a
bootsnap (1.18.4) sha256=ac4c42af397f7ee15521820198daeff545e4c360d2772c601fbdc2c07d92af55
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
bullet (8.0.1) sha256=a18f7aa3a20f063a48e7a9dbcaf868bd2ede6e9b1056818ce825f74a717cd2f0
byebug (11.1.3) sha256=2485944d2bb21283c593d562f9ae1019bf80002143cc3a255aaffd4e9cf4a35b
bullet (8.0.3) sha256=60e41446500a7e59272b3c2398cded214deec3662e883009de5bf30028a0642c
byebug (12.0.0) sha256=d4a150d291cca40b66ec9ca31f754e93fed8aa266a17335f71bb0afa7fca1a1e
capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef
cbor (0.5.9.8) sha256=9ee097fc58d9bc5e406d112cd2d4e112c7354ec16f8b6ff34e4732c1e44b4eb7
certified (1.0.0) sha256=aa4cdf0e90e7ee96f6e0ce3daae39eaa8f0486124e0d92daf64d2105aeb9069c
@ -841,15 +843,15 @@ CHECKSUMS
crack (1.0.0) sha256=c83aefdb428cdc7b66c7f287e488c796f055c0839e6e545fec2c7047743c4a49
crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d
css_parser (1.21.1) sha256=6cfd3ffc0a97333b39d2b1b49c95397b05e0e3b684d68f77ec471ba4ec2ef7c7
csv (3.3.3) sha256=7e2966befb7bdaf7d5e9b36e1de73e6a5e7a72f584f180a1726aec88a1b0a900
csv (3.3.4) sha256=e96ecd5a8c3494aa5b596282249daba5c6033203c199248e6146e36d2a78d8cd
date (3.4.1) sha256=bf268e14ef7158009bfeaec40b5fa3c7271906e88b196d958a89d4b408abe64f
debug_inspector (1.2.0) sha256=9bdfa02eebc3da163833e6a89b154084232f5766087e59573b70521c77ea68a2
diff-lcs (1.6.0) sha256=a1e7f7b272962f8fc769358ad00001b87cdcf32ba349d6c70c6b544613d2da2e
diff-lcs (1.6.1) sha256=12a5a83f3e37a8e2f4427268e305914d5f1879f22b4e73bb1a09f76a3dd86cd4
diffy (3.4.3) sha256=4264b9e7db00d1cd426fcd32e36565779163cedc2340a95b0e6f025e71f9aaa7
digest (3.2.0) sha256=fa2e7092ec683f65d82fadde5ff4ca3b32e23ee0b19f1fc1a5e09993ad2d3991
digest-xxhash (0.2.9) sha256=a989d8309c03c4136a4bea9981ec0a146a2750de7f3dfb7b5624a3038aa598d7
discourse-emojis (1.0.38) sha256=4f9cfcbc7ea8aef69e1210e8796233ad9923ae908ee7696424f3ad92e51d6c51
discourse-fonts (0.0.18) sha256=a7d25c13edd3325ae40010ca277530d69fc7952a05aa7b2ff07c1d07412b72a1
discourse-emojis (1.0.39) sha256=7c7ce56b7a97f7ca2b9b26ffb2c2bb6454d2bfbb45dd3b68d91dd1e90b8e9c04
discourse-fonts (0.0.19) sha256=78d4ddd671615908303a675427039d8d787c935e6deae184c6e143c18c6e0033
discourse-seed-fu (2.3.12) sha256=4f61d95c11ed54609046cd04eb3a51b531c5fa863fa86d1bea7d74264e5c75e4
discourse_dev_assets (0.0.5) sha256=a09a801a210aa3f7247dd382dfd73b389d4bd02be86be675eca2d451f9898285
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
@ -860,30 +862,30 @@ CHECKSUMS
excon (1.2.5) sha256=ca040bb61bc0059968f34a17115a00d2db8562e3c0c5c5c7432072b551c85a9d
execjs (2.10.0) sha256=6bcb8be8f0052ff9d370b65d1c080f2406656e150452a0abdb185a133048450d
exifr (1.4.1) sha256=768374cc6b6ff3743acba57c1c35229bdd8c6b9fbc1285952047fc1215c4b894
extralite-bundle (2.10) sha256=d765b84abe359f8ed8ee1735de016167ab9f7c7760e3f9b49ba7434fa81ff192
extralite-bundle (2.12) sha256=9c9912b7ab592e7064089ee608cf86ab9edd81d20065acb87d1f4fed06052987
fabrication (2.31.0) sha256=2c79f10d1b88034a2ebd47ce77acba66847fc4636581c8282b3408adc68e85aa
faker (3.5.1) sha256=1ad1fbea279d882f486059c23fe3ddb816ccd1d7052c05a45014b4450d859bfc
fakeweb (1.3.0) sha256=1ec996be13020a00b3464560c09180b424477c698f59f82edf2b99b16cfa09a8
faraday (2.12.2) sha256=157339c25c7b8bcb739f5cf1207cb0cefe8fa1c65027266bcbc34c90c84b9ad6
faraday (2.13.0) sha256=f2697cd61a434dc446ee035f0370de654c2ad64707c4fc2541eb2338702e9614
faraday-net_http (3.4.0) sha256=a1f1e4cd6a2cf21599c8221595e27582d9936819977bbd4089a601f24c64e54a
faraday-retry (2.2.1) sha256=4146fed14549c0580bf14591fca419a40717de0dd24f267a8ec2d9a728677608
faraday-retry (2.3.1) sha256=4004faa21f41fc5360d359bc79889fc58fb7fae0ce93bfb737a784ac76805c03
fast_blank (1.0.1) sha256=269fc30414fed4e6403bc4a49081e1ea539f8b9226e59276ed1efaefabaa17ea
fastimage (2.3.1) sha256=23c629f1f3e7d61bcfcc06c25b3d2418bc6bf41d2e615dbf5132c0e3b63ecce9
ffi (1.17.1-aarch64-linux-gnu) sha256=c5d22cb545a3a691d46060f1343c461d1a8d38c3fd71b96b4cbbe6906bf1fd38
ffi (1.17.1-aarch64-linux-musl) sha256=88b9d6ae905d21142df27c94bb300042c1aae41b67291885f600eaad16326b1d
ffi (1.17.1-arm-linux-gnu) sha256=fe14f5ece94082f3b0e651a09008113281f2764e7ea95f522b64e2fe32e11504
ffi (1.17.1-arm-linux-musl) sha256=df14927ca7bd9095148a7d1938bb762bbf189d190cf25d9547395ec7acc198a0
ffi (1.17.1-arm64-darwin) sha256=a8e04f79d375742c54ee7f9fff4b4022b87200a4ec0eb082128d3b6559e67b4d
ffi (1.17.1-x86_64-darwin) sha256=0036199c290462dd7f03bc22933644c1685b7834a21788062bd5df48c72aa7a6
ffi (1.17.1-x86_64-linux-gnu) sha256=8c0ade2a5d19f3672bccfe3b58e016ae5f159e3e2e741c856db87fcf07c903d0
ffi (1.17.1-x86_64-linux-musl) sha256=3a343086820c96d6fbea4a5ef807fb69105b2b8174678f103b3db210c3f78401
ffi (1.17.2-aarch64-linux-gnu) sha256=c910bd3cae70b76690418cce4572b7f6c208d271f323d692a067d59116211a1a
ffi (1.17.2-aarch64-linux-musl) sha256=69e6556b091d45df83e6c3b19d3c54177c206910965155a6ec98de5e893c7b7c
ffi (1.17.2-arm-linux-gnu) sha256=d4a438f2b40224ae42ec72f293b3ebe0ba2159f7d1bd47f8417e6af2f68dbaa5
ffi (1.17.2-arm-linux-musl) sha256=977dfb7f3a6381206dbda9bc441d9e1f9366bf189a634559c3b7c182c497aaa3
ffi (1.17.2-arm64-darwin) sha256=54dd9789be1d30157782b8de42d8f887a3c3c345293b57ffb6b45b4d1165f813
ffi (1.17.2-x86_64-darwin) sha256=981f2d4e32ea03712beb26e55e972797c2c5a7b0257955d8667ba58f2da6440e
ffi (1.17.2-x86_64-linux-gnu) sha256=05d2026fc9dbb7cfd21a5934559f16293815b7ce0314846fee2ac8efbdb823ea
ffi (1.17.2-x86_64-linux-musl) sha256=97c0eb3981414309285a64dc4d466bd149e981c279a56371ef811395d68cb95c
fspath (3.1.2) sha256=b5ac9bafb97e2c8f8f9e303cd98ebd484be76fe9aa588bc4d01c6d99e78c9d75
globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9
google-protobuf (4.30.1) sha256=0c88b433866eac8a07d13ea19182e23334b14ac99d2a186cd88b72e2413d0778
google-protobuf (4.30.1-aarch64-linux) sha256=5e155eae7b686bd4f826f947f311d1c00b4c3b467ca21b27c47610b400a215c5
google-protobuf (4.30.1-arm64-darwin) sha256=f69682f65a0c2f689040714e2c6407c838b9113dce87d76e7e568f1213b7fdeb
google-protobuf (4.30.1-x86_64-darwin) sha256=626836baf2470c5590fedda795598cea1c3385f690a97f2ed6d7d43a31110d9a
google-protobuf (4.30.1-x86_64-linux) sha256=84c87b239dec7a200dddbcbd9bdb03a59c109f4b32667a50bec84286faa9784e
google-protobuf (4.30.2) sha256=0f35168dbeeccf13d928acf6c128cfec17b9a826ae4505246a02c115f4ae16ed
google-protobuf (4.30.2-aarch64-linux) sha256=a99d2f31bc2bebf4994b7cd2dd4c1d3ef28a0acc8f1b37be236982f7dc21bd8d
google-protobuf (4.30.2-arm64-darwin) sha256=c66a93ceef100fb2390615e76310491cc6e1944f7b9cda2cf1e3279887f817d1
google-protobuf (4.30.2-x86_64-darwin) sha256=17f4567dff431f8dd5be5ff6395824ec044413f67d2803a9941ebc8c70dec604
google-protobuf (4.30.2-x86_64-linux) sha256=c96993d98732ea185d98279f6c76e130eb9595437dda39610b3398c9e348518e
guess_html_encoding (0.0.11) sha256=cab6468b945f38673fc41ad147fbbc89693b3c6c34b03b071e2ed669a603e098
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
hashdiff (1.1.2) sha256=2c30eeded6ed3dce8401d2b5b99e6963fe5f14ed85e60dd9e33c545a44b71a77
@ -896,7 +898,7 @@ CHECKSUMS
image_size (3.4.0) sha256=c6a580513fe74947e25e5d3f0aea1e33add6c20f7d0007efa65504317b7f029a
in_threads (1.6.0) sha256=91a7e6138d279dc632f59b8a9a409e47148948e297c0f69c92f9a2479a182149
io-console (0.8.0) sha256=cd6a9facbc69871d69b2cb8b926fc6ea7ef06f06e505e81a64f14a470fddefa2
irb (1.15.1) sha256=d9bca745ac4207a8b728a52b98b766ca909b86ff1a504bcde3d6f8c84faae890
irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba
iso8601 (0.13.0) sha256=298c2b15b7be5fa95a1372813d36a2257656cd8e906dfbc1f5cb409851425aa2
jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
json (2.10.2) sha256=34e0eada93022b2a0a3345bb0b5efddb6e9ff5be7c48e409cfb54ff8a36a8b06
@ -914,7 +916,7 @@ CHECKSUMS
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
listen (3.9.0) sha256=db9e4424e0e5834480385197c139cb6b0ae0ef28cc13310cfd1ca78377d59c67
literate_randomizer (0.4.0) sha256=05073c9b383983b1ed7e26c40b963468e91bc86e663b3eeff3a4af91b84217b1
logger (1.6.6) sha256=dd618d24e637715472732e7eed02e33cfbdf56deaad225edd0f1f89d38024017
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
lograge (0.14.0) sha256=42371a75823775f166f727639f5ddce73dd149452a55fc94b90c303213dc9ae1
logstash-event (1.2.02) sha256=89a7dc60fac67070a5f60ba07409e541b09cb58906c391e90cb74b9f217467ae
logster (2.20.1) sha256=a84bbe58e7f99c4eb0246ea3a952ed87950f63aeea3cdbe7147018d10cc9a2a8
@ -929,7 +931,7 @@ CHECKSUMS
messageformat-wrapper (1.1.0) sha256=ecea879626e412d1bc841c457dacfcbb1a62cf88ca83573e4ea34bb371f160bc
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
mini_racer (0.18.0) sha256=742ea6f25e45ad33efaa3e0b7c5c5a0ab4f2c1f5b116de1324c04cc89fd5607e
mini_racer (0.18.1) sha256=c6694c37672da05027fe0894215a94c18a2117bdbb4ba90d80d275e4b23b3413
mini_scheduler (0.18.0) sha256=d2f084f38da8d76c5844a92f0d6bd01fc9982a8b5e6c7679b6cf44c82da33503
mini_sql (1.6.0) sha256=5296637f6a4af5bb43e06788037e9a2968ff9c8eb65928befcba8cb41f42d6ee
mini_suffix (0.3.3) sha256=8d1d33f92f69a2247c9b7d27173235da90479d955cdb863b63a7f53843b722e7
@ -946,14 +948,14 @@ CHECKSUMS
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9
nokogiri (1.18.6-aarch64-linux-gnu) sha256=1b11f9a814068282cc2b47ebe61395b2a69d1918092d2ca3bd664074f72540e9
nokogiri (1.18.6-aarch64-linux-musl) sha256=797662f201c37a8feac3bd5b0c0e3447053bc71e6633d273fefd4c68b03e6a54
nokogiri (1.18.6-arm-linux-gnu) sha256=2da07a07ef4c9d9e9da809b3dc0937ed90b031e32c2c658d9918941b85d68b95
nokogiri (1.18.6-arm-linux-musl) sha256=e8ae1c9a4d8cfa7a92d632a6f596a88235ebe66d4b70418543378ba16c601f70
nokogiri (1.18.6-arm64-darwin) sha256=727a441d179d934b4b7c73e0e28e6723ee46463d96bb0cc6e2e33a13540962c4
nokogiri (1.18.6-x86_64-darwin) sha256=fb72568c97ccd90a8d68cb765b0ff0720b109bd62e3babbf372e854ef8fef995
nokogiri (1.18.6-x86_64-linux-gnu) sha256=df065db6ba6e1e80f76ef04f860fcf260cc24685125fe33cdc3d1572a1c66b71
nokogiri (1.18.6-x86_64-linux-musl) sha256=75ec7a93cec54687aa63b2eaf830dc4ac5b4f3d8c969f20c035e67c9e6a30cef
nokogiri (1.18.7-aarch64-linux-gnu) sha256=57a064ab5440814a69a0e040817bd8154adea68a30d2ff2b3aa515a6a06dbb5f
nokogiri (1.18.7-aarch64-linux-musl) sha256=3e442dc5b69376e84288295fe37cbb890a21ad816a7e571e5e9967b3c1e30cd3
nokogiri (1.18.7-arm-linux-gnu) sha256=337d9149deb5ae01022dff7c90f97bed81715fd586aacab0c5809ef933994c5e
nokogiri (1.18.7-arm-linux-musl) sha256=97a26edcc975f780a0822aaf7f7d7427c561067c1c9ee56bd3542960f0c28a6e
nokogiri (1.18.7-arm64-darwin) sha256=083abb2e9ed2646860f6b481a981485a658c6064caafaa81bf1cda1bada2e9d5
nokogiri (1.18.7-x86_64-darwin) sha256=081d1aa517454ba3415304e2ea51fe411d6a3a809490d0c4aa42799cada417b7
nokogiri (1.18.7-x86_64-linux-gnu) sha256=3a0bf946eb2defde13d760f869b61bc8b0c18875afdd3cffa96543cfa3a18005
nokogiri (1.18.7-x86_64-linux-musl) sha256=9d83f8ec1fc37a305fa835d7ee61a4f37899e6ccc6dcb05be6645fa9797605af
oauth (1.1.0) sha256=38902b7f0f5ed91e858d6353f5e1e06b2c16a8aa0fd91984671eab1a1d1cddeb
oauth-tty (1.0.5) sha256=34e25c307da4509d4deec266ff3690bbf42e391355f496201c029268862d8b17
oauth2 (1.4.11) sha256=6739fcc8872bc94f476b0cae3e8bd78a56d8364b1b79b0757794c25e152d5c10
@ -969,16 +971,17 @@ CHECKSUMS
openssl-signature_algorithm (1.3.0) sha256=a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80
optimist (3.2.1) sha256=8cf8a0fd69f3aa24ab48885d3a666717c27bc3d9edd6e976e18b9d771e72e34e
ostruct (0.6.1) sha256=09a3fb7ecc1fa4039f25418cc05ae9c82bd520472c5c6a6f515f03e4988cb817
parallel (1.26.3) sha256=d86babb7a2b814be9f4b81587bf0b6ce2da7d45969fab24d8ae4bf2bb4d4c7ef
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
parallel_tests (5.1.0) sha256=c8cc0eef06577543dc3e09220ac5271809d9a1c65fa52ef86dd759bb4cb5aba5
parser (3.3.7.2) sha256=c71de9076be0b84459a268581b5aa75e5eeaac892b564430371d1f8eb55fb65f
parser (3.3.8.0) sha256=2476364142b307fa5a1b1ece44f260728be23858a9c71078e956131a75453c45
pg (1.5.9) sha256=761efbdf73b66516f0c26fcbe6515dc7500c3f0aa1a1b853feae245433c64fdc
pp (0.6.2) sha256=947ec3120c6f92195f8ee8aa25a7b2c5297bb106d83b41baa02983686577b6ff
prettier_print (1.2.1) sha256=a72838b5f23facff21f90a5423cdcdda19e4271092b41f4ea7f50b83929e6ff9
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
prism (1.4.0) sha256=dc0e3e00e93160213dc2a65519d9002a4a1e7b962db57d444cf1a71565bb703e
progress (3.6.0) sha256=360ed306dfa43d6174e847d563c70736dca249e2333cfec4b0387306c86cd573
pry (0.14.2) sha256=c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d
pry-byebug (3.10.1) sha256=c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8
pry (0.15.2) sha256=12d54b8640d3fa29c9211dd4ffb08f3fd8bf7a4fd9b5a73ce5b59c8709385b6b
pry-byebug (3.11.0) sha256=0b0abb7d309bc7f00044d512a3c8567274f7012b944b38becc8440439a1cea72
pry-rails (0.3.11) sha256=a69e28e24a34d75d1f60bcf241192a54253f8f7ef8a62cba1e75750a9653593d
pry-stack_explorer (0.6.1) sha256=a2dbea9b47c4ad00cf5c1ce21499f8128b915089e90015f7bafb6e9453baf340
psych (5.2.3) sha256=84a54bb952d14604fea22d99938348814678782f58b12648fcdfa4d2fce859ee
@ -1003,13 +1006,13 @@ CHECKSUMS
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
rbtrace (0.5.1) sha256=e8cba64d462bfb8ba102d7be2ecaacc789247d52ac587d8003549d909cb9c5dc
rchardet (1.9.0) sha256=26889486cdd83b378652baf7603f71d93e431bb11bc237b4cd8c65151af4a590
rdoc (6.12.0) sha256=7d6f706e070bffa5d18a448f24076cbfb34923a99c1eab842aa18e6ca69f56e0
rdoc (6.13.1) sha256=62a0dac99493c94e8eb7a3fb44e55aefcb4cecb119f7991f25bddc5ed8d472f7
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
redis (5.4.0) sha256=798900d869418a9fc3977f916578375b45c38247a556b61d58cba6bb02f7d06b
redis-client (0.24.0) sha256=ee65ee39cb2c38608b734566167fd912384f3c1241f59075e22858f23a085dbb
redis-namespace (1.11.0) sha256=e91a1aa2b2d888b6dea1d4ab8d39e1ae6fac3426161feb9d91dd5cca598a2239
regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
reline (0.6.0) sha256=57620375dcbe56ec09bac7192bfb7460c716bbf0054dc94345ecaa5438e539d2
reline (0.6.1) sha256=1afcc9d7cb1029cdbe780d72f2f09251ce46d3780050f3ec39c3ccc6b60675fb
request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb
rexml (3.4.1) sha256=c74527a9a0a04b4ec31dbe0dc4ed6004b960af943d8db42e539edde3a871abca
rinku (2.0.6) sha256=8b60670e3143f3db2b37efa262971ce3619ec23092045498ef9f077d82828d7d
@ -1029,12 +1032,12 @@ CHECKSUMS
rss (0.3.1) sha256=b46234c04551b925180f8bedfc6f6045bf2d9998417feda72f300e7980226737
rswag-specs (2.16.0) sha256=8ba26085c408b0bd2ed21dc8015c80f417c7d34c63720ab7133c2549b5bd2a91
rtlcss (0.2.1) sha256=213d5a00bf61267f93a7a516d699d77e1cc5f396743abb33c01e3f3243a7bf60
rubocop (1.74.0) sha256=06138a35d7d11c963d5abc0148b355e3999007cb0225a619940db0e75521379b
rubocop-ast (1.41.0) sha256=0eeff9ad3743f42823e3c687cbc15f8c552ff63c9f8e3d35fbafe746977c7c0d
rubocop (1.75.2) sha256=8efde647e278417e8074421b007e0d7d7c591482ef99d980528b18fea015a7c8
rubocop-ast (1.44.1) sha256=e3cc04203b2ef04f6d6cf5f85fe6d643f442b18cc3b23e3ada0ce5b6521b8e92
rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c
rubocop-discourse (3.12.1) sha256=ebf7e2224f053047372071419052828c3e3a01bccb14ea1f282ac143547df9bc
rubocop-factory_bot (2.27.1) sha256=9d744b5916778c1848e5fe6777cc69855bd96548853554ec239ba9961b8573fe
rubocop-rails (2.30.3) sha256=fc5a6506daa916d15e282cc806943afa64a020bf592b93a94025d89a2a78a715
rubocop-rails (2.31.0) sha256=79476e1075299c3e60fc50549c7c32614f9ebaae719b899ed75785c6786c52bd
rubocop-rspec (3.5.0) sha256=710c942fe1af884ba8eea75cbb8bdbb051929a2208880a6fc2e2dce1eed5304c
rubocop-rspec_rails (2.31.0) sha256=775375e18a26a1184a812ef3054b79d218e85601b9ae897f38f8be24dddf1f45
ruby-prof (1.7.1) sha256=026393448cf92fd24a91739bf71ccd2bfe88fe8a1401ee8afc4948a16d62ea24
@ -1053,8 +1056,8 @@ CHECKSUMS
sass-embedded (1.85.0-x86_64-linux-musl) sha256=ca01931c54b59db1130a779386bcfc798f8dcfee1f98f60fbcf8c5dbdc0f9c23
sassc-embedded (1.80.2) sha256=625ac3923fe9af1a8072210cf22b01b66f3dc23748a4997a9b28bfdff7c7967d
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
selenium-devtools (0.133.0) sha256=aba5a5225ac38d235bbc6ebb4e0b8209e973ac7af4226e4304a1417573f73f64
selenium-webdriver (4.29.1) sha256=0a7fe53cc4d2c515adbb89e115c6e786c64e9b98f85939d21071c6e32883a146
selenium-devtools (0.135.0) sha256=3862ec1f3e940a030f492b3691d11af35544789f961fcd0633cb89e4ea4251ec
selenium-webdriver (4.31.0) sha256=ddb2d88eee23cddb5d6a9dadd909427a9e5163718338e11a91eef2fbead100e9
shoulda-matchers (6.4.0) sha256=9055bb7f4bb342125fb860809798855c630e05ef5e75837b3168b8e6ee1608b0
sidekiq (7.3.9) sha256=1108712e1def89002b28e3545d5ae15d4a57ffd4d2c25d97bb1360988826b5a7
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
@ -1074,7 +1077,7 @@ CHECKSUMS
sqlite3 (2.6.0-x86_64-linux-musl) sha256=6f7c9346430c4aacc9280dee6bd4d23a6f1f31013d4f9c0a720fb14ef6a801c2
sshkey (3.0.0) sha256=655ba351d6e01a48dfe59d65530af8975c777b8cc57a061770de3228ff2d11cd
stackprof (0.2.27) sha256=aff6d28656c852e74cf632cc2046f849033dc1dedffe7cb8c030d61b5745e80c
stringio (3.1.5) sha256=bca92461515a131535743bc81d5559fa1de7d80cff9a654d6c0af6f9f27e35c8
stringio (3.1.6) sha256=292c495d1657adfcdf0a32eecf12a60e6691317a500c3112ad3b2e31068274f5
syntax_tree (6.2.0) sha256=a50a01c246601af3c258edbb6b12e44373d17966ab3bebd1f7224b3b994a343d
test-prof (1.4.4) sha256=1a59513ed9d33a1f5ca17c0b89da4e70f60a91c83ec62e9a873dbb99141353ef
thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda
@ -1090,7 +1093,7 @@ CHECKSUMS
uniform_notifier (1.16.0) sha256=99b39ee4a0864e3b49f375b5e5803eb26d35ed6eb1719c96407573a87bc4dbb5
uri (1.0.3) sha256=e9f2244608eea2f7bc357d954c65c910ce0399ca5e18a7a29207ac22d8767011
useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
version_gem (1.1.6) sha256=b989cf19880ee18907083ba9bb6fdbe40826bd698fbd7cdfab7345a2550bf203
version_gem (1.1.7) sha256=df3bacb16c09d9069d51625f6e009da28e69ed8f9cbd2dd14753cec944e0cacc
web-push (3.0.1) sha256=5b4dd2f2bba3bd8951da6416492fe920a6f203d14d3080f943c5d01c0cc4b18d
webmock (3.25.1) sha256=ab9d5d9353bcbe6322c83e1c60a7103988efc7b67cd72ffb9012629c3d396323
webrick (1.9.1) sha256=b42d3c94f166f3fb73d87e9b359def9b5836c426fc8beacf38f2184a21b2a989

View File

@ -10,6 +10,7 @@ import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
import AdminFilteredSiteSettings from "admin/components/admin-filtered-site-settings";
import AdminSiteSettingsChangesBanner from "admin/components/admin-site-settings-changes-banner";
import SiteSetting from "admin/models/site-setting";
export default class AdminAreaSettings extends Component {
@ -94,5 +95,7 @@ export default class AdminAreaSettings extends Component {
</ConditionalLoadingSpinner>
{{/if}}
</div>
<AdminSiteSettingsChangesBanner />
</template>
}

View File

@ -10,10 +10,10 @@ import DButton from "discourse/components/d-button";
import Form from "discourse/components/form";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { API_KEY_SCOPE_MODES } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ApiKeyUrlsModal from "admin/components/modal/api-key-urls";
import { API_KEY_SCOPE_MODES } from "admin/lib/constants";
import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser";
export default class AdminConfigAreasApiKeysNew extends Component {

View File

@ -207,12 +207,12 @@ export default class AdminConfigAreasColorPalette extends Component {
/>
</div>
<form.Alert class="fonts-and-logos-hint">
<div class="admin-config-color-palettes__fonts-and-logos-hint">
<div class="admin-config-color-palettes__logo-and-fonts-hint">
<span>{{i18n
"admin.config_areas.color_palettes.fonts_and_logos_hint"
"admin.config_areas.color_palettes.logo_and_fonts_hint"
}}</span>
<LinkTo @route="adminConfig.branding">{{i18n
"admin.config_areas.color_palettes.go_to_branding"
<LinkTo @route="adminConfig.logo-and-fonts">{{i18n
"admin.config_areas.color_palettes.go_to_logo_and_fonts"
}}</LinkTo>
</div>
</form.Alert>

View File

@ -1,17 +1,71 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { array, concat, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import DButton from "discourse/components/d-button";
import DPageSubheader from "discourse/components/d-page-subheader";
import DSelect from "discourse/components/d-select";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import DropdownMenu from "discourse/components/dropdown-menu";
import FilterInput from "discourse/components/filter-input";
import LoadMore from "discourse/components/load-more";
import icon from "discourse/helpers/d-icon";
import { ajax } from "discourse/lib/ajax";
import { extractErrorInfo } from "discourse/lib/ajax-error";
import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import getURL from "discourse/lib/get-url";
import { descriptionForRemoteUrl } from "discourse/lib/popular-themes";
import { i18n } from "discourse-i18n";
import InstallThemeCard from "admin/components/admin-config-area-cards/install-theme-card";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
import InstallComponentModal from "admin/components/modal/install-theme";
import ThemesGrid from "admin/components/themes-grid";
import { COMPONENTS } from "admin/models/theme";
import DMenu from "float-kit/components/d-menu";
const STATUS_FILTER_OPTIONS = [
{
value: "all",
label: "admin.config_areas.themes_and_components.components.filter_by_all",
},
{
value: "used",
label: "admin.config_areas.themes_and_components.components.filter_by_used",
},
{
value: "unused",
label:
"admin.config_areas.themes_and_components.components.filter_by_unused",
},
{
value: "updates_available",
label:
"admin.config_areas.themes_and_components.components.filter_by_updates_available",
},
];
export default class AdminConfigAreasComponents extends Component {
@service modal;
@service router;
@service toasts;
@tracked loading = true;
@tracked components = [];
@tracked nameFilter;
@tracked statusFilter;
@tracked hasComponents = false;
@tracked loadingMore = false;
page = 0;
hasMore = false;
constructor() {
super(...arguments);
this.load();
}
@action
installModal() {
this.modal.show(InstallComponentModal, {
@ -28,7 +82,7 @@ export default class AdminConfigAreasComponents extends Component {
selectedType: COMPONENTS,
userId: null,
content: [],
installedThemes: this.args.components,
installedThemes: this.components,
addTheme: this.addComponent,
updateSelectedType: () => {},
showComponentsOnly: true,
@ -45,19 +99,482 @@ export default class AdminConfigAreasComponents extends Component {
},
duration: 2000,
});
this.router.refresh();
this.load();
}
@action
onNameFilterChange(event) {
this.loading = true;
this.nameFilter = event.target.value;
this.page = 0;
discourseDebounce(this, this.load, INPUT_DELAY);
}
@action
onStatusFilterChange(value) {
this.loading = true;
this.statusFilter = value;
this.page = 0;
this.load();
}
@action
async load({ append = false } = {}) {
try {
const data = await ajax("/admin/config/customize/components", {
data: {
name: this.nameFilter,
status: this.statusFilter,
page: this.page,
},
});
if (append) {
this.components = [...this.components, ...data.components];
} else {
this.components = data.components;
}
this.hasMore = data.has_more;
if (!this.hasComponents && !this.nameFilter && !this.statusFilter) {
this.hasComponents = !!data.components.length;
}
} finally {
this.loading = false;
}
}
@action
async loadMore() {
if (this.loadingMore) {
return;
}
if (this.hasMore) {
this.page += 1;
this.loadingMore = true;
try {
await this.load({ append: true });
} finally {
this.loadingMore = false;
}
}
}
<template>
<div class="admin-detail">
<ThemesGrid @themes={{@components}}>
<:specialCard>
<InstallThemeCard
@component={{true}}
@openModal={{this.installModal}}
/>
</:specialCard>
</ThemesGrid>
<DPageSubheader
@titleLabel={{i18n
"admin.config_areas.themes_and_components.components.title"
}}
@descriptionLabel={{i18n
"admin.config_areas.themes_and_components.components.description"
}}
>
<:actions as |actions|>
<actions.Primary
disabled={{this.loading}}
@label="admin.config_areas.themes_and_components.components.install"
@action={{this.installModal}}
/>
</:actions>
</DPageSubheader>
<div class="container">
{{#if this.hasComponents}}
<div class="d-admin-filter">
<div
class="admin-filter__input-container admin-config-components__name-filter"
>
<FilterInput
placeholder={{i18n
"admin.config_areas.themes_and_components.components.search_components"
}}
@filterAction={{this.onNameFilterChange}}
class="admin-filter__input"
/>
</div>
<label class="admin-config-components__status-filter">
{{i18n
"admin.config_areas.themes_and_components.components.filter_by"
}}
<DSelect
@value="all"
@includeNone={{false}}
@onChange={{this.onStatusFilterChange}}
as |select|
>
{{#each STATUS_FILTER_OPTIONS as |option|}}
<select.Option @value={{option.value}}>
{{i18n option.label}}
</select.Option>
{{/each}}
</DSelect>
</label>
</div>
{{/if}}
<ConditionalLoadingSpinner @condition={{this.loading}}>
{{#if this.components.length}}
<LoadMore @selector=".component-list tr" @action={{this.loadMore}}>
<table class="d-admin-table component-list">
<thead>
<th>{{i18n
"admin.config_areas.themes_and_components.components.name"
}}</th>
<th>{{i18n
"admin.config_areas.themes_and_components.components.used_on"
}}</th>
<th>{{i18n
"admin.config_areas.themes_and_components.components.enabled"
}}</th>
<th></th>
</thead>
<tbody>
{{#each this.components as |comp|}}
<ComponentRow @component={{comp}} @refresh={{this.load}} />
{{/each}}
</tbody>
</table>
<ConditionalLoadingSpinner @condition={{this.loadingMore}} />
</LoadMore>
{{else}}
{{#if this.hasComponents}}
{{i18n
"admin.config_areas.themes_and_components.components.no_components_found"
}}
{{else}}
<AdminConfigAreaEmptyList
@emptyLabel="admin.config_areas.themes_and_components.components.no_components"
/>
{{/if}}
{{/if}}
</ConditionalLoadingSpinner>
</div>
</template>
}
class ComponentRow extends Component {
@service toasts;
@service dialog;
@tracked enabled = this.args.component.enabled;
@tracked hasUpdates = this.args.component.remote_theme?.commits_behind > 0;
@tracked disableToggle = false;
@tracked checkingForUpdates = false;
@tracked updating = false;
get parentThemesCell() {
const names = this.args.component.parent_themes.map((theme) => theme.name);
names.sort();
if (!names.length) {
return;
} else if (names.length === 1) {
return names[0];
} else if (names.length === 2) {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_two",
{
name1: names[0],
name2: names[1],
}
);
} else if (names.length === 3) {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_three",
{
name1: names[0],
name2: names[1],
name3: names[2],
}
);
} else {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_more_than_three",
{
name1: names[0],
name2: names[1],
name3: names[2],
count: names.length - 3,
}
);
}
}
get description() {
const remoteUrl = this.args.component.remote_theme?.remote_url;
return (
this.args.component.description ??
(remoteUrl && descriptionForRemoteUrl(remoteUrl))
);
}
@action
async toggleEnabled() {
this.disableToggle = true;
try {
const data = await this.save({ enabled: !this.enabled });
this.enabled = data.theme.enabled;
} finally {
this.disableToggle = false;
}
}
@action
async checkForUpdates() {
this.checkingForUpdates = true;
try {
const data = await this.save({ remote_check: true });
if (data.theme.remote_theme.commits_behind > 0) {
this.hasUpdates = true;
this.toasts.default({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.new_update_for_component",
{ name: this.args.component.name }
),
},
});
} else {
this.hasUpdates = false;
this.toasts.default({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.component_up_to_date",
{ name: this.args.component.name }
),
},
});
}
} finally {
this.checkingForUpdates = false;
}
}
@action
async updateToLatest() {
this.updating = true;
try {
await this.save({ remote_update: true });
this.hasUpdates = false;
this.toasts.success({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.updated_successfully",
{ name: this.args.component.name }
),
},
});
} finally {
this.updating = false;
}
}
@action
delete() {
return this.dialog.yesNoConfirm({
message: i18n(
"admin.config_areas.themes_and_components.components.delete_confirm",
{ name: this.args.component.name }
),
didConfirm: async () => {
try {
await ajax(`/admin/themes/${this.args.component.id}`, {
type: "DELETE",
});
this.toasts.success({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.deleted_successfully",
{ name: this.args.component.name }
),
},
});
this.args.refresh();
} catch (error) {
this.toasts.error({
duration: 5000,
data: {
message: extractErrorInfo(error),
},
});
}
},
});
}
async save(attrs) {
try {
return await ajax(`/admin/themes/${this.args.component.id}.json`, {
type: "PUT",
data: {
theme: attrs,
},
});
} catch (error) {
this.toasts.error({
duration: 5000,
data: {
message: extractErrorInfo(error),
},
});
throw error;
}
}
<template>
<tr
data-component-id={{@component.id}}
class="d-admin-row__content admin-config-components__component-row
{{if this.hasUpdates 'has-update'}}"
>
<td class="d-admin-row__overview">
<div class="d-admin-row__overview-name">
{{@component.name}}
</div>
{{#if @component.remote_theme.authors}}
<div
class="d-admin-row__overview-author admin-config-components__author-name"
>{{i18n
"admin.config_areas.themes_and_components.components.by_author"
(hash name=@component.remote_theme.authors)
}}</div>
{{/if}}
{{#if this.description}}
<div
class="d-admin-row__overview-about admin-config-components__description"
>
{{this.description}}
{{#if @component.remote_theme.about_url}}
<a href={{@component.remote_theme.about_url}}>{{i18n
"admin.config_areas.themes_and_components.components.learn_more"
}}
{{icon "up-right-from-square"}}
</a>
{{/if}}
</div>
{{/if}}
{{#if this.hasUpdates}}
<div
class="d-admin-row__overview-about admin-config-components__update-available"
>
{{i18n
"admin.config_areas.themes_and_components.components.update_available"
}}
</div>
{{/if}}
</td>
<td class="d-admin-row__detail admin-config-components__parent-themes">
<div class="d-admin-row__mobile-label">
{{i18n "admin.config_areas.themes_and_components.components.used_on"}}
</div>
<div class="admin-config-components__parent-themes-list">
{{#if @component.parent_themes.length}}
{{this.parentThemesCell}}
{{else}}
<div class="status-label --inactive">
<div class="status-label-indicator"></div>
<div class="status-label-text">
{{i18n
"admin.config_areas.themes_and_components.components.badge_unused"
}}
</div>
</div>
{{/if}}
</div>
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
{{i18n "admin.config_areas.themes_and_components.components.enabled"}}
</div>
<DToggleSwitch
@state={{this.enabled}}
class="admin-config-components__toggle"
disabled={{this.disableToggle}}
{{on "click" this.toggleEnabled}}
/>
</td>
<td class="d-admin-row__controls">
<div class="d-admin-row__controls-options">
<DButton
class="admin-config-components__edit"
@label="admin.config_areas.themes_and_components.components.edit"
@route="adminCustomizeThemes.show"
@routeModels={{array "themes" @component.id}}
/>
<DMenu
@identifier="component-menu"
@title={{i18n "admin.config_areas.flags.more_options.title"}}
@icon="ellipsis"
@class="btn-default admin-config-components__more-actions"
>
<:content>
<DropdownMenu as |dropdown|>
<dropdown.item>
<DButton
class="btn-transparent admin-config-components__preview"
target="_blank"
rel="noopener noreferrer"
@label="admin.config_areas.themes_and_components.components.preview"
@icon="desktop"
@href={{getURL
(concat "/admin/themes/" @component.id "/preview")
}}
/>
</dropdown.item>
{{#if @component.remote_theme.is_git}}
<dropdown.item>
{{#if this.hasUpdates}}
<DButton
class="btn-transparent admin-config-components__update"
@label="admin.config_areas.themes_and_components.components.update"
@icon="cloud-arrow-down"
@action={{this.updateToLatest}}
@isLoading={{this.updating}}
/>
{{else}}
<DButton
class="btn-transparent admin-config-components__check-updates"
@label="admin.config_areas.themes_and_components.components.check_update"
@icon="arrows-rotate"
@action={{this.checkForUpdates}}
@isLoading={{this.checkingForUpdates}}
/>
{{/if}}
</dropdown.item>
{{/if}}
<dropdown.item>
<DButton
class="btn-transparent admin-config-components__export"
target="_blank"
rel="noopener noreferrer"
@label="admin.config_areas.themes_and_components.components.export"
@icon="download"
@href={{getURL
(concat
"/admin/customize/themes/" @component.id "/export"
)
}}
/>
</dropdown.item>
<dropdown.item>
<DButton
class="btn-danger admin-config-components__delete"
@label="admin.config_areas.themes_and_components.components.delete"
@icon="trash-can"
@action={{this.delete}}
/>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</div>
</td>
</tr>
</template>
}

View File

@ -4,10 +4,10 @@ import { action } from "@ember/object";
import { service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { SYSTEM_FLAG_IDS } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminFlagItem from "admin/components/admin-flag-item";
import { SYSTEM_FLAG_IDS } from "admin/lib/constants";
export default class AdminConfigAreasFlags extends Component {
@service site;

View File

@ -12,8 +12,8 @@ import DropdownMenu from "discourse/components/dropdown-menu";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { SYSTEM_FLAG_IDS } from "discourse/lib/constants";
import { i18n } from "discourse-i18n";
import { SYSTEM_FLAG_IDS } from "admin/lib/constants";
import DMenu from "float-kit/components/d-menu";
export default class AdminFlagItem extends Component {
@ -28,10 +28,11 @@ export default class AdminFlagItem extends Component {
}
get canEdit() {
return (
!Object.values(SYSTEM_FLAG_IDS).includes(this.args.flag.id) &&
!this.args.flag.is_used
);
return !Object.values(SYSTEM_FLAG_IDS).includes(this.args.flag.id);
}
get canDelete() {
return this.canEdit && !this.args.flag.is_used;
}
get editTitle() {
@ -41,9 +42,9 @@ export default class AdminFlagItem extends Component {
}
get deleteTitle() {
return this.canEdit
? "admin.config_areas.flags.form.edit_flag"
: "admin.config_areas.flags.form.non_editable";
return this.canDelete
? "admin.config_areas.flags.form.delete_flag"
: "admin.config_areas.flags.form.non_deletable";
}
@action
@ -191,7 +192,7 @@ export default class AdminFlagItem extends Component {
@icon="trash-can"
class="btn-transparent btn-danger admin-flag-item__delete"
@action={{this.delete}}
@disabled={{not this.canEdit}}
@disabled={{not this.canDelete}}
@title={{this.deleteTitle}}
/>
</dropdown.item>

View File

@ -133,6 +133,14 @@ export default class AdminFlagsForm extends Component {
<AdminConfigAreaCard @heading={{this.header}}>
<:content>
<Form @onSubmit={{this.save}} @data={{this.formData}} as |form|>
<form.Alert @type="warning" @icon="circle-info">
{{#if this.isUpdate}}
{{i18n "admin.config_areas.flags.form.edit_warning"}}
{{else}}
{{i18n "admin.config_areas.flags.form.create_warning"}}
{{/if}}
</form.Alert>
<form.Field
@name="name"
@title={{i18n "admin.config_areas.flags.form.name"}}
@ -210,10 +218,6 @@ export default class AdminFlagsForm extends Component {
</checkboxGroup.Field>
</form.CheckboxGroup>
<form.Alert @icon="circle-info">
{{i18n "admin.config_areas.flags.form.alert"}}
</form.Alert>
<form.Submit @label="admin.config_areas.flags.form.save" />
</Form>
</:content>

View File

@ -0,0 +1,63 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { concat, fn } from "@ember/helper";
import { action } from "@ember/object";
import { dasherize } from "@ember/string";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { i18n } from "discourse-i18n";
import { MAIN_FONTS, MORE_FONTS } from "admin/lib/constants";
import eq from "truth-helpers/helpers/eq";
export default class AdminFontChooser extends Component {
@tracked showMoreFonts = MORE_FONTS.map((font) => font.key).includes(
this.args.selectedFont
);
@action
setButtonValue(fieldSet, value) {
fieldSet(value);
}
@action
toggleMoreFonts() {
this.showMoreFonts = !this.showMoreFonts;
}
<template>
<@field.Custom>
{{#each MAIN_FONTS as |font|}}
<DButton
@action={{fn this.setButtonValue @field.set font.key}}
class={{concatClass
"admin-fonts-form__button-option font btn-flat"
(concat "body-font-" (dasherize font.key))
(if (eq @selectedFont font.key) "active")
}}
>{{font.name}}</DButton>
{{/each}}
{{#if this.showMoreFonts}}
{{#each MORE_FONTS as |font|}}
<DButton
@action={{fn this.setButtonValue @field.set font.key}}
class={{concatClass
"admin-fonts-form__button-option font btn-flat"
(concat "body-font-" (dasherize font.key))
(if (eq @selectedFont font.key) "active")
}}
>{{font.name}}</DButton>
{{/each}}
{{/if}}
<DButton
@action={{this.toggleMoreFonts}}
class="admin-fonts-form__more font"
>
{{#if this.showMoreFonts}}
{{i18n "admin.config.logo_and_fonts.fonts.form.less_fonts"}}
{{else}}
{{i18n "admin.config.logo_and_fonts.fonts.form.more_fonts"}}
{{/if}}
</DButton>
</@field.Custom>
</template>
}

View File

@ -0,0 +1,175 @@
import Component from "@glimmer/component";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { decamelize, underscore } from "@ember/string";
import DButton from "discourse/components/d-button";
import Form from "discourse/components/form";
import UpdateDefaultTextSize from "discourse/components/modal/update-default-text-size";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminFontChooser from "admin/components/admin-font-chooser";
import {
DEFAULT_TEXT_SIZES,
MAIN_FONTS,
MORE_FONTS,
} from "admin/lib/constants";
import eq from "truth-helpers/helpers/eq";
const ALL_FONTS = [...MAIN_FONTS, ...MORE_FONTS];
export default class AdminFontsForm extends Component {
@service siteSettings;
@service siteSettingChangeTracker;
@service toasts;
@service modal;
@service router;
updateExistingUsers = null;
@bind
setUpdateExistingUsers(value) {
this.updateExistingUsers = value;
}
@action
setButtonValue(fieldSet, value) {
fieldSet(decamelize(underscore(value)));
}
@action
async update(data) {
if (this.siteSettings.default_text_size === data.default_text_size) {
await this.#save(data);
return;
}
const result = await ajax(
`/admin/site_settings/default_text_size/user_count.json`,
{
type: "PUT",
data: {
default_text_size: data.default_text_size,
},
}
);
const count = result.user_count;
if (count > 0) {
await this.modal.show(UpdateDefaultTextSize, {
model: {
setUpdateExistingUsers: this.setUpdateExistingUsers,
count,
},
});
await this.#save(data);
} else {
await this.#save(data);
}
}
@action
async #save(data) {
try {
await ajax("/admin/config/fonts.json", {
type: "PUT",
data: {
base_font: data.base_font,
heading_font: data.heading_font,
default_text_size: data.default_text_size,
update_existing_users: this.updateExistingUsers,
},
});
this.toasts.success({
duration: 3000,
data: {
message: i18n("admin.config.logo_and_fonts.fonts.form.saved"),
},
});
this.siteSettingChangeTracker.refreshPage({
base_font: ALL_FONTS.find((font) => font.key === data.base_font).name,
heading_font: ALL_FONTS.find((font) => font.key === data.heading_font)
.name,
default_text_size: data.default_text_size,
});
} catch (err) {
this.toasts.error({
duration: 3000,
data: {
message: err.jqXHR.responseJSON.errors[0],
},
});
}
}
get formData() {
return {
base_font: this.siteSettings.base_font,
heading_font: this.siteSettings.heading_font,
default_text_size: this.siteSettings.default_text_size,
};
}
<template>
<Form
@onSubmit={{this.update}}
@data={{this.formData}}
class="admin-fonts-form"
as |form transientData|
>
<form.Field
@name="base_font"
@title={{i18n "admin.config.logo_and_fonts.fonts.form.base_font.title"}}
@validation="required"
@format="full"
as |field|
>
<AdminFontChooser
@field={{field}}
@selectedFont={{transientData.base_font}}
/>
</form.Field>
<form.Field
@name="heading_font"
@title={{i18n
"admin.config.logo_and_fonts.fonts.form.heading_font.title"
}}
@validation="required"
@format="full"
as |field|
>
<AdminFontChooser
@field={{field}}
@selectedFont={{transientData.heading_font}}
/>
</form.Field>
<form.Field
@name="default_text_size"
@title={{i18n
"admin.config.logo_and_fonts.fonts.form.default_text_size.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.fonts.form.default_text_size.description"
}}
@validation="required"
@format="full"
as |field|
>
<field.Custom>
{{#each DEFAULT_TEXT_SIZES as |textSize|}}
<DButton
@action={{fn this.setButtonValue field.set textSize}}
class={{concatClass
"admin-fonts-form__button-option text-size btn-flat"
textSize
(if (eq transientData.default_text_size textSize) "active")
}}
>{{textSize}}</DButton>
{{/each}}
</field.Custom>
</form.Field>
<form.Submit />
</Form>
</template>
}

View File

@ -1,8 +1,8 @@
import Component from "@ember/component";
import { classNames } from "@ember-decorators/component";
import { eq } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import { i18n } from "discourse-i18n";
import eq from "truth-helpers/helpers/eq";
@classNames("row")
export default class AdminFormRow extends Component {

View File

@ -12,8 +12,9 @@ import { i18n } from "discourse-i18n";
import AdminConfigAreaCardSection from "admin/components/admin-config-area-card-section";
import SimpleList from "admin/components/simple-list";
export default class AdminBrandingLogoForm extends Component {
export default class AdminLogoForm extends Component {
@service siteSettings;
@service siteSettingChangeTracker;
@service toasts;
@tracked placeholders = {};
@ -56,7 +57,7 @@ export default class AdminBrandingLogoForm extends Component {
@action
async save(data) {
try {
await ajax("/admin/config/branding/logo.json", {
await ajax("/admin/config/logo.json", {
type: "PUT",
data: {
logo: data.logo,
@ -78,9 +79,10 @@ export default class AdminBrandingLogoForm extends Component {
this.toasts.success({
duration: 3000,
data: {
message: i18n("admin.config.branding.logo.form.saved"),
message: i18n("admin.config.logo_and_fonts.logo.form.saved"),
},
});
this.siteSettingChangeTracker.refreshPage(data);
} catch (err) {
this.toasts.error({
duration: 3000,
@ -129,11 +131,13 @@ export default class AdminBrandingLogoForm extends Component {
>
<form.Field
@name="logo"
@title={{i18n "admin.config.branding.logo.form.logo.title"}}
@title={{i18n "admin.config.logo_and_fonts.logo.form.logo.title"}}
@description={{i18n
"admin.config.branding.logo.form.logo.description"
"admin.config.logo_and_fonts.logo.form.logo.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.logo.help_text"
}}
@helpText={{i18n "admin.config.branding.logo.form.logo.help_text"}}
@onSet={{fn this.handleUpload "logo"}}
as |field|
>
@ -141,7 +145,9 @@ export default class AdminBrandingLogoForm extends Component {
</form.Field>
<form.Field
@name="logo_dark_required"
@title={{i18n "admin.config.branding.logo.form.logo_dark.required"}}
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_dark.required"
}}
@format="full"
as |field|
>
@ -151,9 +157,11 @@ export default class AdminBrandingLogoForm extends Component {
<form.Section>
<form.Field
@name="logo_dark"
@title={{i18n "admin.config.branding.logo.form.logo_dark.title"}}
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_dark.title"
}}
@helpText={{i18n
"admin.config.branding.logo.form.logo_dark.help_text"
"admin.config.logo_and_fonts.logo.form.logo_dark.help_text"
}}
@onSet={{fn this.handleUpload "logo_dark"}}
as |field|
@ -164,12 +172,14 @@ export default class AdminBrandingLogoForm extends Component {
{{/if}}
<form.Field
@name="large_icon"
@title={{i18n "admin.config.branding.logo.form.large_icon.title"}}
@title={{i18n
"admin.config.logo_and_fonts.logo.form.large_icon.title"
}}
@description={{i18n
"admin.config.branding.logo.form.large_icon.description"
"admin.config.logo_and_fonts.logo.form.large_icon.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.large_icon.help_text"
"admin.config.logo_and_fonts.logo.form.large_icon.help_text"
}}
@onSet={{fn this.handleUpload "large_icon"}}
@placeholderUrl={{this.placeholders.large_icon}}
@ -179,9 +189,9 @@ export default class AdminBrandingLogoForm extends Component {
</form.Field>
<form.Field
@name="favicon"
@title={{i18n "admin.config.branding.logo.form.favicon.title"}}
@title={{i18n "admin.config.logo_and_fonts.logo.form.favicon.title"}}
@description={{i18n
"admin.config.branding.logo.form.favicon.description"
"admin.config.logo_and_fonts.logo.form.favicon.description"
}}
@onSet={{fn this.handleUpload "favicon"}}
@placeholderUrl={{this.placeholders.favicon}}
@ -191,12 +201,14 @@ export default class AdminBrandingLogoForm extends Component {
</form.Field>
<form.Field
@name="logo_small"
@title={{i18n "admin.config.branding.logo.form.logo_small.title"}}
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small.title"
}}
@description={{i18n
"admin.config.branding.logo.form.logo_small.description"
"admin.config.logo_and_fonts.logo.form.logo_small.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.logo_small.help_text"
"admin.config.logo_and_fonts.logo.form.logo_small.help_text"
}}
@onSet={{fn this.handleUpload "logo_small"}}
as |field|
@ -206,7 +218,7 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="logo_small_dark_required"
@title={{i18n
"admin.config.branding.logo.form.logo_small_dark.required"
"admin.config.logo_and_fonts.logo.form.logo_small_dark.required"
}}
@format="full"
as |field|
@ -218,10 +230,10 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="logo_small_dark"
@title={{i18n
"admin.config.branding.logo.form.logo_small_dark.title"
"admin.config.logo_and_fonts.logo.form.logo_small_dark.title"
}}
@helpText={{i18n
"admin.config.branding.logo.form.logo_small_dark.help_text"
"admin.config.logo_and_fonts.logo.form.logo_small_dark.help_text"
}}
@onSet={{fn this.handleUpload "logo_small_dark"}}
as |field|
@ -232,7 +244,7 @@ export default class AdminBrandingLogoForm extends Component {
{{/if}}
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.branding.logo.form.mobile"}}
@heading={{i18n "admin.config.logo_and_fonts.logo.form.mobile"}}
class="admin-logo-form__mobile-section"
@collapsable={{true}}
@collapsed={{true}}
@ -241,13 +253,13 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="mobile_logo"
@title={{i18n
"admin.config.branding.logo.form.mobile_logo.title"
"admin.config.logo_and_fonts.logo.form.mobile_logo.title"
}}
@description={{i18n
"admin.config.branding.logo.form.mobile_logo.description"
"admin.config.logo_and_fonts.logo.form.mobile_logo.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.mobile_logo.help_text"
"admin.config.logo_and_fonts.logo.form.mobile_logo.help_text"
}}
@onSet={{fn this.handleUpload "mobile_logo"}}
@placeholderUrl={{this.placeholders.mobile_logo}}
@ -258,7 +270,7 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="mobile_logo_dark_required"
@title={{i18n
"admin.config.branding.logo.form.mobile_logo_dark.required"
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.required"
}}
@format="full"
as |field|
@ -270,10 +282,10 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="mobile_logo_dark"
@title={{i18n
"admin.config.branding.logo.form.mobile_logo_dark.title"
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.title"
}}
@helpText={{i18n
"admin.config.branding.logo.form.mobile_logo_dark.help_text"
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.help_text"
}}
@onSet={{fn this.handleUpload "mobile_logo_dark"}}
as |field|
@ -285,13 +297,13 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="manifest_icon"
@title={{i18n
"admin.config.branding.logo.form.manifest_icon.title"
"admin.config.logo_and_fonts.logo.form.manifest_icon.title"
}}
@description={{i18n
"admin.config.branding.logo.form.manifest_icon.description"
"admin.config.logo_and_fonts.logo.form.manifest_icon.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.manifest_icon.help_text"
"admin.config.logo_and_fonts.logo.form.manifest_icon.help_text"
}}
@onSet={{fn this.handleUpload "manifest_icon"}}
as |field|
@ -301,10 +313,10 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="manifest_screenshots"
@title={{i18n
"admin.config.branding.logo.form.manifest_screenshots.title"
"admin.config.logo_and_fonts.logo.form.manifest_screenshots.title"
}}
@description={{i18n
"admin.config.branding.logo.form.manifest_screenshots.description"
"admin.config.logo_and_fonts.logo.form.manifest_screenshots.description"
}}
@format="full"
as |field|
@ -322,13 +334,13 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="apple_touch_icon"
@title={{i18n
"admin.config.branding.logo.form.apple_touch_icon.title"
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.title"
}}
@description={{i18n
"admin.config.branding.logo.form.apple_touch_icon.description"
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.apple_touch_icon.help_text"
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.help_text"
}}
@onSet={{fn this.handleUpload "apple_touch_icon"}}
@placeholderUrl={{this.placeholders.apple_touch_icon}}
@ -339,7 +351,7 @@ export default class AdminBrandingLogoForm extends Component {
</:content>
</AdminConfigAreaCardSection>
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.branding.logo.form.email"}}
@heading={{i18n "admin.config.logo_and_fonts.logo.form.email"}}
class="admin-logo-form__email-section"
@collapsable={{true}}
@collapsed={{true}}
@ -348,13 +360,13 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="digest_logo"
@title={{i18n
"admin.config.branding.logo.form.digest_logo.title"
"admin.config.logo_and_fonts.logo.form.digest_logo.title"
}}
@description={{i18n
"admin.config.branding.logo.form.digest_logo.description"
"admin.config.logo_and_fonts.logo.form.digest_logo.description"
}}
@helpText={{i18n
"admin.config.branding.logo.form.digest_logo.help_text"
"admin.config.logo_and_fonts.logo.form.digest_logo.help_text"
}}
@onSet={{fn this.handleUpload "digest_logo"}}
@placeholderUrl={{this.placeholders.digest_logo}}
@ -368,7 +380,7 @@ export default class AdminBrandingLogoForm extends Component {
</:content>
</AdminConfigAreaCardSection>
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.branding.logo.form.social_media"}}
@heading={{i18n "admin.config.logo_and_fonts.logo.form.social_media"}}
class="admin-logo-form__social-media-section"
@collapsable={{true}}
@collapsed={{true}}
@ -377,10 +389,10 @@ export default class AdminBrandingLogoForm extends Component {
<form.Field
@name="opengraph_image"
@title={{i18n
"admin.config.branding.logo.form.opengraph_image.title"
"admin.config.logo_and_fonts.logo.form.opengraph_image.title"
}}
@description={{i18n
"admin.config.branding.logo.form.opengraph_image.description"
"admin.config.logo_and_fonts.logo.form.opengraph_image.description"
}}
@onSet={{fn this.handleUpload "opengraph_image"}}
@placeholderUrl={{this.placeholders.opengraph_image}}

View File

@ -1,25 +0,0 @@
// TODO (martin) Delete this once we have removed references from plugins.
import {
DangerActionListItem,
DangerButton,
DefaultActionListItem,
DefaultButton,
DPageActionButton,
DPageActionListItem,
PrimaryActionListItem,
PrimaryButton,
WrappedActionListItem,
WrappedButton,
} from "discourse/components/d-page-action-button";
export { DangerActionListItem as DangerActionListItem };
export { DangerButton as DangerButton };
export { DefaultActionListItem as DefaultActionListItem };
export { DefaultButton as DefaultButton };
export { DPageActionButton as DPageActionButton };
export { DPageActionListItem as DPageActionListItem };
export { PrimaryActionListItem as PrimaryActionListItem };
export { PrimaryButton as PrimaryButton };
export { WrappedActionListItem as WrappedActionListItem };
export { WrappedButton as WrappedButton };

View File

@ -2,12 +2,12 @@ import Component, { Textarea } from "@ember/component";
import { action } from "@ember/object";
import { equal } from "@ember/object/computed";
import { tagName } from "@ember-decorators/component";
import { eq } from "truth-helpers";
import TextField from "discourse/components/text-field";
import htmlSafe from "discourse/helpers/html-safe";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import eq from "truth-helpers/helpers/eq";
const CUSTOM_REASON_KEY = "custom";

View File

@ -4,6 +4,7 @@ import { Input } from "@ember/component";
import { fn, get, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { not } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import avatar from "discourse/helpers/avatar";
import formatDuration from "discourse/helpers/format-duration";
@ -11,7 +12,6 @@ import htmlSafe from "discourse/helpers/html-safe";
import number from "discourse/helpers/number";
import { ajax } from "discourse/lib/ajax";
import { i18n } from "discourse-i18n";
import not from "truth-helpers/helpers/not";
export default class AdminPenaltySimilarUsers extends Component {
@tracked isLoading;

View File

@ -15,7 +15,6 @@ import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import number from "discourse/helpers/number";
import { reportModeComponent } from "discourse/lib/admin-report-additional-modes";
import { REPORT_MODES } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { isTesting } from "discourse/lib/environment";
import { exportEntity } from "discourse/lib/export-csv";
@ -35,6 +34,7 @@ import ReportFilterBoolComponent from "admin/components/report-filters/bool";
import ReportFilterCategoryComponent from "admin/components/report-filters/category";
import ReportFilterGroupComponent from "admin/components/report-filters/group";
import ReportFilterListComponent from "admin/components/report-filters/list";
import { REPORT_MODES } from "admin/lib/constants";
import Report, { DAILY_LIMIT_DAYS, SCHEMA_VERSION } from "admin/models/report";
import DTooltip from "float-kit/components/d-tooltip";

View File

@ -9,12 +9,12 @@ import { TrackedObject } from "@ember-compat/tracked-built-ins";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { ADMIN_SEARCH_RESULT_TYPES } from "discourse/lib/constants";
import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import autoFocus from "discourse/modifiers/auto-focus";
import { i18n } from "discourse-i18n";
import AdminSearchFilters from "admin/components/admin-search-filters";
import { ADMIN_SEARCH_RESULT_TYPES } from "admin/lib/constants";
const ADMIN_SEARCH_FILTERS = "admin_search_filters";

View File

@ -0,0 +1,109 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import htmlSafe from "discourse/helpers/html-safe";
import { i18n } from "discourse-i18n";
export default class AdminSiteSettingsChangesBanner extends Component {
@service siteSettingChangeTracker;
@tracked isSaving = false;
_resizer = null;
@action
async save() {
this.isSaving = true;
try {
await this.siteSettingChangeTracker.save();
} finally {
this.isSaving = false;
}
}
@action
discard() {
this.siteSettingChangeTracker.discard();
}
@action
setupResizeObserver(element) {
const container = document.getElementById("main-container");
this._resizer = () => this.positionBanner(container, element);
this._resizer();
this._resizeObserver = window.addEventListener("resize", this._resizer);
}
@action
teardownResizeObserver() {
window.removeEventListener("resize", this._resizer);
}
positionBanner(container, element) {
if (container) {
const { width } = container.getBoundingClientRect();
element.style.width = `${width}px`;
}
}
get dirtyCount() {
return this.siteSettingChangeTracker.count;
}
get showBanner() {
return this.dirtyCount > 0;
}
get bannerLabel() {
return i18n("admin.site_settings.dirty_banner", {
count: this.dirtyCount,
});
}
get saveLabel() {
return i18n("admin.site_settings.save", {
count: this.dirtyCount,
});
}
get discardLabel() {
return i18n("admin.site_settings.discard", {
count: this.dirtyCount,
});
}
<template>
{{#if this.showBanner}}
<div
class="admin-site-settings__changes-banner"
{{didInsert this.setupResizeObserver}}
{{willDestroy this.teardownResizeObserver}}
>
<span>{{htmlSafe this.bannerLabel}}</span>
<div class="controls">
<DButton
@action={{this.discard}}
@disabled={{this.isSaving}}
class="btn-secondary btn-small"
>
{{this.discardLabel}}
</DButton>
<DButton
@action={{this.save}}
@isLoading={{this.isSaving}}
class="btn-primary btn-small"
>
{{this.saveLabel}}
</DButton>
</div>
</div>
{{/if}}
</template>
}

View File

@ -6,6 +6,7 @@ import { action, computed } from "@ember/object";
import { LinkTo } from "@ember/routing";
import { next } from "@ember/runloop";
import { service } from "@ember/service";
import { gt, lte } from "truth-helpers";
import AceEditor from "discourse/components/ace-editor";
import icon from "discourse/helpers/d-icon";
import htmlSafe from "discourse/helpers/html-safe";
@ -13,8 +14,6 @@ import { fmt } from "discourse/lib/computed";
import discourseComputed from "discourse/lib/decorators";
import { isDocumentRTL } from "discourse/lib/text-direction";
import { i18n } from "discourse-i18n";
import gt from "truth-helpers/helpers/gt";
import lte from "truth-helpers/helpers/lte";
const JS_DEFAULT_VALUE = `import { apiInitializer } from "discourse/lib/api";

View File

@ -4,8 +4,8 @@ import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import { USER_FIELD_FLAGS } from "discourse/lib/constants";
import { i18n } from "discourse-i18n";
import { USER_FIELD_FLAGS } from "admin/lib/constants";
import UserField from "admin/models/user-field";
import DMenu from "float-kit/components/d-menu";

View File

@ -5,13 +5,13 @@ import { action, set, setProperties } from "@ember/object";
import { schedule } from "@ember/runloop";
import { service } from "@ember/service";
import { classNameBindings } from "@ember-decorators/component";
import { not } from "truth-helpers";
import DButton from "discourse/components/d-button";
import EmojiPicker from "discourse/components/emoji-picker";
import EmojiPickerDetached from "discourse/components/emoji-picker/detached";
import discourseComputed from "discourse/lib/decorators";
import { emojiUrlFor } from "discourse/lib/text";
import { i18n } from "discourse-i18n";
import not from "truth-helpers/helpers/not";
@classNameBindings(":value-list", ":emoji-list")
export default class EmojiValueList extends Component {

View File

@ -1,57 +0,0 @@
import Component from "@ember/component";
import { classNames } from "@ember-decorators/component";
import UserFlagPercentage from "discourse/components/user-flag-percentage";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
import FlagUser from "admin/components/flag-user";
import dispositionIcon from "admin/helpers/disposition-icon";
import postActionTitle from "admin/helpers/post-action-title";
@classNames("flag-user-lists")
export default class FlagUserLists extends Component {
<template>
<div class="flagged-by">
<div class="user-list-title">
{{i18n "admin.flags.flagged_by"}}
</div>
<div class="flag-users">
{{#each this.flaggedPost.post_actions as |postAction|}}
<FlagUser @user={{postAction.user}} @date={{postAction.created_at}}>
<div class="flagger-flag-type">
{{postActionTitle
postAction.post_action_type_id
postAction.name_key
}}
</div>
<UserFlagPercentage
@agreed={{postAction.user.flags_agreed}}
@disagreed={{postAction.user.flags_disagreed}}
@ignored={{postAction.user.flags_ignored}}
/>
</FlagUser>
{{/each}}
</div>
</div>
{{#if this.showResolvedBy}}
<div class="flagged-post-resolved-by">
<div class="user-list-title">
{{i18n "admin.flags.resolved_by"}}
</div>
<div class="flag-users">
{{#each this.flaggedPost.post_actions as |postAction|}}
<FlagUser
@user={{postAction.disposed_by}}
@date={{postAction.disposed_at}}
>
{{dispositionIcon postAction.disposition}}
{{#if postAction.staff_took_action}}
{{icon "gavel" title="admin.flags.took_action"}}
{{/if}}
</FlagUser>
{{/each}}
</div>
</div>
{{/if}}
</template>
}

View File

@ -49,6 +49,10 @@ export default class FormTemplateForm extends Component {
type: "multiselect",
icon: "bullseye",
},
{
type: "tagchooser",
icon: "bullseye",
},
];
get disablePreviewButton() {

View File

@ -4,6 +4,7 @@ import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { isEmpty } from "@ember/utils";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import FutureDateInput from "discourse/components/future-date-input";
@ -13,7 +14,6 @@ import I18n, { i18n } from "discourse-i18n";
import AdminPenaltyPostAction from "admin/components/admin-penalty-post-action";
import AdminPenaltyReason from "admin/components/admin-penalty-reason";
import AdminPenaltySimilarUsers from "admin/components/admin-penalty-similar-users";
import eq from "truth-helpers/helpers/eq";
export default class PenalizeUser extends Component {
@service dialog;

View File

@ -0,0 +1,63 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { isEmpty } from "@ember/utils";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import TextField from "discourse/components/text-field";
import { i18n } from "discourse-i18n";
export default class ScrubRejectedUserModal extends Component {
@tracked isScrubbing = false;
@tracked scrubReason = "";
@action
async confirmScrub() {
this.isScrubbing = true;
await this.args.model.confirmScrub(this.scrubReason);
this.args.closeModal();
}
get scrubButtonDisabled() {
return isEmpty(this.scrubReason);
}
<template>
<DModal
@bodyClass="scrub-rejected-user"
class="admin-scrub-rejected-user-modal"
@title={{i18n "review.user.scrub_record.confirm_title"}}
@closeModal={{if this.isScrubbing null @closeModal}}
>
<:body>
<p>{{i18n "review.user.scrub_record.confirm_body"}}</p>
<label class="scrub-reason-title" for="scrub-reason">{{i18n
"review.user.scrub_record.reason_title"
}}</label>
<TextField
class="scrub-reason"
id="scrub-reason"
@placeholderKey="review.user.scrub_record.reason_placeholder"
{{on "input" (action (mut this.scrubReason) value="target.value")}}
/>
</:body>
<:footer>
<DButton
class="btn btn-danger"
@action={{this.confirmScrub}}
@isLoading={{this.isScrubbing}}
@disabled={{this.scrubButtonDisabled}}
@label="review.user.scrub_record.confirm_button"
/>
<DButton
class="btn btn-default"
@action={{@closeModal}}
@disabled={{this.isScrubbing}}
@label="review.user.scrub_record.cancel_button"
/>
</:footer>
</DModal>
</template>
}

View File

@ -6,11 +6,11 @@ import { action } from "@ember/object";
import { empty } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
import { classNameBindings } from "@ember-decorators/component";
import { gt } from "truth-helpers";
import DButton from "discourse/components/d-button";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import gt from "truth-helpers/helpers/gt";
@classNameBindings(":simple-list", ":value-list")
export default class SimpleList extends Component {

View File

@ -1,11 +1,10 @@
import { cached, tracked } from "@glimmer/tracking";
import { tracked } from "@glimmer/tracking";
import Component from "@ember/component";
import { hash } from "@ember/helper";
import { dependentKeyCompat } from "@ember/object/compat";
import { readOnly } from "@ember/object/computed";
import { getOwner } from "@ember/owner";
import { LinkTo } from "@ember/routing";
import BufferedProxy from "ember-buffered-proxy/proxy";
import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
@ -28,18 +27,15 @@ export default class SiteSettingComponent extends Component.extend(
);
}
@cached
@dependentKeyCompat
get buffered() {
return BufferedProxy.create({
content: this.setting,
});
return this.setting.buffered;
}
_save() {
const setting = this.buffered;
return SiteSetting.update(setting.get("setting"), setting.get("value"), {
updateExistingUsers: this.updateExistingUsers,
updateExistingUsers: this.setting.updateExistingUsers,
});
}
@ -90,7 +86,7 @@ export default class SiteSettingComponent extends Component.extend(
@changeValueCallback={{this.changeValueCallback}}
@setValidationMessage={{this.setValidationMessage}}
/>
<SettingValidationMessage @message={{this.validationMessage}} />
<SettingValidationMessage @message={{this.setting.validationMessage}} />
{{#if this.displayDescription}}
<Description @description={{this.setting.description}} />
{{/if}}
@ -102,13 +98,14 @@ export default class SiteSettingComponent extends Component.extend(
<DButton
@action={{this.update}}
@icon="check"
@disabled={{this.disableSaveButton}}
@isLoading={{this.disableControls}}
@ariaLabel="admin.settings.save"
class="ok setting-controls__ok"
/>
<DButton
@action={{this.cancel}}
@icon="xmark"
@isLoading={{this.disableControls}}
@ariaLabel="admin.settings.cancel"
class="cancel setting-controls__cancel"
/>

View File

@ -1,7 +1,10 @@
import Component from "@glimmer/component";
import { htmlSafe } from "@ember/template";
import { eq } from "truth-helpers";
export default class ThemesGridPlaceholder extends Component {
randomVariant = Math.floor(Math.random() * 4);
get themeColors() {
if (this.args.theme.color_scheme) {
return {
@ -28,59 +31,305 @@ export default class ThemesGridPlaceholder extends Component {
}
}
get safeThemeColors() {
let colors = this.themeColors;
return {
primary: htmlSafe(colors.primary),
secondary: htmlSafe(colors.secondary),
tertiary: htmlSafe(colors.tertiary),
quaternary: htmlSafe(colors.quaternary),
highlight: htmlSafe(colors.highlight),
danger: htmlSafe(colors.danger),
success: htmlSafe(colors.success),
love: htmlSafe(colors.love),
};
}
get gradientId() {
return `bgGradient-${this.args.theme.id}-${this.randomVariant}`;
}
<template>
<svg viewBox="0 0 636 347" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect
width="635.115"
height="347"
fill={{htmlSafe this.themeColors.secondary}}
/>
<path
d="M54.9766 79.1039C55.9198 76.0065 59.8623 75.0916 62.0732 77.4571L121.448 140.986C123.659 143.351 122.48 147.223 119.326 147.955L34.621 167.611C31.467 168.343 28.7034 165.386 29.6466 162.288L54.9766 79.1039Z"
fill={{htmlSafe this.themeColors.primary}}
/>
<path
d="M398.487 211.02C400.651 208.611 404.611 209.448 405.615 212.527L432.579 295.196C433.584 298.274 430.879 301.285 427.711 300.615L342.635 282.633C339.467 281.963 338.212 278.115 340.376 275.707L398.487 211.02Z"
fill={{htmlSafe this.themeColors.tertiary}}
/>
<circle cx="109.357" cy="262.879" r="44.1636" fill="#D1F0FF" />
<circle cx="365.927" cy="103.048" r="44.1636" fill="#E45735" />
<rect
x="166.139"
y="68.751"
width="81.9226"
height="81.9226"
rx="4.20606"
transform="rotate(-15.9297 166.139 68.751)"
fill={{htmlSafe this.themeColors.danger}}
/>
<rect
x="500.521"
y="100.296"
width="81.9226"
height="81.9226"
rx="4.20606"
transform="rotate(-15.9297 500.521 100.296)"
fill={{htmlSafe this.themeColors.success}}
/>
<rect
x="481.857"
y="222.921"
width="121.976"
height="54.6788"
rx="4.20606"
transform="rotate(9.12857 481.857 222.921)"
fill={{htmlSafe this.themeColors.love}}
/>
<rect
x="176.654"
y="240.608"
width="121.976"
height="54.6788"
rx="4.20606"
transform="rotate(-22.7296 176.654 240.608)"
fill={{htmlSafe this.themeColors.primary}}
/>
</svg>
{{#if (eq this.randomVariant 0)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 800 200"
preserveAspectRatio="xMidYMid slice"
>
<rect fill={{this.safeThemeColors.tertiary}} width="800" height="200" />
<g transform="translate(280, -100) scale(0.4)">
<g
fill="none"
stroke={{this.safeThemeColors.secondary}}
stroke-width="1"
>
<path
d="M769 229L1037 260.9M927 880L731 737 520 660 309 538 40 599 295 764 126.5 879.5 40 599-197 493 102 382-31 229 126.5 79.5-69-63"
/>
<path
d="M-31 229L237 261 390 382 603 493 308.5 537.5 101.5 381.5M370 905L295 764"
/>
<path
d="M520 660L578 842 731 737 840 599 603 493 520 660 295 764 309 538 390 382 539 269 769 229 577.5 41.5 370 105 295 -36 126.5 79.5 237 261 102 382 40 599 -69 737 127 880"
/>
<path
d="M520-140L578.5 42.5 731-63M603 493L539 269 237 261 370 105M902 382L539 269M390 382L102 382"
/>
<path
d="M-222 42L126.5 79.5 370 105 539 269 577.5 41.5 927 80 769 229 902 382 603 493 731 737M295-36L577.5 41.5M578 842L295 764M40-201L127 80M102 382L-261 269"
/>
</g>
<g fill={{this.safeThemeColors.tertiary}}>
<circle cx="769" cy="229" r="5" />
<circle cx="539" cy="269" r="5" />
<circle cx="603" cy="493" r="5" />
<circle cx="731" cy="737" r="5" />
<circle cx="520" cy="660" r="5" />
<circle cx="309" cy="538" r="5" />
<circle cx="295" cy="764" r="5" />
<circle cx="40" cy="599" r="5" />
<circle cx="102" cy="382" r="5" />
<circle cx="127" cy="80" r="5" />
<circle cx="370" cy="105" r="5" />
<circle cx="578" cy="42" r="5" />
<circle cx="237" cy="261" r="5" />
<circle cx="390" cy="382" r="5" />
</g>
</g>
</svg>
{{else if (eq this.randomVariant 1)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 800 200"
preserveAspectRatio="xMidYMid slice"
>
<rect fill={{this.safeThemeColors.tertiary}} width="800" height="200" />
<defs>
<radialGradient
id="{{this.gradientId}}-a"
cx="0"
cy="347"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.quaternary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-b"
cx="636"
cy="347"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-c"
cx="600"
cy="0"
r="600"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.highlight}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-d"
cx="600"
cy="347"
r="600"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.quaternary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-e"
cx="0"
cy="0"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-f"
cx="636"
cy="0"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
</defs>
<rect fill="url(#{{this.gradientId}}-a)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-b)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-c)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-d)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-e)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-f)" width="800" height="200" />
</svg>
{{else if (eq this.randomVariant 2)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 1600 800"
preserveAspectRatio="xMidYMid slice"
>
<rect
fill={{this.safeThemeColors.tertiary}}
width="1600"
height="800"
/>
<g fill-opacity=".4" transform="scale(1.2)">
<path
fill={{this.safeThemeColors.tertiary}}
d="M486 705.8c-109.3-21.8-223.4-32.2-335.3-19.4C99.5 692.1 49 703 0 719.8V800h843.8c-115.9-33.2-230.8-68.1-347.6-92.2C492.8 707.1 489.4 706.5 486 705.8z"
/>
<path
fill={{this.safeThemeColors.tertiary}}
d="M1600 0H0v719.8c49-16.8 99.5-27.8 150.7-33.5c111.9-12.7 226-2.4 335.3 19.4c3.4 0.7 6.8 1.4 10.2 2c116.8 24 231.7 59 347.6 92.2H1600V0z"
/>
<path
fill={{this.safeThemeColors.quaternary}}
d="M478.4 581c3.2 0.8 6.4 1.7 9.5 2.5c196.2 52.5 388.7 133.5 593.5 176.6c174.2 36.6 349.5 29.2 518.6-10.2V0H0v574.9c52.3-17.6 106.5-27.7 161.1-30.9C268.4 537.4 375.7 554.2 478.4 581z"
/>
<path
fill={{this.safeThemeColors.quaternary}}
d="M0 0v429.4c55.6-18.4 113.5-27.3 171.4-27.7c102.8-0.8 203.2 22.7 299.3 54.5c3 1 5.9 2 8.9 3c183.6 62 365.7 146.1 562.4 192.1c186.7 43.7 376.3 34.4 557.9-12.6V0H0z"
/>
<path
fill={{this.safeThemeColors.highlight}}
d="M181.8 259.4c98.2 6 191.9 35.2 281.3 72.1c2.8 1.1 5.5 2.3 8.3 3.4c171 71.6 342.7 158.5 531.3 207.7c198.8 51.8 403.4 40.8 597.3-14.8V0H0v283.2C59 263.6 120.6 255.7 181.8 259.4z"
/>
<path
fill={{this.safeThemeColors.highlight}}
d="M1600 0H0v136.3c62.3-20.9 127.7-27.5 192.2-19.2c93.6 12.1 180.5 47.7 263.3 89.6c2.6 1.3 5.1 2.6 7.7 3.9c158.4 81.1 319.7 170.9 500.3 223.2c210.5 61 430.8 49 636.6-16.6V0z"
/>
<path
fill={{this.safeThemeColors.danger}}
d="M454.9 86.3C600.7 177 751.6 269.3 924.1 325c208.6 67.4 431.3 60.8 637.9-5.3c12.8-4.1 25.4-8.4 38.1-12.9V0H288.1c56 21.3 108.7 50.6 159.7 82C450.2 83.4 452.5 84.9 454.9 86.3z"
/>
<path
fill={{this.safeThemeColors.danger}}
d="M1600 0H498c118.1 85.8 243.5 164.5 386.8 216.2c191.8 69.2 400 74.7 595 21.1c40.8-11.2 81.1-25.2 120.3-41.7V0z"
/>
<path
fill={{this.safeThemeColors.success}}
d="M1397.5 154.8c47.2-10.6 93.6-25.3 138.6-43.8c21.7-8.9 43-18.8 63.9-29.5V0H643.4c62.9 41.7 129.7 78.2 202.1 107.4C1020.4 178.1 1214.2 196.1 1397.5 154.8z"
/>
<path
fill={{this.safeThemeColors.success}}
d="M1315.3 72.4c75.3-12.6 148.9-37.1 216.8-72.4h-723C966.8 71 1144.7 101 1315.3 72.4z"
/>
</g>
</svg>
{{else if (eq this.randomVariant 3)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 2 1"
preserveAspectRatio="none"
>
<rect fill={{this.safeThemeColors.tertiary}} width="2" height="1" />
<defs>
<linearGradient
id="{{this.gradientId}}-a"
gradientUnits="userSpaceOnUse"
x1="0"
x2="0"
y1="0"
y2="1"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop offset="1" stop-color={{this.safeThemeColors.quaternary}} />
</linearGradient>
<linearGradient
id="{{this.gradientId}}-b"
gradientUnits="userSpaceOnUse"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="0"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="1"
/>
</linearGradient>
<linearGradient
id="{{this.gradientId}}-c"
gradientUnits="userSpaceOnUse"
x1="0"
y1="0"
x2="2"
y2="2"
>
<stop
offset="0"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="1"
/>
</linearGradient>
</defs>
<rect
x="0"
y="0"
fill="url(#{{this.gradientId}}-a)"
width="2"
height="1"
/>
<g fill-opacity="0.5">
<polygon fill="url(#{{this.gradientId}}-b)" points="0 1 0 0 2 0" />
<polygon fill="url(#{{this.gradientId}}-c)" points="2 1 2 0 0 0" />
</g>
</svg>
{{/if}}
</template>
}

View File

@ -5,6 +5,7 @@ import { action } from "@ember/object";
import { equal, gt, gte } from "@ember/object/computed";
import { service } from "@ember/service";
import { classNames } from "@ember-decorators/component";
import { and, eq, not, or } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DeleteThemesConfirm from "discourse/components/modal/delete-themes-confirm";
import concatClass from "discourse/helpers/concat-class";
@ -14,10 +15,6 @@ import { i18n } from "discourse-i18n";
import ThemesListItem from "admin/components/themes-list-item";
import { COMPONENTS, THEMES } from "admin/models/theme";
import ComboBox from "select-kit/components/combo-box";
import and from "truth-helpers/helpers/and";
import eq from "truth-helpers/helpers/eq";
import not from "truth-helpers/helpers/not";
import or from "truth-helpers/helpers/or";
const ALL_FILTER = "all";
const ACTIVE_FILTER = "active";

View File

@ -8,6 +8,7 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
import { service } from "@ember/service";
import { not } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import CountI18n from "discourse/components/count-i18n";
import DButton from "discourse/components/d-button";
@ -18,7 +19,6 @@ import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import WebhookEvent from "admin/components/webhook-event";
import ComboBox from "select-kit/components/combo-box";
import not from "truth-helpers/helpers/not";
export default class WebhookEvents extends Component {
@service messageBus;

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigAnalyticsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminApiKeysSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +1,10 @@
import Controller from "@ember/controller";
import { service } from "@ember/service";
export default class AdminApiKeysController extends Controller {}
export default class AdminApiKeysController extends Controller {
@service router;
get hideTabs() {
return ["adminApiKeys.show"].includes(this.router.currentRouteName);
}
}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentCategoriesAndTagsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentPostsAndTopicsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentSharingController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentPostsAndTopicsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigFontsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigInterfaceSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigSiteAdminSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigUserDefaultsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,5 +1,5 @@
import Controller from "@ember/controller";
import EmberObject, { action } from "@ember/object";
import { action } from "@ember/object";
import {
empty,
filterBy,
@ -16,6 +16,7 @@ import discourseComputed from "discourse/lib/decorators";
import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n";
import ThemeSettingsEditor from "admin/components/theme-settings-editor";
import SiteSetting from "admin/models/site-setting";
import { COMPONENTS, THEMES } from "admin/models/theme";
import ThemeSettings from "admin/models/theme-settings";
import ThemeUploadAddModal from "../components/theme-upload-add";
@ -91,11 +92,11 @@ export default class AdminCustomizeThemesShowController extends Controller {
@discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForComponent() {
return EmberObject.create({
return SiteSetting.create({
list_type: "compact",
type: "list",
preview: null,
anyValue: false,
allow_any: false,
setting: "parent_theme_ids",
label: i18n("admin.customize.theme.component_on_themes"),
choices: this.availableThemesNames,
@ -109,11 +110,11 @@ export default class AdminCustomizeThemesShowController extends Controller {
@discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForTheme() {
return EmberObject.create({
return SiteSetting.create({
list_type: "compact",
type: "list",
preview: null,
anyValue: false,
allow_any: false,
setting: "child_theme_ids",
label: i18n("admin.customize.theme.included_components"),
choices: this.availableComponentsNames,
@ -311,10 +312,12 @@ export default class AdminCustomizeThemesShowController extends Controller {
@action
updateLocale(value) {
this.set("model.loadingTranslations", true);
this.set("model.locale", value);
ajax(this.getTranslationsUrl).then(({ translations }) =>
this.set("model.translations", translations)
);
ajax(this.getTranslationsUrl).then(({ translations }) => {
this.set("model.translations", translations);
this.set("model.loadingTranslations", false);
});
}
@action

View File

@ -2,11 +2,11 @@ import { inject as controller } from "@ember/controller";
import { computed } from "@ember/object";
import { service } from "@ember/service";
import { setting } from "discourse/lib/computed";
import { REPORT_MODES } from "discourse/lib/constants";
import discourseComputed from "discourse/lib/decorators";
import getURL from "discourse/lib/get-url";
import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n";
import { REPORT_MODES } from "admin/lib/constants";
import AdminDashboard from "admin/models/admin-dashboard";
import Report from "admin/models/report";
import AdminDashboardTabController from "./admin-dashboard-tab";

View File

@ -4,7 +4,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailBouncedController extends AdminEmailLogsController {
export default class AdminEmailLogsBouncedController extends AdminEmailLogsController {
@action
handleShowIncomingEmail(id, event) {
event?.preventDefault();

View File

@ -5,7 +5,7 @@ import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import IncomingEmail from "admin/models/incoming-email";
export default class AdminEmailReceivedController extends AdminEmailLogsController {
export default class AdminEmailLogsReceivedController extends AdminEmailLogsController {
@observes("filter.{status,from,to,subject}")
filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);

View File

@ -5,7 +5,7 @@ import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import IncomingEmail from "admin/models/incoming-email";
export default class AdminEmailRejectedController extends AdminEmailLogsController {
export default class AdminEmailLogsRejectedController extends AdminEmailLogsController {
@observes("filter.{status,from,to,subject,error}")
filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);

View File

@ -3,7 +3,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailSentController extends AdminEmailLogsController {
export default class AdminEmailLogsSentController extends AdminEmailLogsController {
ccAddressDisplayThreshold = 2;
sortWithAddressFilter = (addresses) => {
if (!Array.isArray(addresses) || addresses.length === 0) {

View File

@ -3,7 +3,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailSkippedController extends AdminEmailLogsController {
export default class AdminEmailLogsSkippedController extends AdminEmailLogsController {
@observes("filter.{status,user,address,type}")
filterEmailLogs() {
discourseDebounce(this, this.loadLogs, INPUT_DELAY);

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminEmailSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminReportsDashboardSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,10 @@
import Controller from "@ember/controller";
import { service } from "@ember/service";
export default class AdminReportsController extends Controller {
@service router;
get hideTabs() {
return ["adminReports.show"].includes(this.router.currentRouteName);
}
}

View File

@ -1,5 +1,11 @@
import Controller from "@ember/controller";
import { PLATFORM_KEY_MODIFIER } from "discourse/lib/keyboard-shortcuts";
import { translateModKey } from "discourse/lib/utilities";
export default class AdminSearchIndexController extends Controller {
queryParams = ["filter"];
get shortcutHTML() {
return `<kbd>${translateModKey(PLATFORM_KEY_MODIFIER)}</kbd> + <kbd>/</kbd>`;
}
}

View File

@ -24,7 +24,7 @@ export default class AdminSiteTextEdit extends Controller {
});
}
@discourseComputed("buffered.value")
@discourseComputed("buffered.value", "siteText.value")
saveDisabled(value) {
return this.siteText.value === value;
}

View File

@ -133,7 +133,7 @@ export default class AdminUserIndexController extends Controller {
@computed("model.id", "currentUser.id")
get canCheckEmails() {
return new CanCheckEmailsHelper(
this.model,
this.model.id,
this.canModeratorsViewEmails,
this.currentUser
).canCheckEmails;
@ -142,7 +142,7 @@ export default class AdminUserIndexController extends Controller {
@computed("model.id", "currentUser.id")
get canAdminCheckEmails() {
return new CanCheckEmailsHelper(
this.model,
this.model.id,
this.canModeratorsViewEmails,
this.currentUser
).canAdminCheckEmails;

View File

@ -66,7 +66,7 @@ export default class AdminUsersListShowController extends Controller {
@computed("model.id", "currentUser.id")
get canCheckEmails() {
return new CanCheckEmailsHelper(
this.model,
this.model?.id,
this.canModeratorsViewEmails,
this.currentUser
).canCheckEmails;
@ -75,7 +75,7 @@ export default class AdminUsersListShowController extends Controller {
@computed("model.id", "currentUser.id")
get canAdminCheckEmails() {
return new CanCheckEmailsHelper(
this.model,
this.model?.id,
this.canModeratorsViewEmails,
this.currentUser
).canAdminCheckEmails;

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminUsersSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,9 +1,6 @@
import { htmlSafe } from "@ember/template";
import { registerRawHelper } from "discourse/lib/helpers";
import { renderIcon } from "discourse/lib/icon-library";
registerRawHelper("check-icon", checkIcon);
export default function checkIcon(value) {
let icon = value ? "check" : "xmark";
return htmlSafe(renderIcon("string", icon));

View File

@ -1,28 +0,0 @@
import Helper from "@ember/component/helper";
import { htmlSafe } from "@ember/template";
import { iconHTML } from "discourse/lib/icon-library";
export default class DispositionIcon extends Helper {
compute([disposition]) {
if (!disposition) {
return null;
}
let icon;
let title = "admin.flags.dispositions." + disposition;
switch (disposition) {
case "deferred": {
icon = "up-right-from-square";
break;
}
case "agreed": {
icon = "thumbs-o-up";
break;
}
case "disagreed": {
icon = "thumbs-o-down";
break;
}
}
return htmlSafe(iconHTML(icon, { title }));
}
}

View File

@ -1,17 +0,0 @@
import Helper from "@ember/component/helper";
import { i18n } from "discourse-i18n";
function postActionTitle([id, nameKey]) {
let title = i18n(`admin.flags.short_names.${nameKey}`, {
defaultValue: null,
});
// TODO: We can remove this once other translations have been updated
if (!title) {
return i18n(`admin.flags.summary.action_type_${id}`, { count: 1 });
}
return title;
}
export default Helper.helper(postActionTitle);

View File

@ -1,6 +0,0 @@
import { htmlHelper } from "discourse/lib/helpers";
import { escapeExpression } from "discourse/lib/utilities";
export default htmlHelper((str) =>
escapeExpression(str).replace(/\n/g, "<br>")
);

View File

@ -1,17 +0,0 @@
import { registerRawHelper } from "discourse/lib/helpers";
registerRawHelper("value-at-tl", valueAtTl);
export default function valueAtTl(data, params = {}) {
let tl = parseInt(params.level, 10);
if (data) {
let item = data.find(function (d) {
return parseInt(d.x, 10) === tl;
});
if (item) {
return item.y;
} else {
return 0;
}
}
}

View File

@ -0,0 +1,124 @@
// DO NOT EDIT THIS FILE!!!
// Update it by running `rake javascript:update_constants`
export const ADMIN_SEARCH_RESULT_TYPES = [
"page",
"setting",
"theme",
"component",
"report",
];
export const SITE_SETTING_REQUIRES_CONFIRMATION_TYPES = {
simple: "simple",
user_option: "user_option",
};
export const API_KEY_SCOPE_MODES = ["global", "read_only", "granular"];
export const SYSTEM_FLAG_IDS = {
like: 2,
notify_user: 6,
off_topic: 3,
inappropriate: 4,
spam: 8,
illegal: 10,
notify_moderators: 7,
};
export const REPORT_MODES = {
table: "table",
chart: "chart",
stacked_chart: "stacked_chart",
stacked_line_chart: "stacked_line_chart",
radar: "radar",
counters: "counters",
inline_table: "inline_table",
storage_stats: "storage_stats",
};
export const USER_FIELD_FLAGS = [
"editable",
"show_on_profile",
"show_on_user_card",
"searchable",
];
export const DEFAULT_USER_PREFERENCES = [
"default_email_digest_frequency",
"default_include_tl0_in_digests",
"default_email_level",
"default_email_messages_level",
"default_email_mailing_list_mode",
"default_email_mailing_list_mode_frequency",
"default_email_previous_replies",
"default_email_in_reply_to",
"default_hide_profile",
"default_hide_presence",
"default_other_new_topic_duration_minutes",
"default_other_auto_track_topics_after_msecs",
"default_other_notification_level_when_replying",
"default_other_external_links_in_new_tab",
"default_other_enable_quoting",
"default_other_enable_smart_lists",
"default_other_enable_defer",
"default_other_dynamic_favicon",
"default_other_like_notification_frequency",
"default_other_skip_new_user_tips",
"default_topics_automatic_unpin",
"default_categories_watching",
"default_categories_tracking",
"default_categories_muted",
"default_categories_watching_first_post",
"default_categories_normal",
"default_tags_watching",
"default_tags_tracking",
"default_tags_muted",
"default_tags_watching_first_post",
"default_text_size",
"default_title_count_mode",
"default_navigation_menu_categories",
"default_navigation_menu_tags",
"default_sidebar_link_to_filtered_list",
"default_sidebar_show_count_of_new_items",
];
export const MAIN_FONTS = [
{ key: "open_sans", name: "Open Sans" },
{ key: "roboto", name: "Roboto" },
{ key: "lato", name: "Lato" },
{ key: "inter", name: "Inter" },
{ key: "montserrat", name: "Montserrat" },
{ key: "poppins", name: "Poppins" },
{ key: "merriweather", name: "Merriweather" },
{ key: "mukta", name: "Mukta" },
{ key: "helvetica", name: "Helvetica" },
];
export const MORE_FONTS = [
{ key: "arial", name: "Arial" },
{ key: "system", name: "System" },
{ key: "oxanium", name: "Oxanium" },
{ key: "noto_sans_jp", name: "NotoSansJP" },
{ key: "roboto_condensed", name: "RobotoCondensed" },
{ key: "source_sans_pro", name: "SourceSansPro" },
{ key: "oswald", name: "Oswald" },
{ key: "raleway", name: "Raleway" },
{ key: "roboto_mono", name: "RobotoMono" },
{ key: "noto_sans", name: "NotoSans" },
{ key: "roboto_slab", name: "RobotoSlab" },
{ key: "ubuntu", name: "Ubuntu" },
{ key: "pt_sans", name: "PTSans" },
{ key: "playfair_display", name: "PlayfairDisplay" },
{ key: "nunito", name: "Nunito" },
{ key: "lora", name: "Lora" },
{ key: "jet_brains_mono", name: "JetBrains Mono" },
];
export const DEFAULT_TEXT_SIZES = [
"smallest",
"smaller",
"normal",
"larger",
"largest",
];

View File

@ -73,4 +73,16 @@ export const templateFormFields = [
validations:
# ${i18n("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "tagchooser",
structure: `- type: tag-chooser
id: ${i18n("admin.form_templates.field_placeholders.id")}
tag_group: ""
attributes:
none_label: "${i18n("admin.form_templates.field_placeholders.none_label")}"
label: "${i18n("admin.form_templates.field_placeholders.label")}"
multiple: true
validations:
# ${i18n("admin.form_templates.field_placeholders.validations")}`,
},
];

View File

@ -5,16 +5,12 @@ import Mixin from "@ember/object/mixin";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import { isNone } from "@ember/utils";
import { Promise } from "rsvp";
import JsonSchemaEditorModal from "discourse/components/modal/json-schema-editor";
import { ajax } from "discourse/lib/ajax";
import { fmt, propertyNotEqual } from "discourse/lib/computed";
import { SITE_SETTING_REQUIRES_CONFIRMATION_TYPES } from "discourse/lib/constants";
import { deepEqual } from "discourse/lib/object";
import { humanizedSettingName } from "discourse/lib/site-settings-utils";
import { splitString } from "discourse/lib/utilities";
import { i18n } from "discourse-i18n";
import SiteSettingDefaultCategoriesModal from "../components/modal/site-setting-default-categories";
const CUSTOM_TYPES = [
"bool",
@ -42,55 +38,14 @@ const CUSTOM_TYPES = [
"font_list",
];
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
const DEFAULT_USER_PREFERENCES = [
"default_email_digest_frequency",
"default_include_tl0_in_digests",
"default_email_level",
"default_email_messages_level",
"default_email_mailing_list_mode",
"default_email_mailing_list_mode_frequency",
"default_email_previous_replies",
"default_email_in_reply_to",
"default_hide_profile",
"default_hide_presence",
"default_other_new_topic_duration_minutes",
"default_other_auto_track_topics_after_msecs",
"default_other_notification_level_when_replying",
"default_other_external_links_in_new_tab",
"default_other_enable_quoting",
"default_other_enable_smart_lists",
"default_other_enable_defer",
"default_other_dynamic_favicon",
"default_other_like_notification_frequency",
"default_other_skip_new_user_tips",
"default_topics_automatic_unpin",
"default_categories_watching",
"default_categories_tracking",
"default_categories_muted",
"default_categories_watching_first_post",
"default_categories_normal",
"default_tags_watching",
"default_tags_tracking",
"default_tags_muted",
"default_tags_watching_first_post",
"default_text_size",
"default_title_count_mode",
"default_navigation_menu_categories",
"default_navigation_menu_tags",
"default_sidebar_link_to_filtered_list",
"default_sidebar_show_count_of_new_items",
];
export default Mixin.create({
modal: service(),
router: service(),
site: service(),
dialog: service(),
siteSettingChangeTracker: service(),
attributeBindings: ["setting.setting:data-setting"],
classNameBindings: [":row", ":setting", "overridden", "typeClass"],
validationMessage: null,
content: alias("setting"),
isSecret: oneWay("setting.secret"),
@ -112,7 +67,7 @@ export default Mixin.create({
}),
dirty: computed("buffered.value", "setting.value", function () {
let bufferVal = this.get("buffered.value");
let bufferVal = this.buffered.get("value");
let settingVal = this.setting?.value;
if (isNone(bufferVal)) {
@ -123,12 +78,20 @@ export default Mixin.create({
settingVal = "";
}
return !deepEqual(bufferVal, settingVal);
const dirty = !deepEqual(bufferVal, settingVal);
if (dirty) {
this.siteSettingChangeTracker.add(this.setting);
} else {
this.siteSettingChangeTracker.remove(this.setting);
}
return dirty;
}),
preview: computed("setting", "buffered.value", function () {
const setting = this.setting;
const value = this.get("buffered.value");
const value = this.buffered.get("value");
const preview = setting.preview;
if (preview) {
const escapedValue = preview.replace(/\{\{value\}\}/g, value);
@ -165,7 +128,7 @@ export default Mixin.create({
}),
bufferedValues: computed("buffered.value", function () {
const value = this.get("buffered.value");
const value = this.buffered.get("value");
return splitString(value, "|");
}),
@ -216,102 +179,41 @@ export default Mixin.create({
}
}),
disableSaveButton: computed("validationMessage", function () {
return !!this.validationMessage;
disableControls: computed("setting.isSaving", function () {
return !!this.setting.isSaving;
}),
confirmChanges(settingKey) {
return new Promise((resolve) => {
// Fallback is needed in case the setting does not have a custom confirmation
// prompt/confirm defined.
this.dialog.alert({
message: i18n(
`admin.site_settings.requires_confirmation_messages.${settingKey}.prompt`,
{
translatedFallback: i18n(
"admin.site_settings.requires_confirmation_messages.default.prompt"
),
}
),
buttons: [
{
label: i18n(
`admin.site_settings.requires_confirmation_messages.${settingKey}.confirm`,
{
translatedFallback: i18n(
"admin.site_settings.requires_confirmation_messages.default.confirm"
),
}
),
class: "btn-primary",
action: () => resolve(true),
},
{
label: i18n("no_value"),
class: "btn-default",
action: () => resolve(false),
},
],
});
});
},
update: action(async function () {
const key = this.buffered.get("setting");
if (this.setting.requiresConfirmation) {
const confirm = await this.siteSettingChangeTracker.confirmChanges(
this.setting
);
let confirm = true;
if (
this.buffered.get("requires_confirmation") ===
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES.simple
) {
confirm = await this.confirmChanges(key);
if (!confirm) {
return;
}
}
if (!confirm) {
this.cancel();
return;
if (this.setting.affectsExistingUsers) {
await this.siteSettingChangeTracker.configureBackfill(this.setting);
}
if (!DEFAULT_USER_PREFERENCES.includes(key)) {
await this.save();
return;
}
const data = {
[key]: this.buffered.get("value"),
};
const result = await ajax(`/admin/site_settings/${key}/user_count.json`, {
type: "PUT",
data,
});
const count = result.user_count;
if (count > 0) {
await this.modal.show(SiteSettingDefaultCategoriesModal, {
model: {
siteSetting: { count, key: key.replaceAll("_", " ") },
setUpdateExistingUsers: this.setUpdateExistingUsers,
},
});
this.save();
} else {
await this.save();
}
}),
setUpdateExistingUsers: action(function (value) {
this.updateExistingUsers = value;
await this.save();
}),
save: action(async function () {
try {
this.setting.isSaving = true;
await this._save();
this.set("validationMessage", null);
this.setting.validationMessage = null;
this.buffered.applyChanges();
if (AUTO_REFRESH_ON_SAVE.includes(this.setting.setting)) {
this.afterSave();
if (this.setting.requiresReload) {
this.siteSettingChangeTracker.refreshPage({
[this.setting.setting]: this.setting.value,
});
}
} catch (e) {
const json = e.jqXHR?.responseJSON;
@ -322,29 +224,31 @@ export default Mixin.create({
errorString = htmlSafe(errorString);
}
this.set("validationMessage", errorString);
this.setting.validationMessage = errorString;
} else {
this.set("validationMessage", i18n("generic_error"));
this.setting.validationMessage = i18n("generic_error");
}
} finally {
this.setting.isSaving = false;
}
}),
changeValueCallback: action(function (value) {
this.set("buffered.value", value);
this.buffered.set("value", value);
}),
setValidationMessage: action(function (message) {
this.set("validationMessage", message);
this.setting.validationMessage = message;
}),
cancel: action(function () {
this.buffered.discardChanges();
this.set("validationMessage", null);
this.setting.validationMessage = null;
}),
resetDefault: action(function () {
this.set("buffered.value", this.setting.default);
this.set("validationMessage", null);
this.buffered.set("value", this.setting.default);
this.setting.validationMessage = null;
}),
toggleSecret: action(function () {
@ -352,11 +256,11 @@ export default Mixin.create({
}),
setDefaultValues: action(function () {
this.set(
"buffered.value",
this.buffered.set(
"value",
this.bufferedValues.concat(this.defaultValues).uniq().join("|")
);
this.set("validationMessage", null);
this.setting.validationMessage = null;
return false;
}),

View File

@ -25,7 +25,7 @@ export default class EmailLog extends EmberObject {
const status = filter.status || "sent";
delete filter.status;
return ajax(`/admin/email/${status}.json?offset=${offset}`, {
return ajax(`/admin/email-logs/${status}.json?offset=${offset}`, {
data: filter,
}).then((logs) => logs.map((log) => EmailLog.create(log)));
}

View File

@ -3,7 +3,7 @@ import { ajax } from "discourse/lib/ajax";
export default class EmailSettings extends EmberObject {
static find() {
return ajax("/admin/email.json").then(function (settings) {
return ajax("/admin/email/server-settings.json").then(function (settings) {
return EmailSettings.create(settings);
});
}

View File

@ -14,11 +14,11 @@ export default class IncomingEmail extends EmberObject {
}
static find(id) {
return ajax(`/admin/email/incoming/${id}.json`);
return ajax(`/admin/email-logs/incoming/${id}.json`);
}
static findByBounced(id) {
return ajax(`/admin/email/incoming_from_bounced/${id}.json`);
return ajax(`/admin/email-logs/incoming_from_bounced/${id}.json`);
}
static findAll(filter, offset) {
@ -28,7 +28,7 @@ export default class IncomingEmail extends EmberObject {
const status = filter.status || "received";
delete filter.status;
return ajax(`/admin/email/${status}.json?offset=${offset}`, {
return ajax(`/admin/email-logs/${status}.json?offset=${offset}`, {
data: filter,
}).then((incomings) =>
incomings.map((incoming) => IncomingEmail.create(incoming))

View File

@ -1,10 +1,24 @@
import { tracked } from "@glimmer/tracking";
import EmberObject from "@ember/object";
import { alias } from "@ember/object/computed";
import BufferedProxy from "ember-buffered-proxy/proxy";
import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse/lib/decorators";
import discourseComputed, { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import {
DEFAULT_USER_PREFERENCES,
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES,
} from "admin/lib/constants";
import SettingObjectHelper from "admin/lib/setting-object-helper";
const AUTO_REFRESH_ON_SAVE = [
"logo",
"mobile_logo",
"base_font",
"heading_font",
"default_text_size",
];
export default class SiteSetting extends EmberObject {
static findAll(params = {}) {
return ajax("/admin/site_settings", { data: params }).then(
@ -40,6 +54,17 @@ export default class SiteSetting extends EmberObject {
return ajax(`/admin/site_settings/${key}`, { type: "PUT", data });
}
static bulkUpdate(settings) {
return ajax(`/admin/site_settings/bulk_update.json`, {
type: "PUT",
data: { settings },
});
}
@tracked isSaving = false;
@tracked validationMessage = null;
updateExistingUsers = false;
settingObjectHelper = new SettingObjectHelper(this);
@alias("settingObjectHelper.overridden") overridden;
@ -49,6 +74,11 @@ export default class SiteSetting extends EmberObject {
@alias("settingObjectHelper.allowsNone") allowsNone;
@alias("settingObjectHelper.anyValue") anyValue;
constructor() {
super(...arguments);
this.buffered = BufferedProxy.create({ content: this });
}
@discourseComputed("setting")
staffLogFilter(setting) {
if (!setting) {
@ -60,4 +90,24 @@ export default class SiteSetting extends EmberObject {
action_name: "change_site_setting",
};
}
get requiresConfirmation() {
return (
this.requires_confirmation ===
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES.simple
);
}
get requiresReload() {
return AUTO_REFRESH_ON_SAVE.includes(this.setting);
}
get affectsExistingUsers() {
return DEFAULT_USER_PREFERENCES.includes(this.setting);
}
@bind
setUpdateExistingUsers(value) {
this.updateExistingUsers = value;
}
}

View File

@ -99,7 +99,7 @@ export default class StaffActionLog extends RestModel {
@discourseComputed("details")
useModalForDetails(details) {
return details && details.length > 100;
return details && (details.length > 100 || details.includes("\n"));
}
@discourseComputed("action_name")

View File

@ -1,19 +1,8 @@
import EmberObject from "@ember/object";
import { alias } from "@ember/object/computed";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import SettingObjectHelper from "admin/lib/setting-object-helper";
export default class ThemeSettings extends EmberObject {
settingObjectHelper = new SettingObjectHelper(this);
@alias("settingObjectHelper.overridden") overridden;
@alias("settingObjectHelper.computedValueProperty") computedValueProperty;
@alias("settingObjectHelper.computedNameProperty") computedNameProperty;
@alias("settingObjectHelper.validValues") validValues;
@alias("settingObjectHelper.allowsNone") allowsNone;
@alias("settingObjectHelper.anyValue") anyValue;
import SiteSetting from "admin/models/site-setting";
export default class ThemeSettings extends SiteSetting {
updateSetting(themeId, newValue) {
if (this.objects_schema) {
newValue = JSON.stringify(newValue);

View File

@ -1,8 +1,8 @@
import { i18n } from "discourse-i18n";
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
export default class AdminConfigLogoRoute extends AdminConfigWithSettingsRoute {
export default class AdminAnalyticsRoute extends AdminConfigWithSettingsRoute {
titleToken() {
return i18n("admin.config.logo.title");
return i18n("admin.config.analytics.title");
}
}

View File

@ -1,6 +1,11 @@
import Route from "@ember/routing/route";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminApiKeysIndexRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.api_keys.title");
}
export default class AdminApiKeysIndexRoute extends Route {
model() {
return this.store.findAll("api-key");
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminApiKeysSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("settings");
}
}

View File

@ -1,3 +0,0 @@
import Route from "@ember/routing/route";
export default class AdminApiKeysRoute extends Route {}

View File

@ -12,6 +12,10 @@ export default class AdminBadgesRoute extends DiscourseRoute {
_json = null;
titleToken() {
return i18n("admin.config.badges.title");
}
async model() {
let json = await ajax("/admin/badges.json");
this._json = json;

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentCategoriesAndTagsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.categories_and_tags.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentPostsAndTopicsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.posts_and_topics.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentSharingRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.sharing.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentStatsAndThresholdsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.stats_and_thresholds.title");
}
}

View File

@ -2,11 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigThemesAndComponentsComponentsRoute extends DiscourseRoute {
async model() {
const components = await this.store.findAll("theme");
return components.reject((t) => !t.component);
}
titleToken() {
return i18n("admin.config_areas.themes_and_components.components.title");
}

View File

@ -1,8 +0,0 @@
import { i18n } from "discourse-i18n";
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
export default class AdminConfigFontsRoute extends AdminConfigWithSettingsRoute {
titleToken() {
return i18n("admin.config.font_style.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigInterfaceSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.interface.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigLogoAndFontsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.logo_and_fonts.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigSiteAdminSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.site_admin.title");
}
}

View File

@ -0,0 +1,8 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigUserDefaultsSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.user_defaults.title");
}
}

View File

@ -1,6 +1,10 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
export default class AdminConfigWithSettingsRoute extends DiscourseRoute {
@service siteSettingChangeTracker;
resetController(controller, isExiting) {
// Have to do this because this is the parent route. We don't want to have
// to make a controller for every single settings route when we can reset
@ -12,4 +16,15 @@ export default class AdminConfigWithSettingsRoute extends DiscourseRoute {
settingsController.set("filter", "");
}
}
@action
async willTransition(transition) {
if (this.siteSettingChangeTracker.hasUnsavedChanges) {
transition.abort();
await this.siteSettingChangeTracker.confirmTransition();
transition.retry();
}
}
}

View File

@ -1,7 +1,12 @@
import { scrollTop } from "discourse/mixins/scroll-top";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminDashboardRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.dashboard.title");
}
activate() {
this.controllerFor("admin-dashboard").fetchProblems();
this.controllerFor("admin-dashboard").fetchDashboard();

View File

@ -1,15 +1,20 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n } from "discourse-i18n";
import IncomingEmail from "admin/models/incoming-email";
import AdminEmailLogs from "admin/routes/admin-email-logs";
import IncomingEmailModal from "../components/modal/incoming-email";
export default class AdminEmailBouncedRoute extends AdminEmailLogs {
export default class AdminEmailLogsBouncedRoute extends AdminEmailLogs {
@service modal;
status = "bounced";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.bounced.title");
}
@action
async showIncomingEmail(id) {
const model = await this.loadFromBounced(id);

View File

@ -0,0 +1,10 @@
import { i18n } from "discourse-i18n";
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
export default class AdminEmailLogsReceivedRoute extends AdminEmailIncomings {
status = "received";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.received.title");
}
}

View File

@ -1,14 +1,19 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import { i18n } from "discourse-i18n";
import IncomingEmail from "admin/models/incoming-email";
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
import IncomingEmailModal from "../components/modal/incoming-email";
export default class AdminEmailRejectedRoute extends AdminEmailIncomings {
export default class AdminEmailLogsRejectedRoute extends AdminEmailIncomings {
@service modal;
status = "rejected";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.rejected.title");
}
@action
async showIncomingEmail(id) {
const model = await IncomingEmail.find(id);

View File

@ -0,0 +1,10 @@
import { i18n } from "discourse-i18n";
import AdminEmailLogs from "admin/routes/admin-email-logs";
export default class AdminEmailLogsSentRoute extends AdminEmailLogs {
status = "sent";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.sent.title");
}
}

View File

@ -0,0 +1,10 @@
import { i18n } from "discourse-i18n";
import AdminEmailLogs from "admin/routes/admin-email-logs";
export default class AdminEmailLogsSkippedRoute extends AdminEmailLogs {
status = "skipped";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.skipped.title");
}
}

View File

@ -1,6 +1,11 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminEmailLogsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.email_logs.title");
}
setupController(controller) {
controller.setProperties({
loading: true,

View File

@ -1,5 +0,0 @@
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
export default class AdminEmailReceivedRoute extends AdminEmailIncomings {
status = "received";
}

Some files were not shown because too many files have changed in this diff Show More