ensurePrimaryAdminUser(); $roleIds = $this->ensureRoles($adminUserId); $permissionIds = $this->ensurePermissions($adminUserId); $this->ensureUserHasRole($adminUserId, $roleIds['Admin'], $adminUserId); $this->assignPermissions($adminUserId, $roleIds, $permissionIds, 'Admin', array_keys($permissionIds)); $this->assignPermissions($adminUserId, $roleIds, $permissionIds, 'Editor', [ 'publish:page', 'unpublish:page', 'edit:page', 'delete:page', 'view:draft', 'edit:draft', 'view:blog_post', 'edit:blog_post', 'delete:blog_post', 'publish:blog_post', 'view:media', 'edit:media', 'delete:media', ]); $this->assignPermissions($adminUserId, $roleIds, $permissionIds, 'Author', [ 'publish:page', 'edit:page', 'edit:draft', 'publish:draft', 'view:blog_post', 'edit:blog_post', 'publish:blog_post', 'view:media', 'edit:media', ]); // 'User' (public commenter role) intentionally gets no backend permissions. } private function ensurePrimaryAdminUser(): int { $existing = $this->fetchRow('SELECT id FROM users ORDER BY id ASC LIMIT 1'); if ($existing !== false) { return (int) $existing['id']; } $now = date('Y-m-d H:i:s'); // `created_by` is a NOT NULL self-referencing FK; the very first user // in the system has no other creator, so the row is inserted with a // placeholder and immediately corrected to reference itself. $this->execute( 'INSERT INTO users (username, password, f_name, l_name, email, failed_login_attempts, created_at, created_by) VALUES (?, ?, ?, ?, ?, 0, ?, 0)', [ 'admin', password_hash(bin2hex(random_bytes(8)), PASSWORD_DEFAULT), 'Site', 'Admin', 'admin@siteweaver.local', $now, ], ); $adminUserId = (int) $this->getAdapter()->getConnection()->lastInsertId(); $this->execute('UPDATE users SET created_by = ? WHERE id = ?', [$adminUserId, $adminUserId]); return $adminUserId; } private function ensureUserHasRole(int $userId, int $roleId, int $adminUserId): void { $exists = $this->fetchRow("SELECT 1 FROM user_role WHERE user_id = $userId AND role_id = $roleId"); if ($exists !== false) { return; } $this->execute( 'INSERT INTO user_role (user_id, role_id, created_at, created_by) VALUES (?, ?, ?, ?)', [$userId, $roleId, date('Y-m-d H:i:s'), $adminUserId], ); } /** * @return array role name => id */ private function ensureRoles(int $adminUserId): array { $definitions = [ 'Admin' => 'Full access to all CMS features and settings.', 'Editor' => 'Can create, edit, and delete content, but cannot change site settings.', 'Author' => 'Can create and edit their own content; can view drafts.', 'User' => 'Basic frontend read-only access to published content. No backend admin access. Can comment on Blog Posts.', ]; $ids = []; foreach ($definitions as $name => $description) { $row = $this->fetchRow("SELECT id FROM roles WHERE name = '" . $name . "'"); if ($row !== false) { $ids[$name] = (int) $row['id']; continue; } $this->execute( 'INSERT INTO roles (name, description, created_at, created_by) VALUES (?, ?, ?, ?)', [$name, $description, date('Y-m-d H:i:s'), $adminUserId], ); $ids[$name] = (int) $this->getAdapter()->getConnection()->lastInsertId(); } return $ids; } /** * @return array permission name => id */ private function ensurePermissions(int $adminUserId): array { // Common CMS permissions (per `data_model.example_seed_data_for_roles`) // plus the granular View/Edit/Delete-per-resource permissions called // for by `auth_requirements`'s "Granular Permissions" note, for every // resource named there: Page, Plugin, Theme, Blog Post, User. $definitions = [ ['manage:site', null, 'configure', 'Change site-wide settings (e.g., logo, navigation).'], ['publish:page', 'page', 'publish', 'Publish a page to the public site.'], ['unpublish:page', 'page', 'unpublish', 'Remove a page from public view.'], ['edit:page', 'page', 'edit', 'Modify an existing page.'], ['delete:page', 'page', 'delete', 'Permanently delete a page.'], ['view:draft', 'draft', 'view', 'Read unpublished drafts.'], ['edit:draft', 'draft', 'edit', 'Edit an unpublished draft.'], ['publish:draft', 'draft', 'publish', 'Publish a draft to the public site.'], ['manage:user', null, 'admin', 'Create, edit, disable, or delete user accounts.'], ['view:user', 'user', 'view', 'View user accounts.'], ['edit:user', 'user', 'edit', 'Edit user accounts.'], ['delete:user', 'user', 'delete', 'Delete user accounts.'], ['manage:role', null, 'admin', 'Create, edit, or delete roles and their permissions.'], ['view:plugin', 'plugin', 'view', 'View installed plugins.'], ['edit:plugin', 'plugin', 'edit', 'Configure/activate/deactivate plugins.'], ['delete:plugin', 'plugin', 'delete', 'Uninstall/delete plugins.'], ['view:theme', 'theme', 'view', 'View installed themes.'], ['edit:theme', 'theme', 'edit', 'Edit/activate theme files and settings.'], ['delete:theme', 'theme', 'delete', 'Delete themes.'], ['view:blog_post', 'blog_post', 'view', 'View blog posts (including drafts by others, if granted).'], ['edit:blog_post', 'blog_post', 'edit', 'Create/edit blog posts.'], ['delete:blog_post', 'blog_post', 'delete', "Delete blog posts, including the author's own published posts."], ['publish:blog_post', 'blog_post', 'publish', 'Publish a blog post to the public site.'], ['view:media', 'media', 'view', 'View media library items.'], ['edit:media', 'media', 'edit', 'Upload/edit media library items.'], ['delete:media', 'media', 'delete', 'Delete media library items.'], ]; $ids = []; foreach ($definitions as [$name, $type, $action, $description]) { $row = $this->fetchRow("SELECT id FROM permissions WHERE name = '" . $name . "'"); if ($row !== false) { $ids[$name] = (int) $row['id']; continue; } $this->execute( 'INSERT INTO permissions (name, type, action, description, created_at, created_by) VALUES (?, ?, ?, ?, ?, ?)', [$name, $type, $action, $description, date('Y-m-d H:i:s'), $adminUserId], ); $ids[$name] = (int) $this->getAdapter()->getConnection()->lastInsertId(); } return $ids; } /** * @param array $roleIds * @param array $permissionIds * @param string[] $permissionNames */ private function assignPermissions( int $adminUserId, array $roleIds, array $permissionIds, string $roleName, array $permissionNames, ): void { if (!isset($roleIds[$roleName])) { return; } $roleId = $roleIds[$roleName]; foreach ($permissionNames as $permissionName) { if (!isset($permissionIds[$permissionName])) { continue; } $permissionId = $permissionIds[$permissionName]; $exists = $this->fetchRow( "SELECT 1 FROM role_permission WHERE role_id = $roleId AND permission_id = $permissionId", ); if ($exists !== false) { continue; } $this->execute( 'INSERT INTO role_permission (role_id, permission_id, created_at, created_by) VALUES (?, ?, ?, ?)', [$roleId, $permissionId, date('Y-m-d H:i:s'), $adminUserId], ); } } }