The validity checking for parameter values being passed in expects them
all to be strings, then casts them to their appropriate type after
confirming that they're valid.
Rather than bypassing these checks, it's safer to convert the values to
strings, then continue with the existing validity checks.
Currently, if a user is part of a group in
`experimental_new_new_view_groups`,
they will see tabs for All, Topics, and Replies underneath the New tab
in the
/new topic list.
However, if one of the tabs has no topics (i.e. no more unread replies
or no more new topics), we hide the tabs entirely. This can be confusing
and inconsistent for members.
This commit changes the behavior so that the tabs are always shown.
Also, we change the text and route of the empty topic list CTA based on
the subtab
the user is on:
* Topics subtab, when there are new Replies:
* Button text: Browse new replies
* Button points to the Replies subtab
* Replies subtab, when there are new Topics:
* Button text: Browse new topics
* Button points to Topics subtab
* All subtab, or any subtab when there are no new topics of any kind:
* Button text: Browse latest topics
* Button points to /latest
The test being fixed is flaky and the difference in hash is insufficient
information for us to debug it.
```
Failure/Error: expect(SvgSprite.path(1)).to eq("/svg-sprite/#{Discourse.current_hostname}/svg-1-#{version}.js")
expected: "/svg-sprite/test.localhost/svg-1-d110e8387cd2da5cb435355de1a1f384552374e0.js"
got: "/svg-sprite/test.localhost/svg-1-f6c1243dedd2c8296ca6581fe5610047a0d9ba16.js"
(compared using ==)
./spec/lib/svg_sprite/svg_sprite_spec.rb:21:in `block (2 levels) in <main>'
./spec/rails_helper.rb:704:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.1/lib/benchmark.rb:305:in `measure'
./spec/rails_helper.rb:704:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:662: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:652:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:609: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)>'
```
Refactors `formatCode` as a `code-block` extension command.
Changes its behavior:
1. Inside code block: convert back to paragraphs (respecting \n\n
splits)
2. Empty selection in empty block: create code block
3. Empty selection in non-empty block: toggle stored inline code mark
4. Multi-block selection: create single code block with full content
selected
5. Full block selection: create code block with full content selected
6. Partial text selection: toggle stored inline code marks
ActiveRecord prints these warnings when loading the db schema into its
internal cache. We don't need to do this for the AI embeddings tables,
since we don't use ActiveRecord models for them.
Reverts the system-spec-specific workaround from 446ff04e
The "dismiss composer" button(s) by design either close the composer
immediately or open a modal, so there's no reason to try to keep focus
on the textarea.
This _seems_ to fix the bug that makes the view scroll up after
dismissing the composer.
Chat::MessageProcessor includes CookedProcessorMixin but only sets
@model, leaving @post as nil. The process_hotlinked_image method was
attempting to access @post.post_hotlinked_media, causing undefined
method errors when processing chat messages with images.
This is only shown to staff. Moving it to the admin bundle will reduce
bundle size for regular users, and simplify the code.
Also moves the action code inside the dropdown, so we don't need the
old-fashioned `.send()` calls.
This route is only accessible to staff, so no need to have it in the
main bundle. This means we no longer need the `optionalRequire` for the
color-input component.
Also updates the `edit-category/tabs` route-template to avoid the need
for `lookup()`, and updates the plugin APIs to use transformers.
When one of the moved posts was the first post in it's originnal topic,
it was setting the bumped_at timetamp of the destination post to the
created_at timestamp of the moved first post.
With this fix, instead it leaves the bumped_at timestamp alone.
dev /t/164974
With out AI credit system being rolled out, we no longer rely on
allowing/disallowing seeded models in the enumerator. This update allows
seeded models to be selected and used in the `LlmEnumerator`.
When changing the sorting column/order, we would always make a request
- to retrieve the list of groups ("/groups/search.json")
- to retrieve the list of columns ("/directory-columns.json")
This adds some client-side cache to ensure we don't unecessarily
requests those information over and over again.
I'm also 50% hopeful that it might also fix the race condition described
in https://meta.discourse.org/t/-/385110/4 - but I wasn't able to
reproduce locally
This was unused, and had a broken import. The resetController
functionality was already implemented in another way, and the titleToken
is the same as the parent route.
`category.topics` is already an array of `Topic` objects. Calling
`.create()` again is unnecessary, and leads to strange behavior,
especially when `@tracked` fields are involved.
New `@tracked` were introduced in
075d0b8ea1, which is what surfaced this
issue
Previously `"topic-list-heading"` was added as a reference for all topic
lists because a hidden screenreader-only H1 was added to the topic lists
on discovery pages (latest/top/hot/etc)
On other pages that use the topic list component, like suggested topics
at the bottom of topic pages, the headings are visible on-page so
`aria-labelledby` is referencing a non-existent element.
This removes `aria-labelledby` when it's not needed.
This was a reasonable attempt to connect the button to the replies it
controlled (above/below a post) but the fact that the content isn't
rendered at all until expanded means we have invalid ID references and
accessibility errors. In practice this seems to cause more issues than
it solves, and we can rely on `aria-expanded` instead (already present
and properly indicates button state).
`aria-controls` doesn't really make sense here, the input was being
associated with the number of search results... but it's not really the
expected way to use it (more for interactive controls like accordion
menus and dropdowns). This was also causing a accessibility validation
issue because `search-result-count` isn't initially present and
`aria-controls` was pointing at nothing. Removing it fixes that.
While testing this I also noticed a race condition with the result count
being announced, the count would lag and on a new search the old count
would be read by the screenreader. Clearing the count while searching
with `{{#unless @controller.searching}}` fixes the issue.
We markup video thumbnail `div`s in posts with `tabindex="0"` which
makes them tabbable, but without a role it's unclear they're
interactive.
Since on click or keypress these play the video, it should have
`role="button"` as well as an an accessible label.
I also noticed that `onKeyPress` was added here but isn't wired up to
anything... so I've included that and also added the space key as a
trigger in addition to enter.
When validating the openapi spec we were getting the following errors:
```
{
"messages": [
"attribute paths.'/categories.json'(post).responses.200.content.'application/json'.schema. is not of type `object`",
"attribute paths.'/categories/{id}.json'(put).responses.200.content.'application/json'.schema. is not of type `object`",
"attribute paths.'/c/{id}/show.json'(get).responses.200.content.'application/json'.schema. is not of type `object`",
"attribute paths.'/posts.json'(get).responses.200.content.'application/json'.schema.items is not of type `object`",
"attribute paths.'/groups/by-id/{id}.json'(get).operationId is repeated"
]
}
```
These changes resolve those openapi spec validation errors.
See:
https://github.com/discourse/discourse_api_docs/pull/139#issuecomment-3393138056
We get the following failure from time to time on CI. Just double the
timeout for now.
```
Failure/Error: expect(x).to eq(3)
expected: 3
got: 2
(compared using ==)
./spec/lib/scheduler/defer_spec.rb:95:in `block (2 levels) in <main>'
./spec/rails_helper.rb:704:in `block (3 levels) in <top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/benchmark-0.4.1/lib/benchmark.rb:305:in `measure'
./spec/rails_helper.rb:704:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:662: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:652:in `block (2 levels) in <top (required)>'
./spec/rails_helper.rb:609: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 "watching first post" notification level is not a topic notification
level but a category/tag notification level.
This fixes both the "topics_filter.rb" and "topic_query.rb" to account
for this and allow the filter to work.
Ref - meta/t/385316
This adds a new notification's user preference to allow users to control
whether they are notified whenever one of their topic is being linked in
another post.
Internal ref - t/160259
When making requests to `/t/:id/status.json`, the behaviour of the
`enabled` parameter is a bit confusing: only the string `"true"` is
treated as a true value, everything else is false (including boolean
`true`, for example).
This change makes use of the ActiveModel Boolean type to cast
falsey/truthy values to `false` and `true`.
1. In `initializers/chat-setup`, we call the async
`this.chatService.loadChannels()` function which we do not call `await`
on.
2. `ChatChannelRoute`'s `model` calls
`this.chatChannelsManager.find(params.channelId)` which will trigger an
API call if the `this.chatService.loadChannels` function has not loaded
the given channel yet.
3. The `ChatChannel` component is then rendered once the route's model
resolves and the `setup` function is called.
Within the `setup` function,
`this.chatChannelsManager.find(params.channelId)` is called to set the
channel's draft.
If the async call in step 1 does not resolve before step 3 happens, the
channel draft will be blank until the component is rerendered. To
resolve this, we can move the responsibility of loading a channel's
draft into `chatDraftsManager` from the `chat` service into the
`chatChannelsManager` service
instead so that a channel's drafts are populated whenever we store a
channel into `chatChannelsManager`.
Implements exact URL matching to prioritize specific URL patterns over
Ember route matching in sidebar navigation. When a sidebar link's URL
exactly matches the current URL, it will be highlighted with the
'exact-url-match' class instead of relying on Ember's route-based active
state detection.
Refactor array-clearing logic across the codebase by replacing
`.clear()` with `.length = 0` for improved clarity and compatibility
with standard JavaScript arrays.
Updated relevant tests and utilities to ensure consistency.
These changes align array management with modern JavaScript
practices while preserving existing functionality.
This commit replaces several instances of `.uniq`, `.uniqBy`, and related
array deduplication methods with a new utility function
`uniqueItemsFromArray`. This change ensures proper deduplication logic
across the codebase while addressing the deprecation issues.
**Main Changes:**
* Created new utility function `uniqueItemsFromArray`: Located in a new
file, `array-tools.js`, this function provides a reusable and
configurable alternative for deduplication.
* Replaced old methods: Updated multiple files to use the new utility
function instead of deprecated or custom implementations for
deduplication.
* Replaced manual deduplication: Replace uses of `[...new Set(array)]`
to standardize the deduplication logic across the codebase.
* Added unit tests: Introduced thorough test coverage
(`array-tools-test.js`) to validate functionality and edge cases for the
utility function.
* Updated deprecation workflow: Added logging for deprecated uniq and
uniqBy methods to the `deprecation-workflow.js`.
This change is primarily focused on code quality improvements, ensuring
future-proof deduplication, and maintaining alignment with deprecation
guidelines.
This extends the **MyLittleForum (MLF) importer** with several optional features to make migrations more complete and reliable.
### New Features (optional via environment variables)
**Uploads import**: supports attachments and images from the MLF uploads table.
- Imported uploads are stored with relative links, ensuring they continue to work even if the forum domain changes.
- **Legacy link rewriting**: absolute links (`index.php?id=...`) are converted into working Discourse permalinks.
- Rewritten links are also created as relative links, so they remain valid after a domain move.
- **Parent category option**: all imported MLF categories can be placed under a chosen existing category.
- **Upload repair pass / incremental imports:** already-imported posts can be rescanned to fix broken or newly available upload references, allowing the importer to be run again later with a more up-to-date dataset.
- **Relaxed upload constraints**: temporarily increases file size limits and allows all extensions during the import step.
### Other Improvements
- Adjusted for MyLittleForum 2.x schema changes (e.g. `user_location` instead of `user_place`).
- User import now respects already staged users; avoids errors that would otherwise occur when trying to create them again.
- Improved BBCode → Markdown conversion: lists and quotes are converted more reliably.
- Especially improves handling of quotes at the beginning of posts and ensures following text is not mistakenly included in the quoted block.
- Thread interlinking restored: legacy links between threads are now rewritten to point to the correct Discourse topics.
### Environment Variables
- `PARENT_CATEGORY` – import all categories under an existing Discourse category.
- `REWRITE_LINKS` – rewrite absolute legacy links.
- `UPLOADS_DIR` – path to the MLF uploads directory (flat namespace).
- `REPAIR_UPLOAD_LINKS` – repair pass for already-imported posts with links.
- `LOOSEN_UPLOAD_CONSTRAINTS` – relax upload size/extension checks during import and increases file size limits and allows all extensions during the import step.
This commit replaces the use of `.compact()` with `.filter((item) => item !=
null)` across multiple JavaScript files in the codebase. Additionally,
it introduces a new logging handler for a specific deprecation in the
deprecation workflow.
**Main Changes:**
* Replaced `.compact()` with `.filter((item) => item != null)` to
eliminate the Ember Array dependency and align with native JavaScript
practices.
* Deprecation Workflow: Added a log handler for
discourse.native-array-extensions.compact.
This commit refactors the post-stream module to eliminate deprecated
EmberArray methods and modernize code for improved maintainability.
- Replaces deprecated EmberArray methods with native array operations
and tracked arrays
- Migrates discourseComputed and `.get()` to native getters and fields
- Updates state management to use `@trackedArray` for improved
reactivity
- Aligns computed logic with Ember Octane conventions
- Focuses on deprecation cleanup; no user-facing changes intended