# The KnowledgeBase kit — a knowledge base and the help centre it is read in

> `Osysharp.KnowledgeBase` keeps a knowledge base — markdown articles in categories and sections, written as drafts and published as numbered versions after a review — and the public help centre it is read in: a front page with a search, categories, an article in the reader's language (or the original, saying so), search results, suggestions under a contact form's subject as a person types, and a search small enough for a chat panel. Articles for the team only never reach anybody else, by any read.

<!-- id: ui-knowledge-kit · area: ui · stability: preview · html: https://osysharp.com/reference/ui/knowledge-kit/ -->

## Summary        {#summary}
`use Osysharp.KnowledgeBase@1;` gives an app a knowledge base and a public help centre. Say who does what, and where people
go to ask a person:

```osy syntax
policy ReadsTeamKnowledge => IsStaff;    // reads the articles only the team may read
policy WritesKnowledge => IsStaff;       // writes drafts, sends them for review
policy ReviewsKnowledge => IsEditor;     // publishes, sends back, archives
policy ManagesKnowledge => IsAdmin;      // categories, sections, settings

app.Knowledge = new KnowledgeSetup { Name = "Lumen Docs", ContactPath = "/contact" };
```

The help centre is at `/help-centre`, the team's desk at `/knowledge`, the settings at `/settings/knowledge`.

## Signature      {#signature}
```osy syntax
use Osysharp.KnowledgeBase@1;      // with Osysharp.Identity@0, Osysharp.Markdown@1, Osysharp.MarkdownEditor@1,
                               // Osysharp.Seo@0, Osysharp.Memory, Osysharp.Workflow and Osysharp.Ui

app.Knowledge = new KnowledgeSetup {
  Name = "",            // the help centre's name — "Help centre" when empty
  Path = "/help-centre", DeskPath = "/knowledge",   // where its pages are, when an app moves them
  ContactPath = "",     // where "Still need help?" goes — "" offers no way to ask
  ContactLabel = "Contact us",
};

// what happens to articles — raised by the kit, heard by any [On] handler after the commit, as the app:
//   ArticleSentForReview { Revision } · ArticlePublished { Revision } · ArticleSentBack { Revision, Step }
//   ArticleArchived { Article } · ArticleRestored { Article }
// each says itself ("Sent back: …"), so telling somebody needs no words: Notify(author, e)
[On] void Heard(ArticleSentForReview e);

// writing
KnowledgeRevision NewKnowledgeArticle(KnowledgeSection section, string title, string language = "",
                                      KnowledgeAudience audience = KnowledgeAudience.Everyone, string slug = "")
KnowledgeRevision KnowledgeDraft(KnowledgeArticle article, string language = "")   // the draft being written, or a new one
KnowledgeRevision KnowledgeDraftFrom(KnowledgeRevision earlier)                     // put back what an earlier version said
void DiscardKnowledgeDraft(KnowledgeRevision draft)                                 // a draft never sent

// steps — each recorded, applied after the commit
KnowledgeStep SendKnowledgeForReview(KnowledgeRevision draft, string note = "")
KnowledgeStep TakeKnowledgeBack(KnowledgeRevision version)
KnowledgeStep PublishKnowledge(KnowledgeRevision version, string note = "")
KnowledgeStep SendKnowledgeBack(KnowledgeRevision version, string note)
KnowledgeStep ArchiveKnowledge(KnowledgeArticle article, string note = "")
KnowledgeStep RestoreKnowledge(KnowledgeArticle article)

// reading — every one the reader's own, in the reader's language
KnowledgeArticleView KnowledgeArticleView(string slug)
List<KnowledgeShelf> KnowledgeShelves(bool includeTeam = false)
KnowledgeResults SearchKnowledge(string query, bool includeTeam = false, int take = 20)
KnowledgeResults KnowledgeByMeaning(string query, bool includeTeam = false, int take = 5)   // by what it is about
List<KnowledgeHit> SuggestKnowledge(string text, int take = 0)        // 0: as many as the settings say

// components
KnowledgeSuggestingField(string label, Binding<string> value, …)     // a field that offers articles as a person types
KnowledgeQuickSearch(int take = 4, …)                                // a search for a chat panel or a sidebar
HelpCentreFrame()                                                    // design it to put the help centre in your site
// pages: /help-centre, /help-centre/c/{slug}, /help-centre/a/{slug}, /help-centre/search/{query},
//        /knowledge, /knowledge/a/{id}, /knowledge/a/{id}/{language}, /knowledge/review, /knowledge/categories
// served: /help-centre/sitemap.xml
```

