listen ansichten verbessert

This commit is contained in:
2026-03-05 11:17:42 +01:00
parent 4b31fc7664
commit c5f657c8c8
133 changed files with 2806 additions and 636 deletions

19
.php-cs-fixer.dist.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/config',
__DIR__ . '/lib',
__DIR__ . '/pages',
__DIR__ . '/tests',
])
->name('*.php');
return (new PhpCsFixer\Config())
->setRiskyAllowed(false)
->setRules([
'@PSR12' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
])
->setFinder($finder);

View File

@@ -68,7 +68,11 @@ docker/ # Dockerfiles, Nginx configs, PHP configs
1. **App** (`lib/App/`): DI container bootstrap. Registers all service/repository factories. No business logic. 1. **App** (`lib/App/`): DI container bootstrap. Registers all service/repository factories. No business logic.
2. **Repository** (`lib/Repository/`): All SQL lives here. No HTTP, session, or rendering. 2. **Repository** (`lib/Repository/`): All SQL lives here. No HTTP, session, or rendering.
3. **Service** (`lib/Service/`): Business rules and validation. No HTML. Uses factory pattern (`*ServicesFactory`). 3. **Service** (`lib/Service/`): Business rules and validation. No HTML. Four internal sub-types:
- **Service** (`*Service.php`): Business logic + orchestration. Coordinates repositories and gateways.
- **Gateway** (`*Gateway.php`): Adapter for a single external dependency (repository, settings, HTTP API, scope). No orchestration flow.
- **Factory** (`*Factory.php`): Only place inside `lib/Service/` that may call `new` on services/gateways/repositories. Registers via DI container.
- **Policy** (`*Policy.php` in `lib/Service/Access/`): Authorization decisions per resource type. Returns `AuthorizationDecision`. No HTTP, no business logic.
4. **Action** (`pages/**/*.php`): Reads input, checks permissions + tenant scope, calls services, sets view vars. 4. **Action** (`pages/**/*.php`): Reads input, checks permissions + tenant scope, calls services, sets view vars.
5. **View** (`pages/**/*.phtml`): Pure rendering. **No DB calls.** HTML escaping via `e(...)`. 5. **View** (`pages/**/*.phtml`): Pure rendering. **No DB calls.** HTML escaping via `e(...)`.
6. **Template** (`templates/`): Layouts and partials. 6. **Template** (`templates/`): Layouts and partials.

View File

@@ -193,6 +193,20 @@ docker compose exec php vendor/bin/phpunit --bootstrap vendor/autoload.php tests
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
``` ```
### Coding Style (PHP CS Fixer)
CI-Check (ohne Dateien zu aendern):
```bash
docker compose exec php composer cs:check
```
Lokal automatisch formatieren:
```bash
docker compose exec php composer cs:fix
```
### Frontend-JS Smoke-Check (ohne Node) ### Frontend-JS Smoke-Check (ohne Node)
- Browser-DevTools öffnen (Console + Preserve log) - Browser-DevTools öffnen (Console + Preserve log)

View File

@@ -22,7 +22,8 @@
"mintyphp/debugger": "*", "mintyphp/debugger": "*",
"phpunit/phpunit": "*", "phpunit/phpunit": "*",
"phpstan/phpstan": "^1.9", "phpstan/phpstan": "^1.9",
"icanhazstring/composer-unused": "^0.9.6" "icanhazstring/composer-unused": "^0.9.6",
"friendsofphp/php-cs-fixer": "^3.66"
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
@@ -33,5 +34,9 @@
"psr-4": { "psr-4": {
"MintyPHP\\": "lib/" "MintyPHP\\": "lib/"
} }
},
"scripts": {
"cs:check": "php-cs-fixer fix --config=.php-cs-fixer.dist.php --dry-run --diff --verbose",
"cs:fix": "php-cs-fixer fix --config=.php-cs-fixer.dist.php --verbose"
} }
} }

