# Mishka Document v0.0.1 - Table of Contents > A versioned documentation engine for Mishka CMS: documents with several versions, nested pages and a two-stage review, built with Elixir and Phoenix LiveView. ## Pages - [README](readme.md) - [License](license.md) ## Modules - [MishkaDocument](MishkaDocument.md): The documentation extension's Ash domain: its resources (workspaces and their versions, the sections and notes of a version, their drafts, tags, issues and the event log) and the JSON:API routes the admin API serves them under, `/v1/master/admin/mishka-document/...` for a master and `/v1/tenant/admin/mishka-document/...` for a site's admin. - [MishkaDocument.Address](MishkaDocument.Address.md): A document's own address — where it is read when its admin gives it one, `/chelekom/docs` — and where each of its pages is read under it. - [MishkaDocument.ContentText](MishkaDocument.ContentText.md): The text a page's or a note's HTML content reads as, without its markup: what its card, its row and its drafts preview, what its words are counted in, and what its search description is cut from (`MishkaDocument.Plugins.PageSeo`). - [MishkaDocument.Dashboard.Scope](MishkaDocument.Dashboard.Scope.md): Every read of the document dashboard's widgets (`MishkaDocument.Dashboard.Widgets`) as the user viewing it, through the resource's own policies. - [MishkaDocument.Dashboard.WidgetSettings](MishkaDocument.Dashboard.WidgetSettings.md): Registers every mishka_document dashboard widget's own settings with the Settings page through the `settings:register` mishka_installer hook — so "Documents to show" is documented under the **Documents** tab (`category: "document"`, group **Dashboard widgets**) alongside the rest of the extension's settings, instead of hiding inside the dashboard's per-user layout. - [MishkaDocument.Dashboard.Widgets.OpenIssues](MishkaDocument.Dashboard.Widgets.OpenIssues.md): Dashboard widget (a `dashboard:widgets` plugin): open documentation issues — an open-count stat plus the latest few, each with its issue-type badge, filtered to `status == :open` and read as the viewer through `MishkaDocument.Dashboard.Scope`: a master sees every site's issues, a site's admin only their own site's, and a viewer without `admin:dashboard:document:section` none. - [MishkaDocument.Dashboard.Widgets.RecentDocuments](MishkaDocument.Dashboard.Widgets.RecentDocuments.md): Dashboard widget (a `dashboard:widgets` plugin): the most recently updated workspaces, each with its status badge, one-line description, maintainer, section count, latest version and language — read as the viewer through `MishkaDocument.Dashboard.Scope`: a master sees every site's workspaces, a site's admin only their own site's, and a viewer without a document area none. Author, section count and latest version come from the resource's own `:maintainers_info`, `:health_metrics` calculations and its versions, all loaded on the same read: `:master_versions` for a master, `:tenant_versions` for a site's admin. Offered to holders of `admin:dashboard:document:section`, who read workspaces to reach their sections. - [MishkaDocument.Dashboard.Widgets.VersionActivity](MishkaDocument.Dashboard.Widgets.VersionActivity.md): Dashboard widget (a `dashboard:widgets` plugin): the most recent workspace versions — workspace name, version number, and status badge — read as the viewer through `MishkaDocument.Dashboard.Scope` with the version's workspace loaded: `:master_workspace` for a master, `:workspace` for a site's admin. A master sees every site's versions, a site's admin only their own site's, and a viewer without a document area none. Offered to holders of `admin:dashboard:document:workspace`. - [MishkaDocument.Dashboard.Widgets.WorkspacesSummary](MishkaDocument.Dashboard.Widgets.WorkspacesSummary.md): Dashboard widget (a `dashboard:widgets` plugin): the full-width dark "Documents" summary panel — a docstats row of totals for workspaces, versions, sections, section drafts, issues, note drafts, and the set of primary languages. Every total is counted as the viewer through `MishkaDocument.Dashboard.Scope`: a master counts every site's documents, a site's admin only their own site's, and a total whose resource belongs to an area the viewer does not hold is `0`. Offered to holders of `admin:dashboard:document:workspace`. - [MishkaDocument.Dev.Generator](MishkaDocument.Dev.Generator.md): Generator for MishkaDocument resources. - Test functions (no prefix): simple, deterministic — used by factory and tests. - Dev functions (dev_ prefix): realistic, rich data — used by dev seeds. - [MishkaDocument.Dev.Seeds](MishkaDocument.Dev.Seeds.md): Dev seeds for MishkaDocument. - [MishkaDocument.EventLog](MishkaDocument.EventLog.md): Minimal event log for tracking activities in MishkaDocument. - [MishkaDocument.Issue](MishkaDocument.Issue.md): Manages documentation issues and feedback submissions. - [MishkaDocument.IssueComment](MishkaDocument.IssueComment.md): Manages comments on documentation issues. - [MishkaDocument.Kit](MishkaDocument.Kit.md): Where this extension's UI kit bundle is, and how to install it. - [MishkaDocument.Note](MishkaDocument.Note.md): Manages documentation notes within sections. - [MishkaDocument.NoteDraft](MishkaDocument.NoteDraft.md): Manages draft proposals for note changes within sections. - [MishkaDocument.PageSource](MishkaDocument.PageSource.md): The rows a documentation page is made of: the document its address names, its published versions, the version a reader is shown and that version's approved pages, the page the address names with its text, and that page's notes. - [MishkaDocument.Plugins.ContentComponents](MishkaDocument.Plugins.ContentComponents.md): Tells a site's stylesheet which components its documentation's text draws, which icons it names and which classes it writes, through the `runtime:compile:content_components` hook `MishkaCmsCore.Runtime.Compilers.TailwindCompiler.content_components/1` fires while it builds a site's stylesheet: the names the site's approved pages and notes list in `content_components`, the icon classes they list in `content_icons` and the classes they list in `content_classes`, which `MishkaDocument.Resources.Changes.ContentFacts` stores when either is written. The kit's `doc-section`, which draws both with `rich_content`, then reaches those components (`MishkaCmsCore.Runtime.Compilers.CssClosure`), the stylesheet carries a rule for each icon, and it is built with each class. - [MishkaDocument.Plugins.Installer](MishkaDocument.Plugins.Installer.md): Installs the documentation onto one site, when asked. - [MishkaDocument.Plugins.LiveVisit](MishkaDocument.Plugins.LiveVisit.md): Gives a page a `live_visit` assign: `true` when the reader reached it by a live navigation, a click on a link of the site or the browser's Back and Forward, and `false` on a full load. - [MishkaDocument.Plugins.PageFrame](MishkaDocument.Plugins.PageFrame.md): Tells the site's layout what only a documentation page knows — the languages the document or the page it shows is written in, and the bar under the header — through the `runtime:page:frame` mishka_installer hook `MishkaCmsWeb.RuntimeLive` fires in `handle_params/3` (`MishkaCmsCore.Runtime.SiteLayout` is the contract: the map it is handed and what it reads back). - [MishkaDocument.Plugins.PageRecords](MishkaDocument.Plugins.PageRecords.md): Names the kinds of record the documentation's pages show — a workspace's front page, one of its sections — on the `runtime:page:records` mishka_installer hook, for a page's "This page shows each" choice (see `MishkaCmsCore.Runtime.Seo.record_types/1`), each with a few of the site's own to preview a page with. Each is labelled as the Documents admin names it, where its Permissions and SEO Tags are set. `MishkaDocument.Plugins.PageSeo` answers for the same kinds. - [MishkaDocument.Plugins.PageSeo](MishkaDocument.Plugins.PageSeo.md): Gives each document and each page of one its own head on the documentation page that serves it, through the `runtime:page:seo` mishka_installer hook `MishkaCmsWeb.RuntimeLive` fires in `handle_params/3` (see `MishkaCmsCore.Runtime.Seo` for the map it is handed and what the head builds from it). - [MishkaDocument.Plugins.PageSource](MishkaDocument.Plugins.PageSource.md): Reads the rows a document's front page and each of its pages show, once, through the `runtime:page:source` mishka_installer hook `MishkaCmsWeb.RuntimeLive` fires in `handle_params/3` before the frame and the head (`MishkaCmsCore.Runtime.PageSource` is the contract: the map it is handed and where what it reads goes). - [MishkaDocument.Plugins.SectionShareCard](MishkaDocument.Plugins.SectionShareCard.md): A plain mishka_installer plugin: after a documentation page is saved (`document:section:after_save` — created or updated, a master's twin alike), its share card is looked at in the background and drawn when what it shows changed (`MishkaCmsCore.Runtime.ShareCard.refresh/3`), so it is ready before the first reader. Only while it is approved and its document active (`MishkaDocument.ShareCards.target/1`). Registered in MishkaDocument.Application. - [MishkaDocument.Plugins.ShareCardTargets](MishkaDocument.Plugins.ShareCardTargets.md): A plain mishka_installer plugin: answers `MishkaCmsCore.Runtime.ShareCard.targets_event/0` with the documentation's share cards on the site asked, under `"mishka_document"` — every published document's front page and every page of it, in each language and version a visitor reaches (`MishkaDocument.ShareCards.targets/1`). Names its producer alone when asked for names, and nothing when another producer is asked (`MishkaCmsCore.Runtime.ShareCard.put_targets/3`). - [MishkaDocument.Plugins.SitemapUrls](MishkaDocument.Plugins.SitemapUrls.md): Lists a site's published documentation in its `/sitemap.xml`, through the `seo:sitemap:urls` mishka_installer hook `MishkaCmsCore.Runtime.Compilers.SitemapCompiler` fires when it builds the site's sitemap. Its `pages` are the site's indexable pages holding a capture only their owner can fill; this appends an address for every document and every page of one to its `urls`. A workspace, version or section written rebuilds its site's sitemap, because those resources list `MishkaCmsCore.Runtime.Resources.Notifiers.SitemapRefresh`. - [MishkaDocument.Plugins.UsedTranslationKeys](MishkaDocument.Plugins.UsedTranslationKeys.md): Names the words the documentation looks up in a site's translations, through the `runtime:translations:used_keys` mishka_installer hook (`MishkaCmsCore.Runtime.Translations.used_keys/1`). - [MishkaDocument.Plugins.WorkspaceShareCard](MishkaDocument.Plugins.WorkspaceShareCard.md): A plain mishka_installer plugin: after a document is saved (`document:workspace:after_save` — created or updated, a master's twin alike), its share card is looked at in the background and drawn when what it shows changed (`MishkaCmsCore.Runtime.ShareCard.refresh/3`), so it is ready before the first reader. Only while it is active (`MishkaDocument.ShareCards.target/1`). Registered in MishkaDocument.Application. - [MishkaDocument.Provision](MishkaDocument.Provision.md): What a site needs before anybody can read its documentation: this extension's live components, three sample pages built out of them, and the "latest documents" block saved as a snippet an author can drop on a home page. - [MishkaDocument.Repo](MishkaDocument.Repo.md) - [MishkaDocument.Resources.Calculations.AuthoredByMaster](MishkaDocument.Resources.Calculations.AuthoredByMaster.md): Whether an issue comment was written by a master — a user with no site. - [MishkaDocument.Resources.Calculations.CloneNumbers](MishkaDocument.Resources.Calculations.CloneNumbers.md): The version numbers of every version cloned from a version, however deep, nearest first — what deleting it deletes with it (`MishkaDocument.Resources.Helpers.VersionClones`). Read as the reader. - [MishkaDocument.Resources.Calculations.EffectivePermissions](MishkaDocument.Resources.Calculations.EffectivePermissions.md): The permissions a section — one page of a document — opens with on its page, in its sitemap entry and wherever else a reader is let in: its own, else those of the nearest parent section that names some, else its document's (the workspace's). What a section names itself always wins over what it would take from above, and a section of a document that names nothing, under parents that name nothing, is public. - [MishkaDocument.Resources.Calculations.LastModified](MishkaDocument.Resources.Calculations.LastModified.md): When a section — one page of a document — last changed: the newest of its own `updated_at`, the `updated_at` of the notes its page shows (approved, not archived) and the time a note was archived, which takes the note off the page. A section with no notes keeps its own. - [MishkaDocument.Resources.Calculations.SectionMaintainers](MishkaDocument.Resources.Calculations.SectionMaintainers.md): Calculates unique maintainers count for a section based on EventLog. - [MishkaDocument.Resources.Calculations.WordCount](MishkaDocument.Resources.Calculations.WordCount.md): Reusable calculation for counting words in a content field. - [MishkaDocument.Resources.Calculations.WorkspaceMaintainers](MishkaDocument.Resources.Calculations.WorkspaceMaintainers.md): Calculates unique maintainers for a workspace based on EventLog. - [MishkaDocument.Resources.Changes.AutoApplyNoteDraft](MishkaDocument.Resources.Changes.AutoApplyNoteDraft.md): Automatically applies note draft changes when both approvals are granted. - [MishkaDocument.Resources.Changes.AutoApplySectionDraft](MishkaDocument.Resources.Changes.AutoApplySectionDraft.md): Automatically applies section draft changes when both approvals are granted. - [MishkaDocument.Resources.Changes.CloneVersion](MishkaDocument.Resources.Changes.CloneVersion.md): Writes a new version of a document as a copy of one of its versions: every page of the source with its notes and tags, in the source's tree, in one transaction with the new version itself. - [MishkaDocument.Resources.Changes.ContentFacts](MishkaDocument.Resources.Changes.ContentFacts.md): The components, the icons and the classes a page's or a note's `content` names, worked out when it is written — reading either works out nothing - [MishkaDocument.Resources.Changes.DestroyClones](MishkaDocument.Resources.Changes.DestroyClones.md): Deleting a version deletes every version cloned from it, however deep, each through the same delete (`:destroy`, under the version's site) with the same actor — so each is refused, deleted with its pages and notes, logged and broadcast as its own delete would be. The database's `source_version_id` reference deletes a clone too, for a delete made past the action. - [MishkaDocument.Resources.Changes.DetachVersion](MishkaDocument.Resources.Changes.DetachVersion.md): Makes a cloned version independent of the version it was cloned from, in one transaction - [MishkaDocument.Resources.Changes.EnsureAddressPages](MishkaDocument.Resources.Changes.EnsureAddressPages.md): Gives a document the two pages its own address is read at, when the address is set — its front page and every page of it (`MishkaDocument.Provision.ensure_address!/3`) — so an admin who writes `/product/docs` on the form is answered there without building a page. - [MishkaDocument.Resources.Changes.EnsureSingleLatestVersion](MishkaDocument.Resources.Changes.EnsureSingleLatestVersion.md): Ensures only one version is marked as latest per workspace. - [MishkaDocument.Resources.Changes.HighlightCode](MishkaDocument.Resources.Changes.HighlightCode.md): Highlights the code blocks of a page's `content` — a section's, a note's or a draft of either — with Makeup, once, when it is written. - [MishkaDocument.Resources.Changes.SetEventMetadata](MishkaDocument.Resources.Changes.SetEventMetadata.md): Sets event metadata for activity log tracking. - [MishkaDocument.Resources.Changes.SetSectionLineageId](MishkaDocument.Resources.Changes.SetSectionLineageId.md): Automatically sets section_lineage_id for tracking section versions across workspace versions. - [MishkaDocument.Resources.Changes.SetVersionAttributes](MishkaDocument.Resources.Changes.SetVersionAttributes.md): Change module for setting custom version attributes from context. - [MishkaDocument.Resources.Changes.StartFromSource](MishkaDocument.Resources.Changes.StartFromSource.md): A draft of an existing record — a page (`MishkaDocument.SectionDraft`) or a note (`MishkaDocument.NoteDraft`) — names in its `changed_fields` which of the record's `fields` it changes. Applying the draft writes those and no other (`MishkaDocument.Resources.Changes.AutoApplySectionDraft`, `MishkaDocument.Resources.Changes.AutoApplyNoteDraft`), so a field its editor did not change is never written over, whatever the record holds by the time the draft is applied. - [MishkaDocument.Resources.Helpers.AdvisoryLockKeyGenerator](MishkaDocument.Resources.Helpers.AdvisoryLockKeyGenerator.md): Extends AshEvents default advisory lock key generator to support nil tenants. - [MishkaDocument.Resources.Helpers.ClearRecordsForReplay](MishkaDocument.Resources.Helpers.ClearRecordsForReplay.md): Implements the clear records behavior for event replay. - [MishkaDocument.Resources.Helpers.DayMarks](MishkaDocument.Resources.Helpers.DayMarks.md): Ash preparation that tells each event-log row whether it opens a new day in the timeline, and — when it does — how many events that whole day holds. The day heading `MishkaCmsCoreWeb.Templates.ActivityTimeline` draws is built from both. - [MishkaDocument.Resources.Helpers.Labels](MishkaDocument.Resources.Helpers.Labels.md): The small words, numbers and dates the document resources' tables draw in their cells. - [MishkaDocument.Resources.Helpers.SiteNames](MishkaDocument.Resources.Helpers.SiteNames.md): Ash preparation that resolves, for the whole page in ONE query, the site name the master's site badge (`event_site` column) shows on each event-log row. - [MishkaDocument.Resources.Helpers.TenantSiteFilter](MishkaDocument.Resources.Helpers.TenantSiteFilter.md): Ash preparation that filters event logs by the actor's site_id. - [MishkaDocument.Resources.Helpers.VersionClones](MishkaDocument.Resources.Helpers.VersionClones.md): The versions cloned from a version, and from those, however deep: every version whose `source_version_id` leads back to it. Deleting a version deletes them with it (`MishkaDocument.Resources.Changes.DestroyClones`); its delete confirmation names them (`MishkaDocument.Resources.Calculations.CloneNumbers`). - [MishkaDocument.Resources.Preparations.OpenToActor](MishkaDocument.Resources.Preparations.OpenToActor.md): Keeps a public read to the records its reader may open, by the rule the record's page keeps: a record whose lock names permissions opens only for a reader holding one of them (`AccessPermissions.match?/3`), and never for a visitor who is not signed in. A list leaves out, in its own query, every record the reader may not open, so its pages and its count are of what the reader sees; asking for one of them by itself finds nothing, as an id nothing is stored at does. - [MishkaDocument.Resources.Validations.DocumentAddress](MishkaDocument.Resources.Validations.DocumentAddress.md): Refuses a document's `path` — its own address, `/chelekom/docs` — that the site could not answer, or that another document's pages would answer too (`MishkaDocument.Address`). - [MishkaDocument.Resources.Validations.MasterCommentReadOnly](MishkaDocument.Resources.Validations.MasterCommentReadOnly.md): Stops a tenant changing an issue comment a master wrote. - [MishkaDocument.Resources.Validations.OpenToActor](MishkaDocument.Resources.Validations.OpenToActor.md): Refuses a write onto a record its actor may not open — a bookmark on a locked page of a document, a follow of a locked document — with the message the action gives for a record that is not there, so the refusal tells nothing a reader may not know. Who may open it is what its page asks (`MishkaDocument.Resources.Preparations.OpenToActor.opens?/2`). - [MishkaDocument.Resources.Validations.SameWorkspaceVersion](MishkaDocument.Resources.Validations.SameWorkspaceVersion.md): Refuses a record that names, by id, a record of the wrong workspace version. - [MishkaDocument.Resources.Validations.SectionAddress](MishkaDocument.Resources.Validations.SectionAddress.md): Refuses a page whose whole address in its version (`MishkaDocument.Address`) another page of the version already has. - [MishkaDocument.Section](MishkaDocument.Section.md): Manages documentation sections within workspace versions. - [MishkaDocument.SectionBookmark](MishkaDocument.SectionBookmark.md): Manages user bookmarks for documentation sections. - [MishkaDocument.SectionDraft](MishkaDocument.SectionDraft.md): Manages draft proposals for section changes within workspace versions. - [MishkaDocument.SectionTag](MishkaDocument.SectionTag.md): Manages the many-to-many relationship between documentation sections and tags. - [MishkaDocument.SectionView](MishkaDocument.SectionView.md): Tracks unique page views for documentation sections using upsert. - [MishkaDocument.ShareCards](MishkaDocument.ShareCards.md): Which share card (`MishkaCmsCore.Runtime.ShareCard`) a documentation record shows: the kind of record its page shows (`MishkaDocument.Plugins.PageRecords`) and the captures that name it — what a page of that kind answers for (`MishkaDocument.Plugins.PageSeo`). - [MishkaDocument.Tag](MishkaDocument.Tag.md): Manages tags for categorizing and organizing documentation sections. - [MishkaDocument.Workspace](MishkaDocument.Workspace.md): Manages document workspaces in the MishkaDocument plugin. - [MishkaDocument.WorkspaceAccent](MishkaDocument.WorkspaceAccent.md): The two colours a workspace card wears: one for how far along it is, one for what language it is in. - [MishkaDocument.WorkspaceFollower](MishkaDocument.WorkspaceFollower.md): Manages user follows for documentation workspaces. - [MishkaDocument.WorkspaceScope](MishkaDocument.WorkspaceScope.md): Which workspaces the admin list shows: one row per document, not one per translation. - [MishkaDocument.WorkspaceVersion](MishkaDocument.WorkspaceVersion.md): Manages workspace versions in the MishkaDocument plugin. - [MishkaDocument.WorkspaceVersionLineage](MishkaDocument.WorkspaceVersionLineage.md): Tracks the lineage and cloning history of workspace versions. - [MishkaDocumentWeb.AdminActions](MishkaDocumentWeb.AdminActions.md): The buttons plugins add to a document's admin screen — the Sections screen of a workspace, `/admin/dashboard/document/sections/:workspace_version_id` — through the `document:workspace:admin_actions` mishka_installer event. Each button opens the plugin's own LiveComponent in the screen's modal, one at a time. - [MishkaDocumentWeb.ExamplePreviewController](MishkaDocumentWeb.ExamplePreviewController.md): Answers `/_preview/doc-example/:kind/:id/:index` with `MishkaDocumentWeb.Live.ExamplePreview`: one example of a page of a document, or of one of its notes, drawn alone and live, for the frame of the kit's `doc-example` Preview dialog. - [MishkaDocumentWeb.Live.Admin.Dashboard.EventLog.Index](MishkaDocumentWeb.Live.Admin.Dashboard.EventLog.Index.md): Admin event-log page at `/admin/dashboard/document/event-logs`. Mounts the standard `MishkaGervaz.Table.Web.Live` over `MishkaDocument.EventLog`, drawn by the shared `MishkaCmsCoreWeb.Templates.ActivityTimeline` — the same timeline as the unified log at `/admin/dashboard/runtime/activity`. - [MishkaDocumentWeb.Live.Admin.Dashboard.Home.StatsLive](MishkaDocumentWeb.Live.Admin.Dashboard.Home.StatsLive.md): The row of figures across the top of the Documents dashboard, each read through Ash as the admin looking at it — a master across every site, a site admin on their own - [MishkaDocumentWeb.Live.Admin.Dashboard.Issue.IssueCommentsLive](MishkaDocumentWeb.Live.Admin.Dashboard.Issue.IssueCommentsLive.md): The comments thread of a single issue, rendered inside the expanded issue card. - [MishkaDocumentWeb.Live.ExamplePreview](MishkaDocumentWeb.Live.ExamplePreview.md): One `doc-example` of a page of a document, or of one of its notes, drawn alone and live: the page the kit's `doc-example` opens in its Preview dialog's frame, at `/_preview/doc-example///` (`MishkaDocumentWeb.ExamplePreviewController`). - [MishkaDocumentWeb.Routes](MishkaDocumentWeb.Routes.md): Extension routes for MishkaDocument. - [MishkaDocumentWeb.SidebarMenu](MishkaDocumentWeb.SidebarMenu.md): The document extension's admin pages and the one sidebar entry that links to them — a `"sidebar:menus"` plugin (`MishkaCmsCore.Sidebar`). - [MishkaDocumentWeb.Templates.DocumentCard](MishkaDocumentWeb.Templates.DocumentCard.md): The workspaces grid: a hero banner leading a card grid. - [MishkaDocumentWeb.Templates.DocumentSection](MishkaDocumentWeb.Templates.DocumentSection.md): The sections list: full-width cards beside a rail of workspace panels. - [MishkaDocumentWeb.Templates.IssueCard](MishkaDocumentWeb.Templates.IssueCard.md): The issues list: full-width cards, each one able to open a comments panel underneath itself.