Dandelion is a fair source project. View the code on Github · See the design guide
Commits on Oct 15, 2026
| 289a02e |
Update iframe-resizer from 4.2.10 to 4.4.5, the last MIT release. Versions 4.4.0 to 4.4.4 and all of 5.x are GPL-3.0, with a commercial licence for other projects, which Dandelion's FSL doesn't fit; 4.4.5 went back to MIT. The child script's changes since 4.2.10 are refactors, small fixes (the print events listener) and opt-in mouse events, off by default. The embed docs' parent script moves to the same version. |
| a436476 |
Update jQuery UI to 1.14.2 and countUp.js to 2.10.1. jQuery UI 1.14 drops IE and removes $.ui.ie, $.ui.safeActiveElement, $.ui.safeBlur and $.fn._form, none of which we use; the widgets we use (autocomplete, menu, sortable, draggable, droppable) are unchanged. countUp.js keeps its UMD build at dist/countUp.umd.js with the countUp global and the CountUp constructor and options we use. Both releases are past the 7-day cooldown. |
| 85293a4 |
Update gems with bundle update, and compare membership payments with $expr rather than $where, which Mongoid 9.1 rejects. The update includes padrino 0.17.0 (was 0.16.1), mongoid 9.1.1 (was 9.0.11), mongoid_paranoia 0.7.0 (was 0.6.0), gocardless_pro 4.20.1 (was 4.3.0) and activemodel/actionview 8.1.4. Mongoid 9.1 raises InvalidQuery on JavaScript $where strings unless allow_unsafe_query_operators is set, so the gathering members page's More to pay, No more to pay and Overpaid filters failed. They now compare paid and requested_contribution with $expr, which the server evaluates natively, in one query rather than plucking ids first. Dandelion/MongoidWhere no longer lets a string through |
| 7de2268 |
Stop browsers autofilling the password field on /accounts/edit. The field had no autocomplete, so a browser or password manager could fill in the saved password, and since a present password makes encrypt_password run, every profile save re-hashed it at full BCrypt cost: an untraced gap of about 350 ms in median POST /accounts/edit traces. password_block, used only there, now sets autocomplete="new-password", as secret_block does. |
| c4f509b |
Name the rejected field in GoCardless purchase errors. A failed GoCardless purchase's email (purchase_failed) and the event's locked_error showed only the error's message, "Validation failed", so the field GoCardless rejected could only be found in its dashboard. EventPaymentMethod.purchase_error_message adds a GoCardless error's field errors (field or reason, message and request pointer) and its request ID; other errors keep their message. _purchase_error shows each line of the message escaped, with a break between them. |
| 652d6d0 |
Make location pages' AI summaries in late:errands rather than over XHR. GET /locations/:name loaded its summary as a pagelet that called the model inside the request, cached for a day as a fragment, so the first visitor each day waited on OpenRouter. Location#generate_summary now writes it to Location#summary (markdown, protected from forms) each night, keeping the last one if the model doesn't answer, and the page shows it when there is one. The geocoded bounding box moves from map_helper into lib/geo/geocode_bounds.rb (GeocodeBounds.for), unchanged, so Location#events can find a location's events outside a request. |
| 082ca5e |
Space badges that sit side by side, so they don't touch when they wrap. A badge is taller than its line, and only badges given mb-1 by hand had space below them, so in places that missed it, like an order's price cell (a discount badge above a GoCardless instalments badge), the rows touched. Any badge beside another badge now gets mb-1's space below it in app.css; flex and grid rows keep spacing theirs with gap. The mb-1s this makes redundant, on badges that are siblings of other badges (tag and team lists, gathering options and tickets, the stats rows' Dandelion and Stripe figures, the design guide's examples), are removed; badges wrapped one to a link or span, and lone ones, keep theirs. |
| aa5d03d |
Fix image derivatives never being warmed after an upload. set_warm_image_derivatives checked image_changed? in a before_save that ran after the image was stored, which marks it unchanged, so warm_image_derivatives (the 400x400# thumbs and OpenGraph images) was never queued, and each new image's derivatives were made on its first page view. It now runs before the image is stored. |
| 4da1448 |
Add spans for image uploads, so their time shows in traces: image.assign (the size and format checks, and queuing the conversion and resize, when an image is assigned), image.validate (validate_image_format) and image.store (applying the processing and writing to S3). ImageMagick and S3 (through Fog and Excon) make no spans of their own, so a POST /accounts/edit with a new profile photo showed 3.3 s of its 3.4 s as unexplained gaps. To time the store, the image is now saved by our own before_save, declared before dragonfly_accessor's, which then finds it stored. |
| 4190b1d |
Send event mails' Signal messages in a background job. Send test waited on the Signal API inside the request (5.4 s, returning 400, in this morning's slowest POST /pmails/:pmail_id/edit traces); the job gets the ids of the recipients with a phone and loads them when it runs, as orders already do with send_signal_order_link. |
| 9665841 |
Fix saving traces since sentry-ruby 7: its request interface now gives query_string as the parsed params (request.GET, with its own sensitive keys already [Filtered]) rather than a string, so Trace.filter_query raised calling split on a Hash and no trace was saved. It now rebuilds the query from the params, filtering SECRET_QUERY_PARAMs and keeping Sentry's [Filtered] values, and still filters strings, as spans' http.query are. Fixes DANDELION-48 |
| 9edbbda |
Replace Tribute with our own @ mention menu, and store mentions in comments as @username rather than @Name. Typing @ and part of a name or username in a comment box lists matching people from your network under the box, each with their avatar, name and @username, styled like a dropdown menu (hairline border, base shadow, radius lg, fading in over --transition-enter), with "Keep typing a name or username" before two letters and "No one found" after; arrows and Enter or Tab, or a click, put in @username, and Enter only posts when no one is listed. Mentions are linked on the server rather than by the .tagify script: Comment#body_html shows each mention of an existing account as the account's name, linked to its profile, and leaves other @words (and email addresses) as they are, and emails use the same links. Usernames can contain dots, which the old pattern missed, so those mentions never linked or subscribed anyone; one ending in a dot is that account if it exists and otherwise the account without the dot, at the end of a sentence, and only the account a mention links to is subscribed. /network also returns each account's avatar and avatar class. Production's 94 comments in the old format are rewritten by one-off code, keeping the name of accounts that no longer exist. |
| 1d88dcf |
Update sentry-ruby and sentry-delayed_job from 6.5.0 to 7.1.0. 7.0 removed enable_logs (logs are now on by default), which would have raised at boot, and deprecated send_default_pii for data_collection, so the config sets data_collection to the SDK's full defaults, which is what send_default_pii = true now gives (user, cookies, headers, bodies and query strings, less a denylist of sensitive keys). Metrics are now on by default too, but nothing we use emits any. |
| 6786780 |
Update redis from 5.4.1 to 6.0.0 (and redis-client from 0.28.0 to 0.30.1, which it pins). 6.0 speaks RESP3 by default, which needs Redis 6 or later on the server, and requires Ruby 3.2+. We only use Redis through ActiveSupport's RedisCacheStore (Rack::Attack, the geocode cache and WhatsApp webhook deduplication), whose plain get/set/increment calls RESP3 doesn't change. |
| 480d542 |
Update stripe from 19.1.0 to 20.0.0. None of its breaking changes touch Dandelion (no Stripe::Reversal or payment_method_types), and StripeOpts pins every request's API version from STRIPE_API_VERSION, so the gem's new default API version doesn't apply. |
| 939e8e3 |
Rename FSL-1.1-ALv2.md to LICENSE.md, so GitHub recognises the repo's licence (it looks for license files by name) for people finding Dandelion through the fair-source topic. |
| 6536c07 |
Decide who gets an event's discussion emails by the post's subscriptions alone, dropping Ticket#subscribed_discussion. The ticket setting only counted when the post was made, so the "Unsubscribe from this event" link at the foot of discussion emails, which only turned it off, never stopped them. Turning email notifications on before anyone has written now makes the discussion post (for people who can see it) and subscribes to it; emails link only to the post's unsubscribe, and /events/:id/unsubscribe_discussion, kept for older emails, removes the subscription to the event's post. |
| 91e0d1b |
Make an event's discussion post with its first comment, rather than on the first view of the event page, which created a "Chat for |
| 75f800d |
Bring an event's discussion in line with the design guide: headed "Discussion", with "Only attendees and facilitators can see this" under it, and without a chat a card with a .chill header like the feedback card above it, rather than a bare h2. The chat panel opens and closes from one button whose chevron turns (the Motion rule), rather than two swapped links, and takes all four corners when closed. The reaction picker's emoji and word box wrap as one row, with the placeholder "or word/emoji", and read receipts keep a gap above them when the row wraps. |
| eada18d |
Give the Answers columns of the orders and feedback tables a minimum width of 20rem, with an .answers-col rule in app.css (replacing the feedback table's inline style), so DataTables doesn't squeeze the orders table's into a few words per line. |