## Description    {#description}

### Who reads what   {#audience}
A category and an article are for **everyone** or for **the team only** (`KnowledgeAudience.Team`). An article in a
team category is the team's whatever its own audience says. A person outside the team reads an article only while a
version of it is published, it is not archived, and both it and its category are for everyone — and that is a RULE on
the rows, so it holds for every read: the help centre, a search, a suggestion, a query the app writes itself. An
address that names a team article answers "not here", exactly as an address that names nothing does.

`SearchKnowledge` and the help centre's pages take `includeTeam` for a reader who is on the team: the team sees its own
articles in the help centre, marked. The help centre's search, `SuggestKnowledge` and `KnowledgeQuickSearch` always
leave them out, whoever is typing.

### How does a draft become what readers read?   {#review}
What an article says is in its VERSIONS, one language each and numbered 1, 2, 3. A draft is the only version whose
words change — the title, a line about it, and a markdown body written in the markdown editor. Sending it for review,
publishing it, sending it back and archiving are STEPS: each is a row made by the person taking it, under the step's
rule (writers send and take back; reviewers publish, send back and archive), and applied after the commit by the kit's
own run, which checks it again. A version is never published by editing it. While **Review before publishing** is on
(the default), a reviewer publishes only a version somebody else wrote; with it off, a reviewer may publish a draft of
their own. Publishing a version makes the one it replaces an earlier version, kept and readable by the team; "Start a
draft from it" puts back what an earlier one said.

