diff --git a/config/assets.php b/config/assets.php
index 5d3d9eb..6d6b865 100644
--- a/config/assets.php
+++ b/config/assets.php
@@ -58,9 +58,6 @@ return [
'page-layout' => [
'css/layout/app-page-layout.css',
],
- 'api-docs' => [
- 'css/vendor-overrides/swagger-ui.css',
- ],
'docs' => [
'css/pages/app-docs.css',
],
diff --git a/config/modules.php b/config/modules.php
index 244f298..ca48f2c 100644
--- a/config/modules.php
+++ b/config/modules.php
@@ -12,5 +12,5 @@
* Each entry must match a directory name under modules/ containing a module.php manifest.
*/
return [
- 'enabled_modules' => ['audit', 'addressbook', 'bookmarks', 'notifications'],
+ 'enabled_modules' => ['audit', 'addressbook', 'bookmarks', 'notifications', 'api-docs'],
];
diff --git a/lib/Service/Access/OperationsAuthorizationPolicy.php b/lib/Service/Access/OperationsAuthorizationPolicy.php
index 0a529f7..f69ed54 100644
--- a/lib/Service/Access/OperationsAuthorizationPolicy.php
+++ b/lib/Service/Access/OperationsAuthorizationPolicy.php
@@ -12,7 +12,6 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
public const ABILITY_ADMIN_MAIL_LOG_VIEW = 'ops.admin.mail_log.view';
public const ABILITY_ADMIN_STATS_VIEW = 'ops.admin.stats.view';
public const ABILITY_ADMIN_SYSTEM_INFO_VIEW = 'ops.admin.system_info.view';
- public const ABILITY_ADMIN_API_DOCS_VIEW = 'ops.admin.api_docs.view';
public const ABILITY_ADMIN_IMPORTS_TYPE_USERS = 'ops.admin.imports.type.users';
public const ABILITY_ADMIN_IMPORTS_TYPE_DEPARTMENTS = 'ops.admin.imports.type.departments';
public const ABILITY_ADMIN_USERS_ASSIGNMENTS_MANAGE = 'ops.admin.users.assignments.manage';
@@ -51,7 +50,6 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
self::ABILITY_ADMIN_MAIL_LOG_VIEW,
self::ABILITY_ADMIN_STATS_VIEW,
self::ABILITY_ADMIN_SYSTEM_INFO_VIEW,
- self::ABILITY_ADMIN_API_DOCS_VIEW,
self::ABILITY_ADMIN_IMPORTS_TYPE_USERS,
self::ABILITY_ADMIN_IMPORTS_TYPE_DEPARTMENTS,
self::ABILITY_ADMIN_USERS_ASSIGNMENTS_MANAGE,
@@ -94,7 +92,6 @@ final class OperationsAuthorizationPolicy implements AuthorizationPolicyInterfac
self::ABILITY_ADMIN_MAIL_LOG_VIEW => $this->allowIfHas($actorUserId, PermissionService::MAIL_LOG_VIEW),
self::ABILITY_ADMIN_STATS_VIEW => $this->allowIfHas($actorUserId, PermissionService::STATS_VIEW),
self::ABILITY_ADMIN_SYSTEM_INFO_VIEW => $this->allowIfHas($actorUserId, PermissionService::SYSTEM_INFO_VIEW),
- self::ABILITY_ADMIN_API_DOCS_VIEW => $this->allowIfHas($actorUserId, PermissionService::API_DOCS_VIEW),
self::ABILITY_ADMIN_IMPORTS_TYPE_USERS => $this->allowIfHas($actorUserId, PermissionService::USERS_IMPORT),
self::ABILITY_ADMIN_IMPORTS_TYPE_DEPARTMENTS => $this->allowIfHas($actorUserId, PermissionService::DEPARTMENTS_IMPORT),
self::ABILITY_ADMIN_USERS_ASSIGNMENTS_MANAGE => $this->allowIfHas($actorUserId, PermissionService::USERS_UPDATE_ASSIGNMENTS),
diff --git a/lib/Service/Access/UiCapabilityMap.php b/lib/Service/Access/UiCapabilityMap.php
index e2b33e3..72f9cb3 100644
--- a/lib/Service/Access/UiCapabilityMap.php
+++ b/lib/Service/Access/UiCapabilityMap.php
@@ -21,7 +21,6 @@ final class UiCapabilityMap
'can_view_settings' => SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_VIEW,
'can_view_imports' => OperationsAuthorizationPolicy::ABILITY_ADMIN_IMPORTS_VIEW,
'can_view_jobs' => OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW,
- 'can_view_api_docs' => OperationsAuthorizationPolicy::ABILITY_ADMIN_API_DOCS_VIEW,
'can_view_docs' => OperationsAuthorizationPolicy::ABILITY_ADMIN_DOCS_VIEW,
'can_view_mail_log' => OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW,
'can_view_stats' => OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW,
diff --git a/modules/api-docs/i18n/default_de.json b/modules/api-docs/i18n/default_de.json
new file mode 100644
index 0000000..93f9153
--- /dev/null
+++ b/modules/api-docs/i18n/default_de.json
@@ -0,0 +1,5 @@
+{
+ "API docs": "API-Dokumentation",
+ "API documentation": "API-Dokumentation",
+ "Can view API documentation": "Kann API-Dokumentation anzeigen"
+}
diff --git a/modules/api-docs/i18n/default_en.json b/modules/api-docs/i18n/default_en.json
new file mode 100644
index 0000000..6c19b67
--- /dev/null
+++ b/modules/api-docs/i18n/default_en.json
@@ -0,0 +1,5 @@
+{
+ "API docs": "API docs",
+ "API documentation": "API documentation",
+ "Can view API documentation": "Can view API documentation"
+}
diff --git a/modules/api-docs/lib/Module/ApiDocs/ApiDocsAuthorizationPolicy.php b/modules/api-docs/lib/Module/ApiDocs/ApiDocsAuthorizationPolicy.php
new file mode 100644
index 0000000..99d9347
--- /dev/null
+++ b/modules/api-docs/lib/Module/ApiDocs/ApiDocsAuthorizationPolicy.php
@@ -0,0 +1,37 @@
+permissionService->userHas($actorUserId, self::PERMISSION_KEY)) {
+ return AuthorizationDecision::deny(403, 'forbidden');
+ }
+
+ return AuthorizationDecision::allow();
+ }
+}
diff --git a/modules/api-docs/lib/Module/ApiDocs/ApiDocsContainerRegistrar.php b/modules/api-docs/lib/Module/ApiDocs/ApiDocsContainerRegistrar.php
new file mode 100644
index 0000000..bf16c2d
--- /dev/null
+++ b/modules/api-docs/lib/Module/ApiDocs/ApiDocsContainerRegistrar.php
@@ -0,0 +1,17 @@
+set(ApiDocsAuthorizationPolicy::class, static fn (AppContainer $c): ApiDocsAuthorizationPolicy => new ApiDocsAuthorizationPolicy(
+ $c->get(PermissionService::class)
+ ));
+ }
+}
diff --git a/modules/api-docs/module.php b/modules/api-docs/module.php
new file mode 100644
index 0000000..d655e9c
--- /dev/null
+++ b/modules/api-docs/module.php
@@ -0,0 +1,66 @@
+ 'api-docs',
+ 'version' => '1.0.0',
+ 'enabled_by_default' => false,
+ 'load_order' => 20,
+ 'requires' => [],
+
+ 'routes' => [
+ ['path' => 'admin/api-docs', 'target' => 'api-docs'],
+ ['path' => 'admin/api-docs/spec', 'target' => 'api-docs/spec'],
+ ],
+ 'public_paths' => [],
+ 'container_registrars' => [
+ \MintyPHP\Module\ApiDocs\ApiDocsContainerRegistrar::class,
+ ],
+
+ 'ui_slots' => [
+ 'sidebar.admin_nav_item' => [
+ [
+ 'key' => 'api-docs',
+ 'group' => 'admin-automation',
+ 'label' => 'API docs',
+ 'path' => 'admin/api-docs',
+ 'permission' => 'api_docs.view',
+ 'order' => 300,
+ ],
+ ],
+ ],
+
+ 'authorization_policies' => [
+ \MintyPHP\Module\ApiDocs\ApiDocsAuthorizationPolicy::class,
+ ],
+
+ 'search_resources' => [],
+
+ 'asset_groups' => [
+ 'api-docs' => [
+ 'modules/api-docs/css/vendor-overrides/swagger-ui.css',
+ ],
+ ],
+
+ 'scheduler_jobs' => [],
+ 'layout_context_providers' => [],
+ 'session_providers' => [],
+
+ 'permissions' => [
+ [
+ 'key' => 'api_docs.view',
+ 'description' => 'Can view API documentation',
+ 'active' => true,
+ 'is_system' => true,
+ ],
+ ],
+
+ 'event_listeners' => [],
+ 'deactivation_handler' => null,
+ 'migrations_path' => null,
+ 'i18n_path' => 'i18n',
+];
diff --git a/pages/admin/api-docs/index().php b/modules/api-docs/pages/api-docs/index().php
similarity index 56%
rename from pages/admin/api-docs/index().php
rename to modules/api-docs/pages/api-docs/index().php
index da36ce5..578d086 100644
--- a/pages/admin/api-docs/index().php
+++ b/modules/api-docs/pages/api-docs/index().php
@@ -1,10 +1,11 @@
-
+
diff --git a/pages/admin/api-docs/spec().php b/modules/api-docs/pages/api-docs/spec().php
similarity index 66%
rename from pages/admin/api-docs/spec().php
rename to modules/api-docs/pages/api-docs/spec().php
index babcc5e..51f9335 100644
--- a/pages/admin/api-docs/spec().php
+++ b/modules/api-docs/pages/api-docs/spec().php
@@ -1,14 +1,14 @@
$canViewJobs,
'withTenant' => false,
],
- [
- 'label' => t('API docs'),
- 'path' => 'admin/api-docs',
- 'active' => navActive('admin/api-docs', true),
- 'visible' => $canViewApiDocs,
- 'withTenant' => false,
- ],
],
],
[
@@ -133,7 +125,7 @@ $adminNavGroups = [
'visible' => $hasLogsSection,
'items' => [
[
- 'label' => t('Mail logs'),
+ 'label' => t('nav.logs.mail'),
'path' => 'admin/mail-log',
'active' => navActive('admin/mail-log', true),
'visible' => $canViewMailLog,
@@ -199,6 +191,7 @@ if ($moduleNavItemSlots) {
}
// Known group metadata — modules declare their group key, we map to label + icon.
$moduleGroupMeta = [
+ 'admin-automation' => ['label' => t('Automation & integrations'), 'icon' => 'bi-gear-wide-connected'],
'admin-logs' => ['label' => t('Audit & logs'), 'icon' => 'bi-journal-text'],
];
foreach ($moduleGroupsByKey as $groupKey => $items) {
@@ -209,7 +202,7 @@ if ($moduleNavItemSlots) {
// Also add mail log to the logs group if it exists as a core item
if ($groupKey === 'admin-logs' && $canViewMailLog) {
array_unshift($items, [
- 'label' => t('Mail logs'),
+ 'label' => t('nav.logs.mail'),
'path' => 'admin/mail-log',
'active' => navActive('admin/mail-log', true),
'visible' => $canViewMailLog,