feat: listing type and category admin with the schema builder - #124
Merged
Conversation
Block 2, PR 1 of 4. Tenant-admin configuration for listing types and
their categories, at /admin on the tenant's own domain. Custom views,
not Django ModelAdmin -- writes go through directory/services.py so
they emit tenant.settings_changed and hit the command log
(invariant 6).
- directory/field_schema.py: validate_type_schema() -- the closed type
set, options rules, searchable only on text/long_text/select,
reserved-name and slug checks, and the frozen-field-type rule
(ruling 6: a field's type cannot change once it exists; add a new
key). normalize_type_schema() canonicalises a validated schema.
- directory/services.py: create/update/delete for ListingType and
Category. key is frozen after creation (update_listing_type refuses
it). path_segment is editable; changing it while multi-type, and
gaining a second type, each write a PathRedirect row (spec §4.5).
Patch pointers are a local convention (ruling 1):
/listing_types/{key} and /categories/{type_key}/{slug}. Deleting a
type with listings is blocked (PROTECT -> friendly error).
- HTMX schema builder: field rows are added/removed by fetching a
server-rendered partial; the set posts as index-aligned parallel
arrays (selects, not checkboxes, so alignment survives). Invalid
schemas re-render with errors and never persist.
- directory/access.py: tenant_admin_required -- an active
StaffMembership at >= the given rank (default admin). No membership
-> 404; too low a rank -> 403. AUTHORIZATION SURFACE, flagged for
review; the operator login form is a separate PR and tests use
force_login.
- New model PathRedirect (directory/0003) + templatetag join_lines.
public default for a custom field is True (spec is silent). htmx is
CDN-loaded with a TODO to vendor.
49 new tests (schema validation incl. every rejection case and the
frozen-type rule end to end; the config services; the admin views and
their access control). Full suite 123, green on Postgres.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD
Signed-off-by: Matthew Wren <info@origindev.com>
The schema builder runs on an authenticated tenant admin session. A
CDN <script> would be a third-party request from that session on every
page and would leak the tenant domain (Referer, SNI) to the CDN.
htmx 2.0.4 is now committed at
directory/static/directory/vendor/htmx-2.0.4.min.js with a provenance
header (source URL, retrieval date, licence). base.html loads it via
{% static %}. A test asserts the file is on disk with the header and
that no admin template references a CDN host.
Note: production static serving (whitenoise middleware + STATIC_ROOT +
collectstatic) is still unwired -- that is a release/deploy PR. In
DEBUG, runserver's staticfiles handler serves it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD
Signed-off-by: Matthew Wren <info@origindev.com>
Category browse pages and the listing URLs under them are indexed, so a slug rename needs a 301 just as a path_segment change does. The PathRedirect model is already used in this PR. update_category now records tenant + old_prefix -> new_prefix on a slug change, using /<slug> while single-type and /<segment>/<slug> once the tenant is multi-type (ruling 7 -- one category level either way). A non-slug edit writes nothing. Chained/overlapping redirects are the public router's concern (a later PR). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5qqrLcxpVZRis5jbN5pKD Signed-off-by: Matthew Wren <info@origindev.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ListingType and Category configuration through the service layer, emitting tenant.settings_changed with patch pointers at /listing_types/{key} and /categories/{type_key}/{slug}. field_schema.py validates against the closed type set with keys frozen after creation. HTMX schema builder so field definitions are edited as form rows rather than JSON. path_segment and category slug changes write PathRedirect rows. htmx is vendored, not CDN-loaded.