| Age | Commit message (Collapse) | Author |
|
|
|
|
|
|
|
|
|
|
|
|
|
Sorts rules by hierarchy (base -> layout -> navigation -> buttons ->
tables -> forms -> components), consolidates three duplicated media
query breakpoints into one each, and adds section comments. Bundled
with this reorganization, since the file was rewritten wholesale:
- form.button_to.state_changing: new tinted-pill variant (blue) for
Publish/Restore/Unlock/Revoke, matching .destructive's pattern
- form.button_to.destructive: now a tinted pill at rest instead of
plain colored text, consistent padding at rest and on hover
- Scoped wavy-underline link-visibility fix for #page_editor,
table.node_table, table.assets_table, table.events_table,
table.user_table, .add_child_links, and the dashboard draft list
- #flash decoupled from the page's structural nav-to-content spacing
(.admin_content_spacer), which the flash div was silently providing
as a side effect whenever it rendered
|
|
|
|
|
|
|
|
parent_match Procs on CccConventions::NODE_KINDS, matched against
unique_path, decide which "add child" kinds show on a given node. Fixes
nodes#new not honoring a pre-selected kind (radio group and parent-field
visibility both defaulted to "generic" unconditionally).
|
|
Root-caused this session: appending a child to any node never widened
that parent's own rgt boundary, on the pinned revision (Gemfile tracked
main directly, chasing a too-conservative gemspec constraint - not, as
first assumed, a deliberate pin to avoid a known bug). Reproduced
cleanly on a single ordinary create with no concurrency and no bulk
operation involved, confirmed via the gem's own SetValidator, then
confirmed as the root cause of nodes_controller_test.rb's 3 long-standing
"pre-existing" failures - not three separate mysteries, one bug.
admin_controller's sitemap needed its own real conversion, not just a
drop-in: awesome_nested_set's lft column implicitly provided correct
depth-first tree order for free, which the old code combined with a
separate class-level each_with_level iterator. Both replaced by one
method, self_and_descendants_ordered_with_level, computing an ordered
[node, level] list in a single query-then-walk pass - checked against
the actual view template first (admin/index.html.erb) rather than
assumed, since it relies on list order alone to render correct visual
nesting.
lft/rgt/depth columns intentionally left in schema, unused - dropping
them is a separate, deliberately deferred migration once this is proven
running for a while, not bundled with the behavior change.
|
|
New :children option ("direct" or "all") on the [aggregate] shortcode,
composable with the existing :tags filter - both apply as independent
successive .where clauses, ANDing together automatically, no special
casing needed. content_helper.rb passes the current @page.node through
as :node whenever :children is requested, since Page.aggregate has no
way to resolve "which node" from the shortcode's bare option strings on
its own. Guarded so nothing changes for any existing [aggregate]
shortcode that never uses children= - :node stays nil, the new branch
never fires.
"all" resolves via node.descendants.pluck(:id) rather than embedding the
descendants relation directly as a subquery. Not proven strictly
necessary - extensive debugging this session initially pointed at a
subquery/table-alias collision, but every actual failure traced instead
to accumulated test-node debris left in the dev DB by earlier
interrupted runs, confirmed by re-running against a cleaned database.
Kept anyway as a one-round-trip-cheaper, more defensive default
regardless.
|
|
staged_slug existed to protect a live public URL from changing until
an editor explicitly publishes - correct once a node has a head, but
nodes_controller#update wrote to it unconditionally, so a brand-new
node being renamed before its first-ever publish showed a stale slug
in previews (e.g. nodes#new's resulting-path preview) that silently
diverged from what would actually go live.
Node#staged_slug= now applies directly to slug when head is blank -
nothing is live yet, so there's nothing to protect. Once head is
present, defers to staged_slug exactly as before. Verified both paths
directly: a never-published node's slug now updates immediately, and
an already-published node's rename still stays deferred until the
next publish, unchanged from existing behavior.
|
|
|
|
nodes#show's flat <table> of label/value pairs grew unreadable once
Admin Preview and Public Preview were added alongside Public Link -
switched to the div.node_description/div.node_content pattern already
used by nodes#edit, grouped into People/Dates/Links/Revisions/Tags/
Events/Children sections rather than one undifferentiated list.
People/Dates/Links use a flex-based item layout, not a second <table> -
multiple independent tables sharing one outer width but different
column counts produced misaligned columns with no shared grid. Flex
items size to their own content with a shared minimum instead.
Revisions and Children are both collapsed via <details>, no JS needed -
previously nodes#show didn't list a node's children at all, blocking
the ability to find/navigate erfa and chaostreff pages without already
knowing they're now standalone nodes. Revision list items link to
node_revision_path(@node, page) - the actual per-revision diff/restore
view, not the plain index.
nodes#new converted to the same pattern for visual consistency, plus a
few things surfaced along the way: submit buttons were unstyled
site-wide (a bare input[type=submit] with no border, fill, or hover
state - Create on this exact page was easy to miss entirely), fixed
with a bordered/bold treatment reusing the existing form.button_to
visual language rather than introducing a fourth button style. Title
regained the bold weight it had before the table rewrite dropped it
silently. Tag list grouping and full link/button semantic taxonomy
(show vs edit/add vs publish/revoke vs destroy, applied consistently
across every link on the page, not just buttons) are known follow-ups,
not attempted here.
|
|
A token's page could stop being node.head_id (superseded by a newer
draft) while still being published_at.present? - the old check only
compared against the current head, so a superseded page fell through
to direct rendering instead of redirecting, serving a stale frozen
snapshot indefinitely instead of the current live content.
Also handles scheduled publishing correctly: a page can be head_id but
not yet public? (published_at in the future) - that case must still
render directly, not redirect into a 404 on the not-yet-live public
URL.
|
|
Lets editors share a draft with people outside the admin system, via a
random per-page token rather than an enumerable id - /preview/<token>.
shared_previews#show is intentionally unauthenticated. Redirects to the
real public URL once the page is published. Surfaced on nodes#show
(Admin Preview + Public Preview, next to Public Link) as generate/revoke
buttons.
|
|
New column + unique index, plus ensure_preview_token!/revoke_preview_token!
on Page. Generated lazily (only when explicitly requested) rather than
via has_secure_token's default auto-generate-on-create, so a live,
shareable secret isn't silently minted for every page ever created.
|
|
Forms were already cleaned up in an earlier commit; the column itself
was the last remnant.
Dropping the column surfaced a live reference in events#index (fixed
separately, 970f108) - this migration file itself just never got
staged until now.
|
|
Was the last remaining gap in the occurrence-orphaning fix from earlier
this session: dependent: :destroy on Event#occurrences covers deletion
through ActiveRecord, this covers anything that bypasses it (raw SQL,
Model.delete). Confirmed zero existing orphans and zero NULL event_ids
before adding the constraint (event_id stays nullable - an occurrence
without an event is valid, one with a bogus event_id is not). Also adds
the index that should have existed on this column already - Postgres
needs one for the cascade to be anything other than a full table scan
per delete, and none existed before this. deferrable: :immediate added
so legitimate application code can still defer the check within a
transaction if ever needed - unrelated to, and not a fix for, the
fixture-loading issue below.
Applying this constraint broke fixture loading entirely (config fix:
81a07bf) and exposed a live bug in events#index (fix: 970f108), both
already committed separately - this migration file itself just never
got staged until now.
|
|
test_can_remove_a_node_with_an_event previously just called node.destroy
and get :index with no assertions at all - would pass whether or not
occurrences were actually cleaned up, or even if index rendered
correctly afterward. Now confirms occurrences genuinely exist before
destroy (otherwise a passing post-destroy count of zero is meaningless -
indistinguishable from "nothing to cascade in the first place"), scopes
the count to this event specifically rather than a global Occurrence.count
that could coincidentally pass regardless of whether this cascade works,
and checks the trailing index request actually succeeds rather than just
not raising.
First real test of the occurrences.event_id FK constraint added earlier
this session, not just the application-level dependent: :destroy.
|
|
events#index still read event.custom_rrule - a live bug the column-drop
migration introduced, not just stale test data. Caught by four test
failures (three fixtures/direct attribute hashes still setting the
removed column, one UnknownAttributeError from a stale fixture loaded
before any test method runs), none of which were actually testing this
view - "should get index" passed throughout with zero Event records
present, meaning it could never have caught a per-row rendering bug.
Strengthened to create one real event first, so a future stray
reference to a dropped or renamed column fails loudly instead of
silently passing on an empty table.
|
|
Adding the occurrences.event_id FK constraint broke fixture loading
entirely (every test failing at setup) with a misleading "Foreign key
violations found in your fixture data" error. The real cause: this
Rails feature does its own proactive integrity check during fixture
insertion, independent of the constraint's own deferrability, by
directly flipping pg_constraint.convalidated and re-validating - which
needs catalog-level privilege the app's DB role correctly doesn't have.
No actual fixture data was ever invalid; the check itself couldn't run.
Scoped to test.rb only - this never affected real application behavior,
and Postgres still fully enforces the actual constraint everywhere,
in every environment, regardless of this setting.
|
|
|
|
The following changes were already announced in the last commit, but
the files forgotten. Here's them actually attached.
lib/ccc_conventions.rb: NODE_KINDS registry replaces the kind-specific
branches previously scattered across nodes_controller#create (tags),
unique_path-position check). Each kind declares its parent lookup
(a Proc - Update's "update"/"press_release" continue to genuinely
find-or-create their year-folder via Update.find_or_create_parent;
erfa/chaostreff use a plain find_by_unique_name!, since their parent
nodes are fixed and a missing one should fail loudly, not be silently
created), its tags, and (new) its default template.
New read-only admin/conventions view dumps the NODE_KINDS registry as
a reference table for whoever's confused later about how a given kind
behaves - deliberately just renders the same label/hint text used on
nodes#new rather than re-describing parent/tags/template separately,
since decomposing the parent Proc would mean either invoking it
(Update's has a real side effect - creates the year folder) or
re-stating its logic a second time in a different shape, either of
which risks drifting from the actual behavior.
|
|
|
|
nodes#new: each kind's radio button now shows a label plus a smaller,
muted .field_hint explaining what happens automatically (parent,
tags, template) - previously a single undifferentiated string mixing
a short label with a long parenthetical, cluttering the smaller kinds
(erfa/chaostreff) worst. Title field gets the same hint treatment,
telling editors up front that the slug is auto-generated and where to
adjust it later - the previous silence here meant editors only
discovered slug generation after already committing to a title, with
no visible way to fix it.
New "Resulting path" row live-previews the full URL (origin + kind's
path prefix + a debounced call to the new parameterize_preview route,
reusing the exact same slug_for helper "create" now calls, so the
preview can never diverge from what actually gets created) as the
editor types, plus a copy-to-clipboard button. For "generic", the
prefix comes from whichever parent gets chosen via the search widget,
not the registry - parent_search.link_closure now stashes the chosen
node's unique_name as a data attribute for exactly this.
New read-only admin/conventions view dumps the NODE_KINDS registry as
a reference table for whoever's confused later about how a given kind
behaves - deliberately just renders the same label/hint text used on
nodes#new rather than re-describing parent/tags/template separately,
since decomposing the parent Proc would mean either invoking it
(Update's has a real side effect - creates the year folder) or
re-stating its logic a second time in a different shape, either of
which risks drifting from the actual behavior.
nodes#edit's Template field gets a one-line hint noting the value may
already be pre-filled based on how the node was created - the one
place the previous commit's new inheritance mechanism actually
surfaces on this view.
|
|
lib/ccc_conventions.rb: NODE_KINDS registry replaces the kind-specific
branches previously scattered across nodes_controller#create (tags),
unique_path-position check). Each kind declares its parent lookup
(a Proc - Update's "update"/"press_release" continue to genuinely
find-or-create their year-folder via Update.find_or_create_parent;
erfa/chaostreff use a plain find_by_unique_name!, since their parent
nodes are fixed and a missing one should fail loudly, not be silently
created), its tags, and (new) its default template.
Node gains a default_template_name column (migration, with backfill
for existing update-tree nodes via node.update? - reusing that method
rather than re-deriving its unique_path check in raw SQL). Page#set_template
now inherits from node.default_template_name, falling back to the old
update?-based check only when the column is blank, and only fills in
template_name when nothing's already been explicitly chosen - a
deliberate change from the previous behavior, which unconditionally
overwrote template_name on every save regardless of manual selection.
Node#update? itself is unchanged and still used as-is by
admin_controller's sitemap filtering - a genuinely different, still
valid use of that check.
"generic" stays special-cased in the controller, parametrized by
params[:parent_id] at request time - doesn't fit "kind implies a fixed
lookup" and isn't in the registry.
nodes#new's four hardcoded radio buttons and nodes_controller's
kind-specific case/when branches are both replaced by iterating/looking
up this one registry - adding a new kind now means one new hash entry,
not four scattered edits.
|
|
layouts/admin.html.erb's top-bar search results div shared id
"search_results" with nodes#new/nodes#edit's parent-search widget -
since the layout renders on every admin page, whichever widget's JS
ran last silently won the shared ID, putting top-bar results into the
parent-search box on affected pages. Renamed to "menu_search_results"
and updated admin_search.js to match.
Also modernizes admin_search.js's event bindings from keyup/keydown/
keypress/paste/cut + .attr("value") to input + .val() throughout
(menu_items, parent_search, move_to_search) - the multi-event binding
was working around old IE compatibility that .val() + "input" already
handles correctly. parent_search's result rendering now matches
menu_search's convention (real <p>/<a> markup with a .result_path
span) instead of a bare <a> in a throwaway wrapper div, so the same
CSS rule now correctly applies to both.
menu_search's JSON response gains node_path per result, matching what
admin_search's own results already provide - not yet consumed by
parent_search/move_to_search, which still render click-to-select links
rather than navigable ones (correct for their purpose - selecting a
value, not leaving the page).
Known remaining gap, not fixed here: menu_items, parent_search, and
move_to_search still all target the literal id "search_results"
between themselves. No live collision today since none of the three
currently share a page, but it's the same fragility as the bug above -
tracked alongside the existing menu_items/parent_search/move_to_search
consolidation backlog item rather than treated as resolved.
|
|
The metadata-panel "Event" field on nodes#edit called event_information,
a second, tag-less implementation of "list events + add one" - now
redundant now that nodes#show has the tag-aware version with the
auto-tag flash explanation. Confirmed via grep this was the only call
site before removing both it and the method definition.
|
|
Defined only in ApplicationHelper, which Rails auto-mixes into views
but not controllers - so events#create and events#update, which call
it directly, have been broken since introduction. Likely unexercised
until now because every existing event was created via
node.events.create! in the seed script, never through a real POST.
Moved to ApplicationController as a protected method + helper_method
declaration, so both controllers and views can call it (form_error_
messages stays in ApplicationHelper - it's genuinely view-only,
content_tag isn't available in a controller either). Logic unchanged,
caught by the new EventsController tests in the previous commit.
|
|
nodes#show's events table now renders unconditionally (previously
hidden entirely for a zero-event node) and gains an "add event" link.
The suggested tag_list is derived from the page's own category tags
via NodesHelper::DEFAULT_EVENT_TAG_BY_PAGE_TAG (erfa-detail/
chaostreff-detail -> open-day) rather than hardcoded, and degrades to
a blank field for any node whose tags don't match - deliberately
universal, not chapter-specific, since Updates have historically
carried event dates the same way.
events#new surfaces *why* the tag was pre-filled via flash.now (not
flash - this is a same-request render, not a redirect), using an
explicit auto_tag_source param passed alongside tag_list rather than
having the controller re-derive the reason from node_id.
No destroy link added to nodes#show's events list - deliberate, per
existing subnav-semantics convention (destructive actions live on the
resource's own views). Covered directly by test.
Adds real coverage for EventsController, previously 100% commented-out
scaffold, plus unit tests for the new tag-mapping helper and the
weekday-abbreviation helpers from the prior commit.
|
|
|
|
|
|
|
|
RruleHumanizer gains WEEKDAY_NAMES_ABBR (Mo/Di/Mi/...) alongside the
existing WEEKDAY_NAMES and WEEKDAY_NAMES_ADVERBIAL hashes, plus a
self.wday_abbr(time, locale) utility mapping a Time's wday to its
RFC5545 code and looking up the abbreviation - keeping RFC5545
vocabulary in one place rather than teaching ContentHelper about day
codes directly.
ContentHelper#weekday_abbr is a thin wrapper passing I18n.locale
through. The partial now renders "Fr 19:00" instead of just "19:00",
joined with a non-breaking space so the pair can't split across a
line wrap.
Takes an explicit Time rather than reading Date.today, matching this
file's existing style of passing state in rather than reading it
ambiently - and staying correct if the currently-unused
calendar/_front_page_calendar partial (spanning six weeks, not just
today) is ever revived and reuses this helper.
|
|
|
|
- Event#occurrences is now dependent: :destroy, so destroying an Event
(directly, or via Node's cascade) removes its Occurrence rows instead
of orphaning them.
- Renamed generate_occurences -> generate_occurrences (typo fix);
callback and method updated together.
- Occurrence.generate returns early when event.start_time is nil, so a
chapter with no sourced time produces zero occurrences instead of a
guessed one.
- seed_chapter no longer falls back to 19:00 when start_time is absent;
base_time/end_time are nil in that case.
Database-level foreign key cascade on occurrences.event_id is still
pending.
|
|
- Derive rvm gemset path from .ruby-version and .ruby-gemset in the
app directory, with fallback to current hardcoded values — no more
manual rc.d edits on gemset changes
- New extra command: service cccms regenerate_occurrences
- Poststart staleness check: regenerates occurrences when marker file
/var/db/cccms_occurrences_regenerated is missing or older than one
year; runs only after successful server start, synchronously for
error visibility
Deployment note: updated script must be manually copied to
/usr/local/etc/rc.d/cccms on live.
|
|
- open_erfas_today helper: samples 3 random open-day-tagged occurrences
of the current day; widget hidden entirely on days without open
chapters; heading shows current weekday
- content/_open_erfas_today partial rendered in right column above
tags and featured articles
- _chapter partial: open-day schedules (event_schedule_text) shown
inline in aggregated chapter lists with 'Offene Tage' label
- ccc.css: open_erfas_today added to existing right-column widget
selector groups (headings, lists, links)
|
|
- Occurrence.generate_dates: use ChaosCalendar::occurrences_for_timezone
with Time.zone.tzinfo.identifier — occurrences now keep wall-clock
time across DST boundaries (19:00 stays 19:00 in summer and winter);
requires chaoscalendar gem with occurrences_for_timezone support
(Gemfile.lock bumped accordingly)
- Occurrence.find_in_range: joins(:event) inner join excludes occurrences
whose event no longer exists (107k orphaned rows from the pre-revival
calendar era were purged from the test DB); includes(:node) retained
for eager loading; column references qualified with table name
|
|
First application of subnav visual conventions to nodes: actions
column now shows show | edit | revisions as plain informational
links. edit intentionally uses edit_node_path (locking is expected
behaviour for nodes, unlike event views where node_path is used to
avoid inadvertent locking).
|
|
|
|
- seed_chapter now accepts events: array instead of flat rrule:/
start_time: params; each event hash supports rrule:, start_time:,
tag_list:, location:, duration_hours:
- Chapters with multiple open days now represented correctly
(Stralsund: Thursday Chaostreff + 2nd/4th Saturday OpenSpace;
Hamburg: 2nd Friday + last Tuesday; Stuttgart: 1st Tuesday +
3rd Wednesday; Freiburg: Mon+Tue open + biweekly Plenum;
Backnang: 3rd Sunday + 1st Tuesday; Tübingen: last Sunday +
2nd Monday at different venues)
- is_primary removed from migration entirely — replaced by
tag_list: 'open-day' on events
- Stale EN descriptions corrected: Berlin, Darmstadt, Erlangen,
Essen, Freiburg, Göttingen, Hamburg, Hannover, Karlsruhe,
Paderborn, Stuttgart, Ulm
- chaostreff-stralsund duplicate entry removed (Port39 is erfa only)
|
|
- Split events into open-day tagged events (prominent, via
tagged_with) and other events (secondary listing)
- Use event_schedule_text helper for humanized recurrence +
start time rather than raw rrule string
- Section headings via i18n open_days / upcoming_events keys
|
|
- Event: acts_as_taggable_on :tags — uses existing polymorphic
taggings table, no migration needed
- events/edit and events/new: tag_list field added, tag_list
permitted in events_controller strong params
- locales: add open_days (Offene Tage / Open days),
upcoming_events (Weitere Termine / Upcoming events),
event_schedule_time, event_schedule_unrecognized,
event_schedule_none keys
|
|
- events_controller: wire return_to through show and new actions;
create respects return_to with fallback to node/event path;
new pre-populates node_id and tag_list from params
- events/edit: remove custom_rrule checkbox; node link removed from
subnav (use show for node navigation); destroy button added
- events/new: remove custom_rrule checkbox; add return_to hidden
field and back link; tag_list field added
- events/show: fix back link via safe_return_to; add node link to
subnav; add destroy button; remove custom_rrule display; show
humanized rrule below raw string
- events/index: destructive class on destroy button; node_id column
replaced with node link; show/edit links carry return_to
- nodes/show: add show/edit links to event entries with return_to
|
|
- Extend all selectors to cover button[type="submit"] alongside
input[type="submit"] — Rails 8.1 generates <button> not <input>
- Add appearance: none / -webkit-appearance: none to kill browser
native button chrome
- Default hover now matches plain link hover (color: #ff9600)
- Add form.button_to.destructive variant: #cc0000 at rest,
white-on-red pill on hover
- Usage: form: { class: 'button_to destructive' } in button_to calls
|
|
- app/models/concerns/rrule_humanizer.rb: new concern included into
Event, renders recurring schedule as natural-language German or
English from RRULE string; handles WEEKLY/MONTHLY, biweekly
(INTERVAL=2), ordinal weekday positions (1TU, -1TH, -2WE),
BYMONTH single-month exclusions (December pause convention);
gracefully returns nil for COUNT/UNTIL/unrecognized shapes
- test/models/concerns/rrule_humanizer_test.rb: 15 tests covering
all distinct RRULE shapes found in production data
- app/helpers/nodes_helper.rb: add event_schedule_text helper
combining humanize_rrule with start_time formatting
- app/views/nodes/show.html.erb: add events row, conditionally
rendered when node has associated events
- config/locales/de.yml, en.yml: add event_schedule_time,
event_schedule_unrecognized, event_schedule_none keys
|