This fixes a missed `WHERE NOT deleted_at` condition
in the WritingAnalysis rewind report, we already had it
for the other queries here.
Also makes sure we exclude deleted topics too
Targeted checks (like the `UpcomingChangeStableOptedOut` check)
were sometimes invoked with target `NO_TARGET` when
`targets.call` returned the default `[NO_TARGET]`, so `targeted?` was
false and the guard never ran.
That led to `call()` being run with `NO_TARGET`
(e.g. `SiteSetting.send("__NULL__")`) and `NoMethodError` in the job
above.
We can skip running and enqueueing targeted checks when target is
`NO_TARGET`, it's pointless to do this since these kind of checks
are supposed to iterate over each target and run the check for that
target. This just avoids a bad/stale job issue caused by the current
logic.
Found with automated AI review, all legit minor issues.
* Avoid notifying admins about upcoming changes they already
opted into
* Cache upcomingChanges getter in UI to avoid recreating TrackedObjects
* Fix previous_value not always stored in Toggle
Currently in several endpoints, we return an array of strings for tags.
Our goal with this PR is to expand array tag name strings to an array of
tag objects.
#### before: Tags were returned as string arrays
```
{ "tags": ["support", "bug-report"] }
```
#### after: Tags are returned as object arrays
```
{ "tags": [{"id": 12, "name": "support", "slug": "support"}, {"id": 13, "name": "bug-report", "slug": "bug-report"}] }
```
This allows us to start referencing tags by their ids, and return more
information for a tag for future features.
This commit involves updating several areas:
- topic lists (/latest.json, /top.json, /c/:category/:id.json, etc, for
`top_tags`)
- tag chooser components (`MiniTagChooser`, `TagDrop`, etc)
- topic view (/t/:id.json)
- tag groups (/tag_groups.json, tags, parent_tag)
- category settings
- staff action logs
- synonyms
- ...
APIs that reference tags based on their names will still be supported
with a deprecation warning. Moving on, we will reference them using
their tag ids.
QQ Mail (smtp.qq.com and smtp.exmail.qq.com) is known to cause duplicate
email issues with Discourse. The SMTP server does not return proper
acknowledgments, causing Discourse to retry sends that already succeeded.
This adds a ProblemCheck that displays a warning on the admin dashboard
when QQ Mail SMTP is detected, linking to recommended alternatives.
These broke as a result of 60259dd453,
the specs changed a little because
b8672b3dfb
was merged beforehand. We were getting an error because the instance
of site settings in site_setting_extension_spec was not the same
as SiteSetting called by the UpcomingChanges module.
We can stub it in the spec, so both refer to the same instance,
otherwise things will get complicated fast. We have other specs
that cover the behaviour correctly.
Also cleaned up TODOs from the previous commit that intended
to call UpcomingChanges.resolved_value when it was ready
Followup 12fdb69279
We introduced `depends_on` in the above commit for site settings,
where you can define one or more boolean site settings that a given
setting depends on. However, this was only used for a dependency
graph, and did not have an effect on the UI.
This commit introduces `depends_behavior`, which can be set to
`hidden` only for now. When a setting has `depends_behavior: hidden`
and one of its `depends_on` settings is false, the setting will be
hidden from the UI.
This will be useful for upcoming changes, where we want to hide
certain site settings if the upcoming change is not enabled.
There are several problems with running these upcoming change
checks added in
https://github.com/discourse/discourse/commit/fb9bb3198384b76af9947ad011dda86885e6c536:
* The app is slower to boot because there is DB work to be done
* We sometimes swap out database configs at runtime in our hosting,
which does not call the initializer again
* We have to think about readonly states for the DB
* There are race conditions because of blue-green deployments
to consider
To solve these problems, we move the tracking and promotion into
a scheduled job that runs every twenty minutes, since the notification
of admins about upcoming changes are not time-sensitive. This avoids
a whole host of problems.
In addition, we are making upcoming change settings declarative
via SiteSettingExtension. Now, instead of actually changing the DB
value of the change when promoting, we just return true for the
setting if it has met the promotion status. However, if admins have
changed the value of the setting in the DB, we do still use that
value instead. Finally we fall back to the default value of the setting.
I've also added a change to track modified site settings. Before this we
had no way to tell if a setting in `current` inside
`SiteSettingExtension`
was modified by an admin or not. This change tracks modified settings in
a new
`modified` object and their values.
---------
Co-authored-by: Loïc Guitaut <loic@discourse.org>
This simplifies the experimental category creation general tab by:
* reducing the group permissions to a single step, if someone wants more
finite control they can use the security tab
* hides category descriptions from the subcategory dropdown
I've also made it so when adding a private parent category, you can only
select groups allowed to access the parent for the subcategory. This
avoids an awkward error message explain that you can't make
subcategories more permissive than the parent.
Running `RAILS_ENV=test bin/rails db:migrate` could fail with:
Theme::SettingsMigrationError:
Migration '0001-convert-topic-card-context-to-boolean' is out of
sequence. The last migration for this theme had version number 1
which is higher than the new migration
This occurred because of orphaned `ThemeSettingsMigration` records in
the test database.
Root cause:
The `before(:suite)` block in `spec/rails_helper.rb` calls
`ThemeField.delete_all` but did not delete the associated
`ThemeSettingsMigration` records. This left orphaned records where
`theme_field_id` references a non-existent `ThemeField`.
When `db:migrate` runs seeds and syncs system themes, the migration
runner finds:
- A "pending" migration (new ThemeField has no ThemeSettingsMigration)
- But `current_migration_version = 1` from the orphaned record
- Since version 1 <= 1, it errors with "out of sequence"
How to reproduce (before this fix):
```
RAILS_ENV=test bin/rails db:drop db:create db:migrate
bin/rspec spec/models/theme_spec.rb # any spec triggers before(:suite)
RAILS_ENV=test bin/rails db:migrate # fails
```
This commit:
1. Adds `ThemeSettingsMigration.delete_all` to the test suite setup to
clean up migration records alongside theme fields
2. Adds `dependent: :destroy` to the ThemeField association as a safety
net for any code path that destroys ThemeFields via ActiveRecord
MiniMime.lookup_by_filename can return nil if the extension is not
recognized. Other code in the codebase uses safe navigation (&.) for
this call, but backup_restore/creator.rb did not.
This adds safe navigation with a fallback to "application/gzip" since
backup files are always .tar.gz or .sql.gz.
When the hardlink limit is reached (Errno::EMLINK), copy the file locally
and use the copy as the new primary for subsequent hardlinks. This avoids
re-downloading from S3 and works regardless of filesystem type.
Previously, hitting the limit would fall back to downloading each remaining
file individually. Now we detect the specific error and handle it gracefully
by copying the already-downloaded file.
Added :copied stat to track when this fallback is used.
Fixes GIF modal not displaying when triggered from chat composer
dropdown on mobile.
Async button actions were not being awaited, causing potential timing
issues when the dropdown modal closes and another modal opens.
The "Remove upload markup from deleted posts" automation was incorrectly
processing posts that were deleted but still had pending reviewables in
the review queue.
Scenario: When a post is flagged (hidden from public) and the author
subsequently deletes it, the post has both `hidden: true` and
`deleted_at` set. The automation would strip upload markup from these
posts, causing data loss when moderators later restored them after
completing their review.
This fix adds a NOT EXISTS clause to exclude any post with a pending
reviewable targeting it. The automation will now wait until the review
is resolved (approved/rejected/ignored) before processing the post.
Commit 9cbb0c3da3 fixed Safari file uploads by removing async/await from
the button click handler, but this introduced a regression: modal-based
buttons (like "Insert date/time") stopped working on mobile.
The issue is a race condition where the modal tries to open while the
dropdown menu modal is still closing, causing the new modal to be
dismissed immediately.
This fix introduces a `synchronous` property for chat composer buttons:
- `synchronous: true` - action runs immediately without awaiting the
menu close (required for file inputs on Safari)
- `synchronous: false` (default) - awaits menu close before running
action (required for modal-based buttons)
The upload button is marked with `synchronous: true` to preserve the
Safari fix, while all other buttons use the default async behavior.
When a tag belongs to multiple tag groups (each restricted to different
categories), the parent tag auto-addition logic was adding parent tags
from ALL tag groups—including those restricted to unrelated categories.
For example, a generic tag like "cost" in tag groups across "Finance",
"Sales", and "Engineering" categories would cause parent tags from wrong
categories to appear when editing topics.
The fix filters parent tags based on category context:
1. Category with no tag restrictions: Only include parent tags from tag
groups not restricted to any category
2. Category with tag restrictions:
- If "allow global tags" is enabled: Include parent tags from both
global and category-specific tag groups
- If "allow global tags" is disabled: Only include parent tags from tag
groups restricted to that category
t/394023/7