A knowledge library for Yii2 applications. It consists of three modules:
- a backend module for managing knowledge items with versioned, time-valid content, review by a second person, relations between items and a change history
- a frontend module that renders the currently valid content server-side, e.g. for a RAG crawler
- an MCP module, a stateless Model Context Protocol server giving AI clients read access to the currently valid content
The library stores knowledge types and topics, knowledge items, their content versions (each valid for a time range), files attached to versions, relations between items and a history of changes.
| Entity | Model | Purpose |
|---|---|---|
| Type | Type |
Kind of item; defines whether versions have a validity period (has_validity_period, locked once items of the type have versions) and whether publishing requires a review (requires_review) |
| Topic | Topic |
Keyword; an item can be assigned to several topics (Item::$topicIds) |
| Item | Item |
The knowledge item with title, summary, source information and archive flag |
| Version | Version |
Numbered content version of an item: Markdown text and/or files, validity dates, review and publication data |
| File | File |
File of a version in the file storage, either a main file (carries the content) or an attachment, with an optional title shown instead of the file name |
| Relation | Relation |
Directed relation between two items: based_on, supplements or replaces |
| History | History |
Change log entry of an item, optionally for one version |
draft: being edited, at most one per item; may be empty. No draft can be created while a version of the item is in review or the item is archivedin_review: submitted to a reviewer (submitForReview()), at most one per item; the reviewer approves (approve()) or returns it as draft (returnToDraft())published: released (publish(),approve()); a draft can be published directly only if its type does not require a reviewwithdrawn: no longer valid, withdrawn (withdraw()) or replaced by a correction
Versions in review or published need content: a non-empty text or at least one main file.
A published version has an effective state at a given date (Version::getEffectiveState()):
in_force: valid at the dateupcoming: starts after the datehistorical: ended before the date, or superseded
For types with a validity period a version is valid from valid_from until valid_until (open-ended if empty). A new version must start strictly after the latest published version; publishing it ends the predecessor the day before, unless the predecessor already ends earlier (gaps are allowed). Corrections (corrects_version_id) are exempt from this rule, they keep the period of the corrected version (see Withdrawal and corrections).
For types without a validity period versions have no dates; the published version with the highest number is in force, all older ones are historical.
Version::find()->validAt($date) returns the valid version of each item at a date, at most one per item; Item::getValidVersion() returns it for one item.
- PHP 8.1 or later
- Yii 2.0.45 or later
- dmstr/yii2-rbac-migration for the RBAC setup
- dmstr/yii2-web for route-based access control
- yiisoft/yii2-bootstrap and kartik-v/yii2-widget-select2 for the backend views
- league/flysystem 3 and an application component providing a flysystem filesystem as file storage (default ID
fs), see Files
The default user provider works with the identity and user UUIDs of 2amigos/yii2-usuario.
Install the package with Composer:
composer require dmstr/yii2-knowledge-libraryTo install directly from the repository, add it as a VCS repository first:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/dmstr/yii2-knowledge-library"
}
]
}Register the modules in the application configuration:
return [
'modules' => [
'knowledge-library' => [
'class' => \dmstr\knowledgeLibrary\Module::class,
'fileStorage' => 'fs',
'targetPath' => 'knowledge-library',
'allowedExtensions' => ['pdf', 'docx', 'xlsx', 'pptx', 'odt', 'ods', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'webp'],
'maxFileSize' => 20 * 1024 * 1024,
'userProvider' => null,
],
'knowledge' => [
'class' => \dmstr\knowledgeLibrary\frontend\Module::class,
'backendModuleId' => 'knowledge-library',
],
'knowledge-mcp' => [
'class' => \dmstr\knowledgeLibrary\mcp\Module::class,
'backendModuleId' => 'knowledge-library',
'authenticator' => ['class' => \yii\filters\auth\HttpBearerAuth::class],
],
],
];The MCP module is optional; leave it out if the application has no AI clients.
| Property | Default | Description |
|---|---|---|
fileStorage |
'fs' |
Name of the application component used as file storage for version files; the component must implement League\Flysystem\FilesystemOperator or provide one through getFilesystem() (e.g. eluhr\flysystemRestApi\components\FileStorage of eluhr/yii2-flysystem-rest-api), otherwise an InvalidConfigException is thrown |
targetPath |
'knowledge-library' |
Target directory inside the file storage |
allowedExtensions |
['pdf', 'docx', 'xlsx', 'pptx', 'odt', 'ods', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'webp'] |
File extensions allowed for uploaded version files (lower case, without dot); the MIME type detected from the content must match the extension |
maxFileSize |
20971520 (20 MB) |
Maximum size of an uploaded version file in bytes, checked against the actual file size; the PHP and web server upload limits must allow at least this size |
inlineMimeTypes |
['application/pdf'] |
MIME types that file/download sends for display in the browser (Content-Disposition: inline); all other files are sent as download (attachment). Empty to download everything |
userProvider |
null |
Definition of a user provider object (class name, configuration array or object), resolved via Yii::createObject(); null uses the default provider |
| Property | Default | Description |
|---|---|---|
backendModuleId |
'knowledge-library' |
ID of the backend module whose configuration (file storage, user provider) the frontend module shares |
| Property | Default | Description |
|---|---|---|
backendModuleId |
'knowledge-library' |
ID of the backend module whose file storage the module reads the files from |
authenticator |
['class' => HttpBearerAuth::class] |
Configuration of the authentication filter (yii\filters\auth\AuthMethod) that logs the client in before the access check, e.g. HttpBearerAuth, bizley\jwt\JwtHttpBearerAuth, the package's StaticTokenAuth or a CompositeAuth of several; null attaches none, so only clients with a session of the application get access |
serverName, serverVersion |
'Knowledge Library', '1.0.0' |
Server information announced to the client on initialize |
instructions |
null |
Instructions for the model announced on initialize; null uses the default text describing the tools |
maxInlineFileSize |
5242880 (5 MB) |
Largest file in bytes that knowledge_get_file returns inline; larger files are answered with their download URL |
tools |
the five tools below | Tool classes (or configuration arrays) of the server, each created with the module as constructor argument; override to add or remove tools |
The backend module provides the following controllers. Routes are relative to the module ID, e.g. /knowledge-library/item/index for the configuration above. The module URL itself (/knowledge-library) opens the item library (defaultRoute is item).
| Route | Purpose |
|---|---|
item/index |
Item library: list with filters, sorting and paging |
item/create |
Create an item (title and type) |
item/view |
Detail page of an item |
item/update |
Edit the master data of an item |
item/source |
Edit the source data of an item |
item/delete |
Delete an item with all its versions, files, relations and history (POST only) |
item/archive |
Archive an item, body reason optional (POST only) |
item/restore |
Restore an archived item, body reason optional (POST only) |
type/index |
List of types |
type/create |
Create a type |
type/update |
Edit a type; the validity period cannot be changed once items of the type have versions |
type/delete |
Delete a type that is not used by any item (POST only) |
topic/index |
List of topics |
topic/create |
Create a topic |
topic/update |
Edit a topic |
topic/delete |
Delete a topic that is not used by any item (POST only) |
version/create |
Start the version wizard of an item: creates its draft or continues the existing one (POST only) |
version/update |
Step of the version wizard (step 1 to 4) |
version/publish |
Publish a draft of a type without review (POST only) |
version/discard |
Discard a draft with its files (POST only) |
version/review |
Review page of a version in review, for its reviewer |
version/approve |
Approve and publish a version in review, body note optional (POST only) |
version/return |
Return a version in review to its submitter, body note required (POST only) |
version/reviewer |
Change the reviewer of a version in review, body reviewer and reason optional |
version/withdraw |
Withdraw a published version, body reason and successor (previous, correction or none) |
version/correct |
Start the correction of a published version, body reason optional (POST only) |
file/download |
Download a file of a version |
relation/create |
Add a relation from an item to another (POST only) |
relation/delete |
Remove a relation (POST only) |
The views use yii\bootstrap\ActiveForm and yii\grid\GridView with Bootstrap 3 markup, and the Select2 widget of kartik-v/yii2-widget-select2. They set $this->title and the breadcrumbs ($this->params['breadcrumbs']) and are rendered in the layout configured for the module (layout property).
New versions are created in a wizard that works on the draft of the item (at most one per item). A new draft takes over the text, the files and the details (title, summary, topics) of the latest published version. The wizard has four steps, each saved on its own; "Save as draft" returns to the detail page in every step and writes the history entry draft_saved:
- Content: Markdown text, main files (
mainFiles[<i>]with the titlemainFileTitles[<i>]) and attachments (attachments[<i>]with the titleattachmentTitles[<i>]); further upload rows are added with a button. Files of the draft can be removed (remove[<file-id>]=1) and retitled (titles[<file-id>], empty removes the title; titles longer than 255 characters are rejected like uploads); files uploaded in the draft are highlighted. Retitling a file taken over from the predecessor changes the row of the draft only. An attachment whose content is already attached to another item shows a hint with a link to create an item of its own from it; the hint does not block. The step is complete with a text or at least one main file, attachments alone do not count. Rejected uploads are shown with the reason and keep the wizard on the step. - Validity: Valid From and Valid Until for types with a validity period, with the consequences for the previous version and a preview of the timeline. A correction shows the period of the corrected version read-only.
- Details: title, topics and summary; they are applied to the item when the version is published.
- Check: summary of the version and "Publish" for types without review; for types with review the reviewing person (
reviewer), an optional message (message) and "Submit for approval" (submit-for-review).
The wizard of a correction is titled "Correct version n" with the number of the corrected version. Drafts of archived items cannot be continued, only discarded.
Versions of a type with requires_review are published by a second person. The editor submits the draft in step 4 of the wizard to a reviewing person, optionally with a message; the version is then in_review, and no other draft can be created for the item until the review ends. The detail page shows who reviews the version; the list offers the filter "Awaiting my approval" (ItemSearch[review]=mine) with the number of items waiting for the current user.
The reviewer opens the review page (version/review) with the message, validity, consequences, topics, text and files of the version and either
- approves it (
version/approve): the version is published like a direct publication, the note is kept as reason of the history entry, or - returns it (
version/return) with a required note: the version becomes a draft again, the note and the reviewer are shown on the detail page and in the wizard, and submitting again clears them.
Only the chosen reviewer may approve or return a version, and nobody can submit a version to themselves (four-eyes principle, checked in Version, not only in the forms). Admins can hand a review over to another person (version/reviewer); the submitter and the current reviewer cannot be chosen.
The reviewing persons come from UserProviderInterface::getReviewerOptions(). The default provider offers the users with a direct assignment of the role KnowledgeLibraryReviewer or KnowledgeLibraryAdmin (all users if the application has no authManager). Root users of dmstr\web\User without a role assignment are therefore not offered as reviewers, and as the model checks the reviewer, they cannot approve a review of someone else either.
A published version in force or upcoming can be withdrawn on the page version/withdraw with a required reason. The page asks what applies instead in the period of the version:
- Previous version remains valid (
previous): the published version with the latest Valid From before the withdrawn one takes over its Valid Until (open-ended if empty). Not available without previous version. - Corrected version (
correction): continues to the correction below; the version stays published until the correction is published. - Nothing applies (
none): the item has no valid version in that period.
For types without a validity period the published version with the highest number is valid anyway: "previous version remains valid" only documents that, and "nothing applies" is available only if there is no previous version.
"Correct" (version/correct) creates the draft of a correction of any published version and opens the wizard. The correction takes over text, files and validity period of the faulty version; its period cannot be changed. Publishing the correction (directly or through the review) withdraws the faulty version, other versions are not changed. For types without a validity period only the version in force can be corrected. A correction is not possible while the item has a draft or a version in review, or when it is archived. The reason given on the withdrawal page is kept with the faulty version until the correction is published and cleared when its draft is discarded. The confirmation of "Correct" names the period and, for a period in the past, the years whose answers change.
Admins archive and restore items (item/archive, item/restore). Archived items are hidden in the list by default and marked "Archived". No new versions can be created, corrected, continued, approved or published for them: the routes redirect to the detail page with the message "The knowledge object is archived.". Returning a review, withdrawing a version, discarding a draft, master data, source and relations remain possible. Open drafts and reviews are kept when an item is archived.
Every change of an item is logged in the table history and shown in the tab "History" of the detail page (when, who, what, reason; newest first). Version transitions (submit, hand over, return, approve, publish, withdraw, correct) are logged by Version in the same transaction, archiving and restoring by Item, the other entries (item created, master data and source changed, draft saved and discarded, relations added and removed) by the controllers. Structured facts such as the version number, the reviewer or the successor of a withdrawal are kept as JSON in history.details, so an entry can still be described after its version was deleted (History::describe()). Deleting an item deletes its history.
Deleting an item is logged with Yii::info() in the category knowledge-library after the deletion, including the reference of the current user, the ID and title of the item, the number of deleted versions and file rows, and the number of stored files deleted, kept (still referenced) and failed. A stored file that cannot be deleted does not stop the deletion of the item; it is additionally logged with Yii::warning(). Info messages are usually not routed to a log target in production; to keep the entries, the application adds a target for the category:
'components' => [
'log' => [
'targets' => [
'knowledge-library' => [
'class' => \yii\log\FileTarget::class,
'levels' => ['info'],
'categories' => ['knowledge-library*'],
'logVars' => [],
],
],
],
],The frontend module shows the content valid today to readers, e.g. as entry point for a crawler that indexes the knowledge items. It is read-only and has no search, no navigation and no view of another date. Routes are relative to the module ID, e.g. /knowledge/item/index for the configuration above; the module URL itself (/knowledge) opens the list (defaultRoute is item).
| Route | Purpose |
|---|---|
item/index |
List of the valid items: title as link to the detail page, type and topics, sorted by title and ID, without paging |
item/view |
Detail page of a valid item (id) |
file/download |
Download a file (id) of the version valid today |
All three routes accept GET only; other methods are answered with 405.
Visibility. An item is shown if it is not archived and has a version valid today (ItemQuery::validAt(), see "Effective state"): a published version whose validity period contains today, or for types without validity period the published version with the highest number. Drafts, versions in review, historical, upcoming and withdrawn versions are never shown. Today is date('Y-m-d') in the time zone of the application. Nothing is cached: publishing, withdrawing, correcting, archiving or restoring changes the pages with the next request.
Detail page. The page shows title, type, topics, validity ("Valid from until ", "open-ended" without end; "Valid since " for types without validity period), source, source reference and source URL (as link), the summary, the text of the valid version rendered as Markdown (MarkdownHelper::render(): HTML in the text is shown as text, links and images keep only safe URL schemes) its main files and attachments (title as link text with the file name next to it, the file name as link text without title) with size and download link, and the related items (outgoing and incoming relations, labelled as seen from the item and grouped in the order of the relation types, the items sorted by title). Relations to items the frontend does not show (archived or without a version valid today) are left out. Empty fields and empty sections are left out. The markup is plain semantic HTML without JavaScript: the list is <ul class="knowledge-items">, the detail page <article class="knowledge-item" data-item-id="..."> with a <dl> of the fields and the sections knowledge-summary, knowledge-content, knowledge-files and knowledge-relations (a <dl class="knowledge-relations-list"> with the label as <dt> and one <dd> link per related item). The views set $this->title and the breadcrumbs and are rendered in the layout configured for the module.
Not found. Unknown IDs, archived items and items without a version valid today all answer with 404 ("The requested knowledge object does not exist."), without telling the cases apart. The list without valid items answers with 200 and a hint.
Downloads. file/download delivers only files of the version valid today of an item that is not archived, with the original name: PDFs (the inlineMimeTypes of the backend module) for display in the browser (Content-Disposition: inline), all other files as download (attachment). Files of any other version (draft, in review, historical, upcoming, withdrawn) and files of archived items answer with 404 ("The requested file does not exist."), as do unknown IDs and missing stored files. The files are read from the file storage of the backend module (backendModuleId).
The MCP module gives AI clients (e.g. a RAG service, Claude or any other MCP client) read access to the knowledge items valid today, with exactly the visibility rules of the frontend pages: active items with a version valid today, their files and their relations to such items; drafts, versions in review, withdrawn, historical and upcoming versions and archived items do not exist for the server. The module URL is the endpoint, e.g. https://example.org/knowledge-mcp for the configuration above (route default/index); file/download delivers the files of the valid versions like the frontend download, so a client can fetch a file with the same credentials.
Transport. The server speaks JSON-RPC 2.0 over the Streamable HTTP transport, revisions 2025-03-26, 2025-06-18 and 2025-11-25, and is stateless: every POST is handled on its own, no Mcp-Session-Id is issued and none is expected, so clients keep working across deployments and restarts of the application. There is no server-initiated stream and no session to end, so GET and DELETE on the endpoint answer with 405. Only the tools capability is announced; initialize, ping, tools/list and tools/call are served, notifications and client responses are accepted and ignored (202), and other methods are answered with "method not found". A body that is no JSON-RPC message is answered with 400 and a JSON-RPC error.
Tools. All tools are read-only (readOnlyHint) and return their data as structuredContent and, for clients without support for it, as JSON text:
| Tool | Arguments | Result |
|---|---|---|
knowledge_search |
query (substring of title, summary or text of the valid version, case-insensitive), type_id, topic_id, limit (1-100, default 20), offset |
total and one page of items sorted by title, each with id, title, type, topics, summary, valid_from, valid_until (null for types without validity period), published_at, version, updated_at |
knowledge_get_item |
id |
The item as above plus source (name, reference, url), content (Markdown source of the valid version), files (id, kind, title, name, mime_type, size, download_url) and relations (relation label as seen from the item, id, title; only to items the server shows) |
knowledge_get_file |
id |
The file as embedded resource: text/* files as text, others as base64 blob with their MIME type, the download URL as uri; structuredContent carries the file data and the item. Files larger than maxInlineFileSize are a tool error naming the download URL |
knowledge_list_types |
– | types with id, name, has_validity_period, requires_review and item_count (items valid today) |
knowledge_list_topics |
– | topics with id, name and item_count (items valid today) |
An unknown tool and invalid arguments are JSON-RPC errors (-32602); a call with valid arguments that cannot be answered (unknown or invalid item, missing file) is a tool result with isError, so the client can show the message to the model.
Authentication. Every request carries its own credentials; the module disables the session of the user component for its requests, so no session is created or written. The authenticator filter is attached to the module before the route-based access check and must log the client in; a request it does not authenticate is answered with 401 and a WWW-Authenticate: Bearer challenge. The default yii\filters\auth\HttpBearerAuth resolves the token through findIdentityByAccessToken() of the identity class. dmstr\knowledgeLibrary\mcp\auth\StaticTokenAuth accepts configured tokens, e.g. a service token from the environment for a technical user, and leaves other requests to the next filter of a CompositeAuth:
'knowledge-mcp' => [
'class' => \dmstr\knowledgeLibrary\mcp\Module::class,
'authenticator' => [
'class' => \yii\filters\auth\CompositeAuth::class,
'authMethods' => [
[
'class' => \dmstr\knowledgeLibrary\mcp\auth\StaticTokenAuth::class,
'tokens' => [getenv('KNOWLEDGE_MCP_TOKEN')],
'identity' => static fn () => User::findOne(['username' => getenv('KNOWLEDGE_MCP_USER')]),
],
['class' => \bizley\jwt\JwtHttpBearerAuth::class, 'throwException' => false],
],
],
],Empty tokens never match, so an unset environment variable does not open the endpoint. The authenticated identity still needs the permission of the module, see Access control.
Smoke test. An initialize request must answer without Mcp-Session-Id header, and a tools/list request with an invented session ID must answer normally:
curl --silent --show-error --dump-header - \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}' \
https://example.org/knowledge-mcpVersion files are stored in the flysystem filesystem of the component named by fileStorage under <targetPath>/<item-id>/<file-id>.<ext>, e.g. knowledge-library/<item-uuid>/<file-uuid>.pdf. The ID is the UUID of the file row, the extension comes from the original name; the original name (base name only) is kept in the file row together with MIME type, size, position and the SHA-256 hash of the content (content_hash). Uploads are read from their temporary file as stream, never loaded into memory as a whole.
Uploads are checked against allowedExtensions (also by the MIME type detected from the content) and maxFileSize; rejected files write neither a row nor a stored file. Uploading a main file records the upload at the item (source_uploaded_at, source_uploaded_by, the latest upload wins).
A new version takes over the files of its predecessor as new file rows pointing to the same stored file, the storage is not copied. A stored file is deleted only when no file row refers to it anymore: removing a taken-over file from a draft deletes the row only, removing a file uploaded in the draft deletes the stored file as well. Deleting an item deletes all its stored files.
The package works directly on the flysystem filesystem, so permission layers of a wrapper component do not apply. The files are not registered in a file manager (no storage_item rows of eluhr/yii2-flysystem-rest-api, storage_item_id stays empty); they do not appear in the file manager, and its download or stream routes do not deliver them. Files are delivered only through file/download, which checks the route permission of the package (knowledge-library_file_download) and sends the file with its original name and X-Content-Type-Options: nosniff, PDFs (inlineMimeTypes) for display in the browser (Content-Disposition: inline) and all other files as download (attachment). The frontend module delivers the files of the versions valid today through its own file/download, see "Frontend pages".
The package brings the following migrations:
| Migration | Purpose |
|---|---|
m260928_100000_knowledge_library_rbac |
Permissions and roles |
m260928_100100_knowledge_library_schema |
Tables of the data model |
m260928_185500_knowledge_library_routes |
Route permissions of items, types and topics |
m260928_203000_knowledge_library_versions |
Draft details of versions (draft_title, draft_summary, draft_topic_ids) and the content hash of files (content_hash) |
m260928_203100_knowledge_library_routes_2 |
Route permissions of the version wizard, the file download and the relations |
m260928_223000_knowledge_library_history_details |
Structured details of history entries (history.details) |
m260928_223100_knowledge_library_routes_3 |
Route permissions of review, withdrawal, correction and archive |
m261007_100000_knowledge_library_mcp_rbac |
Permission knowledge-mcp of the MCP module |
i18n/m260928_100200_knowledge_library_translations |
Optional German translations, see Translations |
i18n/m260929_120000_knowledge_library_translations_2 |
Optional German translations of the titles of main documents |
i18n/m261007_120000_knowledge_library_translations_3 |
Optional German translation of the validity period lock of types |
Add the migration path to the migrate controller of your console application:
'controllerMap' => [
'migrate' => [
'class' => \yii\console\controllers\MigrateController::class,
'migrationPath' => [
'@vendor/dmstr/yii2-knowledge-library/src/migrations',
],
],
],Messages use the category knowledge-library. The package registers no message source of its own; the category is served by the message source the application configured for it.
phd5 applications map '*' to a DbMessageSource and need no further configuration. The German texts of the package are provided by optional migrations (src/migrations/i18n, one per release that adds texts); add their path to the migrate controller next to the schema migrations:
'controllerMap' => [
'migrate' => [
'class' => \yii\console\controllers\MigrateController::class,
'migrationPath' => [
'@vendor/dmstr/yii2-knowledge-library/src/migrations',
'@vendor/dmstr/yii2-knowledge-library/src/migrations/i18n',
],
],
],Each migration writes its source messages and German translations into the tables of the DbMessageSource serving knowledge-library (sourceMessageTable and messageTable). Existing translations are never overwritten, so changes made by editors are kept, and running it again only adds missing rows. Languages missing in a {{%language}} table are skipped. down removes the migration's translations and those of its source messages that have no other translations left. If the category is not served by a DbMessageSource, the migration does nothing.
Applications without a database message source must configure a message source for knowledge-library* themselves; providing the translations is then up to the application:
'components' => [
'i18n' => [
'translations' => [
'knowledge-library*' => [
'class' => \yii\i18n\DbMessageSource::class,
'sourceLanguage' => 'en',
],
],
],
],All modules use the route-based access control of dmstr/yii2-web.
The backend module defines the following permissions:
knowledge_library_editorknowledge_library_reviewerknowledge_library_admin
They are grouped into roles that build on each other: KnowledgeLibraryAdmin contains KnowledgeLibraryReviewer, which contains KnowledgeLibraryEditor.
Every action of the backend module is checked against a permission named <module-id>_<controller>_<action>, e.g. knowledge-library_item_delete. The migrations m260928_185500_knowledge_library_routes, m260928_203100_knowledge_library_routes_2 and m260928_223100_knowledge_library_routes_3 create one permission per action and assign them to the roles:
| Permission | Role |
|---|---|
knowledge-library_item_index, knowledge-library_item_create, knowledge-library_item_view, knowledge-library_item_update, knowledge-library_item_source |
KnowledgeLibraryEditor |
knowledge-library_item_delete, knowledge-library_item_archive, knowledge-library_item_restore |
KnowledgeLibraryAdmin |
knowledge-library_type_index, knowledge-library_type_create, knowledge-library_type_update, knowledge-library_type_delete |
KnowledgeLibraryAdmin |
knowledge-library_topic_index, knowledge-library_topic_create, knowledge-library_topic_update, knowledge-library_topic_delete |
KnowledgeLibraryAdmin |
knowledge-library_version_create, knowledge-library_version_update, knowledge-library_version_publish, knowledge-library_version_discard |
KnowledgeLibraryEditor |
knowledge-library_version_withdraw, knowledge-library_version_correct |
KnowledgeLibraryEditor |
knowledge-library_version_review, knowledge-library_version_approve, knowledge-library_version_return |
KnowledgeLibraryReviewer |
knowledge-library_version_reviewer |
KnowledgeLibraryAdmin |
knowledge-library_file_download |
KnowledgeLibraryEditor |
knowledge-library_relation_create, knowledge-library_relation_delete |
KnowledgeLibraryEditor |
Reviewers and admins inherit the editor permissions, admins the reviewer permissions through the role chain. The route permission allows opening the review pages; approving or returning a version additionally requires being its chosen reviewer. The permission names assume the module ID knowledge-library; with another module ID the application creates the permissions itself.
Everyone who may open the detail page of an item may also download its files, including the files of drafts.
dmstr\web\User resolves route permissions by prefix: a permission knowledge-library grants every route of the module, knowledge-library_item every item action including delete. The package therefore creates no such permission; applications should not either unless they want to grant everything below it.
Access to the frontend module is granted by a permission named exactly like its module ID, i.e. knowledge for the configuration above. The RBAC migrations create the permission knowledge without assigning it; through the prefix resolution of dmstr\web\User it grants all frontend routes (knowledge_item_index, knowledge_item_view, knowledge_file_download), but no backend route, as knowledge-library_... does not start with knowledge_. The backend roles do not contain it: the application assigns it to the readers of the frontend, usually through a role of its own. Guests are redirected to the login, logged-in users without the permission get 403.
The MCP module works the same way with the permission knowledge-mcp (migration m261007_100000_knowledge_library_mcp_rbac), which grants its routes knowledge-mcp_default_index and knowledge-mcp_file_download. It is separate from knowledge, so the application decides whether the readers of the frontend may also use the MCP server, e.g. by adding both permissions to the same role. A client the authenticator does not log in gets 401, an authenticated client without the permission 403.
composer install
vendor/bin/phpunitThe suite unit tests the models, the migrations and the JSON-RPC handling of the MCP server on an in-memory SQLite database. The suite web runs the backend and frontend pages and the MCP endpoint through Yii::$app->runAction() in a web application (tests/WebTestCase.php, tests/FrontendWebTestCase.php, tests/McpWebTestCase.php) with the RBAC migrations of the package applied, so the route permissions are tested as well.
GitHub Actions runs both suites on PHP 8.1 to 8.4 for every push to master and every pull request (.github/workflows/tests.yml).
MIT, see LICENSE.