Blog post page and home route fetch container-nav from events + market
concurrently via fetch_fragments(). Blog listing fetches container-cards
from events and parses per-post HTML via comment markers.
widget_paginate proxies calendar pagination to events fragment.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blog now provides a nav-tree fragment at /internal/fragments/nav-tree
that accepts app_name and path params for correct aria-selected
highlighting. Blog itself consumes this fragment alongside cart-mini
and auth-menu in a single concurrent fetch_fragments() call.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The post template needs context (base_title, container_nav_widgets,
page_cart_count) that the post blueprint's context_processor provides.
Since home() runs on the blog blueprint, it must build this context
itself.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/ now renders the Ghost page with slug "home" (site homepage).
/index serves the existing blog listing (posts, pages, filters).
All blog.home references updated to blog.index.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All auth routes (login, magic link, account, newsletters,
widget pages, logout) are handled by the federation app.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Read cart_sid from query params on login page and save to session
so the verify route can emit adoption event with the correct session.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Federated content has no character limit — use the complete plaintext
body so followers see the full post in their timeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Switch object type from Article to Note (Mastodon first-class support)
- Include title + excerpt as HTML content with "Read more" link
- Feature image + up to 3 inline images as AP attachments
- Post tags as AP Hashtag objects with inline links in content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace emit_event("post.published/updated/unpublished") with direct
try_publish() calls. AP activities are now created at write time,
fixing the race condition where multiple EventProcessors competed
for federation events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _upsert_post returns (post, old_status) to detect status transitions
- Emit post.unpublished when published→draft (triggers Delete activity)
- Emit post.updated only when already-published posts are edited
- Emit post.published only for new publishes (not re-syncs)
- Same logic for pages via sync_single_page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
begin_nested() auto-flushes on entry which triggers the INSERT before
the savepoint is active. Move sess.add() inside the savepoint block
and split into update vs insert paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Blog app now registers SqlFederationService (was stub/no-op)
- sync_single_page emits post.published/updated events for pages
- Updated shared submodule: fix sign_request in AP delivery handler
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ghost /pages/ endpoint may not include "page": true in the response.
Explicitly set it so _upsert_post correctly marks is_page=true.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ghost has separate /posts/ and /pages/ endpoints. All admin functions
(get_post_for_edit, update_post, update_post_settings, sync) were
hardcoded to /posts/, causing 404s when editing pages. Now checks
is_page from post_data and uses the correct endpoint.
Also uses sync_single_page (not sync_single_post) after page saves
to prevent IntegrityError from mismatched fetch/upsert paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete coop_api.py (dead internal API endpoint)
- Replace cross-app calendar imports with shared service calls
- Update shared submodule
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The blog app's local templates/_types/post/_nav.html was shadowing
the shared version due to ChoiceLoader priority. Updated local copy
to use container_nav_widgets while keeping the blog-specific admin
cog link. Removed debug logging from context processor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace explicit calendar/market service calls in post routes, auth
routes, and listing cards with widget-driven iteration. Zero cross-domain
imports remain in blog bp layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New GET /auth/tickets/ and /auth/bookings/ routes with HTMX support.
Update shared submodule with TicketDTO, service methods, nav links,
and panel templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Market CRUD in blog now delegates to services.market.create_marketplace()
and soft_delete_marketplace() instead of importing MarketPlace directly.
Adds TODO comments on Calendar imports in admin routes (deferred to
admin UI rework). Updates shared submodule.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct Calendar, MarketPlace, and Post model queries with typed
service calls (services.blog, services.calendar, services.market,
services.cart). Blog registers all 4 services via domain_services_fn
with has() guards for composable deployment.
Key changes:
- app.py: use domain_services_fn instead of inline service registration
- admin routes: MarketPlace queries → services.market.marketplaces_for_container()
- entry_associations: CalendarEntryPost → services.calendar.entry_ids_for_content()
- markets service: Post query → services.blog.get_post_by_id/slug()
- posts_data, post routes: use calendar/market/cart services
- menu_items: glue imports → shared imports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace all imports from cart.models, market.models, events.models
with shared.models equivalents
- Convert blog/models/ghost_content.py to re-export stub
- Update shared + glue submodule pointers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract inline email body into separate Jinja2 templates
(_email/magic_link.html and .txt) with a styled sign-in
button and fallback plain-text link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Emit user.logged_in event instead of HTTP POST to /internal/cart/adopt.
Event is emitted inside the last_login_at transaction for atomicity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up ContainerRelation tracking via attach_child/detach_child in:
- menu_items: create, update (re-link on post change), delete
- markets: create (including revive), soft_delete
- page config: creation in update_features and update_sumup routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1-3 of decoupling:
- path_setup.py adds project root to sys.path
- Blog-owned models in blog/models/ (ghost_content, snippet, tag_group)
- Re-export shims for shared models (user, kv, magic_link, menu_item)
- All imports updated: shared.infrastructure, shared.db, shared.browser, etc.
- No more cross-app post_id FKs in calendar/market/page_config
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When toggling market/calendar features or saving SumUp settings,
create a PageConfig row if one doesn't exist yet instead of returning
a 404 error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches page-scoped cart count from /internal/cart/summary?page_slug=
for page posts and displays a cart icon with count in the post header
that links to the page cart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace SumUp placeholder with real form for merchant code, API key, prefix
- Add PUT /admin/sumup/ route to save per-page SumUp credentials
- Pass sumup_configured/merchant_code/checkout_prefix to template context
- SumUp form only shown when calendar or market feature is enabled
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Post context processor loads MarketPlace rows for the page
- Nav bar displays market links (shopping-bag icon) alongside calendars
- Admin panel includes markets CRUD when market feature is enabled
- Updated shared_lib submodule with nav_entries template change
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shared sidebar/filter templates expect selected_groups, tags, authors
etc. Provide empty defaults when rendering the pages view.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add PageConfig model with feature flags (calendar, market)
- Auto-create PageConfig on Ghost page sync
- Add create_page() for Ghost /pages/ API endpoint
- Add /new-page/ route for creating pages
- Add ?type=pages blog filter with Posts|Pages tab toggle
- Add list_pages() to DBClient with PageConfig eager loading
- Add PUT /<slug>/admin/features/ route for feature toggles
- Add feature badges (calendar, market) on page cards
- Add features panel to page admin dashboard
- Update shared_lib submodule with PageConfig model
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract blog-specific code from the coop monolith into a standalone
repository. Includes auth, blog, post, admin, menu_items, snippets
blueprints, associated templates, Dockerfile (APP_MODULE=app:app),
entrypoint, and Gitea CI workflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>