Repository navigation
Releases
Releases · pixelersagency/leiho
Leiho 1.0.0-rc.6
Fixed
- Fallback images stay in the visual editor. When an image field was empty, the bridge cleared the image the front end shows instead (
srcemptied,srcsetremoved, CSS background removed), so placeholder images of the template disappeared from the preview. An empty value now leaves the element as the front end rendered it; the image is only removed when the user clears the field during the edit.
Leiho 1.0.0-rc.5
Added
- Security tab. The "WordPress as a back office" options move out of Settings into their own tab, next to new hardening options:
- Turn off the theme and plugin file editor (same as
DISALLOW_FILE_EDIT). - Protect the login page: login errors no longer say whether a username exists, and 5 failed attempts block that username from that address for 15 minutes. Behind a proxy such as Cloudflare, the
leiho_login_ipfilter gives the real visitor address. - Turn comments off: comments and pings closed, Comments menu hidden, comments API closed to visitors. Off by default, since a front end can use comments.
- Lock down WordPress now also removes the
X-Pingbackheader and returns a 404 for author pages (?author=1,/author/…/) to visitors, so login names can't be listed. - New installs get the file editor and login protection on. Sites that already have Leiho keep their current behaviour until an administrator changes these options.
- Application passwords, used by AI agents, keep working.
- Turn off the theme and plugin file editor (same as
- Leiho Zero. An empty theme shipped with Leiho, for a WordPress that only serves as a back office: no styles, no scripts, no templates, while featured images and menus keep working in wp-admin. One button in the Security tab installs and activates it, and another one restores the previous theme. Before switching, Leiho reads the active theme's code and lists what would stop working with it (
register_post_type,acf_add_local_field_group,register_nav_menus…). - Login address. In the Security tab, the login page can move to an address of your choice (for example
/my-office/).wp-login.phpandwp-adminthen answer "not found" to visitors, and every login, logout, lost-password and e-mail link points to the new address. Right after saving, a window shows the address with a Copy button, the bookmark shortcut for your system (⌘ D or Ctrl + D), a button to drag to the bookmarks bar and a link to open the login page. The administrator also receives the address by e-mail. Locked out:define( 'LEIHO_DEFAULT_LOGIN', true );inwp-config.phpbrings the usual login page back. Off until an address is set. Not available on multisite. - Leiho login page. Same look as leiho.dev (black background, lime dot field, Geist font). Each screen has its own title ("Log in to …", "Reset your password", "Choose a new password", "Check your inbox"), "Lost your password?" sits next to the password field, and the button spans the card. WordPress's extras are gone: language switcher, link back to the site, privacy policy link. The leiho.dev badge sits at the bottom of the page. The page now wins over another login branding (plugin, mu-plugin or theme) instead of mixing with it: turn the Leiho login page off in the Security tab to keep your own.
- Texts follow the fields plugin in use. Leiho tells ACF, ACF PRO and Secure Custom Fields apart and names the right one in the editor. The "Get started" prompt asks the agent to build the content model in that plugin or, when none is installed, in Leiho Collections, with the matching instructions.
- Edit block-editor content in Leiho, and back in WordPress. Content written with the WordPress block editor was read-only in Leiho. When it's made of common blocks (paragraphs, headings, lists, quotes, images, separators), Leiho now edits it and saves it back as blocks: unchanged blocks are kept as they were, settings included, and edited ones become native blocks the block editor opens without any warning. This works for posts, pages and every content type with a content field. Other blocks (tables, buttons, columns, plugin blocks) show as grey locked boxes inside the text ("Table block — edit it in WordPress") and come back untouched on save, while the rest stays editable. Deleting a box removes its block.
- Hide content edited in Leiho from the WordPress menu (Security tab, off by default). The content types edited in Leiho, Leiho collections included, leave the WordPress sidebar and the "New" menu. Their screens still open from a direct link.
- Leiho favicon in wp-admin, on the login page and at
/favicon.icowhile the site has no icon of its own (Settings → General → Site Icon). - Agents can change the new options through
leiho/update-settings(headless_comments,headless_menus,security_file_edit,security_login).
Leiho 1.0.0-rc.4
Fixed
- The visual editor opens collections whose pages get their address from a field. A Leiho collection without a URL pattern was treated as having no pages on the site and always opened as a form, even when its entries got their path from the "Field holding the URL" setting or from their own address (typical after an ACF import). A collection now has pages when it has a URL pattern, when the field holding the URL is one of its fields, or when its entries carry their own address.
Leiho 1.0.0-rc.3
Fixed
- "Field holding the URL" works for content types served by Leiho Collections. The path setting read the field from WordPress post meta only. Once a type is served by a Leiho collection (a collection of its own, or an ACF import switched to Leiho), the field lives in the collection's table: entries created afterwards got no path, and a path changed in Leiho kept its old value, so the preview, the headless redirect and the
pathof the content API didn't match the front end. Leiho now reads the collection's field when the type has one with that name (post meta otherwise), and finds an entry by that path the same way.
Leiho 1.0.0-rc.2
Fixed
- Text fields keep what you typed. Text fields (Leiho Collections text and multiline text, ACF text and textarea edited with Leiho, link labels) went through WordPress's
sanitize_text_field(), which changed legitimate values: a<on its own was saved as<(so "< 1 000 UFC/L" showed as "< 1 000 UFC/L" on the site), leading and trailing spaces were removed (words stuck together when a front end builds a sentence from several fields), double spaces were merged and sequences such as%dewere dropped. Values are now stored exactly as typed and escaped when they are displayed. What is still removed: HTML tags (their text and the spaces around them stay:a <b>x</b> bgivesa x b; a tag left open, such asa <img src=x, is removed up to the end of the value), invalid UTF-8 and control characters; a single-line text still turns line breaks into spaces. Use a code field to keep HTML as typed. Values saved before this fix keep their<: save them again (or run the ACF import again) to correct them. - ACF import of such values reports no difference. An ACF text such as "< 1 000 UFC/L" or " suite" now imports identically, so the import no longer stops in needs review because of them.
- Changing a text field into rich text escapes
<and&. The converted value displays the same text instead of being refused; an existing<is not escaped twice. - AI translations keep
<in text fields instead of writing<, and search descriptions built from text fields keepx<1or<3. llms.txtand Markdown responses are sent withX-Content-Type-Options: nosniff, so a browser never reads them as HTML.- Publishing with Coolify rebuilds the site. Coolify's webhook URL ends in
force=false, and publishing content doesn't change the commit: Coolify reused the previous image and skipped the build, so published content never reached the site. Leiho now always asks for a fresh build. - ACF import never switches a content type to Leiho when values failed or differ. An import that ends with a refused value or a value served differently used to switch the type to Leiho anyway, so the content API could serve emptied fields and the next build would publish empty pages. Such an import now stops in a needs review state: ACF keeps serving the type, the report (failures and differences) stays available in the import window, the REST status and
wp leiho acf-import run, which now exits with an error. Once reviewed, either go back to ACF (fix the values, then import again) or switch anyway with Switch to Leiho anyway,POST /collections/import-acf/acceptorwp leiho acf-import accept <type>(run --acceptdoes it in one go). - ACF field groups attached to specific entries are part of the import. A group shown on one entry only (location rule Post is equal to …) was ignored by the preview and the import. It is now listed (with its entries), and when such a group defines a field with the same name but a different shape than another group (a repeater here, a flexible content there), the preview names the field and refuses the import instead of losing values.
!=rules don't attach a group to an entry. - Values over an ACF limit are found before the import. ACF only checks limits (maximum rows, minimum rows, maximum length, number bounds, maximum items) in its own editor, so existing data can exceed them, and Leiho refused those values during the import. The preview now lists them with their entries, and the generated collection widens each limit to the existing values, so nothing is refused or lost; you can tighten them later in the collection settings.
- Headless
robots.txtkeeps media crawlable. With noindex on,robots.txtblocked everything, including the images a front end loads from/wp-content/uploads/. It now allows the uploads folder; WordPress pages stay out of search engines throughX-Robots-Tag.
Leiho 1.0.0-rc.1
Added
- Languages for Leiho Collections, without duplicating entries. Declare the site's languages once (Collections → Languages, or
"i18n": { "default": "en", "locales": ["en", "fr"] }inleiho.schema.json); each root field is translatable (text, rich text, link, list, blocks and group by default) or shared by every language (prices, images, dates, references), with a switch in the field builder. The default language stays where it was; translations live in onewp_leiho_l10ntable (entry, language, title, values). Translated entries get prefixed paths (/fr/services/audit), and the front falls back on the original for every field, row or sub-field not translated yet. - Edit in every language. The visual editor and the entry form get a language switch with each language's state (not translated, partly translated, original changed): values, title and preview follow the language, empty fields show the original as a hint, and saving writes the translation only. The entry list shows a badge for the languages left to translate.
- Translate with AI (WordPress 7.0+): fills the texts not translated yet with the site's AI provider and the brand context, keeping HTML tags, product names and length limits; the result arrives as unsaved edits to review, never saved by itself.
- Translation abilities for agents (MCP):
leiho/list-translations(state per entry),leiho/get-translation(original and translation by path),leiho/update-translation(on a published entry it only fills empty translations, as suggestions an editor reviews and saves in the editor: nothing reaches the site before; drafts are free) andleiho/translate-entry(site AI, optional save).leiho/list-collectionsgives each collection's languages and which fields are translatable. - Content API in a language:
?locale=fron/export,/contentand/content/{id}(or/content?path=/fr/…), withlocaleandtranslations(path in each language) on every translated item.loadLeihoContent( { locale: 'fr' } )keeps one snapshot per language, and the generated types describe both properties. - Search and AI, from the fields you have. A collection says what its entries are (schema.org type: article, documentation, FAQ, product, event, service, person…) and which fields hold the description, image and questions (Collections → Search and AI, or
"seo"in the schema). Every item of the content API then carries aseoblock with its title, description, image, FAQ and JSON-LD nodes, in the language of the request. Without a description field, Leiho finds one (summary, intro…), then reads Yoast, Rank Math, SEOPress or All in One SEO, then the excerpt. llms.txtand Markdown from WordPress:GET /wp-json/leiho/v1/llms.txt(?full=1for every entry's text,?locale=fr) andGET /wp-json/leiho/v1/markdown?path=…(one entry as Markdown), for front ends rendered on request.leiho/list-collectionstells agents which fields make the description and the FAQ.leiho/seoin the package:leihoGraph()builds a page's schema.org graph (site, organization, breadcrumbs and the item's nodes on the canonical address),jsonLd()makes it safe in a<script>,seoMeta()gives the description and Open Graph tags.leiho/ai-readyin the package: after a static build, every page as Markdown next to its HTML,llms.txtandllms-full.txtper language, and asitemap.xmlwithhreflangalternates and dates. An Astro integration (aiReady()), a function (buildAiFiles()) and a command (npx leiho ai-ready --site … --dir …). No dependency added.- Code field in Leiho Collections: multiline text kept exactly as typed (HTML tags included, where a text field strips them), shown in a fixed-width font with Tab support in the editor, served unchanged by the content API. For code samples and configuration shown on the page: render it as text, never as HTML. Only users who may publish unfiltered HTML (administrators, editors) can put HTML tags in it; authors, contributors and agents get
markup_not_allowed. - More AI clients in the MCP guide. The AI screen now gives a ready-to-copy configuration for Codex (
codex mcp addcommand and~/.codex/config.tomlblock, shared by the CLI, IDE extension and app), VS Code with GitHub Copilot (serversformat), Gemini CLI and Windsurf, besides Claude Desktop, Claude Code and Cursor, plus a generic one for any client that runs local MCP servers.
Fixed
- Moving between Leiho's screens no longer flashes: the menu opens in its saved state before the page is painted (it used to start collapsed and slide open on every page), and supporting browsers cross-fade from one screen to the next with the menu standing still (off with reduced motion).
- Field type badges in Collections show the type's icon instead of two letters of its translated name.
- Translations: a row added to a list or flexible field of the original after it was translated can now be translated too (it failed with
row_not_found).
Security
- Content API: password-protected entries are no longer served to visitors (filter
leiho_content_include_protectedto serve them anyway), ACF password fields are never served, and an anonymous request can no longer make WordPress scan every post (unknown paths) or rebuild a whole export on each call (other languages, types andllms.txtare now cached even without a persistent object cache)./contentreturns at most 100 items per page. - Editor: stored HTML is read in an inert document (the change history and the AI context could run an
onerrorhandler in wp-admin). The bridge sanitizes rich text with an allowlist and only keeps http(s), mailto, tel and relative addresses; links clicked in the preview only open http(s) addresses. Local drafts are kept per user and site, for a week at most. - Agents (MCP): translations written by an agent on a published entry are suggestions, applied as edits to review when an editor opens that language, and served only once saved;
leiho/update-settingsno longer changes the site, preview, build hook or Coolify addresses or the GitHub repository (an administrator sets them in Settings), agent writes always go throughwp_kses_postand never change the author or date, and translation and search lists only show entries the user can edit. - Data: changing a field's type runs the new type's sanitizing (a code field can't become a script in rich text or a
javascript:link); ACF sub-fields Leiho doesn't edit are filtered on save;l10nis reserved as a collection id; uninstalling also drops the translations table. - Publishing: saving a draft no longer starts a build (only published content, or content leaving the site, does), and the minimum time between two builds is 15 seconds.
- Outgoing requests: the site, build hook and Coolify addresses can no longer point at private or local network addresses outside a local or development environment (filter
leiho_allow_private_urls), build hooks and Coolify are called without following redirects, and the Coolify address must be https. The GitHub repository setting must look likeowner/name. - Signed updates: every release archive is signed (Ed25519, over the name, the version and the archive's SHA-256, so an old archive can't be republished as a newer version) by the release workflow, and Leiho checks the signature against the public key it ships before WordPress installs an update; an unsigned or altered archive is refused. Only archives from the official repository's GitHub releases are considered. The release workflow pins its actions by commit and its npm version.
- Concurrent edits: one save at a time per entry, with the revision checked under that lock (two people editing two rows of the same list no longer overwrite each other); the editor's save route requires the revision.
- AI requests: the per-user limit is counted under a lock (parallel requests no longer slip through) and a site-wide limit is added (120 per minute, filter
leiho_ai_site_rate_limit). - Editing routes only work on the content types Leiho edits (settings and Collections), and Duplicate only copies public meta, the featured image, the template and ACF field references (filter
leiho_duplicate_meta), never the original's own address. - Secrets are encrypted and authenticated with two separate keys derived from the WordPress salts; older secrets are read and re-encrypted on first use.
- Pickers (parent page, references) no longer list the titles of other authors' drafts or private entries, and a parent must be readable.
- Change log: entries are kept 180 days (filter
leiho_activity_retention_days), anonymized when their user is deleted, and erased with WordPress's personal data eraser.
Okno 1.0.0-beta.9
Added
- Articles are fully editable in Collections. The entry form shows WordPress's own fields for the types that support them: Content (WordPress's rich text editor with paragraph / headings, bold, italic, links, lists, quote, alignment and Add media, saved in the classic editor's format), Excerpt (with a character count), URL slug (with a preview of the full address), categories, tags and every other public taxonomy (chips with search; typing a new tag offers « Create “…” », the term is created on save — a new category needs the right to manage categories), Author (users who can write that type; needs
edit_others_posts), Publish date (a future date schedules a published entry), and Parent / Order for hierarchical types. Pages get their content too. Everything goes through Okno's save path: onewp_update_post(), server-side sanitization (wp_kses_post()withoutunfiltered_html), per-field capability checks, conflict detection and the change log. These fields stay out of the visual editor's panel. - Content written with the block editor is protected: the form shows a read-only preview and Open in the WordPress editor; the server refuses to overwrite it (
blocks_protected). Other fields of the entry (excerpt, taxonomies, slug…) stay editable. - REST:
POST /okno/v1/preferences(theme:light|dark). Native save paths_content,_excerpt,_slug,_date,_author,_parent,_menu_order,_tax_<taxonomy>onPOST /okno/v1/save/{id}. - Import from ACF into Okno Collections, one content type at a time (Okno → Collections, Import into Okno on an ACF type, administrators). A preview lists the ACF field groups attached to the type, what each field becomes in Okno and what doesn't carry over (fields without an equivalent such as
page_linkortaxonomy, conditional logic, default values, media restrictions, location rules other than the content type, ACF Pro repeater options), with the number of entries, fields and stored values. The import creates the collection, then copies the entries in batches with a progress bar, through the editor's validation; refused values are listed with a link to the entry without stopping the import, and every entry is checked afterwards: the JSON Okno serves is compared field by field with what ACF served. An interrupted import resumes where it stopped. - After the import the type is served by Okno:
/okno/v1/export,/contentand/typesgive the same JSON as before, and the visual editor's dotted paths keep working. Nothing is deleted from ACF (postmeta, field groups and post types stay); the ACF field groups no longer apply to the imported type, on Okno's side and on the classic edit screen. - Go back to ACF in the settings of an imported collection: ACF serves its original values again and the Okno table of the collection is deleted. Values edited in Okno after the import are not written back to ACF (the dialog says so).
- Content types declared in ACF's Post Types screen stay registered (same labels, slug, archive, supports…) when ACF is deactivated after their import; types registered by the theme or a plugin keep their registration, Okno only owns their fields.
- REST:
GET /okno/v1/collections/import-acf,GET …/import-acf/preview,POST …/import-acf/run,GET …/import-acf/status,POST …/import-acf/rollback. WP-CLI:wp okno acf-import list|preview|run|rollback. Filtersokno_acf_import_batchandokno_collections_save_revision; actionsokno_acf_import_started,okno_acf_imported,okno_acf_import_rolled_back. - Clone fields: a seamless clone is imported as the fields it clones, a clone shown as a group becomes a group.
- Collections schema:
external(a content type registered outside Okno, which only owns its fields) andsource: "acf"(field and block names keep their ACF API name, camelCase and hyphens included).
Changed
- The app is at a comfortable scale on large screens: 14 px body text (13 px secondary, 13 px labels, 16–20 px section titles), 36 px fields, selects and buttons, 38 px list rows and navigation links, a 48 px top bar, larger icons and spacing, and a 720 px form column, through new size tokens (
--okno-text-*,--okno-control*,--okno-row,--okno-bar,--okno-pane-*,--okno-form-width) shared by every view and the visual editor's chrome. Panes are a little narrower on 1440 px screens so the entry keeps its room; the entries filters no longer cut « Tous les statuts ». - Menus instead of native selects everywhere in the app (status filters, sorting, the entry's status, choice fields of forms, settings): a button and a list with a check mark on the current item, a coloured dot per status (published, scheduled, draft, pending review, private, trash), arrows / Home / End / Enter / Escape and type-ahead,
listbox/optionsemantics, closing on outside click, placed inside the window. The native<select>stays in the page, hidden, for its value and for PHP forms. - Multiple choices are chips with search (ACF relationship and taxonomy fields, multiple choices of Okno collections, WordPress taxonomies).
- Checkboxes, radios and switches drawn by Okno in light and dark: rounded square with a lime check, circle with a lime dot, radio cards (collection nature) with a clear selected state (border, tint, dot), switches for true / false fields, proper disabled states.
- WordPress's media library, link dialog and rich text editor match Okno inside the app: Okno surfaces and text in light and dark, lime primary button instead of WordPress blue, accent selection ring and check in the grid, themed tabs, drop zone and attachment details; the editor's toolbar, tabs and text area follow the theme.
- A whole number stored in a decimal
numbercolumn is served as5, not5.0, like ACF and like numbers inside lists. - Rich text of a collection imported from ACF is rendered like ACF renders it (paragraphs, typography, shortcodes, embeds) even once ACF is deactivated.
- An Okno collection imported from ACF can't be removed with
DELETE /okno/v1/collections/{id}: go back to ACF instead.
Fixed
- No more light flash when switching views in dark mode (and no dark flash in light mode). The theme is saved per user on the server (user meta, cookie as a fallback) and printed by PHP on
<html>, the app shell and the visual editor before the first paint; a choice made before this version in the browser is picked up and saved once. Dark stays the default. - No more blue frame on click. Buttons, links, navigation items and form controls only show a focus ring with the keyboard (
:focus-visible), in Okno's accent ink; WordPress's:focusbox-shadows (#2271b1) no longer leak into the app. - The media library no longer looks greyed out when opened from the visual editor or Collections (« Replace » on a featured image, image / gallery / file fields): its backdrop was stacked above the window. Same fix for the link dialog and TinyMCE's windows and menus (paragraph / heading list).
- The Visual editor button of an entry keeps an accessible name when its label is hidden on narrow screens.
Okno 1.0.0-beta.8
Changed
- Content and Collections are one view: Collections. Types on the left, whatever their engine (WordPress pages and posts, ACF or theme post types, Okno collections and single pages), grouped and with their entry counts, plus New collection for administrators; the entries of the selected type in the middle (search, status filter, sort by last modified, date or title, create, duplicate, publish / back to draft, trash and restore); the selected entry edited in place on the right with the editor's form (same widgets, undo / redo, local drafts, conflict protection, history), its status, Save, and Visual editor / View on the site for entries that have a page. Leaving an entry with unsaved changes asks first. Deep links:
view=collections&type=<post_type>&id=<post_id>. The Fields tab of an Okno collection opens the schema builder (administrators); ACF types say their fields are managed in ACF. Editors withoutmanage_optionsget the view too, without the builder. - The former
view=contentandview=formaddresses redirect to Collections with their filters or their entry, and every link that pointed to them (change log, the editor's page list, Edit with Okno on the edit screen, home shortcuts) now opens the entry in Collections. - REST:
GET /okno/v1/postsalso sorts byorderby=date.
Fixed
- Check again (Dashboard → Updates) now clears Okno's cached release, so a version published in the last hours shows up right away instead of after the 6-hour cache.
- The home screen of the Okno app is centred instead of sticking to the left on wide screens.
Okno 1.0.0-beta.7
Added
- Okno opens full screen and is the back office. One wp-admin page (
admin.php?page=okno) hides the WordPress menu, admin bar, footer and other plugins' notices (this page only; the admin bar isn't even rendered) and shows Okno's own navigation: Home, Visual editor, Content, Collections, Media, Publishing, Settings, plus Back to WordPress for administrators. The navigation collapses, works with the keyboard (arrows, Escape) and becomes a drawer below 782 px. The top bar carries the site name, a Publish button that shows when changes aren't published yet, the light / dark switch shared with the editor, and a user menu (profile, log out). - Content view: every editable entry (pages, posts, ACF post types, collections) with search, filters by type and status (trash included), create, duplicate, publish / back to draft, trash and restore, and opening in the visual editor or in a form.
- Entry form: the editor's field panel without the preview, for content that has no page on the site (legal pages, testimonials, settings singletons). Same widgets, undo / redo, local drafts, conflict protection and change history as the visual editor. Collections without a URL open there from everywhere (Content, the editor's page list, the edit screen, the change log).
- Collections builder on top of the
okno/v1/collectionsAPI: list of collections and single pages, create / edit (id, labels, kind, URL pattern, features), field editor with drag and drop and keyboard reordering, every one of the 19 field types and their options (choices, min / max, reference targets, nested sub-fields of lists and groups, block types of blocks fields), keys generated from the name and fixed once applied, review of the migration plan (dry_run) before applying, validation errors attached to the field concerned, archived columns with purge (typed confirmation), removal of a collection, import and export ofokno.schema.json. - Media view: the media library as a grid, with search, type filter, upload by button or drag and drop, and a details panel (alternative text, title, file URL, delete).
- Status from the editor: a status menu in the editor toolbar (draft, pending review, published, private) so content created in Okno can go live without wp-admin. Users who can't publish submit for review.
- Editors land in Okno after logging in (
login_redirect, for users withoutmanage_options, unless another destination was asked for). Setting Editors → After logging in, on by default. - REST:
GET /okno/v1/poststakestype,statusandorderby=modifiedand returnsmodified;POST /okno/v1/posts/{id}/status,POST /okno/v1/posts/{id}/restore,GET /okno/v1/deploy/state(pending changes, active and last deployment).
Changed
- Okno has a single menu entry. The former pages (
okno-start,okno-settings,okno-deploys,okno-editor) still answer and redirect to their view, with their parameters. - Changes are marked as pending even without automatic publishing (as long as a publishing mode is set), so the Publish button can show them; Publish clears the mark as before.
- Duplicating a collection entry copies its field values.
- The editor offers the featured image only for content types that support it (a collection without the Featured image feature no longer shows one), or when the content already has one.
Okno 1.0.0-beta.6
Added
- Content API.
GET /okno/v1/exportserves every published item of the Okno content types in one cached response with anETag(an unchanged build gets a304);/okno/v1/contentfilters by type, path or slug;/okno/v1/content/{id}serves one item. Fields are keyed by name and shaped for a front end: images and files as{ id, url, alt, width, height, sizes }, relations as{ id, type, slug, title, path }, WYSIWYG as rendered HTML, flexible content rows with_layout. The cache is cleared on every content, field, term or settings change. - TypeScript types.
GET /okno/v1/typesgenerates declarations of that JSON from the ACF field groups (flexible content as a discriminated union on_layout, fields of conditional groups optional, select choices as literal unions). - Content client in the npm package:
@pixelersagency/okno-bridge/contentloads the export with retries, keeps a snapshot in.okno/content.jsonand falls back to it when WordPress doesn't answer. CLI:npx okno types,npx okno pull. - Restricted access to the content API with a token (
X-Okno-TokenorAuthorization: Bearer). - Automatic publishing after each change, once edits stop for a configurable delay (changes made in wp-admin included). No server cron to set up: the Okno editor publishes when the delay runs out and right away when the tab is closed or hidden (
navigator.sendBeacon), other wp-admin screens do the same after Update in the classic or block editor, the wp-admin Heartbeat publishes once the delay has passed while any admin tab is open, and WP-Cron stays as the safety net. A pending marker and an atomic lock make sure one batch of changes gives one deployment, whichever trigger fires first; Publish takes pending changes along.POST /okno/v1/deploy/flushpublishes pending changes now (idempotent, accepts the REST nonce as_wpnoncefor beacons). When the build service doesn't answer, pending changes are kept and retried up to five times instead of being dropped. - WordPress as a back office: noindex for the CMS (header,
robots.txt, no sitemap), redirection of public WordPress pages to the front end, XML-RPC off,/wp/v2/usersclosed to visitors. On by default for new installs; sites updating keep their behavior until the options are ticked. - Content cache that uses what the site already has: with a persistent object cache (Redis, Memcached, LiteSpeed, W3 Total Cache…), the export, items, path lookups and types are cached in the object cache and invalidated in O(1) by switching generation; without one, the export keeps its non-autoloaded option. On by default, with a toggle, a status line and a Clear the content cache button in the settings.
- HTTP caching on every content endpoint:
ETag,Cache-Control: public, max-age=0, must-revalidate(privatein restricted mode, for logged-in users and for drafts),304onIf-None-Match. - Page cache purge when served content changes, once per request: LiteSpeed (by tag), WP Rocket, W3 Total Cache, WP Super Cache, SiteGround, WP Fastest Cache, Cloudflare, Breeze, Nginx Helper, plus the
okno_purge_cachesaction andokno_purge_urlsfilter for anything else. Okno_Content_Adapter_Interfacefor adapters that feed the content API, and theokno_content_item,okno_content_post_types,okno_content_htmlfilters andokno_content_changedaction.- Okno Collections: content types and fields defined in Okno, without ACF. Collections (many entries) and singletons (one entry, created by Okno) are registered as post types, so roles, drafts, trash, revisions, media and SEO plugins keep working; field values live in one table per collection (
wp_okno_<id>) with typed, indexed columns for scalar fields and JSON for lists, blocks, groups and multiple values. 19 field types: text, rich text, number, boolean, date, date-time, select, slug, email, URL, color, image, file, gallery, link, reference, list, blocks, group. - Schema as code:
okno.schema.jsonexport and import, validated with precise error paths (fields.3.key). - Safe migrations: a stable
keyper field makes renames free; type changes copy and convert values into a new column; removed and retyped columns are archived, never dropped (purge is explicit);dry_runreturns the plan. Works on MySQL 5.7+, MariaDB and the SQLite drop-in. - Field values in revisions: each revision of an entry stores its fields, restoring it restores them, and a field-only change creates a revision.
- Administrator REST routes
/okno/v1/collections(list, create, replace, delete, export, import, purge) and a PHP API (Okno_Collections::import(),save_entry(),entry()…). - Collections are served by
/okno/v1/export,/contentand/typesand edited in the visual editor with the same dotted paths as ACF fields. ACF keeps working alongside. Saving an entry or migrating the schema clears the content cache, purges page caches and queues automatic publishing, like any other content change. okno_settingsfilter on the effective settings,okno_collection_post_type_args,okno_collections_capabilityfilters andokno_collections_savedaction.
Changed
- The field engine (describe, read, patch dotted paths, sanitize, shape for the front end) is now shared by the ACF and Collections adapters (
Okno_Field_Adapter).
Fixed
- A multiple select is read as a list of choices, and edited with a multi-select in the editor, instead of the string "Array".