Skip to content

Instantly share code, notes, and snippets.

@judell
Created April 13, 2026 22:29
Show Gist options
  • Select an option

  • Save judell/8c8678b90b3ab199360fc967a2728f67 to your computer and use it in GitHub Desktop.

Select an option

Save judell/8c8678b90b3ab199360fc967a2728f67 to your computer and use it in GitHub Desktop.
Event rewriting spec for WFHB: viewer (Jon) + moderator (Chuck)

Event Rewriting for WFHB

WFHB needs to edit certain events as they flow from the community-calendar database to the iframed viewer. The approach: add an eventOverrides object to the existing excluded.json file that WFHB already manages in GitHub. The community-calendar viewer already fetches this file via the ?exclude= query param; it just needs to apply overrides in addition to exclusions.

Two repos are involved:

  • community-calendar (Jon) — the shared viewer that any city can iframe
  • wfhb (Chuck) — the WFHB-specific moderator UI

Part 1: community-calendar viewer (Jon)

What to change

File: xmlui/helpers.js

The function _filterExternalExclusions (line ~1419) currently reads window.externalExclusions and filters events by excluded sources, categories, and individual UIDs. After filtering, add a rewrite step that applies per-event overrides.

Add a new function right after _filterExternalExclusions:

var _applyExternalOverrides = function(events) {
  var exc = window.externalExclusions;
  if (!exc || !exc.eventOverrides) return events;
  var overrides = exc.eventOverrides; // { "source_uid": { "title": "...", "location": "...", ... } }
  return events.map(function(e) {
    if (e.source_uid && overrides[e.source_uid]) {
      return Object.assign({}, e, overrides[e.source_uid]);
    }
    return e;
  });
};
window.applyExternalOverrides = function(events) {
  return window.xsTraceWith
    ? window.xsTraceWith("applyExternalOverrides", function() { return _applyExternalOverrides(events); },
        function(result) { return { inputCount: (events || []).length, overrideCount: Object.keys((window.externalExclusions || {}).eventOverrides || {}).length }; })
    : _applyExternalOverrides(events);
};

File: xmlui/Main.xmlui

Insert applyExternalOverrides into the processing pipeline. Currently (line ~140):

window.filterExternalExclusions(combinedEvents)

Change to:

window.applyExternalOverrides(window.filterExternalExclusions(combinedEvents))

Overridable fields

Allow overrides for any display field: title, start_time, end_time, location, description, url, image_url, all_day, category. The override object is a partial — only specified fields are replaced.

Data format in excluded.json

{
  "excludedSources": ["..."],
  "excludedCategories": ["..."],
  "excludedEvents": ["..."],
  "reviewedEvents": ["..."],
  "lastReviewed": "...",
  "requireApproval": true,
  "eventOverrides": {
    "source-uid-abc": {
      "title": "Corrected Event Title",
      "location": "Updated Venue"
    },
    "source-uid-xyz": {
      "start_time": "2026-04-20T19:00:00"
    }
  }
}

Part 2: wfhb moderator (Chuck)

What to change

The moderator app lives in the wfhb repo. It has two files: index.html (state management + helper functions) and Main.xmlui (UI). Both need changes.

index.html — state and helpers

1. Add eventOverrides to the state model.

In the _state declaration (line ~26), add eventOverrides:

var _state = {
  excludedSources: [], excludedCategories: [], excludedEvents: {},
  reviewedEvents: {}, lastReviewed: null, requireApproval: true,
  eventOverrides: {}   // <-- add this
};

2. Load overrides from excluded.json on startup.

In the block that parses the loaded JSON (around line ~38), add:

_state.eventOverrides = loaded.eventOverrides || {};

3. Add helper functions for managing overrides:

window.getEventOverrides = function() {
  return getState().eventOverrides;
};

window.getEventOverride = function(sourceUid) {
  return getState().eventOverrides[sourceUid] || null;
};

window.setEventOverride = function(sourceUid, field, value) {
  var s = getState();
  if (!s.eventOverrides[sourceUid]) s.eventOverrides[sourceUid] = {};
  s.eventOverrides[sourceUid][field] = value;
};

window.removeEventOverride = function(sourceUid) {
  delete getState().eventOverrides[sourceUid];
};

window.hasOverride = function(sourceUid) {
  return !!getState().eventOverrides[sourceUid];
};

4. Include overrides in buildExcludedContent.

In buildExcludedContent (line ~390), add eventOverrides to the data object:

