Commit Graph

9398 Commits

Author SHA1 Message Date
Zoltan Hosszu 9c01434f68 Fixed missing attachment selection outline 2026-02-19 16:25:41 +01:00
Adrien Maston c2d5070840 Merge pull request #2567 from basecamp/mobile/fix-empty-search 2026-02-19 14:28:54 +01:00
Adrien Maston 5f25ebe3f4 Add a line break, it looks better 2026-02-19 14:25:26 +01:00
Adrien Maston f2fb05cecf Replace unless by if 2026-02-19 14:09:48 +01:00
Adrien Maston 97f2f677d6 Don't render empty state instead of hiding it 2026-02-19 14:04:14 +01:00
Adrien Maston 542777409c Add test 2026-02-19 12:07:08 +01:00
Adrien Maston 0e1feb0f3f Create @query in controller instead of testing for blank? 2026-02-19 12:06:49 +01:00
Jeremy Daer 953942694f Handle MissingEOCD as invalid zip file, not job failure (#2572)
ZipKit::FileReader raises MissingEOCD for truncated, corrupted, or
non-zip files. This exception is a direct StandardError subclass, not
a subclass of InvalidStructure or ReadError, so it escaped as an
unhandled job failure instead of being caught and surfaced to the user.

Broaden the rescue in ZipFile::Reader#initialize to catch ReadError
(parent of InvalidStructure), MissingEOCD, and UnsupportedFeature.
2026-02-18 15:18:56 -08:00
Mike Dalessio 352199785e Merge pull request #2570 from basecamp/upgrade-rails-20260218
Update Rails to 12e24ea (363 commits)
2026-02-18 15:42:15 -05:00
Mike Dalessio 3b229e9aab Pin web-console to GitHub main for Rails compatibility
web-console 4.2.1 overrides the now-renamed filter_proxies method in
ActionDispatch::RemoteIp::GetIp. The fix (rails/web-console#344) is on
main but not yet released.
2026-02-18 15:16:21 -05:00
Jeremy Daer ed81ca760f Restore unique index on board_publications.key (#2568)
* Restore unique index on board_publications.key

The account_id rollout (fe6df7085) replaced the unique index on
board_publications.key with a non-unique composite (account_id, key)
index. This was unintentional — other tables in the same migration
preserved uniqueness (account_join_codes, tags) but this one did not.

Without global uniqueness on key, a crafted data import can insert a
duplicate publication key via insert_all! (which bypasses model
validations). Since find_by_published_key queries globally without
account scoping, this enables public board URL hijacking after the
legitimate owner unpublishes.

Restore the original unique index on key and keep a plain account_id
index for account-scoped queries.

* Reorder index operations: add unique index before dropping composite

On MySQL, DDL is non-transactional. If the unique index creation
fails (e.g. duplicate keys exist), the previous ordering would leave
the composite index already dropped. Adding the unique index first
means a failure leaves the database unchanged.

* Add schema dumps for both SQLite and MySQL

Dump both schema files after running the migration against each
adapter to keep the PR complete for CI.
2026-02-18 11:21:25 -08:00
Jeremy Daer 2d7950879d Normalize schema_sqlite.rb to reflect TableDefinitionColumnLimits initializer (#2569)
The table_definition_column_limits initializer (PR #1669) applies a
default limit of 255 to all string columns during schema:load, but
schema_sqlite.rb was never re-dumped after it landed. This caused
drift on every subsequent migration PR that dumped the schema.

Re-dump from schema:load → schema:dump to make the file idempotent.
2026-02-18 11:13:13 -08:00
Mike Dalessio 41675224d0 Remove redundant insecure context CSRF check
Rails now handles the insecure context case natively in
verified_via_header_only? — when Sec-Fetch-Site is missing and the
request is plain HTTP without force_ssl, the request is allowed.

Remove the now-redundant allowed_insecure_context_request? method and
update the test to set ActionDispatch::Http::URL.secure_protocol
alongside Rails.configuration.force_ssl so the upstream check works
correctly in the test environment.
2026-02-18 14:06:46 -05:00
Mike Dalessio 41c2bd38af Update Rails to 12e24ea (363 commits past 60d92e4)
Also updates transitive dependencies:
- action_text-trix ~> 2.1.15 → ~> 2.1.16
- irb 1.16.0 → 1.17.0
- json 2.18.0 → 2.18.1
- net-imap 0.6.2 → 0.6.3
- nokogiri 1.19.0 → 1.19.1
- prism 1.8.0 → 1.9.0
- rack 3.2.4 → 3.2.5
- rdoc 7.0.3 → 7.2.0
2026-02-18 13:37:13 -05:00
Mike Dalessio 1c3b3c55ef Update rails_structured_logging to v0.3.0
v0.3.0 removes usage of the INTERNAL_PARAMS constant which is being
removed from ActionController in an upcoming Rails upgrade.
2026-02-18 13:33:10 -05:00
Jeremy Daer f39253e5f9 Remove defunct saas/Gemfile and lockfile (#2566)
These are leftovers from when fizzy-saas was a standalone repository.
Since it was moved into fizzy as a path gem, the production bundle
uses Gemfile.saas at the root. Nothing references saas/Gemfile.

Also removes saas/bin/rails (references nonexistent test/dummy app)
and saas/bin/rubocop (references nonexistent saas/.rubocop.yml),
both equally defunct standalone-repo leftovers.
2026-02-18 09:27:28 -08:00
Adrien Maston 5c0508d91c Don't show "no match" when not searching anything yet 2026-02-18 18:19:37 +01:00
Alp Keser 056dfc1367 Merge pull request #2528 from basecamp/mobile/missing-boards-fix
Update boards JSON to return all accessible boards ordered by recently accessed
2026-02-18 13:20:49 +03:00
Alp Keser 610cf7a190 Merge pull request #2398 from basecamp/notifications-tray-api
Add JSON response support for `notifications/tray`
2026-02-18 13:19:59 +03:00
Jeremy Daer 8da0c47a5f Add actionpack-xml_parser to Gemfile.saas (production bundle)
The prior commit added it to saas/Gemfile but the production Docker
build uses Gemfile.saas (via BUNDLE_GEMFILE). This ensures the XML
parameter parser is present in the actual runtime environment.
2026-02-17 20:37:31 -08:00
Jeremy Daer 475fb63c6d Add actionpack-xml_parser for Queenbee sync request support (#2563)
Queenbee sends account sync/cancel/etc requests as XML via
ActiveResource, including the queenbee_signature in the XML body.
Rails removed built-in XML parameter parsing in Rails 4.0, so without
this gem the XML body is silently ignored and the signature check
always fails with 403.
2026-02-17 20:34:33 -08:00
Kevin McConnell b2b8465ae8 Merge pull request #2558 from basecamp/correct-card-url-in-webhook-payload
Correct card URL in comment JSON output
2026-02-17 17:20:02 +00:00
Kevin McConnell 4e9773f9f5 Correct card URL in comment JSON output
When rendering details of a comment, we were using the card's `id` as
the param for the URL. But card routes use the card's number, not ID.
2026-02-17 17:11:39 +00:00
Stanko Krtalić 4b2586769b Merge pull request #2556 from basecamp/fix-notifications-staying-unread-on-card-visit
Reinstate card readings
2026-02-17 14:15:24 +01:00
Stanko K.R. 9d4db14fc3 Reinstate card readings 2026-02-17 13:57:24 +01:00
Alp Keser 13d7cd4303 Paginate boards JSON 2026-02-17 10:37:23 +03:00
dependabot[bot] 38ff89de09 Bump bootsnap from 1.21.1 to 1.22.0 (#2544)
* Bump bootsnap from 1.21.1 to 1.22.0

Bumps [bootsnap](https://github.com/rails/bootsnap) from 1.21.1 to 1.22.0.
- [Release notes](https://github.com/rails/bootsnap/releases)
- [Changelog](https://github.com/rails/bootsnap/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rails/bootsnap/compare/v1.21.1...v1.22.0)

---
updated-dependencies:
- dependency-name: bootsnap
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Sync Gemfile.saas.lock

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-16 16:09:22 -08:00
Jeremy Daer a253c31dee Batch webhook delivery cleanup to prevent DB lock contention (#2555)
Follow-up to #2550 which added a created_at index and increased cleanup
frequency. The delete itself was still unbatched and vulnerable whenever
a backlog accumulates (first run after deploy, clock skew, etc).

Delete in small batches with a pause between each to let other
transactions through, following the SolidQueue pattern.
2026-02-16 15:31:10 -08:00
dependabot[bot] 0ead67f85b Bump the development-dependencies group across 1 directory with 3 updates (#2546)
* Bump the development-dependencies group across 1 directory with 3 updates

Bumps the development-dependencies group with 3 updates in the / directory: [brakeman](https://github.com/presidentbeef/brakeman), [faker](https://github.com/faker-ruby/faker) and [selenium-webdriver](https://github.com/SeleniumHQ/selenium).


Updates `brakeman` from 7.1.2 to 8.0.1
- [Release notes](https://github.com/presidentbeef/brakeman/releases)
- [Changelog](https://github.com/presidentbeef/brakeman/blob/main/CHANGES.md)
- [Commits](https://github.com/presidentbeef/brakeman/compare/v7.1.2...v8.0.1)

Updates `faker` from 3.5.3 to 3.6.0
- [Release notes](https://github.com/faker-ruby/faker/releases)
- [Changelog](https://github.com/faker-ruby/faker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/faker-ruby/faker/compare/v3.5.3...v3.6.0)

Updates `selenium-webdriver` from 4.39.0 to 4.40.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/compare/selenium-4.39.0...selenium-4.40.0)

---
updated-dependencies:
- dependency-name: brakeman
  dependency-version: 8.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development-dependencies
- dependency-name: faker
  dependency-version: 3.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
- dependency-name: selenium-webdriver
  dependency-version: 4.40.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* Auto-sync Gemfile.saas.lock on Dependabot PRs

Dependabot can't update custom-named Gemfiles, so Gemfile.saas.lock
drifts whenever it bumps shared gems in Gemfile.lock.

Add `bin/bundle-drift forward` to push oss lockfile changes into the
saas lockfile (the reverse of `correct`), and a GitHub Actions workflow
that runs it automatically on Dependabot bundler branches.

* Update brakeman ignore fingerprint for 8.0.1

Brakeman 8.0.1 changed how it computes warning fingerprints, so the
existing ignore entry for the safe_constantize warning in Notifier
became obsolete. Update to the new fingerprint.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Daer <jeremy@37signals.com>
2026-02-16 15:29:51 -08:00
Jeremy Daer bc76633f77 Reduce webhook delivery cleanup impact (#2550)
* Add index on webhook_deliveries.created_at

The stale scope (WHERE created_at < ?) and ordered scope
(ORDER BY created_at DESC) both benefit from this index.
Without it, Webhook::Delivery.cleanup does a full table scan.

* Run webhook delivery cleanup every 15 minutes

Each run now deletes ~15 minutes of newly-stale records instead
of ~4 hours worth, a 16x reduction in per-run write volume.
2026-02-16 14:28:05 -08:00
Adrien Maston a1e96670ce Merge pull request #2524 from basecamp/mobile/fix-card-perma-footer
Mobile / Fix card perma footer
2026-02-16 17:39:22 +01:00
Adrien Maston 910500eaf6 Merge pull request #2520 from basecamp/mobile/fix-truncated-card-header
Mobile / Fix truncated card header
2026-02-16 17:38:36 +01:00
Andy Smith eee93cb17d Merge pull request #2553 from basecamp/apostrophe-fix
Use the correct apostrophe character
2026-02-16 10:12:50 -06:00
Andy Smith c41e5aa981 Merge pull request #2437 from nqst/dialog-ux-improvements
Modal dialogs: smoother animations and QR code dialog improvements
2026-02-16 10:12:30 -06:00
Andy Smith 26c380448b Use the correct apostrophe character 2026-02-16 10:03:53 -06:00
Andy Smith edb2f91bbb Merge pull request #2451 from nqst/fix-safari-glitches
Fix Safari transition glitches
2026-02-16 09:58:26 -06:00
Andy Smith b638998789 Merge pull request #2456 from nqst/qr-code-in-dark-mode
Improve QR code appearance in dark mode
2026-02-16 09:54:54 -06:00
Rosa Gutierrez 95114b1d67 Fix race condition between concurrent Event and Mention notifier jobs
When concurrent NotifyRecipientsJobs process an Event and a Mention for
the same user+card, Rails' dirty tracking can skip writing source_type
in the UPDATE if it hasn't changed from the stale in-memory value,
leaving source_type and source_id mismatched (e.g. source_type='Event'
with a Mention's source_id, resulting in a nil source).

Force source_type to always be included in the UPDATE via
source_type_will_change! to prevent this.

Here's a sample timeline of this race condition happening in the real
world (with simplified IDs):

Two `NotifyRecipientsJob` involving notification `03fklpu`, same card,
same user, same comment — enqueued within 50ms of each other:

1. Both jobs load notification `03fklpu` — it has source_type='Mention'
   (from a previous job)
2. `EventNotifier` writes at 21:22:36.051: ```sql SET
   source_type='Event', source_id=<event_id>, unread_count=1 ```
3. `source_type` included because it changed ('Mention' → 'Event')
4. `MentionNotifier` writes at 21:22:36.057 (~6ms later): ```sql SET
   source_id=<mention_id>, unread_count=1 ```
5. No `source_type`! It was `'Mention'` when loaded and `'Mention'` is
   what it's setting → not dirty → skipped
6. Final DB state: `source_type='Event'` (from step 2, untouched),
   `source_id=<mention_id>` (from step 3)

`Notification.source` now does `Event.find(<mention_id>) → nil.`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:50:46 +01:00
Andy Smith bee16f5f9c Merge pull request #2548 from nqst/fix-notifications-typography
Push notification section typography fixes
2026-02-16 09:45:42 -06:00
Andy Smith d31756fed3 Merge pull request #2549 from basecamp/fix-typo
Fix typo
2026-02-16 09:41:03 -06:00
Alp Keser a48c88cb5d Stabilize notifications tray test ordering via fixture updated_at timestamps 2026-02-16 18:33:32 +03:00
Alp Keser bcc0f93bb9 Preload notification card associations used by JSON responses 2026-02-16 17:58:33 +03:00
Rosa Gutierrez b742963f7c Add account ownership check to import GID resolution
The convert_gids_to_sgids method resolved GIDs globally and minted
valid SGIDs without verifying the record belongs to the importing
account. This adds the same account_id check that the export path
already has, plus RecordNotFound handling for invalid GIDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:49:00 +01:00
Andy Smith 1572e70b65 Fix typo 2026-02-13 17:45:58 -06:00
Andy Smith f2bf7c7460 Merge pull request #2547 from basecamp/import-export-clarification
Import export clarification
2026-02-13 17:40:35 -06:00
Alexander Zaytsev bef5bb60c9 Target correct selector 2026-02-14 00:17:16 +01:00
Alexander Zaytsev 635ef4fbe5 Fix paragraph rendering as an inline element 2026-02-14 00:02:56 +01:00
Andy Smith d7814792e3 Tweak language 2026-02-13 15:17:33 -06:00
Alexander Zaytsev 6f839c5b2f Remove periods after ellipses 2026-02-13 18:21:47 +01:00
Alexander Zaytsev da5e67a0a6 Use h3 instead of h2 2026-02-13 18:19:38 +01:00