### Which language does a reader get?   {#languages}
Each language the app speaks has its own versions of an article, written, reviewed and published on their own. A new
language's draft starts as a copy of the original's words, to translate. A reader reads the version in their language
([Which language a reader gets — and switching it](https://osysharp.com/reference/i18n/language/)); with none, the original — and the page says so. Category and section names are
[[Translatable] — an entity's own words in every language](https://osysharp.com/reference/i18n/translatable/): the team types them in each language on the desk. An article in a right-to-left language reads
right to left inside a left-to-right page, and the other way round.

### How does a reader find an article?   {#search}
Every published version's words are kept as plain text and searched in full text ([field search (Matches / TextScore / Similarity)](https://osysharp.com/reference/memory/field-search/)) — every word
first, and when no article has them all, any of them, saying so. A search finds an article in any of its languages and
shows it in the reader's.

**Inflected words find each other.** Each version also keeps its words in their base form, stemmed in ITS language
([The stemming kit — an inflected word finds its other forms](https://osysharp.com/reference/ui/stemming-kit/)), and a query is stemmed in the reader's: a Swedish reader asking about "betalningen" finds the
article that says "betalningar", and an Arabic reader's "بالبطاقة" finds "البطاقات". English, Swedish, Danish,
Norwegian and Arabic have stemmers; any other language matches word for word.

**By meaning, too.** The current version of every published article is indexed by meaning (`[Searchable(Memory)]`), so
`KnowledgeByMeaning(query)` finds an article that answers a question in other words — or in another language. It is
for an app with an embedder: without one, the results say so in `Unavailable` and hold nothing, and word search goes on
working. Both searches apply the same rule — an article for the team only is never in a customer's results, however it
was found. `KnowledgeSuggestingField` asks once a person pauses and shows nothing until something matches; an article
it offers opens over the form, so what they wrote is still there.

### The help centre in your site   {#site}
The front page, a category, an article and the search are PARTS (`HelpCentreFront`, `HelpCentreCategory`,
`HelpCentreArticle`, `HelpCentreSearch`), so a look ([Looks — a site's whole design, picked per version](https://osysharp.com/reference/ui/looks/)) or the app can design them. `HelpCentreFrame` is the
frame around every help-centre page — nothing by default; design it once to put the help centre in your site's header
and footer. The kit's pages write a head for search engines ([head](https://osysharp.com/reference/ui/head/)): an article's title, line and canonical
address, a `TechArticle` and its breadcrumb (`Osysharp.Seo`), every language's address, and `noindex` on a team article
and on "not here". `/help-centre/sitemap.xml` lists every public category and article.

### Where does the team write and review?   {#desk}
`/knowledge` lists every article with how each of its languages stands; an article's page has its draft in the editor,
what can be done next, where it sits and who reads it, its versions and its steps. `/knowledge/review` is the queue,
longest-waiting first; `/knowledge/categories` arranges the shelves. They stand in your app's own shell
(`[Layout(WorkspaceFrame)]` — design `WorkspaceShell` once, [WorkspaceShell, WorkspaceFrame and [Nav] — a kit's pages in your app's shell and navigation](https://osysharp.com/reference/ui/workspace-frame/)) and the knowledge base lists itself
in your navigation (`[Nav]`, `Nav.Pages()`); take a page over by name to change it.

### What personal data does it keep?   {#privacy}
Who wrote and who reviewed an article, and what a reviewer wrote back, are classified (`Personal.Identity`,
`Personal.Content`). With `Osysharp.Privacy`, `Osysharp.Privacy.KnowledgeBase` arrives by itself ([Bridges — a package that arrives when an app uses the two it completes](https://osysharp.com/reference/project/bridges/)): a person's articles, versions and
steps are in their copy, their review notes are kept out of use on request, and an erasure takes their name off
everything and their notes with it — the articles stay. Whoever answers privacy requests must be able to read the
knowledge base (a writer, reviewer or manager of it).

### What is not here   {#not-here}
AI drafts and AI translation of an article — add-ons on this core. Article voting ("was this helpful?") — the
SatisfactionSurveys kit's question, asked about an article, when an app wants it.

## Examples       {#examples}

### A photo editor documents its features   {#example-docs}
```osy title="who does what, the setup, and a contact form that suggests articles" test app=ui-knowledge-kit
app LumenDocs {
  use Osysharp.Identity@0;
  use Osysharp.KnowledgeBase@1;
  use Osysharp.MarkdownEditor@1;
  use Osysharp.Seo@0;
  use Osysharp.Memory;
  use Osysharp.Workflow;
  use Osysharp.Ui;
  model "model/**/*.osy";
}

using Osysharp.Identity;
using Osysharp.KnowledgeBase;

[Role] enum DocsRole { Authenticator, Writer, Editor }
policy IsWriter => RoleGrant.Any(g => g.Member == user && (g.Role == DocsRole.Writer || g.Role == DocsRole.Editor));
policy IsEditor => RoleGrant.Any(g => g.Member == user && g.Role == DocsRole.Editor);

policy ReadsTeamKnowledge => IsWriter;
policy WritesKnowledge => IsWriter;
policy ReviewsKnowledge => IsEditor;
policy ManagesKnowledge => IsEditor;

app.Knowledge = new KnowledgeSetup { Name = "Lumen Docs", ContactPath = "/contact", ContactLabel = "Ask the Lumen team" };

/// Where "Still need help?" goes — a subject that offers articles as it is typed.
[Route("/contact")] [AllowAnonymous] [Render(CSR)]
component ContactPage() {
  string subject = "";
  render {
    Stack(gap: 4, p: 6) {
      KnowledgeSuggestingField("What is it about?", value: subject, placeholder: "One line about it");
    }
  }
}
```

## See also       {#see-also}
- [Markdown](https://osysharp.com/reference/types/markdown/) — what an article's body is; [The markdown editor kit — a rich editor you opt into](https://osysharp.com/reference/ui/markdown-editor-kit/) — where it is written
- [field search (Matches / TextScore / Similarity)](https://osysharp.com/reference/memory/field-search/) — the full-text search under `SearchKnowledge`
- [Looks — a site's whole design, picked per version](https://osysharp.com/reference/ui/looks/) — designing the help centre's parts and frame
- [head](https://osysharp.com/reference/ui/head/) — what a search engine reads on each page
- [The Conversations kit — one timeline per conversation, across every channel](https://osysharp.com/reference/ui/conversations-kit/) — `KnowledgeQuickSearch()` in the chat's `help` slot
- [[Settings] and [Setting]: a settings section declared on its row, each field drawn by its type](https://osysharp.com/reference/ui/settings-declared/) — the settings page the kit declares
- [The Privacy kit — erasure that keeps only what the company must or may](https://osysharp.com/reference/security/privacy-kit/) — a writer's articles and notes in their privacy requests