1404
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
<?php <?php
return [ return [
['path' => 'login', 'target' => 'auth/login', 'public' => true], ['path' => 'login', 'target' => 'auth/login', 'public' => true],
['path' => 'register', 'target' => 'auth/register', 'public' => true], ['path' => 'register', 'target' => 'auth/register', 'public' => true],

View File

@@ -1,4 +1,5 @@
<?php <?php
return [ return [
'app_title' => 'CoreCore', 'app_title' => 'CoreCore',
'app_locale' => 'de', 'app_locale' => 'de',

View File

@@ -1,4 +1,5 @@
<?php <?php
return [ return [
'light' => 'Light', 'light' => 'Light',
'dark' => 'Dark', 'dark' => 'Dark',

View File

@@ -13,6 +13,54 @@ Verbindliche Kurzregeln für Architekturentscheidungen.
- Autorisierung läuft zentral über Policies. - Autorisierung läuft zentral über Policies.
- Externe API-Verträge sind UUID-first. - Externe API-Verträge sind UUID-first.
## Schichtenübersicht
```mermaid
flowchart TD
Browser([Browser / API-Client])
subgraph APP["lib/App/ — Composition Root"]
DI["AppContainer + Registrars"]
end
subgraph SVC["lib/Service/"]
direction LR
FAC["Factory"]
SERVICE["Service"]
GW["Gateway"]
POL["Policy"]
end
subgraph REPO["lib/Repository/"]
REP["Repository"]
end
subgraph pages["pages/**"]
ACT["Action (*.php)"]
VIEW["View (*.phtml)"]
end
subgraph TPL["templates/"]
LAY["Layouts & Partials"]
end
DB[(MariaDB)]
DI -->|"ruft zur Laufzeit"| FAC
FAC -->|"erzeugt"| SERVICE
FAC -->|"erzeugt"| GW
Browser --> ACT
ACT -->|"Input · AuthZ · Response"| SERVICE
ACT --> POL
ACT --> VIEW
VIEW --> LAY
SERVICE --> GW
GW --> REP
SERVICE --> REP
REP --> DB
```
## Request-Flow ## Request-Flow
1. `web/index.php` bootstrappt Routing/Konfiguration und initialisiert den `AppContainer`. 1. `web/index.php` bootstrappt Routing/Konfiguration und initialisiert den `AppContainer`.

View File

@@ -95,6 +95,45 @@ Hinweise:
- `order` nur bekannte Sort-Key-Spalten - `order` nur bekannte Sort-Key-Spalten
- Bei Search-/Filter-Änderung wird auf Seite 1 zurückgesetzt. - Bei Search-/Filter-Änderung wird auf Seite 1 zurückgesetzt.
### Row Interaction (Globaler Standard)
Listen mit `rowDblClick.getUrl(...)` nutzen standardmäßig eine zugängliche Row-Interaktion:
- sichtbare Action-Spalte rechts (`Open`/`Edit`)
- Doppelklick bleibt als Fallback aktiv
- `Enter` auf fokussierter Zeile navigiert zur selben URL
Optionale Konfiguration über `createServerGrid(...)`:
```js
createServerGrid({
// ...
rowDblClick: {
getUrl: (rowData) => new URL(`admin/users/edit/${rowData?.cells?.[1]?.data}`, appBase).toString(),
},
rowInteraction: {
enabled: true,
showActionButton: true,
keepDblClick: true,
enableEnterOnRow: true,
actionColumnLabel: 'Actions',
resolveActionLabel: (url) => (url.includes('/edit/') ? 'Edit' : 'Open'),
}
});
```
Defaults:
- `rowInteraction.enabled`: `true`, wenn `rowDblClick.getUrl` vorhanden ist
- `showActionButton`: `true`
- `keepDblClick`: `true`
- `enableEnterOnRow`: `true`
- `actionColumnLabel`: `language.actions.column` oder `'Actions'`
Hinweis:
- Falls eine Seite `actions.enabled: true` explizit setzt, wird keine zusätzliche automatische Row-Action-Spalte injiziert.
### Filter UX 2.0 (Globaler Standard) ### Filter UX 2.0 (Globaler Standard)
Für Listen gilt standardmäßig: Für Listen gilt standardmäßig:
@@ -275,5 +314,5 @@ Prüfen im Browser:
1. DevTools öffnen (Console, optional Preserve log aktivieren). 1. DevTools öffnen (Console, optional Preserve log aktivieren).
2. Kernseiten laden (`/admin/users`, `/address-book`, Audit-Listen). 2. Kernseiten laden (`/admin/users`, `/address-book`, Audit-Listen).
3. Filter setzen/zurücksetzen, Doppelklick-Navigation und Bulk-Aktionen testen. 3. Filter setzen/zurücksetzen, Row-Action-Button + Enter + Doppelklick sowie Bulk-Aktionen testen.
4. Erwartung: keine JavaScript-Errors und keine Unhandled Promise Rejections. 4. Erwartung: keine JavaScript-Errors und keine Unhandled Promise Rejections.

View File

@@ -1,6 +1,6 @@
# lib-Standards # lib-Standards
Letzte Aktualisierung: 2026-03-03 Letzte Aktualisierung: 2026-03-05
Praktische Regeln nur für `lib/**`. Ergänzt `docs/architektur.md`. Praktische Regeln nur für `lib/**`. Ergänzt `docs/architektur.md`.
@@ -9,12 +9,17 @@ Praktische Regeln nur für `lib/**`. Ergänzt `docs/architektur.md`.
- Verantwortlich für SQL, Filter, Paging und Mapping. - Verantwortlich für SQL, Filter, Paging und Mapping.
- Keine HTTP-, Session-, Redirect- oder Policy-Logik. - Keine HTTP-, Session-, Redirect- oder Policy-Logik.
## 2) Service-Regeln ## 2) Service-Schicht im Detail
`lib/Service/` enthält vier klar getrennte Typen — jeder hat eine eigene Verantwortung:
### 2a) Service (`*Service.php`)
- Verantwortlich für Fachlogik und Orchestrierung. - Verantwortlich für Fachlogik und Orchestrierung.
- Koordiniert Repositories und Gateways; enthält Business-Regeln und Validierung.
- Keine Superglobals (`$_POST`, `$_GET`, `$_SESSION`). - Keine Superglobals (`$_POST`, `$_GET`, `$_SESSION`).
- Keine Header- oder Redirect-Ausgabe. - Keine Header- oder Redirect-Ausgabe.
- Keine direkten `DB::...`-Aufrufe im Service. - Keine direkten `DB::...`-Aufrufe.
- Abhängigkeiten per Constructor Injection. - Abhängigkeiten per Constructor Injection.
- DB-Locks und Transaktionen über Repository-Schicht (z. B. `DatabaseSessionRepository`). - DB-Locks und Transaktionen über Repository-Schicht (z. B. `DatabaseSessionRepository`).
- Audit-Hooks für fachliche Schreibaktionen laufen in Services (`SystemAuditService`), nicht in Templates. - Audit-Hooks für fachliche Schreibaktionen laufen in Services (`SystemAuditService`), nicht in Templates.
@@ -22,6 +27,28 @@ Praktische Regeln nur für `lib/**`. Ergänzt `docs/architektur.md`.
- Scheduler-Run-/Job-Ereignisse werden zentral im `SchedulerRunService` in `SystemAuditService` geschrieben. - Scheduler-Run-/Job-Ereignisse werden zentral im `SchedulerRunService` in `SystemAuditService` geschrieben.
- CLI-System-Audit für zentrale Commands wird am Entrypoint (`bin/**`) geschrieben, mit minimaler Metadata (ohne Argumentwerte). - CLI-System-Audit für zentrale Commands wird am Entrypoint (`bin/**`) geschrieben, mit minimaler Metadata (ohne Argumentwerte).
### 2b) Gateway (`*Gateway.php`)
- Adapter-Schicht: kapselt eine einzelne externe Abhängigkeit für Services.
- Typische Abhängigkeiten: ein Repository, Settings-Zugriff, externe HTTP-APIs, Scope-Daten.
- Kein Fach-Orchestrierungsfluss — ein Gateway löst genau eine Infrastruktur-Frage.
- Beispiele: `AuthPermissionGateway` (Permission-Lookup), `UserSettingsGateway` (Settings-Zugriff), `OidcHttpGateway` (externe OIDC-API).
- Abhängigkeiten per Constructor Injection; keine direkten `new ...Repository()` außerhalb von Factories.
### 2c) Factory (`*Factory.php` / `*ServicesFactory.php` / `*GatewayFactory.php`)
- Einzige erlaubte Stelle für `new ...Service()`, `new ...Gateway()`, `new ...Repository()` innerhalb von `lib/Service/`.
- Erzeugt konfigurierte Service-/Gateway-Gruppen für den DI-Container.
- Keine Fachlogik; reine Kompositions-Helfer.
- Werden im Composition Root (`lib/App/registerContainer.php`) registriert.
### 2d) Policy (`*Policy.php` / `*AuthorizationPolicy.php`)
- Liegen in `lib/Service/Access/`.
- Treffen Authorization-Entscheidungen (Ability-Checks) für einen bestimmten Ressourcentyp.
- Geben `AuthorizationDecision` zurück; kein HTTP-Redirect, keine Fachlogik.
- Werden über `AccessPolicyFactory` + `AuthorizationService` aufgelöst — nie direkt in Actions instanziiert.
## 3) Instanziierung ## 3) Instanziierung
- Composition Root ist `web/index.php` + `lib/App/registerContainer.php` - Composition Root ist `web/index.php` + `lib/App/registerContainer.php`

View File

@@ -198,6 +198,7 @@
"Modified by": "Bearbeitet von", "Modified by": "Bearbeitet von",
"Modified": "Aktualisiert", "Modified": "Aktualisiert",
"No users found": "Keine Benutzer gefunden", "No users found": "Keine Benutzer gefunden",
"Open": "Öffnen",
"Yes": "Ja", "Yes": "Ja",
"No": "Nein", "No": "Nein",
"User created": "Benutzer erstellt", "User created": "Benutzer erstellt",
@@ -345,6 +346,7 @@
"Next": "Weiter", "Next": "Weiter",
"Page %d of %d": "Seite %d von %d", "Page %d of %d": "Seite %d von %d",
"Page %d": "Seite %d", "Page %d": "Seite %d",
"Rows per page": "Einträge pro Seite",
"Showing": "Zeige", "Showing": "Zeige",
"of": "von", "of": "von",
"to": "bis", "to": "bis",

View File

@@ -198,6 +198,7 @@
"Modified by": "Modified by", "Modified by": "Modified by",
"Modified": "Modified", "Modified": "Modified",
"No users found": "No users found", "No users found": "No users found",
"Open": "Open",
"Yes": "Yes", "Yes": "Yes",
"No": "No", "No": "No",
"User created": "User created", "User created": "User created",
@@ -345,6 +346,7 @@
"Next": "Next", "Next": "Next",
"Page %d of %d": "Page %d of %d", "Page %d of %d": "Page %d of %d",
"Page %d": "Page %d", "Page %d": "Page %d",
"Rows per page": "Rows per page",
"Showing": "Showing", "Showing": "Showing",
"of": "of", "of": "of",
"to": "to", "to": "to",

View File

@@ -11,7 +11,6 @@ use MintyPHP\Service\Access\AccessPolicyFactory;
use MintyPHP\Service\Access\AccessRepositoryFactory; use MintyPHP\Service\Access\AccessRepositoryFactory;
use MintyPHP\Service\Access\AccessServicesFactory; use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway; use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory; use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Service\Audit\AuditRepositoryFactory; use MintyPHP\Service\Audit\AuditRepositoryFactory;
use MintyPHP\Service\Audit\AuditServicesFactory; use MintyPHP\Service\Audit\AuditServicesFactory;
@@ -22,6 +21,7 @@ use MintyPHP\Service\Branding\BrandingServicesFactory;
use MintyPHP\Service\CustomField\CustomFieldServicesFactory; use MintyPHP\Service\CustomField\CustomFieldServicesFactory;
use MintyPHP\Service\Directory\DirectoryGatewayFactory; use MintyPHP\Service\Directory\DirectoryGatewayFactory;
use MintyPHP\Service\Directory\DirectoryRepositoryFactory; use MintyPHP\Service\Directory\DirectoryRepositoryFactory;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Directory\DirectoryServicesFactory; use MintyPHP\Service\Directory\DirectoryServicesFactory;
use MintyPHP\Service\Import\ImportRepositoryFactory; use MintyPHP\Service\Import\ImportRepositoryFactory;
use MintyPHP\Service\Import\ImportServicesFactory; use MintyPHP\Service\Import\ImportServicesFactory;
@@ -75,7 +75,6 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$c->get(UserServicesFactory::class), $c->get(UserServicesFactory::class),
$c->get(AccessServicesFactory::class), $c->get(AccessServicesFactory::class),
$c->get(SettingServicesFactory::class), $c->get(SettingServicesFactory::class),
$c->get(UserRepositoryFactory::class),
$c->get(DirectoryServicesFactory::class), $c->get(DirectoryServicesFactory::class),
$c->get(ImportRepositoryFactory::class) $c->get(ImportRepositoryFactory::class)
)); ));
@@ -114,10 +113,10 @@ final class ServiceFactoryRegistrar implements ContainerRegistrar
$c->get(DirectoryScopeGateway::class) $c->get(DirectoryScopeGateway::class)
)); ));
$container->set(UserGatewayFactory::class, static fn (AppContainer $c): UserGatewayFactory => new UserGatewayFactory( $container->set(UserGatewayFactory::class, static fn (AppContainer $c): UserGatewayFactory => new UserGatewayFactory(
$c->get(UserRepositoryFactory::class),
$c->get(AccessServicesFactory::class), $c->get(AccessServicesFactory::class),
$c->get(SettingServicesFactory::class), $c->get(SettingServicesFactory::class),
$c->get(TenantScopeService::class) $c->get(TenantScopeService::class),
$c->get(DirectoryRepositoryFactory::class),
)); ));
$container->set(UserServicesFactory::class, static fn (AppContainer $c): UserServicesFactory => new UserServicesFactory( $container->set(UserServicesFactory::class, static fn (AppContainer $c): UserServicesFactory => new UserServicesFactory(
$c->get(AuditServicesFactory::class), $c->get(AuditServicesFactory::class),

View File

@@ -7,6 +7,7 @@ use MintyPHP\App\Container\ContainerRegistrar;
use MintyPHP\Repository\Auth\ApiTokenRepository; use MintyPHP\Repository\Auth\ApiTokenRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository; use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Service\Access\RoleService; use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Branding\BrandingFaviconService; use MintyPHP\Service\Branding\BrandingFaviconService;
use MintyPHP\Service\Branding\BrandingLogoService; use MintyPHP\Service\Branding\BrandingLogoService;
use MintyPHP\Service\Branding\BrandingServicesFactory; use MintyPHP\Service\Branding\BrandingServicesFactory;
@@ -18,7 +19,6 @@ use MintyPHP\Service\Settings\SettingService;
use MintyPHP\Service\Settings\SettingServicesFactory; use MintyPHP\Service\Settings\SettingServicesFactory;
use MintyPHP\Service\Settings\ThemeConfigService; use MintyPHP\Service\Settings\ThemeConfigService;
use MintyPHP\Service\Tenant\TenantService; use MintyPHP\Service\Tenant\TenantService;
use MintyPHP\Service\Audit\SystemAuditService;
final class SettingsRegistrar implements ContainerRegistrar final class SettingsRegistrar implements ContainerRegistrar
{ {

View File

@@ -6,14 +6,15 @@ use MintyPHP\App\AppContainer;
use MintyPHP\App\Container\ContainerRegistrar; use MintyPHP\App\Container\ContainerRegistrar;
use MintyPHP\Repository\Access\UserRoleRepository; use MintyPHP\Repository\Access\UserRoleRepository;
use MintyPHP\Repository\Org\UserDepartmentRepository; use MintyPHP\Repository\Org\UserDepartmentRepository;
use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Repository\User\UserReadRepository; use MintyPHP\Repository\User\UserReadRepository;
use MintyPHP\Repository\User\UserWriteRepository; use MintyPHP\Repository\User\UserWriteRepository;
use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Service\Auth\TenantSsoService; use MintyPHP\Service\Auth\TenantSsoService;
use MintyPHP\Service\Branding\BrandingLogoService; use MintyPHP\Service\Branding\BrandingLogoService;
use MintyPHP\Service\User\UserAccessPdfService; use MintyPHP\Service\User\UserAccessPdfService;
use MintyPHP\Service\User\UserAccessTemplateService; use MintyPHP\Service\User\UserAccessTemplateService;
use MintyPHP\Service\User\UserAccountService; use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserApiWriteInputMapper;
use MintyPHP\Service\User\UserAssignmentService; use MintyPHP\Service\User\UserAssignmentService;
use MintyPHP\Service\User\UserAvatarService; use MintyPHP\Service\User\UserAvatarService;
use MintyPHP\Service\User\UserDirectoryGateway; use MintyPHP\Service\User\UserDirectoryGateway;
@@ -21,7 +22,6 @@ use MintyPHP\Service\User\UserLifecycleRestoreService;
use MintyPHP\Service\User\UserLifecycleService; use MintyPHP\Service\User\UserLifecycleService;
use MintyPHP\Service\User\UserPasswordPolicyService; use MintyPHP\Service\User\UserPasswordPolicyService;
use MintyPHP\Service\User\UserPasswordService; use MintyPHP\Service\User\UserPasswordService;
use MintyPHP\Service\User\UserApiWriteInputMapper;
use MintyPHP\Service\User\UserRepositoryFactory; use MintyPHP\Service\User\UserRepositoryFactory;
use MintyPHP\Service\User\UserSavedFilterService; use MintyPHP\Service\User\UserSavedFilterService;
use MintyPHP\Service\User\UserScopeGateway; use MintyPHP\Service\User\UserScopeGateway;

View File

@@ -2,7 +2,6 @@
namespace MintyPHP\Http; namespace MintyPHP\Http;
use MintyPHP\Http\RequestContext;
use MintyPHP\Service\Audit\ApiAuditService; use MintyPHP\Service\Audit\ApiAuditService;
use MintyPHP\Service\Security\RateLimiterService; use MintyPHP\Service\Security\RateLimiterService;
use MintyPHP\Service\Settings\SettingGateway; use MintyPHP\Service\Settings\SettingGateway;

View File

@@ -3,7 +3,6 @@
namespace MintyPHP\Http; namespace MintyPHP\Http;
use MintyPHP\Http\Input\FormErrors; use MintyPHP\Http\Input\FormErrors;
use MintyPHP\Http\RequestContext;
use MintyPHP\Service\Access\AuthorizationService; use MintyPHP\Service\Access\AuthorizationService;
use MintyPHP\Service\Audit\ApiAuditService; use MintyPHP\Service\Audit\ApiAuditService;
@@ -20,8 +19,7 @@ class ApiResponse
callable $apiAuditServiceResolver, callable $apiAuditServiceResolver,
callable $authorizationServiceResolver, callable $authorizationServiceResolver,
callable $apiSystemAuditReporterResolver callable $apiSystemAuditReporterResolver
): void ): void {
{
self::$apiAuditServiceResolver = $apiAuditServiceResolver; self::$apiAuditServiceResolver = $apiAuditServiceResolver;
self::$authorizationServiceResolver = $authorizationServiceResolver; self::$authorizationServiceResolver = $authorizationServiceResolver;
self::$apiSystemAuditReporterResolver = $apiSystemAuditReporterResolver; self::$apiSystemAuditReporterResolver = $apiSystemAuditReporterResolver;

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Http; namespace MintyPHP\Http;
use MintyPHP\Router;
use MintyPHP\I18n; use MintyPHP\I18n;
use MintyPHP\Router;
class Request class Request
{ {

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Repository\Audit; namespace MintyPHP\Repository\Audit;
use MintyPHP\Domain\Taxonomy\ImportAuditStatus;
use MintyPHP\DB; use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\ImportAuditStatus;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class ImportAuditRunRepository implements ImportAuditRunRepositoryInterface class ImportAuditRunRepository implements ImportAuditRunRepositoryInterface

View File

@@ -2,9 +2,9 @@
namespace MintyPHP\Repository\Audit; namespace MintyPHP\Repository\Audit;
use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\SystemAuditChannel; use MintyPHP\Domain\Taxonomy\SystemAuditChannel;
use MintyPHP\Domain\Taxonomy\SystemAuditOutcome; use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface class SystemAuditLogRepository implements SystemAuditLogRepositoryInterface

View File

@@ -2,10 +2,10 @@
namespace MintyPHP\Repository\Audit; namespace MintyPHP\Repository\Audit;
use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\UserLifecycleAction; use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
use MintyPHP\Domain\Taxonomy\UserLifecycleStatus; use MintyPHP\Domain\Taxonomy\UserLifecycleStatus;
use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType; use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType;
use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterface class UserLifecycleAuditRepository implements UserLifecycleAuditRepositoryInterface

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Repository\Mail; namespace MintyPHP\Repository\Mail;
use MintyPHP\Domain\Taxonomy\MailLogStatus;
use MintyPHP\DB; use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\MailLogStatus;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class MailLogRepository implements MailLogRepositoryInterface class MailLogRepository implements MailLogRepositoryInterface

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Repository\Scheduler; namespace MintyPHP\Repository\Scheduler;
use MintyPHP\Domain\Taxonomy\ScheduledJobStatus;
use MintyPHP\DB; use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\ScheduledJobStatus;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class ScheduledJobRepository implements ScheduledJobRepositoryInterface class ScheduledJobRepository implements ScheduledJobRepositoryInterface

View File

@@ -2,9 +2,9 @@
namespace MintyPHP\Repository\Scheduler; namespace MintyPHP\Repository\Scheduler;
use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus; use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus;
use MintyPHP\Domain\Taxonomy\ScheduledJobTriggerType; use MintyPHP\Domain\Taxonomy\ScheduledJobTriggerType;
use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class ScheduledJobRunRepository implements ScheduledJobRunRepositoryInterface class ScheduledJobRunRepository implements ScheduledJobRunRepositoryInterface

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Repository\Scheduler; namespace MintyPHP\Repository\Scheduler;
use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult;
use MintyPHP\DB; use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult;
class SchedulerRuntimeRepository implements SchedulerRuntimeRepositoryInterface class SchedulerRuntimeRepository implements SchedulerRuntimeRepositoryInterface
{ {

View File

@@ -2,6 +2,7 @@
namespace MintyPHP\Repository\Stats; namespace MintyPHP\Repository\Stats;
use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\ImportAuditStatus; use MintyPHP\Domain\Taxonomy\ImportAuditStatus;
use MintyPHP\Domain\Taxonomy\MailLogStatus; use MintyPHP\Domain\Taxonomy\MailLogStatus;
use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus; use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus;
@@ -10,7 +11,6 @@ use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
use MintyPHP\Domain\Taxonomy\TenantStatus; use MintyPHP\Domain\Taxonomy\TenantStatus;
use MintyPHP\Domain\Taxonomy\UserLifecycleAction; use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
use MintyPHP\Domain\Taxonomy\UserLifecycleStatus; use MintyPHP\Domain\Taxonomy\UserLifecycleStatus;
use MintyPHP\DB;
class AdminStatsRepository implements AdminStatsRepositoryInterface class AdminStatsRepository implements AdminStatsRepositoryInterface
{ {
@@ -43,7 +43,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$inactiveDepartmentCount = (int) (DB::selectValue('select count(*) from departments where active = 0') ?? 0); $inactiveDepartmentCount = (int) (DB::selectValue('select count(*) from departments where active = 0') ?? 0);
$activeRoleCount = (int) (DB::selectValue('select count(*) from roles where active = 1') ?? 0); $activeRoleCount = (int) (DB::selectValue('select count(*) from roles where active = 1') ?? 0);
$inactiveRoleCount = (int) (DB::selectValue('select count(*) from roles where active = 0') ?? 0); $inactiveRoleCount = (int) (DB::selectValue('select count(*) from roles where active = 0') ?? 0);
$rolesWithoutPermissionsCount = (int) (DB::selectValue( $rolesWithoutPermissionsCount = (int) (DB::selectValue(
'select count(*) from roles r left join role_permissions rp on rp.role_id = r.id where r.active = 1 and rp.role_id is null' 'select count(*) from roles r left join role_permissions rp on rp.role_id = r.id where r.active = 1 and rp.role_id is null'
) ?? 0); ) ?? 0);
@@ -56,7 +56,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$usersWithoutTenantsCount = (int) (DB::selectValue( $usersWithoutTenantsCount = (int) (DB::selectValue(
'select count(*) from users u left join user_tenants ut on ut.user_id = u.id where u.active = 1 and ut.user_id is null' 'select count(*) from users u left join user_tenants ut on ut.user_id = u.id where u.active = 1 and ut.user_id is null'
) ?? 0); ) ?? 0);
$flattenStatsRow = static function ($row): array { $flattenStatsRow = static function ($row): array {
if (!is_array($row)) { if (!is_array($row)) {
return []; return [];
@@ -74,7 +74,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
} }
return $flat; return $flat;
}; };
$ssoStatsAvailable = (int) (DB::selectValue( $ssoStatsAvailable = (int) (DB::selectValue(
"select count(*) from information_schema.tables where table_schema = database() and table_name = 'tenant_auth_microsoft'" "select count(*) from information_schema.tables where table_schema = database() and table_name = 'tenant_auth_microsoft'"
) ?? 0) === 1; ) ?? 0) === 1;
@@ -86,7 +86,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$ssoTenantRows = []; $ssoTenantRows = [];
$usersMicrosoftOnlyRows = []; $usersMicrosoftOnlyRows = [];
$usersMicrosoftSyncGapRows = []; $usersMicrosoftSyncGapRows = [];
if ($ssoStatsAvailable) { if ($ssoStatsAvailable) {
$ssoEnabledTenantCount = (int) (DB::selectValue( $ssoEnabledTenantCount = (int) (DB::selectValue(
"select count(*) from tenants t " . "select count(*) from tenants t " .
@@ -103,7 +103,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
if ($activeTenantCount > 0) { if ($activeTenantCount > 0) {
$ssoEnabledTenantPercent = round(((float) $ssoEnabledTenantCount * 100) / (float) $activeTenantCount, 1); $ssoEnabledTenantPercent = round(((float) $ssoEnabledTenantCount * 100) / (float) $activeTenantCount, 1);
} }
$usersMicrosoftOnlyCount = (int) (DB::selectValue( $usersMicrosoftOnlyCount = (int) (DB::selectValue(
"select count(*) from users u " . "select count(*) from users u " .
"where u.active = 1 " . "where u.active = 1 " .
@@ -122,7 +122,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$tenantActiveStatus, $tenantActiveStatus,
$tenantActiveStatus $tenantActiveStatus
) ?? 0); ) ?? 0);
$usersMicrosoftSyncGapsCount = (int) (DB::selectValue( $usersMicrosoftSyncGapsCount = (int) (DB::selectValue(
"select count(*) from ( " . "select count(*) from ( " .
" select u.id, " . " select u.id, " .
@@ -147,7 +147,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
" or (x.need_mobile = 1 and x.mobile_value = '')", " or (x.need_mobile = 1 and x.mobile_value = '')",
$tenantActiveStatus $tenantActiveStatus
) ?? 0); ) ?? 0);
$ssoTenantRowsRaw = DB::select( $ssoTenantRowsRaw = DB::select(
"select " . "select " .
" t.uuid as tenant_uuid, " . " t.uuid as tenant_uuid, " .
@@ -182,7 +182,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
}, },
$ssoTenantRowsRaw $ssoTenantRowsRaw
))) : []; ))) : [];
$usersMicrosoftOnlyRowsRaw = DB::select( $usersMicrosoftOnlyRowsRaw = DB::select(
"select " . "select " .
" u.uuid as user_uuid, " . " u.uuid as user_uuid, " .
@@ -217,7 +217,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
}, },
$usersMicrosoftOnlyRowsRaw $usersMicrosoftOnlyRowsRaw
))) : []; ))) : [];
$usersMicrosoftSyncGapRowsRaw = DB::select( $usersMicrosoftSyncGapRowsRaw = DB::select(
"select " . "select " .
" x.user_uuid, x.user_display_name, x.user_email, " . " x.user_uuid, x.user_display_name, x.user_email, " .
@@ -278,7 +278,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$usersMicrosoftSyncGapRowsRaw $usersMicrosoftSyncGapRowsRaw
))) : []; ))) : [];
} }
$customFieldStatsAvailable = (int) (DB::selectValue( $customFieldStatsAvailable = (int) (DB::selectValue(
"select count(*) from information_schema.tables " . "select count(*) from information_schema.tables " .
"where table_schema = database() " . "where table_schema = database() " .
@@ -293,7 +293,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$customFieldsInactiveWithValuesHref = 'admin/tenants'; $customFieldsInactiveWithValuesHref = 'admin/tenants';
$customFieldsUnusedActiveHref = 'admin/tenants'; $customFieldsUnusedActiveHref = 'admin/tenants';
$customFieldIssueTenantRows = []; $customFieldIssueTenantRows = [];
if ($customFieldStatsAvailable) { if ($customFieldStatsAvailable) {
$loadCustomFieldIssueTenants = static function (string $query): array { $loadCustomFieldIssueTenants = static function (string $query): array {
$rows = DB::select($query); $rows = DB::select($query);
@@ -318,7 +318,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
} }
return $items; return $items;
}; };
$customFieldsSelectionWithoutOptionsCount = (int) (DB::selectValue( $customFieldsSelectionWithoutOptionsCount = (int) (DB::selectValue(
"select count(*) from ( " . "select count(*) from ( " .
"select d.id " . "select d.id " .
@@ -329,7 +329,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
"having count(o.id) = 0 " . "having count(o.id) = 0 " .
') missing_options' ') missing_options'
) ?? 0); ) ?? 0);
$customFieldTenantsWithSelectionWithoutOptionsCount = (int) (DB::selectValue( $customFieldTenantsWithSelectionWithoutOptionsCount = (int) (DB::selectValue(
"select count(distinct missing_options.tenant_id) from ( " . "select count(distinct missing_options.tenant_id) from ( " .
"select d.tenant_id " . "select d.tenant_id " .
@@ -340,13 +340,13 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
"having count(o.id) = 0 " . "having count(o.id) = 0 " .
') missing_options' ') missing_options'
) ?? 0); ) ?? 0);
$customFieldsInactiveWithValuesCount = (int) (DB::selectValue( $customFieldsInactiveWithValuesCount = (int) (DB::selectValue(
'select count(distinct d.id) from tenant_custom_field_definitions d ' . 'select count(distinct d.id) from tenant_custom_field_definitions d ' .
'inner join user_custom_field_values v on v.definition_id = d.id ' . 'inner join user_custom_field_values v on v.definition_id = d.id ' .
'where d.active = 0' 'where d.active = 0'
) ?? 0); ) ?? 0);
$customFieldsUnusedActiveCount = (int) (DB::selectValue( $customFieldsUnusedActiveCount = (int) (DB::selectValue(
"select count(*) from ( " . "select count(*) from ( " .
"select d.id " . "select d.id " .
@@ -357,7 +357,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
"having count(v.id) = 0 " . "having count(v.id) = 0 " .
') unused_active' ') unused_active'
) ?? 0); ) ?? 0);
$selectionWithoutOptionsTenantUuid = (string) (DB::selectValue( $selectionWithoutOptionsTenantUuid = (string) (DB::selectValue(
"select t.uuid from tenants t " . "select t.uuid from tenants t " .
"inner join ( " . "inner join ( " .
@@ -372,7 +372,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
') issue on issue.tenant_id = t.id ' . ') issue on issue.tenant_id = t.id ' .
'limit 1' 'limit 1'
) ?? ''); ) ?? '');
$inactiveWithValuesTenantUuid = (string) (DB::selectValue( $inactiveWithValuesTenantUuid = (string) (DB::selectValue(
"select t.uuid from tenants t " . "select t.uuid from tenants t " .
"inner join ( " . "inner join ( " .
@@ -386,7 +386,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
') issue on issue.tenant_id = t.id ' . ') issue on issue.tenant_id = t.id ' .
'limit 1' 'limit 1'
) ?? ''); ) ?? '');
$unusedActiveTenantUuid = (string) (DB::selectValue( $unusedActiveTenantUuid = (string) (DB::selectValue(
"select t.uuid from tenants t " . "select t.uuid from tenants t " .
"inner join ( " . "inner join ( " .
@@ -401,7 +401,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
') issue on issue.tenant_id = t.id ' . ') issue on issue.tenant_id = t.id ' .
'limit 1' 'limit 1'
) ?? ''); ) ?? '');
if ($selectionWithoutOptionsTenantUuid !== '') { if ($selectionWithoutOptionsTenantUuid !== '') {
$customFieldsSelectionWithoutOptionsHref = 'admin/tenants/edit/' . $selectionWithoutOptionsTenantUuid . '?tab=custom_fields'; $customFieldsSelectionWithoutOptionsHref = 'admin/tenants/edit/' . $selectionWithoutOptionsTenantUuid . '?tab=custom_fields';
$customFieldTenantsWithSelectionWithoutOptionsHref = $customFieldsSelectionWithoutOptionsHref; $customFieldTenantsWithSelectionWithoutOptionsHref = $customFieldsSelectionWithoutOptionsHref;
@@ -412,7 +412,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
if ($unusedActiveTenantUuid !== '') { if ($unusedActiveTenantUuid !== '') {
$customFieldsUnusedActiveHref = 'admin/tenants/edit/' . $unusedActiveTenantUuid . '?tab=custom_fields'; $customFieldsUnusedActiveHref = 'admin/tenants/edit/' . $unusedActiveTenantUuid . '?tab=custom_fields';
} }
$selectionIssueTenants = $loadCustomFieldIssueTenants( $selectionIssueTenants = $loadCustomFieldIssueTenants(
"select t.uuid, t.description, count(*) as issue_count " . "select t.uuid, t.description, count(*) as issue_count " .
"from tenants t " . "from tenants t " .
@@ -436,7 +436,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
'issue_count' => $row['issue_count'], 'issue_count' => $row['issue_count'],
]; ];
} }
$inactiveIssueTenants = $loadCustomFieldIssueTenants( $inactiveIssueTenants = $loadCustomFieldIssueTenants(
"select t.uuid, t.description, count(distinct d.id) as issue_count " . "select t.uuid, t.description, count(distinct d.id) as issue_count " .
"from tenants t " . "from tenants t " .
@@ -454,7 +454,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
'issue_count' => $row['issue_count'], 'issue_count' => $row['issue_count'],
]; ];
} }
$unusedIssueTenants = $loadCustomFieldIssueTenants( $unusedIssueTenants = $loadCustomFieldIssueTenants(
"select t.uuid, t.description, count(*) as issue_count " . "select t.uuid, t.description, count(*) as issue_count " .
"from tenants t " . "from tenants t " .
@@ -479,7 +479,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
]; ];
} }
} }
$apiStatsAvailable = (int) (DB::selectValue( $apiStatsAvailable = (int) (DB::selectValue(
"select count(*) from information_schema.tables " . "select count(*) from information_schema.tables " .
"where table_schema = database() and table_name in ('api_audit_log', 'user_api_tokens')" "where table_schema = database() and table_name in ('api_audit_log', 'user_api_tokens')"
@@ -493,7 +493,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$apiTokensExpiring7dCount = 0; $apiTokensExpiring7dCount = 0;
$apiTopErrorRows = []; $apiTopErrorRows = [];
$apiSlowEndpointRows = []; $apiSlowEndpointRows = [];
if ($apiStatsAvailable) { if ($apiStatsAvailable) {
$apiRequests24hCount = (int) (DB::selectValue( $apiRequests24hCount = (int) (DB::selectValue(
'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR)' 'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR)'
@@ -504,14 +504,14 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
if ($apiRequestsPrev24hCount > 0) { if ($apiRequestsPrev24hCount > 0) {
$apiRequestTrendPercent = round((($apiRequests24hCount - $apiRequestsPrev24hCount) * 100) / $apiRequestsPrev24hCount, 1); $apiRequestTrendPercent = round((($apiRequests24hCount - $apiRequestsPrev24hCount) * 100) / $apiRequestsPrev24hCount, 1);
} }
$apiErrors4xx24hCount = (int) (DB::selectValue( $apiErrors4xx24hCount = (int) (DB::selectValue(
'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 400 and 499' 'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 400 and 499'
) ?? 0); ) ?? 0);
$apiErrors5xx24hCount = (int) (DB::selectValue( $apiErrors5xx24hCount = (int) (DB::selectValue(
'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 500 and 599' 'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 500 and 599'
) ?? 0); ) ?? 0);
$durationCount = (int) (DB::selectValue( $durationCount = (int) (DB::selectValue(
'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 200 and 299 and duration_ms is not null' 'select count(*) from api_audit_log where created_at >= (NOW() - INTERVAL 24 HOUR) and status_code between 200 and 299 and duration_ms is not null'
) ?? 0); ) ?? 0);
@@ -523,12 +523,12 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
'order by duration_ms asc limit ' . $offset . ', 1' 'order by duration_ms asc limit ' . $offset . ', 1'
) ?? 0); ) ?? 0);
} }
$apiTokensExpiring7dCount = (int) (DB::selectValue( $apiTokensExpiring7dCount = (int) (DB::selectValue(
'select count(*) from user_api_tokens ' . 'select count(*) from user_api_tokens ' .
'where revoked_at is null and expires_at is not null and expires_at > NOW() and expires_at <= (NOW() + INTERVAL 7 DAY)' 'where revoked_at is null and expires_at is not null and expires_at > NOW() and expires_at <= (NOW() + INTERVAL 7 DAY)'
) ?? 0); ) ?? 0);
$apiTopErrorRowsRaw = DB::select( $apiTopErrorRowsRaw = DB::select(
"select " . "select " .
" coalesce(nullif(error_code, ''), concat('http_', status_code)) as error_label, " . " coalesce(nullif(error_code, ''), concat('http_', status_code)) as error_label, " .
@@ -554,7 +554,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
}, },
$apiTopErrorRowsRaw $apiTopErrorRowsRaw
))) : []; ))) : [];
$apiSlowEndpointRowsRaw = DB::select( $apiSlowEndpointRowsRaw = DB::select(
"select " . "select " .
" path, " . " path, " .
@@ -586,7 +586,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$apiSlowEndpointRowsRaw $apiSlowEndpointRowsRaw
))) : []; ))) : [];
} }
$importStatsAvailable = (int) (DB::selectValue( $importStatsAvailable = (int) (DB::selectValue(
"select count(*) from information_schema.tables " . "select count(*) from information_schema.tables " .
"where table_schema = database() and table_name = 'import_audit_runs'" "where table_schema = database() and table_name = 'import_audit_runs'"
@@ -602,7 +602,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$importRecentRunRows = []; $importRecentRunRows = [];
$importProfileRows = []; $importProfileRows = [];
$importTopErrorCodeRows = []; $importTopErrorCodeRows = [];
if ($importStatsAvailable) { if ($importStatsAvailable) {
$importRuns7dCount = (int) (DB::selectValue( $importRuns7dCount = (int) (DB::selectValue(
"select count(*) from import_audit_runs where started_at >= (NOW() - INTERVAL 7 DAY)" "select count(*) from import_audit_runs where started_at >= (NOW() - INTERVAL 7 DAY)"
@@ -613,7 +613,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
if ($importRunsPrev7dCount > 0) { if ($importRunsPrev7dCount > 0) {
$importRunTrendPercent = round((($importRuns7dCount - $importRunsPrev7dCount) * 100) / $importRunsPrev7dCount, 1); $importRunTrendPercent = round((($importRuns7dCount - $importRunsPrev7dCount) * 100) / $importRunsPrev7dCount, 1);
} }
$importRowsCreated7dCount = (int) (DB::selectValue( $importRowsCreated7dCount = (int) (DB::selectValue(
"select coalesce(sum(created_count), 0) from import_audit_runs where started_at >= (NOW() - INTERVAL 7 DAY)" "select coalesce(sum(created_count), 0) from import_audit_runs where started_at >= (NOW() - INTERVAL 7 DAY)"
) ?? 0); ) ?? 0);
@@ -635,7 +635,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
if ($importRuns7dCount > 0) { if ($importRuns7dCount > 0) {
$importSuccessRate7dPercent = round(($importSuccessRuns7dCount * 100) / $importRuns7dCount, 1); $importSuccessRate7dPercent = round(($importSuccessRuns7dCount * 100) / $importRuns7dCount, 1);
} }
$importRecentRunRowsRaw = DB::select( $importRecentRunRowsRaw = DB::select(
"select " . "select " .
" import_audit_runs.id, " . " import_audit_runs.id, " .
@@ -680,7 +680,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
}, },
$importRecentRunRowsRaw $importRecentRunRowsRaw
))) : []; ))) : [];
$importProfileRowsRaw = DB::select( $importProfileRowsRaw = DB::select(
"select " . "select " .
" profile_key, " . " profile_key, " .
@@ -715,7 +715,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
}, },
$importProfileRowsRaw $importProfileRowsRaw
))) : []; ))) : [];
$importTopErrorRowsRaw = DB::select( $importTopErrorRowsRaw = DB::select(
"select error_codes_json from import_audit_runs " . "select error_codes_json from import_audit_runs " .
"where started_at >= (NOW() - INTERVAL 7 DAY) " . "where started_at >= (NOW() - INTERVAL 7 DAY) " .
@@ -1001,7 +1001,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$lifecycleRecentRiskRowsRaw $lifecycleRecentRiskRowsRaw
))) : []; ))) : [];
} }
$scheduledStatsAvailable = (int) (DB::selectValue( $scheduledStatsAvailable = (int) (DB::selectValue(
"select count(*) from information_schema.tables " . "select count(*) from information_schema.tables " .
"where table_schema = database() and table_name in ('scheduled_jobs', 'scheduled_job_runs', 'scheduler_runtime_status')" "where table_schema = database() and table_name in ('scheduled_jobs', 'scheduled_job_runs', 'scheduler_runtime_status')"
@@ -1014,7 +1014,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$schedulerRunnerLastResult = ''; $schedulerRunnerLastResult = '';
$schedulerRunnerLastErrorCode = ''; $schedulerRunnerLastErrorCode = '';
$schedulerRunnerIsActive = false; $schedulerRunnerIsActive = false;
if ($scheduledStatsAvailable) { if ($scheduledStatsAvailable) {
$scheduledEnabledJobsCount = (int) (DB::selectValue( $scheduledEnabledJobsCount = (int) (DB::selectValue(
"select count(*) from scheduled_jobs where enabled = 1" "select count(*) from scheduled_jobs where enabled = 1"
@@ -1034,12 +1034,12 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$schedulerTriggerScheduler, $schedulerTriggerScheduler,
$schedulerRunStatusFailed $schedulerRunStatusFailed
) ?? 0); ) ?? 0);
$schedulerRunnerIsActive = (int) (DB::selectValue( $schedulerRunnerIsActive = (int) (DB::selectValue(
"select case when last_heartbeat_at >= (NOW() - INTERVAL 3 MINUTE) then 1 else 0 end " . "select case when last_heartbeat_at >= (NOW() - INTERVAL 3 MINUTE) then 1 else 0 end " .
"from scheduler_runtime_status where id = 1" "from scheduler_runtime_status where id = 1"
) ?? 0) === 1; ) ?? 0) === 1;
$schedulerRuntimeRowRaw = DB::selectOne( $schedulerRuntimeRowRaw = DB::selectOne(
"select last_heartbeat_at, last_result, last_error_code from scheduler_runtime_status where id = 1 limit 1" "select last_heartbeat_at, last_result, last_error_code from scheduler_runtime_status where id = 1 limit 1"
); );
@@ -1048,7 +1048,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
$schedulerRunnerLastResult = trim((string) ($schedulerRuntimeRow['last_result'] ?? '')); $schedulerRunnerLastResult = trim((string) ($schedulerRuntimeRow['last_result'] ?? ''));
$schedulerRunnerLastErrorCode = trim((string) ($schedulerRuntimeRow['last_error_code'] ?? '')); $schedulerRunnerLastErrorCode = trim((string) ($schedulerRuntimeRow['last_error_code'] ?? ''));
} }
$usersUnverifiedCount = (int) (DB::selectValue( $usersUnverifiedCount = (int) (DB::selectValue(
'select count(*) from users where active = 1 and email_verified_at is null' 'select count(*) from users where active = 1 and email_verified_at is null'
) ?? 0); ) ?? 0);
@@ -1069,7 +1069,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
static fn ($row) => $row['users'] ?? $row, static fn ($row) => $row['users'] ?? $row,
$neverLoggedUsers $neverLoggedUsers
) : []; ) : [];
$mailLogFailedCount = (int) (DB::selectValue('select count(*) from mail_log where status = ?', $mailStatusFailed) ?? 0); $mailLogFailedCount = (int) (DB::selectValue('select count(*) from mail_log where status = ?', $mailStatusFailed) ?? 0);
$mailLogSentCount = (int) (DB::selectValue('select count(*) from mail_log where status = ?', $mailStatusSent) ?? 0); $mailLogSentCount = (int) (DB::selectValue('select count(*) from mail_log where status = ?', $mailStatusSent) ?? 0);
$mailLogLastSentAt = (string) (DB::selectValue('select max(sent_at) from mail_log where status = ?', $mailStatusSent) ?? ''); $mailLogLastSentAt = (string) (DB::selectValue('select max(sent_at) from mail_log where status = ?', $mailStatusSent) ?? '');
@@ -1081,7 +1081,7 @@ class AdminStatsRepository implements AdminStatsRepositoryInterface
static fn ($row) => $row['mail_log'] ?? $row, static fn ($row) => $row['mail_log'] ?? $row,
$mailLogRecentFailed $mailLogRecentFailed
) : []; ) : [];
$vars = get_defined_vars(); $vars = get_defined_vars();
unset($vars['this']); unset($vars['this']);

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Repository\Tenant; namespace MintyPHP\Repository\Tenant;
use MintyPHP\Domain\Taxonomy\TenantStatus;
use MintyPHP\DB; use MintyPHP\DB;
use MintyPHP\Domain\Taxonomy\TenantStatus;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
class TenantRepository implements TenantRepositoryInterface class TenantRepository implements TenantRepositoryInterface

