Dandelion is a fair source project. View the code on Github

Commits on Sep 12, 2026
79e4bec

Move payment provider name from the event view onto Order.

f6f9543

Move event access tests into their own file.

Keep events_test focused on create/edit, slugs, and reminders, and put manager, cohost, and revenue permission cases in event_permissions_test.

1443551

Drop tests that only restate coverage already in a broader case.

Fold waitlist subscription checks into the join test, and remove MCP/order/ticket/webhook cases that duplicate a parent assertion.

6f06d7a

Purge soft-deleted rows between tests

5876f39

Don't let nested ticket type/group attributes move records to another event

cc4bd26

Don't let optionships, teamships or shifts adopt their parent from a child.

Gathering and rota must be set by the caller, matching spends and inventory items, so a missing parent cannot skip the same-parent check.

3bb53cf

Don't let cohost admins change revenue share settings

Event.revenue_admin? trusted admins of cohost organisations, and any event admin can add an organisation they control as a cohost. Together that let a facilitator make themselves revenue sharer and redirect ticket revenue.

Add Event.revenue_settings_admin? (host org, activity and local group admins only) and use it for the revenue field validation and the build form. revenue_admin? keeps including cohosts for read-side checks.

Also only run the permission lookup when a revenue field actually changed.

782b0c6

Follow-ups from review of foreign-key and order-token changes

  • Validate that a ticket type's ticket group belongs to the same event, closing the nested ticket_types_attributes[..][ticket_group_id] path that bypassed the top-level foreign-key filter. Add a controller test.
  • Treat nil and 0 as equivalent in the revenue-field permission check so re-validating an event does not flag the model's own nil -> 0 normalisation.
  • Guard the new find(...) calls in activities, local_groups and rotas so a missing id returns 404 instead of raising Mongoid::Errors::InvalidFind.
  • Only emit the hidden event[organisation_id] on the new-event form.
  • Drop a redundant to_s in Order.find_by_id_or_token.
Commits on Sep 11, 2026
16ebc03

Restrict revenue share fields to revenue admins

Only organisation admins/event managers, activity, local group and co-host admins can change the revenue sharer, revenue split, profit shares and Stripe revenue adjustment. Enforce this in Event validation (bypassed for duplicates), disable the fields in the event form for other event admins, add tests and update the organisations docs.

d8ccf24

Don't let AI-generated feedback reports render unsanitized HTML.

db16146

Don't let mass-assigned foreign keys attach records to another organisation or gathering.

9a686f0

Use UUIDs for order tokens

Mint order tokens with SecureRandom.uuid instead of urlsafe_base64(24). UUIDv4 still carries 122 random bits, uses a familiar hex-and-hyphen shape, and cannot be mistaken for a 24-hex ObjectId, so Order.find_by_id_or_token's legacy fallback is unaffected. Lookup is by exact value, so any base64 tokens already minted keep working.

Inline the generator into mint_token, its only caller, and assert the UUID shape in the token test.

9c7b776

Show a pending card for unconfirmed orders on payment return

Follow-up to 0114e423, which stopped rendering the success card for orders that are not yet payment_completed. That left buyers returning from Stripe (webhook-driven completion) or Open Collective on the plain event page with the ticket form and no indication their payment was being processed.

Generalise the GoCardless "Confirming your payment" card into @pending_order: any incomplete order reached via ?success= or a GoCardless request id now shows the card, which polls /payment_completed and reloads into the success card once paid. The card names Stripe, GoCardless or Open Collective where identifiable. An incomplete order reached via ?order_id= without a payment return still shows neither card.

Reload the order after check_oc_event instead of re-running the token lookup, and keep a GoCardless miss from clobbering an @order already resolved from ?order_id=.

Order.find_by_id_or_token now returns nil unless given a non-empty String, so Hash/Array params cannot reach Mongoid as operators regardless of upstream param filtering.

0114e42

Use secret tokens for public order links

Order confirmation pages (/orders/:id, unauthenticated, serving HTML/PDF/ICS) and the post-purchase ticketholder editing routes were addressed by Mongo ObjectId, which is semi-guessable. Add a random token field to Order, generated on creation, and use it as the public identifier via Order#public_id in the success page, tickets email, admin "Link to order confirmation" actions, attendee order list, Signal message, ICS descriptions, the ?order_id= success URL returned by the payment methods, and the ticketholder name/email pagelets.

Order.find_by_id_or_token resolves by token first and falls back to the ObjectId only for orders that predate tokens, so links in already-sent emails keep working while tokenised orders are not reachable by id. Tokens are minted only on create, so a later save does not backfill a token onto a legacy order (those keep ObjectId as public_id).

Public token URLs send Referrer-Policy: no-referrer so the secret is not forwarded to third parties, including GoCardless return and pending confirmation URLs. The event page only shows the success card once the order is actually paid (after an Open Collective check on ?success=). The payment_completed poll JSON no longer includes the Mongo id.

Ticketholder GET routes now 404 on an unknown ticket instead of raising.

Merge order_refunds_test.rb into a new orders_test.rb covering the token behaviour alongside the existing refund tests.

e1eabd2

Don't let an abandoned OmniAuth signup attach to a later account.

86092ac

Don't let unsent pmail drafts be read on the public web view.

9a677ae

Don't let editor HTML run as script on web-rendered ticket emails.

41ef53d

Don't let an event editor spoof last_saved_by or duplicate to change privileged flags.

f757f97

Don't show ticket-email content for unpaid orders.

EVM and Open Collective return the order id before payment, and the confirmation page rendered Zoom/recording details for any order.

90e2323

Don't let mass assignment trigger Dragonfly remote URL fetches.