The supported entry point is Databases::createDatabase(), which builds the records table, the default title and content fields, and a default category.
$db = new \IPS\cms\Databases; $db->key = 'kb'; $db->use_categories = 1; $db->save(); // createDatabase() READS these while building the fields, so write them first \IPS\Lang::saveCustom( 'cms', 'content_db_' . $db->id, 'Knowledge Base' ); \IPS\Lang::saveCustom( 'cms', 'content_db_lang_pu_' . $db->id, 'articles' ); \IPS\cms\Databases::createDatabase( $db );
Four traps
1. There are no database_template_* columns. Setting $db->template_listing throws Unknown column 'database_template_listing' in 'field list'. Templates moved out of the row entirely.
2. Categories<id> and Records<id> are generated on demand from a cached database list. In a fresh CLI process that cache is stale, so class_exists() returns false for a database that plainly exists. Clear the datastore and load the database first.
3. setPermissions() inserts your array verbatim. It does not fill in app, perm_type or perm_type_id. Omit them and every category collides on an empty key, surfacing as Duplicate entry '0' for key core_permission_index.perm_type, leaving categories with no permission row at all — invisible to everybody including an administrator.
$insert = array(
'app' => $catClass::$permApp,
'perm_type' => $catClass::$permType,
'perm_type_id' => $id,
);
foreach ( $catClass::$permissionMap as $what => $column )
{
$insert[ 'perm_' . $column ] = '*';
}
$catClass::load( $id )->setPermissions( $insert );
4. Categories::$permType is NULL on the base class. The generated per-database subclass sets it (categories_2 for database 2). Read it at runtime.
The page
A database is invisible until a page hosts it. The page body needs the database tag, the page needs a populated page_full_path and a permission row, and Page::buildPageUrlStore() must be rebuilt afterwards or it 404s despite every row being correct.
Note that a permission row existing is not the same as it being populated: save() writes an empty one, so checking only for presence will leave perm_view blank and the page will 403.
Naming the page
A page title is a language string keyed cms_page_<id>, not a column. Create a page in code and the key is never written, so breadcrumbs, the browser title and menus all display the raw key — cms_page_4.
\IPS\Lang::saveCustom( 'cms', 'cms_page_' . $pageId, 'Knowledge Base' ); \IPS\Lang::saveCustom( 'cms', 'cms_page_' . $pageId . '_desc', 'What it is for.' );
Setting page_name on the row is not a substitute — that is only the label shown in the AdminCP. Set both, or the two disagree.
Category paths, or every article redirects forever
Creating a category in code sets category_furl_name and leaves category_full_path NULL. Record URLs are assembled as <page>/<category_full_path>/<record>, so a NULL path produces a double slash:
https://example.com/kb//my-article-r7/
Invision Community then issues a 301 to what it considers canonical, computes the same double-slash URL, and the browser stops with ERR_TOO_MANY_REDIRECTS.
The category listing works perfectly throughout, so it presents as a problem with articles rather than with categories.
// top-level: the path is just the slug
// nested: parent path . '/' . slug
Db::i()->update( 'cms_database_categories',
array( 'category_full_path' => $path ),
array( 'category_id=?', $id ) );
Build them parent-first so a child can prepend its parent, then clear the datastore.
Recommended Comments