View File

@@ -12,8 +12,7 @@ class AccessGatewayFactory
public function __construct( public function __construct(
private readonly AccessRepositoryFactory $accessRepositoryFactory, private readonly AccessRepositoryFactory $accessRepositoryFactory,
private readonly AuditServicesFactory $auditServicesFactory private readonly AuditServicesFactory $auditServicesFactory
) ) {
{
} }
public function createPermissionService(): PermissionService public function createPermissionService(): PermissionService

View File

@@ -47,4 +47,3 @@ class AuthorizationDecision
return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : $default; return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : $default;
} }
} }

View File

@@ -8,4 +8,3 @@ interface AuthorizationPolicyInterface
public function authorize(string $ability, array $context = []): AuthorizationDecision; public function authorize(string $ability, array $context = []): AuthorizationDecision;
} }

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Service\AddressBook; namespace MintyPHP\Service\AddressBook;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Access\RoleService; use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Directory\DirectoryScopeGateway;
use MintyPHP\Service\Org\DepartmentService; use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantService; use MintyPHP\Service\Tenant\TenantService;

View File

@@ -17,7 +17,8 @@ class AddressBookServicesFactory
private readonly UserServicesFactory $userServicesFactory, private readonly UserServicesFactory $userServicesFactory,
private readonly DirectoryServicesFactory $directoryServicesFactory, private readonly DirectoryServicesFactory $directoryServicesFactory,
private readonly CustomFieldServicesFactory $customFieldServicesFactory private readonly CustomFieldServicesFactory $customFieldServicesFactory
) {} ) {
}
public function createAddressBookService(): AddressBookService public function createAddressBookService(): AddressBookService
{ {

View File

@@ -17,7 +17,8 @@ class AuditServicesFactory
public function __construct( public function __construct(
private readonly AuditRepositoryFactory $auditRepositoryFactory, private readonly AuditRepositoryFactory $auditRepositoryFactory,
private readonly SettingGateway $settingGateway private readonly SettingGateway $settingGateway
) {} ) {
}
public function createApiAuditLogRepository(): ApiAuditLogRepositoryInterface public function createApiAuditLogRepository(): ApiAuditLogRepositoryInterface
{ {

View File

@@ -4,8 +4,8 @@ namespace MintyPHP\Service\Audit;
use MintyPHP\Domain\Taxonomy\SystemAuditChannel; use MintyPHP\Domain\Taxonomy\SystemAuditChannel;
use MintyPHP\Domain\Taxonomy\SystemAuditOutcome; use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
use MintyPHP\Http\RequestContext;
use MintyPHP\Http\ApiAuth; use MintyPHP\Http\ApiAuth;
use MintyPHP\Http\RequestContext;
use MintyPHP\Repository\Audit\SystemAuditLogRepositoryInterface; use MintyPHP\Repository\Audit\SystemAuditLogRepositoryInterface;
use MintyPHP\Repository\Support\RepoQuery; use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Service\Settings\SettingGateway; use MintyPHP\Service\Settings\SettingGateway;
@@ -27,8 +27,7 @@ class SystemAuditService
string $eventType, string $eventType,
string $outcome = SystemAuditOutcome::Success->value, string $outcome = SystemAuditOutcome::Success->value,
array $context = [] array $context = []
): ?int ): ?int {
{
if (!$this->isEnabled()) { if (!$this->isEnabled()) {
return null; return null;
} }

View File

@@ -6,9 +6,9 @@ use MintyPHP\Auth;
use MintyPHP\Repository\User\UserReadRepositoryInterface; use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface; use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\User\UserAccountService; use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserTenantContextService; use MintyPHP\Service\User\UserTenantContextService;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;

View File

@@ -26,7 +26,8 @@ class AuthServicesFactory
private readonly AuthRepositoryFactory $authRepositoryFactory, private readonly AuthRepositoryFactory $authRepositoryFactory,
private readonly AuthGatewayFactory $authGatewayFactory, private readonly AuthGatewayFactory $authGatewayFactory,
private readonly DatabaseSessionRepository $databaseSessionRepository private readonly DatabaseSessionRepository $databaseSessionRepository
) {} ) {
}
public function createApiTokenService(): ApiTokenService public function createApiTokenService(): ApiTokenService
{ {

View File

@@ -6,7 +6,6 @@ use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface; use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Repository\User\UserWriteRepositoryInterface; use MintyPHP\Repository\User\UserWriteRepositoryInterface;
use MintyPHP\Service\User\UserAssignmentService; use MintyPHP\Service\User\UserAssignmentService;
use MintyPHP\Service\User\UserAvatarService;
class SsoUserLinkService class SsoUserLinkService
{ {

View File

@@ -2,9 +2,9 @@
namespace MintyPHP\Service\Content; namespace MintyPHP\Service\Content;
use MintyPHP\Repository\Content\PageRepository;
use MintyPHP\Repository\Content\PageContentRepository;
use MintyPHP\I18n; use MintyPHP\I18n;
use MintyPHP\Repository\Content\PageContentRepository;
use MintyPHP\Repository\Content\PageRepository;
class PageService class PageService
{ {
@@ -59,8 +59,7 @@ class PageService
string $content, string $content,
int $currentUserId = 0, int $currentUserId = 0,
?string $locale = null ?string $locale = null
): array ): array {
{
$slug = trim($slug); $slug = trim($slug);
if ($slug === '') { if ($slug === '') {
return ['ok' => false, 'status' => 404, 'error' => 'not_found']; return ['ok' => false, 'status' => 404, 'error' => 'not_found'];

View File

@@ -22,7 +22,8 @@ class DirectoryServicesFactory
private readonly AuditServicesFactory $auditServicesFactory, private readonly AuditServicesFactory $auditServicesFactory,
private readonly DirectoryRepositoryFactory $directoryRepositoryFactory, private readonly DirectoryRepositoryFactory $directoryRepositoryFactory,
private readonly DirectoryGatewayFactory $directoryGatewayFactory private readonly DirectoryGatewayFactory $directoryGatewayFactory
) {} ) {
}
public function createTenantService(): TenantService public function createTenantService(): TenantService
{ {

View File

@@ -143,8 +143,12 @@ trait ImageUploadTrait
$scale = min($width / $srcWidth, $height / $srcHeight); $scale = min($width / $srcWidth, $height / $srcHeight);
$dstWidth = (int) round($srcWidth * $scale); $dstWidth = (int) round($srcWidth * $scale);
$dstHeight = (int) round($srcHeight * $scale); $dstHeight = (int) round($srcHeight * $scale);
if ($dstWidth < 1) $dstWidth = 1; if ($dstWidth < 1) {
if ($dstHeight < 1) $dstHeight = 1; $dstWidth = 1;
}
if ($dstHeight < 1) {
$dstHeight = 1;
}
$dst = imagecreatetruecolor($dstWidth, $dstHeight); $dst = imagecreatetruecolor($dstWidth, $dstHeight);
if ($ext === 'png' || $ext === 'webp') { if ($ext === 'png' || $ext === 'webp') {

View File

@@ -2,8 +2,8 @@
namespace MintyPHP\Service\Import; namespace MintyPHP\Service\Import;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Access\PermissionGateway; use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Audit\ImportAuditService; use MintyPHP\Service\Audit\ImportAuditService;
use MintyPHP\Service\Import\Profile\ImportProfileInterface; use MintyPHP\Service\Import\Profile\ImportProfileInterface;
use MintyPHP\Service\Settings\SettingGateway; use MintyPHP\Service\Settings\SettingGateway;

View File

@@ -13,7 +13,6 @@ use MintyPHP\Service\Import\Profile\UserImportGateway;
use MintyPHP\Service\Import\Profile\UserImportProfile; use MintyPHP\Service\Import\Profile\UserImportProfile;
use MintyPHP\Service\Settings\SettingGateway; use MintyPHP\Service\Settings\SettingGateway;
use MintyPHP\Service\Settings\SettingServicesFactory; use MintyPHP\Service\Settings\SettingServicesFactory;
use MintyPHP\Service\User\UserRepositoryFactory;
use MintyPHP\Service\User\UserServicesFactory; use MintyPHP\Service\User\UserServicesFactory;
class ImportServicesFactory class ImportServicesFactory
@@ -30,10 +29,10 @@ class ImportServicesFactory
private readonly UserServicesFactory $userServicesFactory, private readonly UserServicesFactory $userServicesFactory,
private readonly AccessServicesFactory $accessServicesFactory, private readonly AccessServicesFactory $accessServicesFactory,
private readonly SettingServicesFactory $settingServicesFactory, private readonly SettingServicesFactory $settingServicesFactory,
private readonly UserRepositoryFactory $userRepositoryFactory,
private readonly DirectoryServicesFactory $directoryServicesFactory, private readonly DirectoryServicesFactory $directoryServicesFactory,
private readonly ImportRepositoryFactory $importRepositoryFactory private readonly ImportRepositoryFactory $importRepositoryFactory
) {} ) {
}
public function createImportService(): ImportService public function createImportService(): ImportService
{ {
@@ -45,7 +44,9 @@ class ImportServicesFactory
'users' => new UserImportProfile(new UserImportGateway( 'users' => new UserImportProfile(new UserImportGateway(
$this->userServicesFactory->createUserReadRepository(), $this->userServicesFactory->createUserReadRepository(),
$this->userServicesFactory->createUserAccountService(), $this->userServicesFactory->createUserAccountService(),
$this->userRepositoryFactory $this->directoryServicesFactory->createTenantRepository(),
$this->directoryServicesFactory->createRoleRepository(),
$this->directoryServicesFactory->createDepartmentRepository(),
)), )),
'departments' => new DepartmentImportProfile(new DepartmentImportGateway( 'departments' => new DepartmentImportProfile(new DepartmentImportGateway(
$this->directoryServicesFactory->createDepartmentRepository(), $this->directoryServicesFactory->createDepartmentRepository(),

View File

@@ -60,4 +60,3 @@ class ImportStateStoreService
unset($_SESSION[self::SESSION_KEY][$token]); unset($_SESSION[self::SESSION_KEY][$token]);
} }
} }

View File

@@ -7,14 +7,15 @@ use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface; use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Repository\User\UserReadRepositoryInterface; use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Service\User\UserAccountService; use MintyPHP\Service\User\UserAccountService;
use MintyPHP\Service\User\UserRepositoryFactory;
class UserImportGateway class UserImportGateway
{ {
public function __construct( public function __construct(
private readonly UserReadRepositoryInterface $userReadRepository, private readonly UserReadRepositoryInterface $userReadRepository,
private readonly UserAccountService $userAccountService, private readonly UserAccountService $userAccountService,
private readonly UserRepositoryFactory $userRepositoryFactory private readonly TenantRepositoryInterface $tenantRepository,
private readonly RoleRepositoryInterface $roleRepository,
private readonly DepartmentRepositoryInterface $departmentRepository,
) { ) {
} }
@@ -25,32 +26,32 @@ class UserImportGateway
public function findTenantByUuid(string $uuid): ?array public function findTenantByUuid(string $uuid): ?array
{ {
return $this->tenantRepository()->findByUuid($uuid); return $this->tenantRepository->findByUuid($uuid);
} }
public function findTenantById(int $id): ?array public function findTenantById(int $id): ?array
{ {
return $this->tenantRepository()->find($id); return $this->tenantRepository->find($id);
} }
public function findRoleByUuid(string $uuid): ?array public function findRoleByUuid(string $uuid): ?array
{ {
return $this->roleRepository()->findByUuid($uuid); return $this->roleRepository->findByUuid($uuid);
} }
public function findRoleById(int $id): ?array public function findRoleById(int $id): ?array
{ {
return $this->roleRepository()->find($id); return $this->roleRepository->find($id);
} }
public function findDepartmentByUuid(string $uuid): ?array public function findDepartmentByUuid(string $uuid): ?array
{ {
return $this->departmentRepository()->findByUuid($uuid); return $this->departmentRepository->findByUuid($uuid);
} }
public function findDepartmentById(int $id): ?array public function findDepartmentById(int $id): ?array
{ {
return $this->departmentRepository()->find($id); return $this->departmentRepository->find($id);
} }
/** /**
@@ -61,19 +62,4 @@ class UserImportGateway
{ {
return $this->userAccountService->createFromAdmin($input, $currentUserId); return $this->userAccountService->createFromAdmin($input, $currentUserId);
} }
private function tenantRepository(): TenantRepositoryInterface
{
return $this->userRepositoryFactory->createTenantRepository();
}
private function roleRepository(): RoleRepositoryInterface
{
return $this->userRepositoryFactory->createRoleRepository();
}
private function departmentRepository(): DepartmentRepositoryInterface
{
return $this->userRepositoryFactory->createDepartmentRepository();
}
} }

View File

@@ -13,7 +13,8 @@ class MailServicesFactory
public function __construct( public function __construct(
private readonly MailRepositoryFactory $mailRepositoryFactory, private readonly MailRepositoryFactory $mailRepositoryFactory,
private readonly SettingGateway $settingGateway private readonly SettingGateway $settingGateway
) {} ) {
}
public function createMailLogRepository(): MailLogRepositoryInterface public function createMailLogRepository(): MailLogRepositoryInterface
{ {

View File

@@ -2,10 +2,10 @@
namespace MintyPHP\Service\Scheduler; namespace MintyPHP\Service\Scheduler;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Scheduler\Handler\ScheduledJobHandlerInterface; use MintyPHP\Service\Scheduler\Handler\ScheduledJobHandlerInterface;
use MintyPHP\Service\Scheduler\Handler\SystemAuditPurgeJobHandler; use MintyPHP\Service\Scheduler\Handler\SystemAuditPurgeJobHandler;
use MintyPHP\Service\Scheduler\Handler\UserLifecycleJobHandler; use MintyPHP\Service\Scheduler\Handler\UserLifecycleJobHandler;
use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\User\UserLifecycleService; use MintyPHP\Service\User\UserLifecycleService;
class ScheduledJobRegistry class ScheduledJobRegistry
@@ -30,8 +30,7 @@ class ScheduledJobRegistry
public function __construct( public function __construct(
UserLifecycleService $userLifecycleService, UserLifecycleService $userLifecycleService,
SystemAuditService $systemAuditService SystemAuditService $systemAuditService
) ) {
{
$this->handlers = [ $this->handlers = [
self::USER_LIFECYCLE_RUN => new UserLifecycleJobHandler($userLifecycleService), self::USER_LIFECYCLE_RUN => new UserLifecycleJobHandler($userLifecycleService),
self::SYSTEM_AUDIT_PURGE => new SystemAuditPurgeJobHandler($systemAuditService), self::SYSTEM_AUDIT_PURGE => new SystemAuditPurgeJobHandler($systemAuditService),

View File

@@ -23,7 +23,8 @@ class SchedulerServicesFactory
private readonly AuditServicesFactory $auditServicesFactory, private readonly AuditServicesFactory $auditServicesFactory,
private readonly DatabaseSessionRepository $databaseSessionRepository, private readonly DatabaseSessionRepository $databaseSessionRepository,
private readonly SchedulerRepositoryFactory $schedulerRepositoryFactory private readonly SchedulerRepositoryFactory $schedulerRepositoryFactory
) {} ) {
}
public function createScheduledJobService(): ScheduledJobService public function createScheduledJobService(): ScheduledJobService
{ {

View File

@@ -10,7 +10,8 @@ class SecurityServicesFactory
public function __construct( public function __construct(
private readonly SecurityRepositoryFactory $securityRepositoryFactory private readonly SecurityRepositoryFactory $securityRepositoryFactory
) {} ) {
}
public function createRateLimitRepository(): RateLimitRepositoryInterface public function createRateLimitRepository(): RateLimitRepositoryInterface
{ {

View File

@@ -5,8 +5,8 @@ namespace MintyPHP\Service\Settings;
use MintyPHP\Repository\Auth\ApiTokenRepository; use MintyPHP\Repository\Auth\ApiTokenRepository;
use MintyPHP\Repository\Auth\RememberTokenRepository; use MintyPHP\Repository\Auth\RememberTokenRepository;
use MintyPHP\Service\Access\RoleService; use MintyPHP\Service\Access\RoleService;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Audit\SystemAuditService; use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Service\Org\DepartmentService;
use MintyPHP\Service\Tenant\TenantService; use MintyPHP\Service\Tenant\TenantService;
class AdminSettingsService class AdminSettingsService

View File

@@ -16,7 +16,8 @@ class SettingServicesFactory
public function __construct( public function __construct(
private readonly SettingRepositoryFactory $settingRepositoryFactory private readonly SettingRepositoryFactory $settingRepositoryFactory
) {} ) {
}
public function createSettingRepository(): SettingRepositoryInterface public function createSettingRepository(): SettingRepositoryInterface
{ {

View File

@@ -16,7 +16,8 @@ class TenantServicesFactory
public function __construct( public function __construct(
private readonly PermissionGateway $permissionGateway, private readonly PermissionGateway $permissionGateway,
private readonly TenantRepositoryFactory $tenantRepositoryFactory private readonly TenantRepositoryFactory $tenantRepositoryFactory
) {} ) {
}
public function createTenantScopeService(): TenantScopeService public function createTenantScopeService(): TenantScopeService
{ {

View File

@@ -565,7 +565,7 @@ class UserAccountService
$errors[] = t('Tenant not found'); $errors[] = t('Tenant not found');
} else { } else {
$userTenantIds = $this->userAssignmentService->buildAssignmentsForUser($userId)['tenants'] ?? []; $userTenantIds = $this->userAssignmentService->buildAssignmentsForUser($userId)['tenants'] ?? [];
$userTenantIds = array_map(static fn(array $t): int => (int) ($t['id'] ?? 0), $userTenantIds); $userTenantIds = array_map(static fn (array $t): int => (int) ($t['id'] ?? 0), $userTenantIds);
$tenantId = (int) $tenant['id']; $tenantId = (int) $tenant['id'];
if (!in_array($tenantId, $userTenantIds, true)) { if (!in_array($tenantId, $userTenantIds, true)) {
$errors[] = t('No access to this tenant'); $errors[] = t('No access to this tenant');

View File

@@ -9,72 +9,59 @@ use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
class UserDirectoryGateway class UserDirectoryGateway
{ {
public function __construct( public function __construct(
private readonly UserRepositoryFactory $userRepositoryFactory private readonly TenantRepositoryInterface $tenantRepository,
private readonly RoleRepositoryInterface $roleRepository,
private readonly DepartmentRepositoryInterface $departmentRepository,
) { ) {
} }
public function listTenantIds(): array public function listTenantIds(): array
{ {
return $this->tenantRepository()->listIds(); return $this->tenantRepository->listIds();
} }
public function listTenantsByIds(array $tenantIds): array public function listTenantsByIds(array $tenantIds): array
{ {
return $this->tenantRepository()->listByIds($tenantIds); return $this->tenantRepository->listByIds($tenantIds);
} }
public function findTenant(int $tenantId): ?array public function findTenant(int $tenantId): ?array
{ {
return $this->tenantRepository()->find($tenantId); return $this->tenantRepository->find($tenantId);
} }
public function findTenantByUuid(string $tenantUuid): ?array public function findTenantByUuid(string $tenantUuid): ?array
{ {
return $this->tenantRepository()->findByUuid($tenantUuid); return $this->tenantRepository->findByUuid($tenantUuid);
} }
public function listActiveTenantIdsByIds(array $tenantIds): array public function listActiveTenantIdsByIds(array $tenantIds): array
{ {
return $this->tenantRepository()->listActiveIdsByIds($tenantIds); return $this->tenantRepository->listActiveIdsByIds($tenantIds);
} }
public function listActiveRoleIds(): array public function listActiveRoleIds(): array
{ {
return $this->roleRepository()->listActiveIds(); return $this->roleRepository->listActiveIds();
} }
public function listRolesByIds(array $roleIds): array public function listRolesByIds(array $roleIds): array
{ {
return $this->roleRepository()->listByIds($roleIds); return $this->roleRepository->listByIds($roleIds);
} }
public function listDepartmentsByIds(array $departmentIds, bool $includeInactive = true): array public function listDepartmentsByIds(array $departmentIds, bool $includeInactive = true): array
{ {
return $this->departmentRepository()->listByIds($departmentIds, $includeInactive); return $this->departmentRepository->listByIds($departmentIds, $includeInactive);
} }
public function listActiveDepartmentIdsByTenantIds(array $tenantIds): array public function listActiveDepartmentIdsByTenantIds(array $tenantIds): array
{ {
return $this->departmentRepository()->listActiveIdsByTenantIds($tenantIds); return $this->departmentRepository->listActiveIdsByTenantIds($tenantIds);
} }
public function listDepartmentsByTenantIds(array $tenantIds): array public function listDepartmentsByTenantIds(array $tenantIds): array
{ {
return $this->departmentRepository()->listByTenantIds($tenantIds); return $this->departmentRepository->listByTenantIds($tenantIds);
}
private function tenantRepository(): TenantRepositoryInterface
{
return $this->userRepositoryFactory->createTenantRepository();
}
private function roleRepository(): RoleRepositoryInterface
{
return $this->userRepositoryFactory->createRoleRepository();
}
private function departmentRepository(): DepartmentRepositoryInterface
{
return $this->userRepositoryFactory->createDepartmentRepository();
} }
} }

View File

@@ -4,6 +4,7 @@ namespace MintyPHP\Service\User;
use MintyPHP\Service\Access\AccessServicesFactory; use MintyPHP\Service\Access\AccessServicesFactory;
use MintyPHP\Service\Access\PermissionGateway; use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Directory\DirectoryRepositoryFactory;
use MintyPHP\Service\Settings\SettingGateway; use MintyPHP\Service\Settings\SettingGateway;
use MintyPHP\Service\Settings\SettingServicesFactory; use MintyPHP\Service\Settings\SettingServicesFactory;
use MintyPHP\Service\Tenant\TenantScopeService; use MintyPHP\Service\Tenant\TenantScopeService;
@@ -18,10 +19,10 @@ class UserGatewayFactory
private ?UserPermissionGateway $userPermissionGateway = null; private ?UserPermissionGateway $userPermissionGateway = null;
public function __construct( public function __construct(
private readonly UserRepositoryFactory $userRepositoryFactory,
private readonly AccessServicesFactory $accessServicesFactory, private readonly AccessServicesFactory $accessServicesFactory,
private readonly SettingServicesFactory $settingServicesFactory, private readonly SettingServicesFactory $settingServicesFactory,
private readonly TenantScopeService $tenantScopeService private readonly TenantScopeService $tenantScopeService,
private readonly DirectoryRepositoryFactory $directoryRepositoryFactory,
) { ) {
} }
@@ -37,7 +38,11 @@ class UserGatewayFactory
public function createUserDirectoryGateway(): UserDirectoryGateway public function createUserDirectoryGateway(): UserDirectoryGateway
{ {
return $this->userDirectoryGateway ??= new UserDirectoryGateway($this->userRepositoryFactory); return $this->userDirectoryGateway ??= new UserDirectoryGateway(
$this->directoryRepositoryFactory->createTenantRepository(),
$this->directoryRepositoryFactory->createRoleRepository(),
$this->directoryRepositoryFactory->createDepartmentRepository(),
);
} }
public function createUserPermissionGateway(): UserPermissionGateway public function createUserPermissionGateway(): UserPermissionGateway
@@ -54,5 +59,4 @@ class UserGatewayFactory
{ {
return $this->settingGateway ??= $this->settingServicesFactory->createSettingGateway(); return $this->settingGateway ??= $this->settingServicesFactory->createSettingGateway();
} }
} }

View File

@@ -2,16 +2,10 @@
namespace MintyPHP\Service\User; namespace MintyPHP\Service\User;
use MintyPHP\Repository\Access\RoleRepository;
use MintyPHP\Repository\Access\RoleRepositoryInterface;
use MintyPHP\Repository\Access\UserRoleRepository; use MintyPHP\Repository\Access\UserRoleRepository;
use MintyPHP\Repository\Access\UserRoleRepositoryInterface; use MintyPHP\Repository\Access\UserRoleRepositoryInterface;
use MintyPHP\Repository\Org\DepartmentRepository;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Org\UserDepartmentRepository; use MintyPHP\Repository\Org\UserDepartmentRepository;
use MintyPHP\Repository\Org\UserDepartmentRepositoryInterface; use MintyPHP\Repository\Org\UserDepartmentRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Repository\Tenant\UserTenantRepository; use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface; use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
use MintyPHP\Repository\User\UserListQueryRepository; use MintyPHP\Repository\User\UserListQueryRepository;
@@ -29,9 +23,6 @@ class UserRepositoryFactory
private ?UserWriteRepository $userWriteRepository = null; private ?UserWriteRepository $userWriteRepository = null;
private ?UserListQueryRepository $userListQueryRepository = null; private ?UserListQueryRepository $userListQueryRepository = null;
private ?UserSavedFilterRepository $userSavedFilterRepository = null; private ?UserSavedFilterRepository $userSavedFilterRepository = null;
private ?TenantRepository $tenantRepository = null;
private ?RoleRepository $roleRepository = null;
private ?DepartmentRepository $departmentRepository = null;
private ?UserTenantRepository $userTenantRepository = null; private ?UserTenantRepository $userTenantRepository = null;
private ?UserRoleRepository $userRoleRepository = null; private ?UserRoleRepository $userRoleRepository = null;
private ?UserDepartmentRepository $userDepartmentRepository = null; private ?UserDepartmentRepository $userDepartmentRepository = null;
@@ -56,21 +47,6 @@ class UserRepositoryFactory
return $this->userSavedFilterRepository ??= new UserSavedFilterRepository(); return $this->userSavedFilterRepository ??= new UserSavedFilterRepository();
} }
public function createTenantRepository(): TenantRepositoryInterface
{
return $this->tenantRepository ??= new TenantRepository();
}
public function createRoleRepository(): RoleRepositoryInterface
{
return $this->roleRepository ??= new RoleRepository();
}
public function createDepartmentRepository(): DepartmentRepositoryInterface
{
return $this->departmentRepository ??= new DepartmentRepository();
}
public function createUserTenantRepository(): UserTenantRepositoryInterface public function createUserTenantRepository(): UserTenantRepositoryInterface
{ {
return $this->userTenantRepository ??= new UserTenantRepository(); return $this->userTenantRepository ??= new UserTenantRepository();

View File

@@ -31,7 +31,8 @@ class UserServicesFactory
private readonly UserRepositoryFactory $userRepositoryFactory, private readonly UserRepositoryFactory $userRepositoryFactory,
private readonly UserGatewayFactory $userGatewayFactory, private readonly UserGatewayFactory $userGatewayFactory,
private readonly DatabaseSessionRepository $databaseSessionRepository private readonly DatabaseSessionRepository $databaseSessionRepository
) {} ) {
}
public function createUserAccountService(): UserAccountService public function createUserAccountService(): UserAccountService
{ {

View File

@@ -409,7 +409,9 @@ function appFaviconUrl(string $file): string
*/ */
function sortByDescription(array &$items): void function sortByDescription(array &$items): void
{ {
usort($items, static fn($a, $b) => usort(
$items,
static fn ($a, $b) =>
strcasecmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? '')) strcasecmp((string) ($a['description'] ?? ''), (string) ($b['description'] ?? ''))
); );
} }

View File

@@ -18,11 +18,17 @@ function gridLang(): array
'next' => t('Next'), 'next' => t('Next'),
'navigate' => t('Page %d of %d'), 'navigate' => t('Page %d of %d'),
'page' => t('Page %d'), 'page' => t('Page %d'),
'rowsPerPage' => t('Rows per page'),
'showing' => t('Showing'), 'showing' => t('Showing'),
'of' => t('of'), 'of' => t('of'),
'to' => t('to'), 'to' => t('to'),
'results' => t('results'), 'results' => t('results'),
], ],
'actions' => [
'column' => t('Actions'),
'open' => t('Open'),
'edit' => t('Edit'),
],
'loading' => t('Loading...'), 'loading' => t('Loading...'),
'noRecordsFound' => t('No records found'), 'noRecordsFound' => t('No records found'),
'error' => t('An error happened while fetching the data'), 'error' => t('An error happened while fetching the data'),

View File

@@ -1,9 +1,9 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Access\AuthorizationService; use MintyPHP\Service\Access\AuthorizationService;
use MintyPHP\Service\Access\UserAuthorizationPolicy; use MintyPHP\Service\Access\UserAuthorizationPolicy;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,7 +1,7 @@
<?php <?php
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Service\Access\OperationsAuthorizationPolicy; use MintyPHP\Service\Access\OperationsAuthorizationPolicy;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,7 +1,6 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory; use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -2,7 +2,6 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\AddressBook\AddressBookServicesFactory;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Audit\ApiAuditService; use MintyPHP\Service\Audit\ApiAuditService;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Audit\ImportAuditService; use MintyPHP\Service\Audit\ImportAuditService;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,4 +1,5 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Support\Guard;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();
Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW); Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW);

View File

@@ -1,9 +1,9 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Router;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
use MintyPHP\Router;
Guard::requireLogin(); Guard::requireLogin();
Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW); Guard::requireAbility(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW);

View File

@@ -1,10 +1,10 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Access\PermissionAuthorizationPolicy; use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();
$request = requestInput(); $request = requestInput();

View File

@@ -1,7 +1,7 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Service\Access\PermissionAuthorizationPolicy; use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();
gridRequireGetRequest(); gridRequireGetRequest();

View File

@@ -1,10 +1,10 @@
<?php <?php
use MintyPHP\Router;
use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Service\Access\PermissionGateway;
use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -2,8 +2,8 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\PermissionAuthorizationPolicy; use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
use MintyPHP\Support\Guard;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -2,8 +2,8 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\RoleAuthorizationPolicy; use MintyPHP\Service\Access\RoleAuthorizationPolicy;
use MintyPHP\Support\Guard;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Router;
use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus; use MintyPHP\Domain\Taxonomy\ScheduledJobRunStatus;
use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult; use MintyPHP\Domain\Taxonomy\SchedulerRuntimeResult;
use MintyPHP\Router;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
Guard::requireAbilityOrForbidden(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW); Guard::requireAbilityOrForbidden(\MintyPHP\Service\Access\OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW);

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -1,7 +1,7 @@
<?php <?php
use MintyPHP\Router;
use MintyPHP\Domain\Taxonomy\SystemAuditOutcome; use MintyPHP\Domain\Taxonomy\SystemAuditOutcome;
use MintyPHP\Router;
use MintyPHP\Service\Access\OperationsAuthorizationPolicy; use MintyPHP\Service\Access\OperationsAuthorizationPolicy;
use MintyPHP\Service\Audit\SystemAuditService; use MintyPHP\Service\Audit\SystemAuditService;
use MintyPHP\Session; use MintyPHP\Session;

View File

@@ -21,13 +21,14 @@ $dir = $filters['dir'];
$computedOrderKeys = ['theme', 'sso', 'active_users', 'inactive_users']; $computedOrderKeys = ['theme', 'sso', 'active_users', 'inactive_users'];
$userTenantRepository = app(\MintyPHP\Repository\Tenant\UserTenantRepository::class); $userTenantRepository = app(\MintyPHP\Repository\Tenant\UserTenantRepository::class);
$tenantMicrosoftAuthRepository = app(TenantMicrosoftAuthRepository::class); $tenantMicrosoftAuthRepository = app(TenantMicrosoftAuthRepository::class);
$tenantAvatarService = app(\MintyPHP\Service\Tenant\TenantAvatarService::class);
$settingGateway = app(\MintyPHP\Service\Settings\SettingGateway::class); $settingGateway = app(\MintyPHP\Service\Settings\SettingGateway::class);
$fetchRows = static function ( $fetchRows = static function (
array $tenantRows, array $tenantRows,
array $themes, array $themes,
string $globalDefaultTheme string $globalDefaultTheme
) use ($userTenantRepository, $tenantMicrosoftAuthRepository, $settingGateway): array { ) use ($userTenantRepository, $tenantMicrosoftAuthRepository, $tenantAvatarService, $settingGateway): array {
$tenantIds = []; $tenantIds = [];
foreach ($tenantRows as $tenantRow) { foreach ($tenantRows as $tenantRow) {
$tenantId = (int) ($tenantRow['id'] ?? 0); $tenantId = (int) ($tenantRow['id'] ?? 0);
@@ -69,10 +70,11 @@ $fetchRows = static function (
$usersActive = (int) ($tenantActiveUserCounts[$tenantId] ?? 0); $usersActive = (int) ($tenantActiveUserCounts[$tenantId] ?? 0);
$usersInactive = max(0, $usersTotal - $usersActive); $usersInactive = max(0, $usersTotal - $usersActive);
$tenantStatus = TenantStatus::normalizeOr((string) ($row['status'] ?? ''), TenantStatus::Active); $tenantStatus = TenantStatus::normalizeOr((string) ($row['status'] ?? ''), TenantStatus::Active);
$tenantUuid = (string) ($row['uuid'] ?? '');
$rows[] = [ $rows[] = [
'id' => $row['id'] ?? null, 'id' => $row['id'] ?? null,
'uuid' => $row['uuid'] ?? '', 'uuid' => $tenantUuid,
'is_default' => $tenantId > 0 && $defaultTenantId === $tenantId, 'is_default' => $tenantId > 0 && $defaultTenantId === $tenantId,
'description' => $row['description'] ?? '', 'description' => $row['description'] ?? '',
'status' => $tenantStatus->value, 'status' => $tenantStatus->value,
@@ -92,6 +94,7 @@ $fetchRows = static function (
'inactive_users' => $usersInactive, 'inactive_users' => $usersInactive,
'created' => dt($row['created'] ?? ''), 'created' => dt($row['created'] ?? ''),
'modified' => dt($row['modified'] ?? ''), 'modified' => dt($row['modified'] ?? ''),
'has_avatar' => $tenantUuid !== '' && $tenantAvatarService->hasAvatar($tenantUuid),
]; ];
} }

View File

@@ -6,6 +6,7 @@ use MintyPHP\Service\Access\TenantAuthorizationPolicy;
use MintyPHP\Service\CustomField\TenantCustomFieldService; use MintyPHP\Service\CustomField\TenantCustomFieldService;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();
$request = requestInput(); $request = requestInput();

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -2,8 +2,8 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Service\Access\TenantAuthorizationPolicy; use MintyPHP\Service\Access\TenantAuthorizationPolicy;
use MintyPHP\Support\Guard;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -49,6 +49,7 @@ $pageConfig = [
'filterChipMeta' => $filterChipMeta, 'filterChipMeta' => $filterChipMeta,
'gridLang' => $gridLang, 'gridLang' => $gridLang,
'labels' => [ 'labels' => [
'avatar' => t('Avatar'),
'description' => t('Description'), 'description' => t('Description'),
'status' => t('Status'), 'status' => t('Status'),
'theme' => t('Theme'), 'theme' => t('Theme'),

View File

@@ -4,8 +4,8 @@ use MintyPHP\Buffer;
use MintyPHP\Domain\Taxonomy\UserLifecycleAction; use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
use MintyPHP\Domain\Taxonomy\UserLifecycleStatus; use MintyPHP\Domain\Taxonomy\UserLifecycleStatus;
use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType; use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Audit\UserLifecycleAuditService; use MintyPHP\Service\Audit\UserLifecycleAuditService;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -2,8 +2,8 @@
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();
Guard::requireAbility(\MintyPHP\Service\Access\SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_UPDATE); Guard::requireAbility(\MintyPHP\Service\Access\SettingsAuthorizationPolicy::ABILITY_ADMIN_SETTINGS_UPDATE);

View File

@@ -3,8 +3,8 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Domain\Taxonomy\UserLifecycleAction; use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Support\Flash; use MintyPHP\Support\Flash;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\I18n;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\I18n;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap; use MintyPHP\Service\Access\UiCapabilityMap;

View File

@@ -1,8 +1,8 @@
<?php <?php
use MintyPHP\Support\Guard;
use MintyPHP\Router;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin(); Guard::requireLogin();

View File

@@ -3,8 +3,8 @@
use MintyPHP\Buffer; use MintyPHP\Buffer;
use MintyPHP\Http\Request; use MintyPHP\Http\Request;
use MintyPHP\Router; use MintyPHP\Router;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UiAccessService; use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use MintyPHP\Service\Access\UserAuthorizationPolicy; use MintyPHP\Service\Access\UserAuthorizationPolicy;
use MintyPHP\Session; use MintyPHP\Session;
use MintyPHP\Support\Guard; use MintyPHP\Support\Guard;

View File

@@ -51,28 +51,28 @@ if (!($emailResult['allowed'] ?? true)) {
$userReadRepository = (app(UserServicesFactory::class))->createUserReadRepository(); $userReadRepository = (app(UserServicesFactory::class))->createUserReadRepository();
$user = $userReadRepository->findByEmail($email); $user = $userReadRepository->findByEmail($email);
if (!$user) { if (!$user) {
$rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300); $rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300);
$rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900); $rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900);
ApiResponse::error('invalid_credentials', 401); ApiResponse::error('invalid_credentials', 401);
} }
// ---- Account checks ---- // ---- Account checks ----
if (!(int) ($user['active'] ?? 0)) { if (!(int) ($user['active'] ?? 0)) {
$rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300); $rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300);
$rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900); $rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900);
ApiResponse::error('account_inactive', 401); ApiResponse::error('account_inactive', 401);
} }
if (empty($user['email_verified_at'])) { if (empty($user['email_verified_at'])) {
$rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300); $rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300);
$rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900); $rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900);
ApiResponse::error('invalid_credentials', 401); ApiResponse::error('invalid_credentials', 401);
} }
// ---- Password verification ---- // ---- Password verification ----
if (!password_verify($password, (string) ($user['password'] ?? ''))) { if (!password_verify($password, (string) ($user['password'] ?? ''))) {
$rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300); $rateLimiter->registerFailure('api.auth.login.ip', $ip, 10, 600, 300);
$rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900); $rateLimiter->registerFailure('api.auth.login.email_ip', $emailKey, 5, 900, 900);
ApiResponse::error('invalid_credentials', 401); ApiResponse::error('invalid_credentials', 401);
} }

Some files were not shown because too many files have changed in this diff Show More