var data = {
  excludedSources: s.excludedSources.sort(),
  excludedCategories: s.excludedCategories.sort(),
  excludedEvents: Object.keys(s.excludedEvents).sort(),
  reviewedEvents: Object.keys(s.reviewedEvents).sort(),
  lastReviewed: s.lastReviewed || null,
  requireApproval: s.requireApproval,
  eventOverrides: s.eventOverrides   // <-- add this
};

5. Include overrides in hasUnsavedChanges / _savedSnapshot.

Add JSON.stringify(_state.eventOverrides) as an element in both the snapshot array (line ~121) and the comparison array in hasUnsavedChanges (line ~131). Do the same in getChangeSummary and markSaved.

For getChangeSummary, add a diff for overrides:

var savedOverrides = JSON.parse(saved[6] || '{}');
var curOverrides = _state.eventOverrides;
var overrideChanges = JSON.stringify(curOverrides) !== JSON.stringify(savedOverrides);
if (overrideChanges) parts.push('event edits changed');

Main.xmlui — UI

1. Add an edit dialog.

Add a ModalDialog for editing events, similar to the existing detailDialog. It should have TextBox fields for title, location, start_time, end_time, and a TextArea for description. Pre-populate from the event's current values (which may already have an override applied). Example:

<ModalDialog id="editDialog" title="Edit Event" var.editTitle="{''}" var.editLocation="{''}" var.editStartTime="{''}" var.editEndTime="{''}" var.editDescription="{''}" var.editUid="{''}">
  <VStack gap="$space-2">
    <Text fontSize="$fontSize-sm" value="Title" />
    <TextBox initialValue="{editTitle}" onDidChange="(val) => editTitle = val" />
    <Text fontSize="$fontSize-sm" value="Location" />
    <TextBox initialValue="{editLocation}" onDidChange="(val) => editLocation = val" />
    <Text fontSize="$fontSize-sm" value="Start time (ISO)" />
    <TextBox initialValue="{editStartTime}" onDidChange="(val) => editStartTime = val" />
    <Text fontSize="$fontSize-sm" value="End time (ISO)" />
    <TextBox initialValue="{editEndTime}" onDidChange="(val) => editEndTime = val" />
    <Text fontSize="$fontSize-sm" value="Description" />
    <TextArea initialValue="{editDescription}" onDidChange="(val) => editDescription = val" rows="{4}" />
    <HStack gap="$space-2" marginTop="$space-2">
      <Button label="Save edits" onClick="() => {
        if (editTitle !== $param.title) window.setEventOverride(editUid, 'title', editTitle);
        if (editLocation !== $param.location) window.setEventOverride(editUid, 'location', editLocation);
        if (editStartTime !== $param.start_time) window.setEventOverride(editUid, 'start_time', editStartTime);
        if (editEndTime !== $param.end_time) window.setEventOverride(editUid, 'end_time', editEndTime);
        if (editDescription !== $param.description) window.setEventOverride(editUid, 'description', editDescription);
        refreshKey = refreshKey + 1;
        editDialog.close()
      }" />
      <Button when="{window.hasOverride(editUid)}" label="Remove edits" variant="outlined" onClick="() => { window.removeEventOverride(editUid); refreshKey = refreshKey + 1; editDialog.close() }" />
      <Button label="Cancel" variant="outlined" onClick="editDialog.close()" />
    </HStack>
  </VStack>
</ModalDialog>

2. Add an Edit button to event cards.

In the Included tab's event cards (and optionally the New tab), add an Edit button next to the existing Exclude button:

<Button label="Edit" variant="outlined" size="sm" onClick="() => {
  editTitle = $item.title;
  editLocation = $item.location || '';
  editStartTime = $item.start_time || '';
  editEndTime = $item.end_time || '';
  editDescription = $item.description || '';
  editUid = $item.source_uid;
  editDialog.open($item)
}" />

3. Visual indicator for edited events.

Show that an event has been edited, e.g. add to each card:

<Text when="{window.hasOverride($item.source_uid)}" fontSize="$fontSize-xs" color="$color-primary-500" value="(edited)" />

4. Apply overrides to the display in the moderator itself.

So the moderator shows the edited versions, add a function that applies overrides to the event list before display. In getVisibleEvents and related functions, or as a wrapper when passing events to the UI, map events through eventOverrides the same way the viewer will.

Testing

  1. Edit an event in the moderator, save to GitHub
  2. Load the community-calendar viewer with ?exclude= pointing to the wfhb excluded.json
  3. Verify the event displays with the overridden field values
  4. Verify the "Remove edits" button in the moderator restores original values
  5. Verify hasUnsavedChanges correctly detects override changes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment