Metadata-Version: 2.4
Name: splent_feature_post
Version: 0.7.0
License-Expression: LGPL-3.0-or-later
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: COPYING
License-File: COPYING.LESSER
Requires-Dist: splent_framework>=1.20.0
Dynamic: license-file

# splent_feature_post

The `post` feature of a [SPLENT](https://splent.io) product line.

A SPLENT feature is a Python package that a product composes with others.
Its models, routes, services, templates and hooks are registered when the
product starts. What this feature provides and requires is listed in the
`[tool.splent.contract]` section of `pyproject.toml`, which
`splent feature:contract --write` keeps in sync with the source.

## What it does

Blog posts with categories. The public index lives at `POST_INDEX_PATH`
(`/blog` by default), each category at `<index>/category/<slug>`, and each
post at the permalink `POST_PERMALINK` describes (`/%Y/%m/%d/%postname%` by
default). A draft, or a published post dated in the future, answers 404
until its time comes. The home page gets a latest posts section through the
`home.section` hook.

The panel manages the posts at `/admin/posts`, where they are written,
edited, previewed before they go live and deleted.

A post's content and excerpt are stored HTML, and every page that shows
them, the preview included, draws them through the framework's `rich_text`
filter, which keeps the formatting and drops scripts, event handlers and
`javascript:` links.

The panel writes the content with a small editor bundled with the feature
(`assets/vendor/splent-editor.js`), and the stored body never runs there
either. The editor parses the stored body, anything pasted and the HTML
typed in its source view into an inert document, where no script runs and
no image loads. There it drops the elements that run or load something
(`script`, `style`, `object`, `svg`, form controls and the like), every
`on*` and `style` attribute and every address that is not `http`, `https`,
`mailto` or relative, and only then moves the nodes into the editing
surface.

An `iframe` stays, so a post that embeds a YouTube or Vimeo player keeps it
when it is edited and saved. It loses its `on*` handlers, `srcdoc`, `name`
and `style`, and its `src` when that is not `https`, which leaves an empty
frame the author can still see. The `rich_text` filter and
`RICH_TEXT_EMBED_HOSTS` still decide which players the public pages show.

## Access

The screens under `/admin/posts`, the preview of a draft among them, open
for the accounts whose role is `admin` or `staff`, the same pair the admin
dashboard lets in. Any other signed-in account gets 403 and does not see the
Posts link in the sidebar, and an anonymous visitor is sent to log in. The
public blog stays open to everyone, signed in or not. A draft or a scheduled
post answers 404 there for every account, the panel's included, and the
panel checks it through the preview.

There is no setting for these roles, and a refinement cannot replace the
views that check them, because it may add routes to this feature but never
replace one of its own. Another feature can decide for these screens with an
access policy registered for the prefix `post.admin`
(`register_access_policy`, splent_framework 1.20.0). The framework asks the
policy before the roles. It answers True to let an account in, False to
refuse it, or None to leave the decision to the roles, and the sidebar link
follows the same answer. That is the way to let a group of accounts manage
only this section, for example the editors of the blog, without giving them
a panel role.
See [Access control](https://docs.splent.io/framework/access-control).

Without such a feature, give the person's account the staff role. Staff
opens every panel screen that asks for `PANEL_ROLES`, not only this one, so
a person who should manage nothing but the posts needs a policy instead.

```bash
splent feature:auth set-role someone@example.com staff
```

## Use it in a product

```bash
splent feature:add splent_feature_post
```

## Develop

```bash
splent feature:test splent_feature_post
splent feature:contract splent_feature_post
```

## Release

```bash
splent feature:release splent_feature_post --dry-run --bump patch
splent feature:release splent_feature_post
```

The first command rehearses the release and changes nothing. The second
publishes it to the git forge and to PyPI, and a PyPI version is permanent.
