| Age | Commit message (Collapse) | Author |
|
Title on the first line, date, revision and editor on the second, with the
two actions as an icon grid. Four columns become two, so the table needs no
mobile card treatment.
|
|
Builder escapes by default; the three feed templates no longer call
CGI.escapeHTML. This fixes two sites that never escaped at all: the tag
feed's externally supplied :tag segment, interpolated into its title, self
link and id, and dc:creator in the RDF template.
Subscribers see one difference: quotes and apostrophes arrive raw, which is
valid in element text.
config/initializers/xmlparser.rb, which redefined Builder::XmlBase#_escape
as the identity function, is gone. XML::Node#replace_with went with it, no
callers.
|
|
|
|
diff_against gained external_url, published_at and user. The view renders
each metadata section only when that attribute changed, and says so once
when none did.
Metadata now precedes the locale pointer, which speaks only of translations:
above the metadata it read as "nothing changed" on a revision that had moved
the page.
|
|
The picker moves from assets/edit to assets/show, where the list of attached
pages already is.
A member route, POST assets/:id/attach_to_node, replaces the ride on #update.
Supersedes the picker wiring in the previous commit; what remains of it is
@attach_node in #create, the flash composition and the corrected hint.
|
|
|
|
Node#attach_asset! writes to the node's draft alone, creating one from the
head when none is pending. It refuses when another user holds the lock, and
when an autosave already exists
The asset_attach verb goes with it. create_new_draft names the editor on the
draft it creates and the publish entry's asset delta reports the attachment.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Any holder may add or remove another account, witnessed as
redaktion_grant/revoke so the vouching is legible. Not behind elevation:
onboarding must not wait for a keyholder, and a compromised Redaktion
account can already publish.
|
|
Administrative actions are gated behind a 30-minute elevation window:
creating and retiring accounts, editing roles, clearing a second factor.
Reading the list is not gated, and content work is untouched.
elevated? is tied to is_admin?, so losing the role closes the window at
once. The window opens when the second factor verifies at login, so an
admin heading straight for user management is already elevated, and closes
on logout with the other session state. Five wrong codes end the session,
mirroring the login challenge.
users#update carries no elevation filter, since self-service reaches it;
the role field is gated in user_params instead and fails closed.
|
|
RoleRequired supplies require_redaktion and require_admin for surfaces that
are not nodes and so cannot be reached by Node#restricted?.
Navigation is content rather than plumbing, so menu_items requires
redaktion. User management is janitorial and requires admin: index, new,
create, reset_otp, deactivate, reactivate. verify_status now also covers
show, without which any logged-in user could read any account by walking a
small id space. Editing your own account stays open.
The dashboard hides the Users and Navigation buttons from those who cannot
use them; everything else stays visible to everyone. Both denials share one
message and land on the dashboard.
Adds redella (redaktion) and alufa (redaktion + alumni) fixtures.
|
|
CccConventions::TAG_SCOPES maps "update" and "pressemitteilung" to /updates
and "disclosure" to /disclosure. Page.aggregate applies the mapping, so an
aggregate over one of those tags is bounded by its subtree regardless of what
the shortcode says. Tags stay unrestricted; positions are publish-gated.
Both RSS actions call Page.aggregate rather than repeating its tag join.
Empty feeds no longer crash: Atom falls back to the current time for the
required <updated>, and the optional dc:date is omitted.
|
|
publish_draft!, trash!, destroy_from_trash!, attach_asset! and
Asset#destroy_witnessed! now refuse unless the acting user holds redaktion,
and only when the subject is on a restricted surface: the front page, the
updates tree that feeds ~100k subscribers, or disclosure. Drafting,
autosaving, tagging and creating stay free everywhere for everyone.
Enforcement is in the models rather than the controllers, since attach_asset!
and the rest are reachable from rake tasks and internal paths. It follows the
errors.add-plus-bare-raise pattern the rest of Node already uses, so every
existing RecordInvalid rescue reports it with a localised message; only
assets_controller#destroy needed a rescue added.
A nil user is treated as a system context and bypasses the gate. The default
nil on three of those verbs is what makes that reachable, and removing those
defaults once every call site passes a user is the next tightening.
|
|
Deactivation adds the alumni role and leaves the others in place, so
reactivation is lossless and nobody has to remember what an account held.
login_from_session checks alumni? on every request, so a signed-in user is
locked out on their next one without any session invalidation. Guards
prevent deactivating yourself or the last active admin, and both verbs are
witnessed in the action log.
|
|
Replaces the two-way admin/user split with four groups ordered by
capability: administration, Redaktion, editors, alumni. alumni takes
precedence over capability in role_group, so a retired admin appears at the
bottom rather than the top.
Forms now offer the three roles as checkboxes rather than a single admin
checkbox, with a trailing hidden blank so an empty set can be posted, and
user_params permits roles only for admins. Three create buttons prefill the
common combinations.
|
|
|
|
German content is reachable at both /updates/foo and /de/updates/foo,
and a page with no English translation renders German at
/en/updates/foo through the fallback chain, so three URLs each
claimed to be canonical. Both tags now derive from one
locale-to-URL function, which points every duplicate at the address of
the version actually served, the unprefixed German URL, matching
default_url_options.
Alternates are declared only for locales in which the page genuinely has
a translation, since /en/ serving German through the fallback is not an
English version of the page. x-default points at German, the site's
primary language.
|
|
Replaces one hardcoded German description and an unrenderable SVG with
per-page title, description, canonical URL, locale and publication date,
plus the card variant or a site-wide default.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Enforced at restore via a login-time stamp, written only at genuine
logins so the limit stays absolute rather than sliding. The cookie
name rotation logs everyone out once at deploy. Second-factor users
are deliberately not treated worse than password-only ones.
|
|
Enrolled users get a pending marker instead of a session after the
password step; a valid code through the challenge writes the real
session via reset_session. otp_required without enrollment funnels
into setup everywhere except the enrollment, user, and login
machinery.
|
|
|
|
Asset names in summaries and publish deltas link to assets#show with
an inline Chronik beside each, suppressed inside that asset's own
zoom, per the node convention. assets#show gains a history button.
Also renames the details summary (no longer only translations) and
moves View Diff onto its own line.
|
|
ensure_autosave! gives body keystrokes and asset curation one shared
layer, so head is never mutated in place and every curation change
surfaces in the publish delta. Stale rendered join ids are mapped
across the clone via asset_id. Curation now requires holding the
lock; a missing lock answers 423, matching the autosave endpoint.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|