diff --git a/Abstracts/CoreSubmoduleAbstract.php b/Abstracts/CoreSubmoduleAbstract.php index bea2b2e..d02c87c 100644 --- a/Abstracts/CoreSubmoduleAbstract.php +++ b/Abstracts/CoreSubmoduleAbstract.php @@ -10,6 +10,7 @@ use ArrayAccess\TrayDigita\Module\Modules; use ArrayAccess\TrayDigita\Module\Traits\ModuleTrait; use function debug_backtrace; +use function func_get_args; use function sprintf; use const DEBUG_BACKTRACE_IGNORE_ARGS; @@ -63,4 +64,40 @@ public function translatePlural( ): string { return $this->core->translatePlural(...func_get_args()); } + /** + * Translate context + * + * @param string $original + * @param string $context + * @param string $domain + * @return string + * @see TranslatorInterface::translateContext() + */ + public function translateContext( + string $original, + string $context, + string $domain = TranslatorInterface::DEFAULT_DOMAIN, + ): string { + return $this->core->translateContext(...func_get_args()); + } + + /** + * Translate plural context + * + * @param string $singular + * @param string $plural + * @param int|float|numeric-string $number + * @param string $context + * @param string $domain + * @return string + */ + public function translatePluralContext( + string $singular, + string $plural, + int|float|string $number, + string $context, + string $domain = TranslatorInterface::DEFAULT_DOMAIN + ) : string { + return $this->core->translatePluralContext(...func_get_args()); + } } diff --git a/Benchmarks/CoreModuleAggregator.php b/Benchmarks/CoreModuleAggregator.php index a4489fb..7b8e07a 100644 --- a/Benchmarks/CoreModuleAggregator.php +++ b/Benchmarks/CoreModuleAggregator.php @@ -39,7 +39,11 @@ public function getName(): string $this->translated = true; return $this->name = $this ->getCore() - ?->translate('Event', context: 'benchmark')??$this->name; + ?->translateContext( + 'Event', + 'benchmark', + 'core-module' + )??$this->name; } public function accepted(RecordInterface $record): bool diff --git a/Controllers/.gitkeep b/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Core.php b/Core.php index 8adccaf..81537e4 100644 --- a/Core.php +++ b/Core.php @@ -12,19 +12,17 @@ use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Controllers\Controllers; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\EducationalInstitution\EducationalInstitution; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Library\Library; -use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Media\Media; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Option\Option; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Posts\Posts; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Quiz\Quiz; -use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Roles\Roles; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Scheduler\Scheduler; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\ServiceInitializer; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Templates\Templates; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Translator\Translator; -use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Users\Users; use ArrayAccess\TrayDigita\Benchmark\Aggregator\EventAggregator; use ArrayAccess\TrayDigita\Benchmark\Injector\ManagerProfiler; use ArrayAccess\TrayDigita\Database\Connection; +use ArrayAccess\TrayDigita\L10n\Translations\Adapter\Gettext\PoMoAdapter; use ArrayAccess\TrayDigita\Module\AbstractModule; use ArrayAccess\TrayDigita\Traits\Service\TranslatorTrait; use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper; @@ -61,16 +59,13 @@ final class Core extends AbstractModule Controllers::class, EducationalInstitution::class, Library::class, - Media::class, Option::class, Posts::class, Quiz::class, - Roles::class, Scheduler::class, ServiceInitializer::class, Templates::class, Translator::class, - Users::class, ]; /** @@ -85,17 +80,19 @@ final class Core extends AbstractModule public function getName(): string { - return $this->translate( + return $this->translateContext( 'Core', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Main core module', - context: 'module' + 'module', + 'core-module' ); } @@ -105,6 +102,15 @@ protected function doInit(): void return; } + foreach ($this->getTranslator()?->getAdapters()??[] as $adapter) { + if ($adapter instanceof PoMoAdapter) { + $adapter->registerDirectory( + __DIR__ .'/Languages', + 'core-module' + ); + } + } + $this->didInit = true; $this->doRegisterEntities(); diff --git a/Entities/Admin.php b/Entities/Admin.php deleted file mode 100644 index 0a9e67b..0000000 --- a/Entities/Admin.php +++ /dev/null @@ -1,197 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Administrator users' - ] -)] -#[UniqueConstraint( - name: 'unique_username', - columns: ['username'] -)] -#[UniqueConstraint( - name: 'unique_email', - columns: ['email'] -)] -#[UniqueConstraint( - name: 'unique_identity_number', - columns: ['identity_number'] -)] -#[Index( - columns: ['username', 'status', 'role', 'first_name', 'last_name'], - name: 'index_username_status_role_first_name_last_name' -)] -#[Index( - columns: ['attachment_id'], - name: 'relation_admins_attachment_id_attachments_id' -)] -#[Index( - columns: ['role'], - name: 'relation_admins_role_roles_identity' -)] -#[HasLifecycleCallbacks] -class Admin extends AbstractUser -{ - const TABLE_NAME = 'admins'; - - const ROLE_SUPER_ADMIN = SuperAdminRole::NAME; - // administrator and it was co admin - const ROLE_ADMIN = 'admin'; - const ROLE_PRESIDENT = 'president'; - const ROLE_VICE_PRESIDENT = 'vice_president'; - const ROLE_RECTOR = 'rector'; - const ROLE_VICE_RECTOR = 'vice_rector'; - // dean - const ROLE_DEAN = 'dean'; - const ROLE_VICE_DEAN = 'vice_dean'; - // faculty - const ROLE_HEAD_FACULTY = 'head_faculty'; - const ROLE_VICE_HEAD_FACULTY = 'vice_head_faculty'; - - // department - const ROLE_HEAD_DEPARTMENT = 'head_department'; - const ROLE_VICE_HEAD_DEPARTMENT = 'vice_head_department'; - - // headmaster - const ROLE_HEADMASTER = 'headmaster'; - const ROLE_VICE_HEADMASTER = 'vice_headmaster'; - // hrd - const ROLE_HUMAN_RESOURCE_DEPARTMENT = 'human_resource_department'; - const ROLE_HUMAN_RESOURCE_MANAGEMENT = 'human_resource_management'; - - // lecturer - const ROLE_LECTURER = 'lecturer'; - const ROLE_TEACHER = 'teacher'; - const ROLE_COUNSELING_GUIDANCE = 'counseling_guidance'; - const ROLE_SUPERVISOR = 'supervisor'; - const ROLE_LIBRARIAN = 'librarian'; - // staff - const ROLE_OFFICE_STAFF = 'office_staff'; - // treasurer - const ROLE_TREASURER = 'treasurer'; - // office admin - const ROLE_OFFICE_ADMINISTRATION = 'office_admin'; - // other staff / worker - const ROLE_STAFF = 'staff'; - - protected array $availableRoles = [ - self::ROLE_SUPER_ADMIN, - self::ROLE_ADMIN, - self::ROLE_PRESIDENT, - self::ROLE_VICE_PRESIDENT, - self::ROLE_RECTOR, - self::ROLE_VICE_RECTOR, - self::ROLE_DEAN, - self::ROLE_VICE_DEAN, - self::ROLE_HEAD_FACULTY, - self::ROLE_VICE_HEAD_FACULTY, - self::ROLE_HEAD_DEPARTMENT, - self::ROLE_VICE_HEAD_DEPARTMENT, - self::ROLE_HEADMASTER, - self::ROLE_VICE_HEADMASTER, - self::ROLE_HUMAN_RESOURCE_DEPARTMENT, - self::ROLE_HUMAN_RESOURCE_MANAGEMENT, - self::ROLE_LECTURER, - self::ROLE_TEACHER, - self::ROLE_COUNSELING_GUIDANCE, - self::ROLE_SUPERVISOR, - self::ROLE_LIBRARIAN, - self::ROLE_OFFICE_STAFF, - self::ROLE_TREASURER, - self::ROLE_OFFICE_ADMINISTRATION, - self::ROLE_STAFF, - ]; - - #[ - JoinColumn( - name: 'attachment_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_admins_attachment_id_attachments_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: Attachment::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY' - ) - ] - protected ?Attachment $attachment = null; - - #[ - JoinColumn( - name: 'role', - referencedColumnName: 'identity', - nullable: true, - onDelete: 'RESTRICT', - options: [ - 'relation_name' => 'relation_admins_role_roles_identity', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'RESTRICT' - ], - ), - ManyToOne( - targetEntity: Role::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY' - ) - ] - protected ?Role $roleObject = null; - - public function getObjectRole(): Role - { - if (!$this->roleObject) { - $this->roleObject = new Role(); - $this->roleObject->setIdentity($this->getRole()); - $this->roleObject->setName($this->getRole()); - $entity = $this->getEntityManager(); - $entity && $this->roleObject->setEntityManager($entity); - } - return $this->roleObject; - } - - public function setRoleObject(Role $roleObject): void - { - $this->roleObject = $roleObject; - $this->setRole($roleObject->getIdentity()); - } - - public function getAttachment(): ?Attachment - { - return $this->attachment; - } - - public function setAttachment(?Attachment $attachment): void - { - $this->attachment = $attachment; - $this->setAttachmentId($attachment?->getId()); - } -} diff --git a/Entities/AdminLog.php b/Entities/AdminLog.php deleted file mode 100644 index 618c0f4..0000000 --- a/Entities/AdminLog.php +++ /dev/null @@ -1,79 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Administrator user logs' - ] -)] -#[Index( - columns: ['user_id'], - name: 'relation_admin_logs_user_id_admins_id' -)] -#[Index( - columns: ['user_id', 'name', 'type'], - name: 'index_user_id_name_type' -)] -#[HasLifecycleCallbacks] -class AdminLog extends AbstractUserBasedLog -{ - const TABLE_NAME = 'admin_logs'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_admin_logs_user_id_admins_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ], - ), - ManyToOne( - targetEntity: Admin::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected Admin $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function setUser(Admin $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): Admin - { - return $this->user; - } -} diff --git a/Entities/AdminMeta.php b/Entities/AdminMeta.php deleted file mode 100644 index 3e788c8..0000000 --- a/Entities/AdminMeta.php +++ /dev/null @@ -1,113 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Administrator user metadata', - 'primaryKey' => [ - 'user_id', - 'name' - ] - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[Index( - columns: ['user_id'], - name: 'relation_admin_meta_user_id_admins_id' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $user_id - * @property-read Admin $user - */ -class AdminMeta extends AbstractBasedMeta -{ - const TABLE_NAME = 'admin_meta'; - - #[Id] - #[Column( - name: 'user_id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key composite identifier' - ] - )] - protected int $user_id; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_admin_meta_user_id_admins_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: Admin::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'EAGER' - ) - ] - protected Admin $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function getUserId(): int - { - return $this->user_id; - } - - public function setUserId(int $user_id): void - { - $this->user_id = $user_id; - } - - public function setUser(Admin $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): Admin - { - return $this->user; - } -} diff --git a/Entities/AdminOnlineActivity.php b/Entities/AdminOnlineActivity.php deleted file mode 100644 index 6e0c2a1..0000000 --- a/Entities/AdminOnlineActivity.php +++ /dev/null @@ -1,79 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Administrator user online activity', - ] -)] -#[Index( - columns: ['user_id', 'name', 'created_at', 'updated_at'], - name: 'index_user_id_name_created_at_updated_at' -)] -#[Index( - columns: ['user_id'], - name: 'relation_admin_online_activities_user_id_admins_id' -)] -#[HasLifecycleCallbacks] -class AdminOnlineActivity extends AbstractBasedOnlineActivity -{ - const TABLE_NAME = 'admin_online_activities'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_admin_online_activities_user_id_admins_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - OneToOne( - targetEntity: Admin::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected Admin $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function setUser(Admin $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): Admin - { - return $this->user; - } -} diff --git a/Entities/Announcement.php b/Entities/Announcement.php index a95ee3b..82bc722 100644 --- a/Entities/Announcement.php +++ b/Entities/Announcement.php @@ -5,6 +5,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Announcement\Helper\AnnouncementTarget; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; diff --git a/Entities/Attachment.php b/Entities/Attachment.php deleted file mode 100644 index 4bae50d..0000000 --- a/Entities/Attachment.php +++ /dev/null @@ -1,80 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Attachments created by admin user', - ] -)] -#[UniqueConstraint( - name: 'unique_path_storage_type', - columns: ['path', 'storage_type'] -)] -#[Index( - columns: ['storage_type', 'mime_type'], - name: 'index_storage_type_mime_type' -)] -#[Index( - columns: ['user_id'], - name: 'relation_attachments_user_id_admins_id' -)] -#[Index( - columns: ['name', 'file_name', 'status', 'mime_type', 'storage_type'], - name: 'index_name_file_name_status_mime_type_storage_type' -)] -#[HasLifecycleCallbacks] -class Attachment extends AbstractAttachment -{ - const TABLE_NAME = 'attachments'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_attachments_user_id_admins_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: Admin::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY', - ) - ] - protected ?Admin $user = null; - - public function setUser(?Admin $user): void - { - $this->user = $user; - $this->setUserId($user?->getId()); - } - - public function getUser(): ?Admin - { - return $this->user; - } -} diff --git a/Entities/Book.php b/Entities/Book.php index 69c59c0..8d3d60d 100644 --- a/Entities/Book.php +++ b/Entities/Book.php @@ -4,6 +4,8 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; @@ -568,6 +570,7 @@ public function getReleaseYear(): ?DateTimeInterface public function setReleaseYear(DateTimeInterface|int|null $release_year): void { + /** @noinspection DuplicatedCode */ if (is_int($release_year)) { $release_year = (string) $release_year; if ($release_year < 1000) { diff --git a/Entities/BookAuthor.php b/Entities/BookAuthor.php index 2a91a6c..e36f9aa 100644 --- a/Entities/BookAuthor.php +++ b/Entities/BookAuthor.php @@ -4,6 +4,8 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; diff --git a/Entities/BookCategory.php b/Entities/BookCategory.php index 0c8e7d4..4069dc1 100644 --- a/Entities/BookCategory.php +++ b/Entities/BookCategory.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\ParentIdEventStateTrait; @@ -231,6 +232,7 @@ class BookCategory extends AbstractEntity implements IdentityBasedEntityInterfac ] public function preCheckSlug(PrePersistEventArgs|PreUpdateEventArgs $event): void { + /** @noinspection DuplicatedCode */ $oldSlug = null; $slug = $this->getSlug(); diff --git a/Entities/BookPublisher.php b/Entities/BookPublisher.php index 95a318e..13466b4 100644 --- a/Entities/BookPublisher.php +++ b/Entities/BookPublisher.php @@ -4,6 +4,8 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; use DateTimeImmutable; diff --git a/Entities/Capability.php b/Entities/Capability.php deleted file mode 100644 index 8464721..0000000 --- a/Entities/Capability.php +++ /dev/null @@ -1,229 +0,0 @@ - $roleCapability - */ -#[Entity] -#[Table( - name: self::TABLE_NAME, - options: [ - 'charset' => 'utf8mb4', // remove this or change to utf8 if not use mysql - 'collation' => 'utf8mb4_unicode_ci', // remove this if not use mysql - 'comment' => 'Capabilities' - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[Index( - columns: ['type'], - name: 'index_type' -)] -#[HasLifecycleCallbacks] -class Capability extends AbstractEntity implements CapabilityEntityInterface -{ - const TABLE_NAME = 'capabilities'; - const TYPE_USER = 'user'; - const TYPE_ADMIN = 'admin'; - - const TYPE_GLOBAL = null; - const TYPE_GLOBAL_ALTERNATE = 'global'; - - #[Id] - #[Column( - name: 'identity', - type: Types::STRING, - length: 128, - updatable: true, - options: [ - 'comment' => 'Primary key capability identity' - ] - )] - protected string $identity; - - #[Column( - name: 'name', - type: Types::STRING, - length: 255, - options: [ - 'comment' => 'Capability name' - ] - )] - protected string $name; - - #[Column( - name: 'description', - type: Types::TEXT, - length: AbstractMySQLPlatform::LENGTH_LIMIT_TEXT, - nullable: true, - options: [ - 'comment' => 'Capability description' - ] - )] - protected ?string $description = null; - - #[Column( - name: 'type', - type: Types::STRING, - length: 20, - nullable: true, - options: [ - 'default' => self::TYPE_GLOBAL, - 'comment' => 'Capability type. user -> as users, admin -> as admins user & null/empty as global' - ] - )] - protected ?string $type = null; - - #[OneToMany( - mappedBy: 'capability', - targetEntity: RoleCapability::class, - cascade: [ - 'detach', - 'merge', - 'persist', - 'remove', - ], - fetch: 'LAZY' - )] - /** - * @var ?Collection $roleCapability - */ - protected ?Collection $roleCapability = null; - - public function getIdentity(): string - { - return $this->identity; - } - - public function setIdentity(string $identity): void - { - $this->identity = $identity; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function getDescription(): ?string - { - return $this->description; - } - - public function setDescription(?string $description): void - { - $this->description = $description; - } - - public function getType(): ?string - { - return $this->type; - } - - public function setType(?string $type): void - { - $type = is_string($type) ? strtolower(trim($type)) : $type; - $this->type = $type; - } - - public function isGlobal() : bool - { - $type = $this->getType(); - $type = is_string($type) ? strtolower(trim($type)) : $type; - return $type === '' || $type === null || $type === self::TYPE_GLOBAL; - } - - public function isUser() : bool - { - $type = $this->getType(); - return (is_string($type) ? trim($type) : $type) === self::TYPE_USER; - } - - public function isAdmin() : bool - { - $type = $this->getType(); - return (is_string($type) ? trim($type) : $type) === self::TYPE_ADMIN; - } - - /** - * @return ?Collection - */ - public function getRoleCapability(): ?Collection - { - return $this->roleCapability; - } - - /** @noinspection PhpUnusedParameterInspection */ - #[ - PostLoad, - PrePersist - ] - public function postLoadChangeIdentity( - PrePersistEventArgs|PostLoadEventArgs $event - ) : void { - $this->identity = strtolower(trim($this->identity)); - $this->identity = preg_replace('~[\s_]+~', '_', $this->identity); - $this->identity = trim($this->identity, '_'); - if ($this->identity === '') { - throw new EmptyArgumentException( - 'Identity could not being empty or contain whitespace only' - ); - } - } - - public function has(RoleInterface|string $role) : bool - { - $role = is_object($role) ? $role->getRole() : $role; - return $this - ->getRoleCapability() - ->exists(static fn ($i, RoleCapability $r) => $r->getRoleIdentity() === $role); - } - - /** - * @return iterable - */ - public function getRoles(): iterable - { - return IterableHelper::each( - $this->getRoleCapability(), - static function (&$key, RoleCapability $r) { - $key = $r->getRoleIdentity(); - return $r->getRole(); - } - ); - } -} diff --git a/Entities/Classes.php b/Entities/Classes.php index df2d266..d42d88a 100644 --- a/Entities/Classes.php +++ b/Entities/Classes.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; diff --git a/Entities/Department.php b/Entities/Department.php index d01fdba..0a6ecaa 100644 --- a/Entities/Department.php +++ b/Entities/Department.php @@ -4,6 +4,8 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; diff --git a/Entities/Faculty.php b/Entities/Faculty.php index b5a24ea..f301f54 100644 --- a/Entities/Faculty.php +++ b/Entities/Faculty.php @@ -4,6 +4,8 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; diff --git a/Entities/Post.php b/Entities/Post.php index a79c8da..49033d7 100644 --- a/Entities/Post.php +++ b/Entities/Post.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; @@ -387,6 +388,7 @@ public function __construct() ] public function preCheckSlug(PrePersistEventArgs|PreUpdateEventArgs $event): void { + /** @noinspection DuplicatedCode */ $oldSlug = null; $slug = $this->getSlug(); $isUpdate = $event instanceof PreUpdateEventArgs; diff --git a/Entities/PostCategory.php b/Entities/PostCategory.php index d1dfd8d..92b8d72 100644 --- a/Entities/PostCategory.php +++ b/Entities/PostCategory.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Traits\ParentIdEventStateTrait; use ArrayAccess\TrayDigita\Util\Generator\UUID; @@ -230,6 +231,7 @@ public function __construct() ] public function preCheckSlug(PrePersistEventArgs|PreUpdateEventArgs $event): void { + /** @noinspection DuplicatedCode */ $oldSlug = null; $slug = $this->getSlug(); $isUpdate = $event instanceof PreUpdateEventArgs; diff --git a/Entities/Question.php b/Entities/Question.php index 666bc0c..14505e5 100644 --- a/Entities/Question.php +++ b/Entities/Question.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; diff --git a/Entities/QuestionCategory.php b/Entities/QuestionCategory.php index 13ed00f..17c19c6 100644 --- a/Entities/QuestionCategory.php +++ b/Entities/QuestionCategory.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Traits\ParentIdEventStateTrait; use DateTimeImmutable; diff --git a/Entities/Quiz.php b/Entities/Quiz.php index 4c82eb0..37922cc 100644 --- a/Entities/Quiz.php +++ b/Entities/Quiz.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Traits\AvailabilityStatusTrait; diff --git a/Entities/Role.php b/Entities/Role.php deleted file mode 100644 index f968efc..0000000 --- a/Entities/Role.php +++ /dev/null @@ -1,194 +0,0 @@ - 'utf8mb4', // remove this or change to utf8 if not use mysql - 'collation' => 'utf8mb4_unicode_ci', // remove this if not use mysql - 'comment' => 'Table roles' - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[HasLifecycleCallbacks] -/** - * @property-read string $identity - * @property-read string $name - * @property-read ?string $description - */ -class Role extends AbstractEntity implements RoleInterface -{ - const TABLE_NAME = 'roles'; - - private ?string $originIdentity = null; - - #[Id] - #[Column( - name: 'identity', - type: Types::STRING, - length: 128, - updatable: true, - options: [ - 'comment' => 'Primary key role identity' - ] - )] - protected string $identity; - - #[Column( - name: 'name', - type: Types::STRING, - length: 255, - options: [ - 'comment' => 'Role name' - ] - )] - protected string $name; - - #[Column( - name: 'description', - type: Types::TEXT, - length: AbstractMySQLPlatform::LENGTH_LIMIT_TEXT, - nullable: true, - options: [ - 'comment' => 'Role description' - ] - )] - protected ?string $description = null; - - #[OneToMany( - mappedBy: 'role', - targetEntity: RoleCapability::class, - cascade: [ - 'detach', - 'merge', - 'persist', - 'remove', - ], - fetch: 'LAZY' - )] - protected ?Collection $roleCapability = null; - - public function getIdentity(): string - { - return $this->identity; - } - - public function setIdentity(string $identity): void - { - $identity = strtolower(trim($identity)); - if ($identity === '') { - throw new EmptyArgumentException( - 'Identity could not being empty or contain whitespace only' - ); - } - $this->identity = $identity; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function getDescription(): ?string - { - return $this->description; - } - - public function setDescription(?string $description): void - { - $this->description = $description; - } - - public function getRole(): string - { - return $this->getIdentity(); - } - - public function getRoleCapability(): ?Collection - { - return $this->roleCapability; - } - - #[ - PreUpdate, - PrePersist - ] - public function postCheckChangeIdentity() : void - { - $this->identity = strtolower(trim($this->identity)); - if ($this->identity === '') { - throw new EmptyArgumentException( - 'Identity could not being empty or contain whitespace only' - ); - } - } - - /** @noinspection PhpUnusedParameterInspection */ - #[PostLoad] - public function postLoadIdentityLower(PostLoadEventArgs $eventArgs): void - { - $this->originIdentity ??= $this->identity; - $this->identity = strtolower(trim($this->originIdentity)); - } - - public function serialize(): ?string - { - return serialize($this->__serialize()); - } - - public function unserialize(string $data): void - { - $this->unserialize(unserialize($data)); - } - - public function __serialize(): array - { - return [ - 'identity' => $this->identity, - 'name' => $this->name, - 'description' => $this->description, - ]; - } - - public function __unserialize(array $data): void - { - $this->identity = $data['identity']; - $this->name = $data['name']; - $this->description = $data['description']; - } - - public function __toString(): string - { - return $this->getIdentity(); - } -} diff --git a/Entities/RoleCapability.php b/Entities/RoleCapability.php deleted file mode 100644 index 2bae237..0000000 --- a/Entities/RoleCapability.php +++ /dev/null @@ -1,159 +0,0 @@ - 'utf8mb4', // remove this or change to utf8 if not use mysql - 'collation' => 'utf8mb4_unicode_ci', // remove this if not use mysql - 'comment' => 'Role relation capability & meta', - 'primaryKey' => [ - 'class_id', - 'name' - ] - ] -)] -#[Index( - columns: ['role_identity'], - name: 'relation_capabilities_role_identity_roles_identity' -)] -#[Index( - columns: ['capability_identity'], - name: 'relation_capabilities_capability_identity_capabilities_identity' -)] -#[HasLifecycleCallbacks] -class RoleCapability extends AbstractEntity -{ - const TABLE_NAME = 'role_capabilities'; - - #[Id] - #[Column( - name: 'role_identity', - type: Types::STRING, - length: 128, - updatable: true, - options: [ - 'comment' => 'Primary key composite identifier' - ] - )] - protected string $role_identity; - - #[Id] - #[Column( - name: 'capability_identity', - type: Types::STRING, - length: 128, - updatable: true, - options: [ - 'comment' => 'Primary key composite identifier' - ] - )] - protected string $capability_identity; - - #[ - JoinColumn( - name: 'role_identity', - referencedColumnName: 'identity', - nullable: false, - onDelete: 'RESTRICT', - options: [ - 'relation_name' => 'relation_capabilities_role_identity_roles_identity', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'RESTRICT' - ] - ), - ManyToOne( - targetEntity: Role::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected Role $role; - - #[ - JoinColumn( - name: 'capability_identity', - referencedColumnName: 'identity', - nullable: false, - onDelete: 'RESTRICT', - options: [ - 'relation_name' => 'relation_capabilities_capability_identity_capabilities_identity', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'RESTRICT' - ] - ), - ManyToOne( - targetEntity: Capability::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected Capability $capability; - - public function getRoleIdentity(): string - { - return $this->role_identity; - } - - public function setRoleIdentity(string $role_identity): void - { - $this->role_identity = $role_identity; - } - - public function getCapabilityIdentity(): string - { - return $this->capability_identity; - } - - public function setCapabilityIdentity(string $capability_identity): void - { - $this->capability_identity = $capability_identity; - } - - public function getRole(): Role - { - return $this->role; - } - - public function setRole(Role $role): void - { - $this->role = $role; - $this->setRoleIdentity($role->getIdentity()); - } - - public function getCapability(): Capability - { - return $this->capability; - } - - public function setCapability(Capability $capability): void - { - $this->capability = $capability; - $this->setCapabilityIdentity($capability->getIdentity()); - } -} diff --git a/Entities/Site.php b/Entities/Site.php index 2d58c95..b5954e7 100644 --- a/Entities/Site.php +++ b/Entities/Site.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\Entities; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; use ArrayAccess\TrayDigita\Database\Entities\Abstracts\AbstractEntity; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\AvailabilityStatusEntityInterface; use ArrayAccess\TrayDigita\Database\Entities\Interfaces\IdentityBasedEntityInterface; diff --git a/Entities/User.php b/Entities/User.php deleted file mode 100644 index 24906e5..0000000 --- a/Entities/User.php +++ /dev/null @@ -1,386 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'User lists', - ] -)] -#[UniqueConstraint( - name: 'unique_username', - columns: ['username'] -)] -#[UniqueConstraint( - name: 'unique_email', - columns: ['email'] -)] -#[UniqueConstraint( - name: 'unique_identity_number', - columns: ['identity_number'] -)] -#[Index( - columns: ['school_year', 'status'], - name: 'index_school_year_status' -)] -#[Index( - columns: ['class_id', 'role'], - name: 'index_class_id_role' -)] -#[Index( - columns: ['username', 'status', 'role', 'first_name', 'last_name', 'school_year'], - name: 'index_username_status_role_first_name_last_name_school_year' -)] -#[Index( - columns: ['class_id'], - name: 'relation_users_class_id_classes_id' -)] -#[Index( - columns: ['related_user_id'], - name: 'relation_users_related_user_id_users_id' -)] -#[Index( - columns: ['attachment_id'], - name: 'relation_users_attachment_id_user_attachments_id' -)] -#[Index( - columns: ['role'], - name: 'relation_users_role_roles_identity' -)] -#[HasLifecycleCallbacks] -class User extends AbstractUser -{ - const TABLE_NAME = 'users'; - const ROLE_STUDENT = 'student'; - const ROLE_ALUMNI = 'alumni'; - const ROLE_GUARDIAN = 'guardian'; - const ROLE_GUEST = 'guest'; - - protected array $availableRoles = [ - self::ROLE_STUDENT, - self::ROLE_ALUMNI, - self::ROLE_GUARDIAN, - self::ROLE_GUEST, - ]; - - #[Column( - name: 'class_id', - type: Types::BIGINT, - length: 20, - nullable: true, - options: [ - 'unsigned' => true, - 'default' => null, - 'comment' => 'Class id' - ] - )] - protected ?int $class_id = null; - - #[Column( - name: 'related_user_id', - type: Types::BIGINT, - length: 20, - nullable: true, - options: [ - 'unsigned' => true, - 'default' => null, - 'comment' => 'Relational user id' - ] - )] - protected ?int $related_user_id = null; - - #[Column( - name: 'school_year', - type: TypeList::YEAR, - nullable: false, - options: [ - 'comment' => 'User school year' - ] - )] - protected DateTimeInterface $school_year; - - #[ - JoinColumn( - name: 'class_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_users_class_id_classes_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: Classes::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'EAGER' - ) - ] - protected ?Classes $class; - - #[ - JoinColumn( - name: 'related_user_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_users_related_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: User::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY' - ) - ] - protected ?User $related_user = null; - - #[ - JoinColumn( - name: 'attachment_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_users_attachment_id_user_attachments_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: UserAttachment::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY' - ) - ] - protected ?UserAttachment $attachment = null; - - #[ - JoinColumn( - name: 'role', - referencedColumnName: 'identity', - nullable: true, - onDelete: 'RESTRICT', - options: [ - 'relation_name' => 'relation_users_role_roles_identity', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'RESTRICT' - ], - ), - ManyToOne( - targetEntity: Role::class, - cascade: [ - 'persist' - ], - fetch: 'EAGER' - ) - ] - protected ?Role $roleObject = null; - - public function getSchoolYear(): DateTimeInterface - { - return $this->school_year; - } - - public function setSchoolYear(DateTimeInterface|int|null $school_year): void - { - if (is_int($school_year)) { - $school_year = (string) $school_year; - if ($school_year < 1000) { - do { - $school_year = "0$school_year"; - } while (strlen($school_year) < 4); - } - $school_year = substr($school_year, 0, 4); - $school_year = DateTimeImmutable::createFromFormat( - '!Y-m-d', - "$school_year-01-01" - )?:new DateTimeImmutable("$school_year-01-01 00:00:00"); - } - - $this->school_year = $school_year; - } - - public function getClassId(): ?int - { - return $this->class_id; - } - - public function setClassId(?int $class_id): void - { - $this->class_id = $class_id; - } - - public function getClass(): ?Classes - { - return $this->class; - } - - public function setClass(?Classes $class): void - { - $this->class = $class; - $this->setClassId($class?->getId()); - } - - public function getObjectRole(): Role - { - if (!$this->roleObject) { - $this->roleObject = new Role(); - $this->roleObject->setIdentity($this->getRole()); - $this->roleObject->setName($this->getRole()); - $entity = $this->getEntityManager(); - $entity && $this->roleObject->setEntityManager($entity); - } - return $this->roleObject; - } - - public function setRoleObject(Role $roleObject): void - { - $this->roleObject = $roleObject; - $this->setRole($roleObject->getIdentity()); - } - - public function getRelatedUserId(): ?int - { - return $this->related_user_id; - } - - public function setRelatedUserId(?int $related_user_id): void - { - $this->related_user_id = $related_user_id; - } - - public function getRelatedUser(): ?User - { - return $this->related_user; - } - - public function setRelatedUser(?User $related_user): void - { - $this->related_user = $related_user; - $this->setRelatedUserId($related_user?->getId()); - } - - public function getAttachment(): ?UserAttachment - { - return $this->attachment; - } - - public function setAttachment(?UserAttachment $attachment): void - { - $this->attachment = $attachment; - $this->setAttachmentId($attachment?->getId()); - } - - #[ - PreUpdate, - PostLoad, - PrePersist - ] - public function relationIdCheck( - PrePersistEventArgs|PostLoadEventArgs|PreUpdateEventArgs $event - ) : void { - if ($event instanceof PreUpdateEventArgs - && $event->hasChangedField('related_user_id') - && $event->getNewValue('related_user_id') === $this->getId() - ) { - $oldValue = $event->getOldValue('related_user_id'); - if ($oldValue !== null) { - /** - * @var self $parent - */ - $parent = $event - ->getObjectManager() - ->getRepository($this::class) - ->find($this->getId()) - ?->getRelatedUser(); - if ($parent?->getId() === $parent?->getRelatedUserId()) { - $parent = null; - $oldValue = null; - } - } - $this->setRelatedUser($parent??null); - $this->setRelatedUserId($oldValue); - $event->setNewValue('related_user_id', $oldValue); - } elseif (!$event instanceof PreUpdateEventArgs - && $this->getRelatedUserId() === $this->getId() - ) { - $parent = $event - ->getObjectManager() - ->getRepository($this::class) - ->find($this->getId()) - ?->getRelatedUser(); - if ($parent?->getId() === $parent?->getRelatedUserId()) { - $parent = null; - } - // prevent - $this->setRelatedUserId($parent?->getId()); - $this->setRelatedUser($parent); - $q = $event - ->getObjectManager() - ->createQueryBuilder() - ->update($this::class, 'x') - ->set('x.related_user_id', ':val') - ->where('x.id = :id') - ->setParameters([ - 'val' => null, - 'id' => $this->getId(), - ]); - $date = $this->getUpdatedAt(); - /** @noinspection PhpConditionAlreadyCheckedInspection */ - if ($date instanceof DateTimeInterface) { - $date = str_starts_with($date->format('Y'), '-') - ? '0000-00-00 00:00:00' - : $date->format('Y-m-d H:i:s'); - } - $q - ->set('x.updated_at', ':updated_at') - ->setParameter('updated_at', $date); - $q->getQuery()->execute(); - } - } -} diff --git a/Entities/UserAttachment.php b/Entities/UserAttachment.php deleted file mode 100644 index aac6ab2..0000000 --- a/Entities/UserAttachment.php +++ /dev/null @@ -1,80 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Attachments created by common user', - ] -)] -#[UniqueConstraint( - name: 'unique_path_storage_type', - columns: ['path', 'storage_type'] -)] -#[Index( - columns: ['storage_type', 'mime_type'], - name: 'index_storage_type_mime_type' -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_attachments_user_id_users_id' -)] -#[Index( - columns: ['name', 'file_name', 'status', 'mime_type', 'storage_type'], - name: 'index_name_file_name_status_mime_type_storage_type' -)] -#[HasLifecycleCallbacks] -class UserAttachment extends AbstractAttachment -{ - const TABLE_NAME = 'user_attachments'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_user_attachments_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: User::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY', - ) - ] - protected ?User $user = null; - - public function setUser(?User $user): void - { - $this->user = $user; - $this->setUserId($user?->getId()); - } - - public function getUser(): ?User - { - return $this->user; - } -} diff --git a/Entities/UserLog.php b/Entities/UserLog.php deleted file mode 100644 index 97bfc3d..0000000 --- a/Entities/UserLog.php +++ /dev/null @@ -1,79 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Common user logs', - ], -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_logs_user_id_users_id' -)] -#[Index( - columns: ['user_id', 'name', 'type'], - name: 'index_user_id_name_type' -)] -#[HasLifecycleCallbacks] -class UserLog extends AbstractUserBasedLog -{ - const TABLE_NAME = 'user_logs'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_logs_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: User::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected User $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function setUser(User $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): User - { - return $this->user; - } -} diff --git a/Entities/UserMeta.php b/Entities/UserMeta.php deleted file mode 100644 index 21ad30f..0000000 --- a/Entities/UserMeta.php +++ /dev/null @@ -1,113 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Common user metadata', - 'primaryKey' => [ - 'user_id', - 'name' - ] - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_meta_user_id_users_id' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $user_id - * @property-read User $user - */ -class UserMeta extends AbstractBasedMeta -{ - const TABLE_NAME = 'user_meta'; - - #[Id] - #[Column( - name: 'user_id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key composite identifier' - ] - )] - protected int $user_id; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_meta_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: User::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'EAGER' - ) - ] - protected User $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function getUserId(): int - { - return $this->user_id; - } - - public function setUserId(int $user_id): void - { - $this->user_id = $user_id; - } - - public function setUser(User $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): User - { - return $this->user; - } -} diff --git a/Entities/UserOnlineActivity.php b/Entities/UserOnlineActivity.php deleted file mode 100644 index 1915aaa..0000000 --- a/Entities/UserOnlineActivity.php +++ /dev/null @@ -1,77 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Common user online activity', - ] -)] -#[Index( - columns: ['user_id', 'name', 'created_at', 'updated_at'], - name: 'index_user_id_name_created_at_updated_at' -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_online_activities_user_id_users_id' -)] -class UserOnlineActivity extends AbstractBasedOnlineActivity -{ - const TABLE_NAME = 'user_online_activities'; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_online_activities_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - OneToOne( - targetEntity: User::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'LAZY' - ) - ] - protected User $user; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function setUser(User $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getUser(): User - { - return $this->user; - } -} diff --git a/Entities/UserTerm.php b/Entities/UserTerm.php deleted file mode 100644 index 4a5a58d..0000000 --- a/Entities/UserTerm.php +++ /dev/null @@ -1,358 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'User terms grouping metadata like quiz sorting', - ] -)] -#[UniqueConstraint( - name: 'unique_name', - columns: ['name'] -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_terms_user_id_admins_id' -)] -#[Index( - columns: ['name', 'title', 'status'], - name: 'index_name_title_status' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $id - * @property-read ?int $user_id - * @property-read string $name - * @property-read ?string $title - * @property-read string $status - * @property-read ?string $password - * @property-read ?DateTimeInterface $published_at - * @property-read DateTimeInterface $created_at - * @property-read DateTimeInterface $updated_at - * @property-read ?DateTimeInterface $deleted_at - */ -class UserTerm extends AbstractEntity implements AvailabilityStatusEntityInterface -{ - const TABLE_NAME = 'user_terms'; - - use AvailabilityStatusTrait, - PasswordTrait; - - #[Id] - #[GeneratedValue('AUTO')] - #[Column( - name: 'id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key' - ] - )] - protected int $id; - - #[Column( - name: 'user_id', - type: Types::BIGINT, - length: 20, - nullable: true, - options: [ - 'unsigned' => true, - 'default' => null, - 'comment' => 'Admin id' - ] - )] - protected ?int $user_id = null; - - #[Column( - name: 'name', - type: Types::STRING, - length: 255, - unique: true, - nullable: false, - options: [ - 'comment' => 'Unique name' - ] - )] - protected string $name; - - #[Column( - name: 'title', - type: Types::STRING, - length: 255, - nullable: true, - options: [ - 'default' => null, - 'comment' => 'Term title' - ] - )] - protected ?string $title; - - #[Column( - name: 'content', - type: Types::TEXT, - length: 4294967295, - nullable: true, - options: [ - 'default' => null, - 'comment' => 'Term content' - ] - )] - protected ?string $content = null; - - #[Column( - name: 'status', - type: Types::STRING, - length: 64, - nullable: false, - options: [ - 'default' => self::DRAFT, - 'comment' => 'Term status' - ] - )] - protected string $status = self::DRAFT; - - #[Column( - name: 'password', - type: Types::STRING, - length: 255, - nullable: true, - updatable: true, - options: [ - 'default' => null, - 'comment' => 'Term password' - ] - )] - protected ?string $password = null; - - #[Column( - name: 'published_at', - type: Types::DATETIME_MUTABLE, - nullable: true, - options: [ - 'default' => null, - 'comment' => 'Published at' - ] - )] - protected ?DateTimeInterface $published_at; - - #[Column( - name: 'created_at', - type: Types::DATETIME_MUTABLE, - updatable: false, - options: [ - 'default' => 'CURRENT_TIMESTAMP', - 'comment' => 'User term created time' - ] - )] - protected DateTimeInterface $created_at; - - #[Column( - name: 'updated_at', - type: Types::DATETIME_IMMUTABLE, - unique: false, - updatable: false, - options: [ - 'attribute' => 'ON UPDATE CURRENT_TIMESTAMP', // this column attribute - 'default' => '0000-00-00 00:00:00', - 'comment' => 'User term update time' - ], - // columnDefinition: "DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP" - )] - protected DateTimeInterface $updated_at; - - #[Column( - name: 'deleted_at', - type: Types::DATETIME_IMMUTABLE, - nullable: true, - options: [ - 'default' => null, - 'comment' => 'User term delete time' - ] - )] - protected ?DateTimeInterface $deleted_at = null; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'SET NULL', - options: [ - 'relation_name' => 'relation_user_terms_user_id_admins_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'SET NULL' - ], - ), - ManyToOne( - targetEntity: Admin::class, - cascade: [ - 'persist' - ], - fetch: 'LAZY' - ) - ] - protected ?Admin $user = null; - - public function __construct() - { - $this->user_id = null; - $this->title = null; - $this->content = ''; - $this->user = null; - $this->status = self::DRAFT; - $this->password = null; - $this->created_at = new DateTimeImmutable(); - $this->updated_at = new DateTimeImmutable('0000-00-00 00:00:00'); - $this->published_at = null; - $this->deleted_at = null; - } - - public function getId() : int - { - return $this->id; - } - - public function getUserId(): ?int - { - return $this->user_id; - } - - public function setUserId(?int $user_id): void - { - $this->user_id = $user_id; - } - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function getTitle(): string - { - return $this->title; - } - - public function setTitle(string $title): void - { - $this->title = $title; - } - - public function getContent(): ?string - { - return $this->content; - } - - public function setContent(?string $content): void - { - $this->content = $content; - } - - public function getStatus(): string - { - return $this->status; - } - - public function setStatus(string $status): void - { - $this->status = $status; - } - - public function getPassword(): ?string - { - return $this->password; - } - - public function setPassword(?string $password): void - { - $this->password = $password; - } - - public function getPublishedAt(): ?DateTimeInterface - { - return $this->published_at; - } - - public function setPublishedAt(?DateTimeInterface $published_at): void - { - $this->published_at = $published_at; - } - - public function getCreatedAt(): DateTimeInterface - { - return $this->created_at; - } - - public function getUpdatedAt(): DateTimeInterface - { - return $this->updated_at; - } - - public function getDeletedAt(): ?DateTimeInterface - { - return $this->deleted_at; - } - - public function setDeletedAt(?DateTimeInterface $deletedAt) : void - { - $this->deleted_at = $deletedAt; - } - - public function getUser(): ?Admin - { - return $this->user; - } - - public function setUser(?Admin $user): void - { - $this->user = $user; - $this->setUserId($user?->getId()); - } - - #[ - PreUpdate, - PostLoad, - PrePersist - ] - public function passwordCheck( - PrePersistEventArgs|PostLoadEventArgs|PreUpdateEventArgs $event - ) : void { - $this->passwordBasedIdUpdatedAt($event); - } -} diff --git a/Entities/UserTermGroup.php b/Entities/UserTermGroup.php deleted file mode 100644 index 48cb78e..0000000 --- a/Entities/UserTermGroup.php +++ /dev/null @@ -1,209 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'Group terms user collection', - ] -)] -#[UniqueConstraint( - name: 'unique_user_id_term_id', - columns: ['user_id', 'term_id'] -)] -#[Index( - columns: ['term_id'], - name: 'relation_user_term_groups_term_id_user_terms_id' -)] -#[Index( - columns: ['user_id'], - name: 'relation_user_term_groups_user_id_users_id' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $id - * @property-read int $user_id - * @property-read int $term_id - * @property-read string $status - * @property-read UserTerm $term - */ -class UserTermGroup extends AbstractEntity implements AvailabilityStatusEntityInterface -{ - const TABLE_NAME = 'user_term_groups'; - - use AvailabilityStatusTrait; - - #[Id] - #[GeneratedValue('AUTO')] - #[Column( - name: 'id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key term group id' - ] - )] - protected int $id; - - #[Column( - name: 'user_id', - type: Types::BIGINT, - length: 20, - nullable: false, - options: [ - 'unsigned' => true, - 'comment' => 'User id' - ] - )] - protected int $user_id; - - #[Column( - name: 'term_id', - type: Types::BIGINT, - length: 20, - nullable: false, - options: [ - 'unsigned' => true, - 'comment' => 'User term id' - ] - )] - protected int $term_id; - - #[Column( - name: 'status', - type: Types::STRING, - length: 64, - nullable: false, - options: [ - 'comment' => 'Term group status' - ] - )] - protected string $status; - - #[ - JoinColumn( - name: 'user_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_term_groups_user_id_users_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ], - ), - ManyToOne( - targetEntity: User::class, - cascade: [ - 'persist' - ], - fetch: 'EAGER' - ) - ] - protected User $user; - - #[ - JoinColumn( - name: 'term_id', - referencedColumnName: 'id', - nullable: true, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_term_groups_term_id_user_terms_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ], - ), - ManyToOne( - targetEntity: UserTerm::class, - cascade: [ - 'persist' - ], - fetch: 'EAGER' - ) - ] - protected UserTerm $term; - - public function __construct() - { - } - - public function getId() : int - { - return $this->id; - } - - public function getUserId(): int - { - return $this->user_id; - } - - public function setUserId(int $user_id): void - { - $this->user_id = $user_id; - } - - public function getTermId(): int - { - return $this->term_id; - } - - public function setTermId(?int $term_id): void - { - $this->term_id = $term_id; - } - - public function getStatus(): string - { - return $this->status; - } - - public function setStatus(string $status): void - { - $this->status = $status; - } - - public function getUser(): User - { - return $this->user; - } - - public function setUser(User $user): void - { - $this->user = $user; - $this->setUserId($user->getId()); - } - - public function getTerm(): UserTerm - { - return $this->term; - } - - public function setTerm(UserTerm $term): void - { - $this->term = $term; - $this->setTermId($term->getId()); - } -} diff --git a/Entities/UserTermGroupMeta.php b/Entities/UserTermGroupMeta.php deleted file mode 100644 index 659475c..0000000 --- a/Entities/UserTermGroupMeta.php +++ /dev/null @@ -1,113 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'User term group metadata', - 'primaryKey' => [ - 'post_id', - 'name' - ] - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[Index( - columns: ['term_group_id'], - name: 'relation_user_term_group_meta_term_group_id_user_terms_group_id' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $term_group_id - * @property-read UserTermGroup $userTermGroup - */ -class UserTermGroupMeta extends AbstractBasedMeta -{ - const TABLE_NAME = 'user_term_group_meta'; - - #[Id] - #[Column( - name: 'term_group_id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key composite identifier' - ] - )] - protected int $term_group_id; - - #[ - JoinColumn( - name: 'term_group_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_term_group_meta_term_group_id_user_terms_group_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: UserTermGroup::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'EAGER' - ) - ] - protected UserTermGroup $userTermGroup; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function getTermGroupId(): int - { - return $this->term_group_id; - } - - public function setTermGroupId(int $term_group_id): void - { - $this->term_group_id = $term_group_id; - } - - public function getUserTermGroup(): UserTermGroup - { - return $this->userTermGroup; - } - - public function setUserTermGroup(UserTermGroup $userTermGroup): void - { - $this->userTermGroup = $userTermGroup; - $this->setTermGroupId($userTermGroup->getId()); - } -} diff --git a/Entities/UserTermMeta.php b/Entities/UserTermMeta.php deleted file mode 100644 index f2885eb..0000000 --- a/Entities/UserTermMeta.php +++ /dev/null @@ -1,113 +0,0 @@ - 'utf8mb4', - 'collation' => 'utf8mb4_unicode_ci', - 'comment' => 'User term metadata', - 'primaryKey' => [ - 'post_id', - 'name' - ] - ] -)] -#[Index( - columns: ['name'], - name: 'index_name' -)] -#[Index( - columns: ['term_id'], - name: 'relation_user_term_metadata_term_id_user_terms_id' -)] -#[HasLifecycleCallbacks] -/** - * @property-read int $term_id - * @property-read UserTerm $userTerm - */ -class UserTermMeta extends AbstractBasedMeta -{ - const TABLE_NAME = 'user_term_meta'; - - #[Id] - #[Column( - name: 'term_id', - type: Types::BIGINT, - length: 20, - updatable: false, - options: [ - 'unsigned' => true, - 'comment' => 'Primary key composite identifier' - ] - )] - protected int $term_id; - - #[ - JoinColumn( - name: 'term_id', - referencedColumnName: 'id', - nullable: false, - onDelete: 'CASCADE', - options: [ - 'relation_name' => 'relation_user_term_metadata_term_id_user_terms_id', - 'onUpdate' => 'CASCADE', - 'onDelete' => 'CASCADE' - ] - ), - ManyToOne( - targetEntity: UserTerm::class, - cascade: [ - "persist", - "remove", - "merge", - "detach" - ], - fetch: 'EAGER' - ) - ] - protected UserTerm $userTerm; - - /** - * Allow associations mapping - * @see jsonSerialize() - * - * @var bool - */ - protected bool $entityAllowAssociations = true; - - public function getTermId(): int - { - return $this->term_id; - } - - public function setTermId(int $term_id): void - { - $this->term_id = $term_id; - } - - public function getUserTerm(): UserTerm - { - return $this->userTerm; - } - - public function setUserTerm(UserTerm $userTerm): void - { - $this->userTerm = $userTerm; - $this->setTermId($userTerm->getId()); - } -} diff --git a/Languages/core-module.pot b/Languages/core-module.pot new file mode 100644 index 0000000..189b5dc --- /dev/null +++ b/Languages/core-module.pot @@ -0,0 +1,29 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: TrayDigita Core Module 1.0.0\n" +"POT-Creation-Date: 2023-10-16 00:01+0700\n" +"PO-Revision-Date: 2023-09-24 19:00+0700\n" +"Last-Translator: ArrayAccess\n" +"Language-Team: ArrayAccess\n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.4\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: translate:1,3c;__;translate;trans;" +"translatePlural:1,2,5c;translatePlural:1,2;transN:1,2;" +"_n:1,2;translateContext:1,2c;transX:1,2c;_x:1,2c;" +"translatePluralContext:1,2,4c;transNX:1,2,4c;_nx:1,2,4c\n" +"X-Poedit-SearchPath-0: Abstracts\n" +"X-Poedit-SearchPath-1: Benchmarks\n" +"X-Poedit-SearchPath-2: Controllers\n" +"X-Poedit-SearchPath-3: Entities\n" +"X-Poedit-SearchPath-4: SubModules\n" +"X-Poedit-SearchPath-5: Core.php\n" +"X-Poedit-SearchPathExcluded-0: *.css\n" +"X-Poedit-SearchPathExcluded-1: *.html\n" +"X-Poedit-SearchPathExcluded-2: *.json\n" diff --git a/README.md b/README.md index 7e177fb..d6b6c8a 100644 --- a/README.md +++ b/README.md @@ -8,24 +8,26 @@ Directory name of core module is : `Core` > Sub Modules -- [Announcement](SubModules%2FAnnouncement) -- [Api](SubModules%2FApi) -- [Assets](SubModules%2FAssets) -- [Controllers](SubModules%2FControllers) -- [EducationalInstitution](SubModules%2FEducationalInstitution) -- [Library](SubModules%2FLibrary) -- [Media](SubModules%2FMedia) -- [Option](SubModules%2FOption) -- [Posts](SubModules%2FPosts) -- [Quiz](SubModules%2FQuiz) -- [Roles](SubModules%2FRoles) -- [Scheduler](SubModules%2FScheduler) -- [ServiceInitializer](SubModules%2FServiceInitializer) -- [Sites](SubModules%2FSites) -- [Templates](SubModules%2FTemplates) -- [Translator](SubModules%2FTranslator) -- [Users](SubModules%2FUsers) +- [Announcement](SubModules/Announcement) +- [Api](SubModules/Api) +- [Assets](SubModules/Assets) +- [Controllers](SubModules/Controllers) +- [EducationalInstitution](SubModules/EducationalInstitution) +- [Library](SubModules/Library) +- [Option](SubModules/Option) +- [Posts](SubModules/Posts) +- [Quiz](SubModules/Quiz) +- [Scheduler](SubModules/Scheduler) +- [ServiceInitializer](SubModules/ServiceInitializer) +- [Sites](SubModules/Sites) +- [Templates](SubModules/Templates) +- [Translator](SubModules/Translator) > Entities See the [Entities Directory](Entities) + +> Require + +- Users Module [https://github.com/ArrayAccess/TrayDigita-Users-Module](https://github.com/ArrayAccess/TrayDigita-Users-Module) +- Media Module [https://github.com/ArrayAccess/TrayDigita-Media-Module](https://github.com/ArrayAccess/TrayDigita-Media-Module) diff --git a/SubModules/Announcement/Announcement.php b/SubModules/Announcement/Announcement.php index 4e61380..f4c089a 100644 --- a/SubModules/Announcement/Announcement.php +++ b/SubModules/Announcement/Announcement.php @@ -16,17 +16,19 @@ final class Announcement extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Announcement', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module that make application support announcements', - context: 'module' + 'module', + 'core-module' ); } } diff --git a/SubModules/Api/Api.php b/SubModules/Api/Api.php index f58528f..2ec855f 100644 --- a/SubModules/Api/Api.php +++ b/SubModules/Api/Api.php @@ -17,9 +17,10 @@ final class Api extends CoreSubmoduleAbstract public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to help api controller working properly', - context: 'module' + 'module', + 'core-module' ); } diff --git a/SubModules/Assets/Assets.php b/SubModules/Assets/Assets.php index 3182a45..e58cfb5 100644 --- a/SubModules/Assets/Assets.php +++ b/SubModules/Assets/Assets.php @@ -26,17 +26,19 @@ final class Assets extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Assets Helper', - context: 'module' + 'core-module', + 'module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module that help assets rendering', - context: 'module' + 'core-module', + 'module' ); } diff --git a/SubModules/Controllers/Controllers.php b/SubModules/Controllers/Controllers.php index 6331020..062abdd 100644 --- a/SubModules/Controllers/Controllers.php +++ b/SubModules/Controllers/Controllers.php @@ -11,17 +11,19 @@ final class Controllers extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Controller Module', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to controller working properly', - context: 'module' + 'module', + 'core-module' ); } } diff --git a/SubModules/EducationalInstitution/EducationalInstitution.php b/SubModules/EducationalInstitution/EducationalInstitution.php index 9e86e81..eda4f3a 100644 --- a/SubModules/EducationalInstitution/EducationalInstitution.php +++ b/SubModules/EducationalInstitution/EducationalInstitution.php @@ -11,17 +11,19 @@ final class EducationalInstitution extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Educational Institution', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support educational structure', - context: 'module' + 'module', + 'core-module' ); } } diff --git a/SubModules/Library/Library.php b/SubModules/Library/Library.php index 22ec511..c3e91b1 100644 --- a/SubModules/Library/Library.php +++ b/SubModules/Library/Library.php @@ -11,17 +11,19 @@ final class Library extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Library & Books', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support library application', - context: 'module' + 'module', + 'core-module' ); } } diff --git a/SubModules/Media/Media.php b/SubModules/Media/Media.php deleted file mode 100644 index 4c50dbe..0000000 --- a/SubModules/Media/Media.php +++ /dev/null @@ -1,100 +0,0 @@ -translate( - 'Media Manager', - context: 'module' - ); - } - - public function getDescription(): string - { - return $this->translate( - 'Module to make application support media & file attachments', - context: 'module' - ); - } - - protected function doInit(): void - { - $this->doFilterPath(); - } - - public function getRequest(): ?ServerRequestInterface - { - return $this->request; - } - - public function setRequest(ServerRequestInterface $request): void - { - $this->request = $request; - } - - public function getDataServe(): DataServe - { - return $this->dataServe ??= new DataServe($this); - } - - public function getChunk(): Chunk - { - return $this->chunk ??= ContainerHelper::use(Chunk::class, $this->getContainer()); - } - - /** - * @param UploadedFileInterface $uploadedFile - * @param ServerRequestInterface $request - * @return StartProgress - */ - public function upload( - UploadedFileInterface $uploadedFile, - ServerRequestInterface $request - ): StartProgress { - return StartProgress::create( - $this->getChunk(), - $uploadedFile, - $request - ); - } - - public function getAdminUpload(): AdminUpload - { - return $this->adminUpload ??= new AdminUpload($this); - } - - public function getUserUpload(): UserUpload - { - return $this->userUpload ??= new UserUpload($this); - } -} diff --git a/SubModules/Media/Servant/DataServe.php b/SubModules/Media/Servant/DataServe.php deleted file mode 100644 index 1c2caff..0000000 --- a/SubModules/Media/Servant/DataServe.php +++ /dev/null @@ -1,121 +0,0 @@ -cachedNormalize[$file])) { - return $this->cachedNormalize[$file]?:null; - } - $file = DataNormalizer::normalizeDirectorySeparator($file); - $uploadDir = $this->uploader->getDataDirectory(); - if (!str_starts_with($file, $uploadDir)) { - $file = DataNormalizer::normalizeDirectorySeparator( - $uploadDir . '/' .$file - ); - } - if (is_file($file) && is_readable($file)) { - $this->cachedNormalize[$file] = $file; - } else { - $this->cachedNormalize[$file] = false; - } - return $this->cachedNormalize[$file]?:null; - } - - public function size(string $file): bool|int - { - $file = $this->getNormalizeFile($file); - if (!$file) { - return false; - } - if (isset($this->cachedSize[$file])) { - return $this->cachedSize[$file]; - } - return $this->cachedSize[$file] = filesize($file); - } - - public function getMimeType(string $file): ?string - { - $file = $this->getNormalizeFile($file); - if (!$file) { - return null; - } - return MimeType::fileMimeType($file); - } - - public function getLastModified(string $file) : ?DateTimeImmutable - { - $file = $this->getNormalizeFile($file); - if (!$file) { - return null; - } - return DateTimeImmutable::createFromFormat( - 'c', - gmdate('c', filemtime($file)), - new DateTimeZone('UTC') - ); - } - - public function display( - string $file, - bool $sendHeaderContentLength = false - ) : int|false { - $size = $this->size($file); - if ($size === false) { - return false; - } - - $file = $this->uploader->getDataDirectory() . '/'. $file; - $resource = fopen($file, 'rb'); - $level = ob_get_level(); - if ($level > 0) { - ob_end_flush(); - } - if ($sendHeaderContentLength && !headers_sent()) { - header('Content-Length: %d', $size); - } - while (!feof($resource)) { - echo fread($resource, 8192); - } - fclose($resource); - - // re-start buffer - if ($level > ob_get_level()) { - ob_start(); - } - return $size; - } -} diff --git a/SubModules/Media/Traits/MediaFilterTrait.php b/SubModules/Media/Traits/MediaFilterTrait.php deleted file mode 100644 index b1dd434..0000000 --- a/SubModules/Media/Traits/MediaFilterTrait.php +++ /dev/null @@ -1,322 +0,0 @@ -allowedAvatarMimeTypes; - } - - public function setAllowedAvatarMimeTypes(string ...$allowedAvatarMimeTypes): void - { - $this->allowedAvatarMimeTypes = array_map('strtolower', $allowedAvatarMimeTypes); - } - - /** - * @param string $path - * @return ?string - */ - public function getAttachmentFileToBasePath( - string $path - ): ?string { - $path = DataNormalizer::normalizeUnixDirectorySeparator($path); - $uploadDir = DataNormalizer::normalizeUnixDirectorySeparator( - $this->getUploadDirectory() - ); - $dataDir = DataNormalizer::normalizeUnixDirectorySeparator( - $this->getUploadDirectory() - ); - $avatarDir = DataNormalizer::normalizeUnixDirectorySeparator( - $this->getUploadDirectory() - ); - - $currentDirectory = str_starts_with($path, $avatarDir) ? $avatarDir : null; - $currentDirectory ??= str_starts_with($path, $uploadDir) ? $uploadDir : null; - $currentDirectory ??= str_starts_with($path, $dataDir) ? $dataDir : null; - if (!$currentDirectory) { - return null; - } - - $currentDirectory = ltrim(substr($path, strlen($currentDirectory)), '/'); - if (str_starts_with($currentDirectory, $this->getFrontendPath()) - || str_starts_with($currentDirectory, $this->getBackendPath()) - ) { - $explode = explode('/', $currentDirectory, 2); - array_shift($explode); - return implode('/', $explode); - } - - return $currentDirectory; - } - - public function getUploadFileToBasePath( - string $path - ) : ?string { - $path = DataNormalizer::normalizeUnixDirectorySeparator($path); - $uploadDir = DataNormalizer::normalizeUnixDirectorySeparator( - $this->getUploadDirectory() - ); - if (!str_starts_with($path, $uploadDir)) { - return null; - } - $uploadDir = ltrim(substr($path, strlen($uploadDir)), '/'); - if (str_starts_with($uploadDir, $this->getFrontendPath()) - || str_starts_with($uploadDir, $this->getBackendPath()) - ) { - $explode = explode('/', $uploadDir, 2); - array_shift($explode); - return implode('/', $explode); - } - return $uploadDir; - } - - public function getDataFileToBasePath( - string $path - ) : ?string { - $path = DataNormalizer::normalizeUnixDirectorySeparator($path); - $dataDir = DataNormalizer::normalizeUnixDirectorySeparator( - $this->getDataDirectory() - ); - if (!str_starts_with($path, $dataDir)) { - return null; - } - $dataDir = ltrim(substr($path, strlen($dataDir)), '/'); - if (str_starts_with($dataDir, $this->getFrontendPath()) - || str_starts_with($dataDir, $this->getBackendPath()) - ) { - $explode = explode('/', $dataDir, 2); - array_shift($explode); - return implode('/', $explode); - } - return $dataDir; - } - - public function getUploadFileToURI( - string $path, - ServerRequestInterface|UriInterface $requestOrUri - ) : ?UriInterface { - $path = DataNormalizer::normalizeUnixDirectorySeparator($path); - if (!str_starts_with($path, $this->getPublicDirectory())) { - return null; - } - $path = substr($path, strlen($this->getPublicDirectory())); - $view = ContainerHelper::use(ViewInterface::class, $this->getContainer()); - return $view->getBaseURI( - $path, - $requestOrUri - ); - } - - public function normalizeFileNameExtension( - UploadedFileInterface|string $uploadedFile, - bool $noResolve = false - ) : ?string { - $clientFileName = $uploadedFile instanceof UploadedFileInterface - ? $uploadedFile->getClientFilename() - : trim($uploadedFile); - if (!$clientFileName) { - return null; - } - - if ($uploadedFile instanceof UploadedFileInterface) { - $mimeType = $noResolve - ? $uploadedFile->getClientMediaType() - : MimeType::mimeTypeUploadedFile($uploadedFile); - } else { - $extension = pathinfo($uploadedFile, PATHINFO_EXTENSION); - if (!$extension) { - return null; - } - $mimeType = MimeType::mime($extension); - } - if (!$mimeType) { - return null; - } - - $newExtensions = MimeType::fromMimeType($mimeType); - $newExtension = $newExtensions ? reset($newExtensions) : null; - if (($extension = pathinfo($clientFileName, PATHINFO_EXTENSION))) { - $newMimeType = MimeType::mime($extension); - if (is_string($newMimeType) && $newMimeType !== $mimeType) { - $mimes = MimeType::fromMimeType($newMimeType); - if (!empty($mimes) && in_array(strtolower($extension), $mimes)) { - return $clientFileName; - } - $newExtension = MimeType::extension($newMimeType); - return $newExtension !== $extension - ? $clientFileName . '.' . $newExtension - : $clientFileName; - } - - if ($newExtensions && in_array($extension, $newExtensions)) { - return $clientFileName; - } - return $newExtension - ? pathinfo($clientFileName, PATHINFO_FILENAME) . '.' . $newExtension - : $clientFileName; - } - - return $clientFileName . ($newExtension ? '.' . $newExtension : ''); - } - - /** - * filter filename - * @param UploadedFileInterface|string $uploadedFile - * @param bool $noResolve - * @return string|null - */ - public function filterFileNameUseLower( - UploadedFileInterface|string $uploadedFile, - bool $noResolve = false - ): ?string { - $file = $this->normalizeFileNameExtension($uploadedFile, $noResolve); - if (!$file) { - return null; - } - $baseName = basename($file); - $dirname = dirname($file); - $baseName = DataNormalizer::normalizeFileName($baseName); - return ( - $dirname && $dirname !== '.' ? $dirname . '/' : '' - ) . $this->filterFileExtension(strtolower($baseName)); - } - - /** - * Filter file to make sure safe for public access - * - * @param string $fileName - * @return string - */ - public function filterFileExtension( - string $fileName - ): string { - // filter : - // php, html, js, inc, py, c, h, cpp, go, jsp, jsx, mjs - if (!preg_match(self::SCRIPT_REGEX, $fileName)) { - return $fileName; - } - return preg_replace_callback( - // maybe user add query string or hash (? / #) - '~\.([^.?#]+)([?#].*)?$~', - static function ($e) { - $ex = $e[2]??''; - return ".$e[1]._x.txt$ex"; - }, - $fileName - ); - } - - /** - * @param UploadedFileInterface $uploadedFile - * @param AbstractUser $user - * @param bool $noResolve - * @return string - */ - public function getAvatarUploadFullPathByUser( - UploadedFileInterface $uploadedFile, - AbstractUser $user, - bool $noResolve = false - ) : string { - $file = $this->determineAvatarUploadDirectory($user); - if (!$noResolve) { - $uploadedFile = MimeType::resolveMediaTypeUploadedFiles($uploadedFile); - } - if (!in_array($uploadedFile->getClientMediaType(), $this->getAllowedAvatarMimeTypes())) { - throw new UnsupportedArgumentException( - sprintf( - '%s is not supported for avatar extension', - $uploadedFile->getClientMediaType() - ) - ); - } - - $extension = MimeType::extension($uploadedFile->getClientMediaType()); - $recommendedPrefix = 'uid-'; - $prefix = $this->getManager()?->dispatch( - 'media.avatarPrefix', - $recommendedPrefix - )?:$recommendedPrefix; - if (!is_string($prefix) - || trim($prefix) === '' - || preg_match('~[^a-zA-Z0-9_\-]~', $prefix) - ) { - $prefix = $recommendedPrefix; - } - $fileName = sprintf('%s%s', $prefix, $user->getId()); - return sprintf( - '%s/%s.%s', - $file, - $fileName, - $extension - ); - } -} diff --git a/SubModules/Media/Traits/MediaPathTrait.php b/SubModules/Media/Traits/MediaPathTrait.php deleted file mode 100644 index 625726b..0000000 --- a/SubModules/Media/Traits/MediaPathTrait.php +++ /dev/null @@ -1,294 +0,0 @@ -registeredPathInit) { - return $this; - } - - $this->registeredPathInit = true; - $path = ContainerHelper::service(Config::class, $this->getContainer())->get('path'); - $path = $path instanceof Config ? $path : new Config(); - $dataDir = $path->get('data'); - if (!$dataDir || !is_string($dataDir)) { - $dataDir = dirname(\TD_APP_DIRECTORY) . '/data'; - } - if (!is_dir($dataDir)) { - mkdir($dataDir, 0755, true); - } - $uploadDir = $path->get('upload'); - $publicDir = $path->get('public'); - if (!$uploadDir || !is_string($uploadDir) || !is_dir($uploadDir)) { - if (!is_string($publicDir) - || !$publicDir - || !realpath($publicDir) - ) { - if (!defined('TD_INDEX_FILE')) { - throw new RuntimeException( - 'Could not determine public directory' - ); - } - $publicDir = dirname(TD_INDEX_FILE); - } else { - $publicDir = realpath($publicDir); - } - $uploadDir = $publicDir . '/uploads'; - } - - $this->dataDirectory = DataNormalizer::normalizeDirectorySeparator($dataDir); - $this->uploadDirectory = DataNormalizer::normalizeUnixDirectorySeparator($uploadDir); - $this->publicDirectory = DataNormalizer::normalizeDirectorySeparator(dirname($this->uploadDirectory)); - return $this; - } - - public function getUploadDirectory(): string - { - return $this->doFilterPath()->uploadDirectory; - } - - public function getDataDirectory(): string - { - return $this->doFilterPath()->dataDirectory; - } - - public function getPublicDirectory(): string - { - return $this->doFilterPath()->publicDirectory; - } - - public function getPathScript(): string - { - return $this->doFilterPath()->pathScript; - } - - public function getPathImages(): string - { - return $this->doFilterPath()->pathImages; - } - - public function getPathAudio(): string - { - return $this->doFilterPath()->pathAudio; - } - - public function getPathVideo(): string - { - return $this->doFilterPath()->pathVideo; - } - - public function getPathDocuments(): string - { - return $this->doFilterPath()->pathDocuments; - } - - public function getPathFiles(): string - { - return $this->doFilterPath()->pathFiles; - } - - public function getAvatarPath(): string - { - return $this->doFilterPath()->avatarPath; - } - - public function getFrontendPath(): string - { - return $this->doFilterPath()->frontendPath; - } - - public function getBackendPath(): string - { - return $this->doFilterPath()->backendPath; - } - - public function determineUploadPath(?AbstractUser $user = null): string - { - return $user instanceof Admin - ? $this->getBackendPath() - : $this->getFrontendPath(); - } - - public function determinePathMimeType( - string|UploadedFileInterface $mimetype, - bool $noResolve = false - ): string { - $clientFileName = null; - if ($mimetype instanceof UploadedFileInterface) { - $clientFileName = $mimetype->getClientFilename(); - if (!$noResolve) { - $newMimeType = MimeType::mimeTypeUploadedFile($mimetype); - $mimetype = $newMimeType ?? $mimetype->getClientMediaType(); - } else { - $mimetype = $mimetype->getClientMediaType(); - } - } - - // stop here if extension contains scripts - if ($clientFileName - && ( - preg_match(self::SCRIPT_REGEX, $clientFileName) - || preg_match( - // json shell exec etc. - '~/.*(json|shell|php|ecmascript|bash|exec|binary).*$~', - $mimetype - ) - ) - ) { - return $this->getPathScript(); - } - - $mimetype = $mimetype instanceof UploadedFileInterface - ? strtolower($mimetype->getClientMediaType()) - : strtolower(trim($mimetype)); - - $match = match ($mimetype) { - 'image/svg+xml' => $this->getPathImages(), - 'text/css', - 'text/javascript', - 'text/ecmascript', - 'application/sql', - 'text/x-php', - 'application/x-php', - 'application/json', - 'application/javascript' => $this->getPathScript(), - 'text/plain', - 'text/html', - 'text/xml', - 'text/csv', - 'text/calendar', - 'application/pgp-signature', - 'application/pdf', - 'application/rtf', - 'text/csv-schema', - 'application/xml' => $this->getPathDocuments(), - default => null - }; - $match = $match?? match (explode('/', $mimetype, 2)[0]) { - 'audio' => $this->getPathAudio(), - 'video' => $this->getPathVideo(), - 'image' => $this->getPathImages(), - default => null - }; - - if ($match) { - return $match; - } - if (preg_match( - '~^application/ - .* - (?:officedocument|openxmlformats|xml|xsl|msword|ppt|odt|pdf|wpd|docx?) - ~x', - $mimetype - )) { - return $this->getPathDocuments(); - } - - // fallback - return $this->getPathFiles(); - } - - /** - * Determine recommended upload directory - * - * @param UploadedFileInterface $uploadedFile - * @param ?AbstractUser $user - * @param bool $noResolve - * @return string - */ - public function determineUploadDirectory( - UploadedFileInterface $uploadedFile, - ?AbstractUser $user = null, - bool $noResolve = false - ) : string { - $directory = $this->getUploadDirectory(); - $directory .= '/' . $this->determineUploadPath($user); - $directory .= '/' . $this->determinePathMimeType($uploadedFile, noResolve: $noResolve); - $directory .= '/' . date('Y/m/d'); - return $directory; - } - - /** - * @param AbstractUser|null $user - * @return string - */ - public function determineAvatarUploadDirectory( - ?AbstractUser $user = null - ) : string { - $directory = $this->getUploadDirectory(); - $directory .= '/' . $this->determineUploadPath($user); - $directory .= '/' . $this->getAvatarPath(); - return $directory; - } - - /** - * Determine recommended upload directory - * - * @param UploadedFileInterface $uploadedFile - * @param AbstractUser|null $user - * @param bool $noResolve - * @return string - */ - public function determineDataDirectory( - UploadedFileInterface $uploadedFile, - ?AbstractUser $user = null, - bool $noResolve = false - ) : string { - $directory = $this->getDataDirectory(); - $directory .= '/' . $this->determineUploadPath($user); - $directory .= '/' . $this->determinePathMimeType($uploadedFile, noResolve: $noResolve); - $directory .= '/' . date('Y/m/d'); - return $directory; - } -} diff --git a/SubModules/Media/Uploader/Abstracts/AbstractUploader.php b/SubModules/Media/Uploader/Abstracts/AbstractUploader.php deleted file mode 100644 index b62872e..0000000 --- a/SubModules/Media/Uploader/Abstracts/AbstractUploader.php +++ /dev/null @@ -1,282 +0,0 @@ -getMedia()->getContainer(); - } - - public function getMedia(): Media - { - return $this->media; - } - - public function uploadAvatar( - ServerRequestInterface $request, - UploadedFileInterface $uploadedFile, - AbstractUser $user - ): ?UploadedFileMetadata { - return $this->uploadAttachment( - $request, - $uploadedFile, - $user, - self::TYPE_AVATAR - ); - } - - public function uploadData( - ServerRequestInterface $request, - UploadedFileInterface $uploadedFile, - AbstractUser $user - ): UploadedFileMetadata { - return $this->uploadAttachment( - $request, - $uploadedFile, - $user, - self::TYPE_DATA - ); - } - - public function uploadPublic( - ServerRequestInterface $request, - UploadedFileInterface $uploadedFile, - AbstractUser $user - ): UploadedFileMetadata { - return $this->uploadAttachment( - $request, - $uploadedFile, - $user, - self::TYPE_UPLOAD - ); - } - - public function uploadAttachment( - ServerRequestInterface $request, - UploadedFileInterface $uploadedFile, - ?AbstractUser $user, - string $type - ) : UploadedFileMetadata { - $type = match ($type) { - self::TYPE_AVATAR => self::TYPE_AVATAR, - self::TYPE_UPLOAD => self::TYPE_UPLOAD, - default => self::TYPE_DATA - }; - if (!$uploadedFile->getClientFilename()) { - throw new UploadedFileNameException( - $this->translate('File does not have file name') - ); - } - $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION); - if (!$extension) { - $extensions = MimeType::fromMimeType($uploadedFile->getClientMediaType()); - $extension = $extensions ? reset($extensions) : null; - } - if (!$extension) { - throw new UploadedFileExtensionException( - sprintf( - $this->translate('Could not determine file type from mimetype %s'), - $uploadedFile->getClientMediaType() - ) - ); - } - - $em = ContainerHelper::service( - Connection::class, - $this->getContainer() - )->getEntityManager(); - /** - * @var class-string $className - */ - $className = $user instanceof Admin - ? Attachment::class - : UserAttachment::class; - $repository = $em->getRepository($className); - $uploadedFile = MimeType::resolveMediaTypeUploadedFiles($uploadedFile); - $originalFileName = $uploadedFile->getClientFilename(); - $progress = $this->media->upload($uploadedFile, $request); - if (!$progress->isDone()) { - return new UploadedFileMetadata( - $this, - $type, - $request, - $uploadedFile, - $progress, - false - ); - } - - $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION); - $newMimeType = MimeType::mime($extension); - if ($progress->getSize() > $uploadedFile->getSize() - && $newMimeType !== $uploadedFile->getClientMediaType() - // check the mime type - && ( - function_exists('mime_content_type') - && ($newMime = mime_content_type($progress->targetCacheFile)) - && $newMime !== $uploadedFile->getClientMediaType() - || ! ($newMime??null) - ) - ) { - $newMime ??= $newMimeType; - $uploadedFile = new UploadedFile( - $uploadedFile->getStream(), - $uploadedFile->getSize(), - $uploadedFile->getError(), - $uploadedFile->getClientFilename(), - $newMime - ); - } - - if ($type === self::TYPE_AVATAR) { - $filePath = $this->media->getAvatarUploadFullPathByUser($uploadedFile, $user, true); - $uploadedFile = $this->getUploadedFileAvatar( - $uploadedFile, - $filePath - ); - } else { - $uploadDirectory = match ($type) { - self::TYPE_UPLOAD => $this->media->determineUploadDirectory($uploadedFile, $user, true), - default => $this->media->determineDataDirectory($uploadedFile, $user, true), - }; - $clientFileName = $this->media->filterFileNameUseLower($uploadedFile, true); - $filePath = $uploadDirectory . '/' . $clientFileName; - } - - $fullPath = $progress->put( - $filePath, - $type === self::TYPE_AVATAR - ); - - if (!$fullPath) { - $progress->deletePartial(); - throw new RuntimeException( - $this->translate('Could not save uploaded file') - ); - } - - $basePath = $this->media->getAttachmentFileToBasePath($fullPath); - if (!$basePath) { - Consolidation::callbackReduceError(fn () => unlink($fullPath)); - throw new RuntimeException( - $this->translate( - 'Could not save uploaded file & determine target file.' - ) - ); - } - - try { - $attachment = $repository - ->findOneBy([ - 'path' => $basePath, - 'storage_type' => $type - ]); - if (!$attachment) { - $name = pathinfo($originalFileName, PATHINFO_FILENAME); - $attachment = new $className(); - $attachment->setEntityManager($em); - $attachment->setPath($basePath); - $attachment->setName($name?:$originalFileName); - $attachment->setStatus($attachment::PUBLISHED); - } - - if ($user instanceof Admin || $user instanceof User) { - $attachment->setUser($user); - $attachment->setUserId($user->getId()); - } - - $attachment->setFileName($originalFileName); - $attachment->setStorageType($attachment::TYPE_UPLOAD); - $attachment->setSize($progress->getSize()); - $attachment->setDeletedAt(null); - $attachment->setMimeType($uploadedFile->getClientMediaType()); - $em->persist($attachment); - $em->flush(); - } catch (Throwable $e) { - if (file_exists($fullPath)) { - Consolidation::callbackReduceError(fn() => unlink($fullPath)); - } - throw $e; - } - - $result = new UploadedFileMetadata( - $this, - $type, - $request, - $uploadedFile, - $progress, - true, - $fullPath, - $attachment - ); - if ($type === self::TYPE_AVATAR) { - $result = $this->dispatchAvatarUpload($result); - } - - return $result; - } - - /** - * This for event change dispatch - * - * @param UploadedFileInterface $uploadedFile - * @param string $filePath - * @return UploadedFileInterface - * @noinspection PhpUnusedParameterInspection - */ - protected function getUploadedFileAvatar( - UploadedFileInterface $uploadedFile, - string $filePath - ) : UploadedFileInterface { - return $uploadedFile; - } - - protected function dispatchAvatarUpload(UploadedFileMetadata $metadata) : UploadedFileMetadata - { - return $metadata; - } -} diff --git a/SubModules/Media/Uploader/AdminUpload.php b/SubModules/Media/Uploader/AdminUpload.php deleted file mode 100644 index 2ae39da..0000000 --- a/SubModules/Media/Uploader/AdminUpload.php +++ /dev/null @@ -1,28 +0,0 @@ -size = $this->fullPath && file_exists($this->fullPath) - ? filesize($this->fullPath) - : $this->progress->getSize(); - } - - /** - * @param bool $pathOnly - * @return array{ - * id: ?string, - * uuid: string, - * file_name: string, - * mime_type: string, - * saved_name: string, - * uri: ?UriInterface|?string, - * size: int - * } - */ - public function toArray(bool $pathOnly = false): array - { - $uploadedFile = $this->uploadedFile; - $fileName = $uploadedFile->getClientFilename(); - $uri = $this->finished - ? $this->uploader->media->getUploadFileToURI($this->fullPath, $this->request) - : null; - if ($pathOnly) { - $uri = $uri?->getPath(); - } - return [ - 'id' => $this->attachment?->getId(), - 'request_id' => $this->progress->processor->requestIdHeader->header, - 'name' => pathinfo($fileName, PATHINFO_FILENAME), - 'file_name' => $fileName, - 'mime_type' => $uploadedFile->getClientMediaType(), - 'saved_name' => $this->finished ? basename($this->fullPath) : null, - 'uri' => $uri, - 'size' => $this->size, - ]; - } -} diff --git a/SubModules/Media/Uploader/UserUpload.php b/SubModules/Media/Uploader/UserUpload.php deleted file mode 100644 index fe2aa0d..0000000 --- a/SubModules/Media/Uploader/UserUpload.php +++ /dev/null @@ -1,28 +0,0 @@ -translate( + return $this->translateContext( 'Site Option', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support option setting', - context: 'module' + 'module', + 'core-module' ); } /** * @return ObjectRepository&Selectable - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - * @throws Throwable */ public function getRepository() : ObjectRepository&Selectable { diff --git a/SubModules/Posts/PostTypes/Abstracts/AbstractPostType.php b/SubModules/Posts/PostTypes/Abstracts/AbstractPostType.php index 26a860a..55dca7b 100644 --- a/SubModules/Posts/PostTypes/Abstracts/AbstractPostType.php +++ b/SubModules/Posts/PostTypes/Abstracts/AbstractPostType.php @@ -6,7 +6,7 @@ use ArrayAccess\TrayDigita\App\Modules\Core\Entities\Post; use ArrayAccess\TrayDigita\App\Modules\Core\Entities\PostCategory; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Posts\Posts; -use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Users\Users; +use ArrayAccess\TrayDigita\App\Modules\Users\Users; use ArrayAccess\TrayDigita\Auth\Roles\Interfaces\PermissionInterface; use ArrayAccess\TrayDigita\Auth\Roles\Interfaces\RoleInterface; use ArrayAccess\TrayDigita\Database\Helper\Expression; diff --git a/SubModules/Posts/PostTypes/Singular/TypeCustom.php b/SubModules/Posts/PostTypes/Singular/TypeCustom.php index e61bfed..b4414a2 100644 --- a/SubModules/Posts/PostTypes/Singular/TypeCustom.php +++ b/SubModules/Posts/PostTypes/Singular/TypeCustom.php @@ -16,6 +16,7 @@ class TypeCustom extends SinglePostAbstract public function getPost(): ?Post { + /** @noinspection DuplicatedCode */ if ($this->postInit) { return $this->post; } diff --git a/SubModules/Posts/PostTypes/Singular/TypePost.php b/SubModules/Posts/PostTypes/Singular/TypePost.php index 79c2d57..9bded39 100644 --- a/SubModules/Posts/PostTypes/Singular/TypePost.php +++ b/SubModules/Posts/PostTypes/Singular/TypePost.php @@ -19,6 +19,7 @@ public function getPost(): ?Post if ($this->postInit) { return $this->post; } + /** @noinspection DuplicatedCode */ if ($this->post) { return $this->post; } diff --git a/SubModules/Posts/PostTypes/Singular/TypeRevision.php b/SubModules/Posts/PostTypes/Singular/TypeRevision.php index c9cc70f..cfadfab 100644 --- a/SubModules/Posts/PostTypes/Singular/TypeRevision.php +++ b/SubModules/Posts/PostTypes/Singular/TypeRevision.php @@ -13,6 +13,7 @@ class TypeRevision extends SinglePostAbstract public function getPost(): ?Post { + /** @noinspection DuplicatedCode */ if ($this->postInit) { return $this->post; } diff --git a/SubModules/Posts/Posts.php b/SubModules/Posts/Posts.php index eb755eb..4a8d4e2 100644 --- a/SubModules/Posts/Posts.php +++ b/SubModules/Posts/Posts.php @@ -8,10 +8,14 @@ use ArrayAccess\TrayDigita\App\Modules\Core\Entities\PostCategory; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Posts\Finder\CategoryFinder; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Posts\Finder\PostFinder; +use ArrayAccess\TrayDigita\Database\Connection; use ArrayAccess\TrayDigita\Database\Result\LazyResultCriteria; use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper; use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\SchemaException; +use Throwable; final class Posts extends CoreSubmoduleAbstract { @@ -23,34 +27,48 @@ final class Posts extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Post & Articles', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support posts publishing', - context: 'module' + 'module', + 'core-module' ); } public function getPostFinder(): ?PostFinder { - return $this->postFinder ??= ContainerHelper::resolveCallable( - PostFinder::class, - $this->getContainer() - ); + try { + return $this->postFinder ??= ContainerHelper::resolveCallable( + PostFinder::class, + $this->getContainer() + ); + } catch (Throwable) { + return new PostFinder( + ContainerHelper::service(Connection::class, $this->getContainer()) + ); + } } public function getCategoryFinder(): ?CategoryFinder { - return $this->categoryFinder ??= ContainerHelper::resolveCallable( - CategoryFinder::class, - $this->getContainer() - ); + try { + return $this->categoryFinder ??= ContainerHelper::resolveCallable( + CategoryFinder::class, + $this->getContainer() + ); + } catch (Throwable) { + return new CategoryFinder( + ContainerHelper::service(Connection::class, $this->getContainer()) + ); + } } public function findPostById(int $id): ?Post @@ -73,6 +91,11 @@ public function findCategoryBySlug(string $slug): ?PostCategory return $this->getCategoryFinder()->findBySlug($slug); } + /** + * @throws SchemaException + * @throws Exception + * @noinspection PhpUnused + */ public function searchPost( string $searchQuery, int $limit = 10, @@ -89,6 +112,11 @@ public function searchPost( ); } + /** + * @throws SchemaException + * @throws Exception + * @noinspection PhpUnused + */ public function searchCategory( string $searchQuery, int $limit = 10, diff --git a/SubModules/Quiz/Quiz.php b/SubModules/Quiz/Quiz.php index 1f1c328..8c58c37 100644 --- a/SubModules/Quiz/Quiz.php +++ b/SubModules/Quiz/Quiz.php @@ -11,17 +11,19 @@ final class Quiz extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Quiz', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support quiz & course', - context: 'module' + 'module', + 'core-module' ); } } diff --git a/SubModules/Roles/Roles.php b/SubModules/Roles/Roles.php deleted file mode 100644 index db1b6c0..0000000 --- a/SubModules/Roles/Roles.php +++ /dev/null @@ -1,29 +0,0 @@ -translate( - 'Role & Capabilities', - context: 'module' - ); - } - - public function getDescription(): string - { - return $this->translate( - 'Core module to make application support role capabilities / permissions', - context: 'module' - ); - } -} diff --git a/SubModules/Scheduler/Scheduler.php b/SubModules/Scheduler/Scheduler.php index 7559127..3babc06 100644 --- a/SubModules/Scheduler/Scheduler.php +++ b/SubModules/Scheduler/Scheduler.php @@ -21,17 +21,19 @@ final class Scheduler extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Database Scheduler', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application support database storage record for scheduler', - context: 'module' + 'module', + 'core-module' ); } diff --git a/SubModules/ServiceInitializer/Controllers/Views/assets/css/install.css b/SubModules/ServiceInitializer/Controllers/Views/assets/css/install.css index abaf3e5..acc4c9e 100644 --- a/SubModules/ServiceInitializer/Controllers/Views/assets/css/install.css +++ b/SubModules/ServiceInitializer/Controllers/Views/assets/css/install.css @@ -1,20 +1,163 @@ /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none} +button, hr, input { + overflow: visible +} + +progress, sub, sup { + vertical-align: baseline +} + +[type=checkbox], [type=radio], legend { + box-sizing: border-box; + padding: 0 +} + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +details, main { + display: block +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +hr { + box-sizing: content-box; + height: 0 +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +a { + background-color: transparent +} + +abbr[title] { + border-bottom: none; + text-decoration: underline dotted +} + +b, strong { + font-weight: bolder +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +img { + border-style: none +} + +button, input, optgroup, select, textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0 +} + +button, select { + text-transform: none +} + +[type=button], [type=reset], [type=submit], button { + -webkit-appearance: button +} + +[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner { + border-style: none; + padding: 0 +} + +[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring { + outline: ButtonText dotted 1px +} + +fieldset { + padding: .35em .75em .625em +} + +legend { + color: inherit; + display: table; + max-width: 100%; + white-space: normal +} + +textarea { + overflow: auto +} + +[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +summary { + display: list-item +} + +[hidden], template { + display: none +} + :root { --background: #faf9f9; --text-color: #333; --font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --mono-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } + body { - margin:0; + margin: 0; padding: 0; color: var(--text-color); background: var(--background); font-family: var(--font-family); } -pre, code,kbd { + +pre, code, kbd { font-family: var(--mono-family); } + .page-wrapper { } diff --git a/SubModules/ServiceInitializer/Controllers/Views/assets/js/install.js b/SubModules/ServiceInitializer/Controllers/Views/assets/js/install.js index dc619df..dcd50f1 100644 --- a/SubModules/ServiceInitializer/Controllers/Views/assets/js/install.js +++ b/SubModules/ServiceInitializer/Controllers/Views/assets/js/install.js @@ -1,4 +1,4 @@ -;(function(w) { +;(function (w) { let htmlClassList = w.document.documentElement.classList; htmlClassList.remove('no-js'); htmlClassList.add('js'); diff --git a/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php b/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php index e08ad24..25312f4 100644 --- a/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php +++ b/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php @@ -4,6 +4,7 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Middlewares; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Controllers\InstallController; +use ArrayAccess\TrayDigita\Http\Interfaces\HttpExceptionInterface; use ArrayAccess\TrayDigita\Kernel\Interfaces\KernelInterface; use ArrayAccess\TrayDigita\L10n\Translations\Interfaces\TranslatorInterface; use ArrayAccess\TrayDigita\Middleware\AbstractMiddleware; @@ -20,6 +21,9 @@ class InitMiddleware extends AbstractMiddleware { protected int $priority = PHP_INT_MAX - 10000; + /** + * @throws HttpExceptionInterface + */ protected function doProcess(ServerRequestInterface $request): ServerRequestInterface|ResponseInterface { // enhance views diff --git a/SubModules/ServiceInitializer/ServiceInitializer.php b/SubModules/ServiceInitializer/ServiceInitializer.php index 708b761..4a46751 100644 --- a/SubModules/ServiceInitializer/ServiceInitializer.php +++ b/SubModules/ServiceInitializer/ServiceInitializer.php @@ -35,17 +35,19 @@ final class ServiceInitializer extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Service Initializer', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to make application run properly', - context: 'module' + 'module', + 'core-module' ); } diff --git a/SubModules/Sites/Sites.php b/SubModules/Sites/Sites.php index 23ebb5d..4b1c57c 100644 --- a/SubModules/Sites/Sites.php +++ b/SubModules/Sites/Sites.php @@ -28,17 +28,19 @@ final class Sites extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Site', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module for site', - context: 'module' + 'module', + 'core-module' ); } diff --git a/SubModules/Templates/Middlewares/TemplateLoaderMiddleware.php b/SubModules/Templates/Middlewares/TemplateLoaderMiddleware.php index 696eafb..3f9153f 100644 --- a/SubModules/Templates/Middlewares/TemplateLoaderMiddleware.php +++ b/SubModules/Templates/Middlewares/TemplateLoaderMiddleware.php @@ -18,6 +18,9 @@ class TemplateLoaderMiddleware extends AbstractMiddleware { protected int $priority = PHP_INT_MAX - 10000; + /** + * @throws Throwable + */ protected function doProcess(ServerRequestInterface $request): ServerRequestInterface|ResponseInterface { // register diff --git a/SubModules/Templates/Templates.php b/SubModules/Templates/Templates.php index a08e3f3..cb4a2b9 100644 --- a/SubModules/Templates/Templates.php +++ b/SubModules/Templates/Templates.php @@ -10,6 +10,7 @@ use ArrayAccess\TrayDigita\Templates\Wrapper; use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper; use ArrayAccess\TrayDigita\View\Interfaces\ViewInterface; +use Throwable; use function is_string; final class Templates extends CoreSubmoduleAbstract @@ -24,17 +25,19 @@ final class Templates extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Templates Helper', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to support templating', - context: 'module' + 'module', + 'core-module' ); } @@ -56,8 +59,11 @@ private function initSetTemplate(KernelInterface $kernel): void if ($kernel->getConfigError()) { return; } - $wrapper = ContainerHelper::decorate(Wrapper::class, $this->getContainer()); - $view = ContainerHelper::decorate(ViewInterface::class, $this->getContainer()); + $wrapper = ContainerHelper::service(Wrapper::class, $this->getContainer()); + $view = ContainerHelper::service(ViewInterface::class, $this->getContainer()); + if (!$wrapper || !$view) { + return; + } $this->templateRule = new TemplateRule($wrapper); $this->templateRule->initialize(); $view?->setTemplateRule($this->templateRule); @@ -76,11 +82,14 @@ private function initSetTemplate(KernelInterface $kernel): void } // add middleware to load templates.php - $kernel->getHttpKernel()->addMiddleware( - ContainerHelper::resolveCallable( - TemplateLoaderMiddleware::class, - $this->getContainer() - ) - ); + try { + $kernel->getHttpKernel()->addMiddleware( + ContainerHelper::resolveCallable( + TemplateLoaderMiddleware::class, + $this->getContainer() + ) + ); + } catch (Throwable $e) { + } } } diff --git a/SubModules/Translator/Translator.php b/SubModules/Translator/Translator.php index 4fa2f8c..a25a39c 100644 --- a/SubModules/Translator/Translator.php +++ b/SubModules/Translator/Translator.php @@ -14,17 +14,19 @@ class Translator extends CoreSubmoduleAbstract public function getName(): string { - return $this->translate( + return $this->translateContext( 'Extended Translator', - context: 'module' + 'module', + 'core-module' ); } public function getDescription(): string { - return $this->translate( + return $this->translateContext( 'Core module to support translation', - context: 'module' + 'module', + 'core-module' ); } diff --git a/SubModules/Users/Factory/AdminEntityFactory.php b/SubModules/Users/Factory/AdminEntityFactory.php deleted file mode 100644 index 6615ce4..0000000 --- a/SubModules/Users/Factory/AdminEntityFactory.php +++ /dev/null @@ -1,32 +0,0 @@ -connection->find( - Admin::class, - $id - ); - } - - public function findByUsername(string $username) : ?UserEntityInterface - { - return $this->connection->findOneBy( - Admin::class, - ['username' => $username] - ); - } -} diff --git a/SubModules/Users/Factory/CapabilityFactory.php b/SubModules/Users/Factory/CapabilityFactory.php deleted file mode 100644 index 2f0bd3c..0000000 --- a/SubModules/Users/Factory/CapabilityFactory.php +++ /dev/null @@ -1,41 +0,0 @@ -getRepository( - Capability::class - )->find($identity); - } - - public function all( - EntityManagerInterface $entityManager - ) : iterable { - return $entityManager->getRepository( - Capability::class - )->findAll(); - } - - public function getCapabilityIdentities(EntityManagerInterface $entityManager) : array - { - return (array) $entityManager - ->getRepository(Capability::class) - ->createQueryBuilder('u') - ->select('u.identity') - ->getQuery() - ->getSingleColumnResult(); - } -} diff --git a/SubModules/Users/Factory/UserEntityFactory.php b/SubModules/Users/Factory/UserEntityFactory.php deleted file mode 100644 index 7531f4d..0000000 --- a/SubModules/Users/Factory/UserEntityFactory.php +++ /dev/null @@ -1,32 +0,0 @@ -connection->find( - User::class, - $id - ); - } - - public function findByUsername(string $username) : ?UserEntityInterface - { - return $this->connection->findOneBy( - User::class, - ['username' => $username] - ); - } -} diff --git a/SubModules/Users/Users.php b/SubModules/Users/Users.php deleted file mode 100644 index cef3745..0000000 --- a/SubModules/Users/Users.php +++ /dev/null @@ -1,563 +0,0 @@ - very important - */ - protected int $priority = PHP_INT_MIN; - - protected PermissionInterface $permission; - - private bool $didInit = false; - - const ADMIN_MODE = 'admin'; - - const USER_MODE = 'user'; - - private array $cookieNames = [ - self::USER_MODE => [ - 'name' => 'auth_user', - 'lifetime' => 0, - 'wildcard' => false - ], - self::ADMIN_MODE => [ - 'name' => 'auth_admin', - 'lifetime' => 0, - 'wildcard' => false - ] - ]; - - private ?User $userAccount = null; - - private ?Admin $adminAccount = null; - - private ?ServerRequestInterface $request = null; - - private bool $authProcessed = false; - - private string $currentMode = self::ADMIN_MODE; - - public function getName(): string - { - return $this->translate( - 'Users & Auth', - context: 'module' - ); - } - - public function getDescription(): string - { - return $this->translate( - 'Core module that support users & authentication', - context: 'module' - ); - } - - protected function doInit(): void - { - if ($this->didInit) { - return; - } - - $this->didInit = true; - // $this->doResolvePermission(); - // $this->doResolveCookieName(); - $this->doAddMiddleware(); - // stop here if config error - if ($this->getKernel()?->getConfigError()) { - return; - } - $this->getManager()?->attach( - 'view.beforeRender', - [$this, 'viewBeforeRender'] - ); - $this->getManager()?->attach( - 'view.bodyAttributes', - [$this, 'viewBodyAttributes'] - ); - } - - private function viewBodyAttributes($attributes): array - { - $this->getManager()?->detach( - 'view.bodyAttributes', - [$this, 'viewBodyAttributes'] - ); - - $attributes = !is_array($attributes) ? $attributes : []; - $attributes['class'] = DataNormalizer::splitStringToArray($attributes['class']??null)??[]; - $user = $this->getUserAccount(); - $admin = $this->getAdminAccount(); - if (!$user && !$admin) { - return $attributes; - } - $attributes['data-user-logged-in'] = true; - return $attributes; - } - - /** @noinspection PhpUnusedParameterInspection */ - private function viewBeforeRender( - $path, - $parameters, - ViewInterface $view - ) { - $this->getManager()?->detach( - 'view.beforeRender', - [$this, 'viewBeforeRender'] - ); - $view->setParameter('user_user', $this->getUserAccount()); - $view->setParameter('admin_user', $this->getAdminAccount()); - return $path; - } - - private bool $permissionResolved = false; - - private function doResolvePermission(): self - { - if ($this->permissionResolved) { - return $this; - } - $this->permissionResolved = true; - $container = $this->getContainer(); - $connection = ContainerHelper::use(Connection::class, $container); - $manager = $this->getManager(); - if (!$container->has(CapabilityEntityFactoryInterface::class)) { - $container->set( - CapabilityEntityFactoryInterface::class, - static fn () => new CapabilityFactory() - ); - } - $hasPermission = $container->has(PermissionInterface::class); - if ($hasPermission) { - $permission = $container->get(PermissionInterface::class); - if (!$permission instanceof PermissionInterface) { - $container->remove(PermissionInterface::class); - $hasPermission = false; - } - } - if (!$hasPermission) { - $permission = new PermissionWrapper( - $connection, - $container, - $manager - ); - if ($container instanceof Container) { - $container->set(PermissionInterface::class, $permission); - } else { - $container->set(PermissionInterface::class, fn () => $permission); - } - } - - $permission ??= $container->get(PermissionInterface::class); - if (!$permission instanceof PermissionWrapper) { - $container->remove(PermissionInterface::class); - $permission = new PermissionWrapper( - $connection, - $container, - $manager, - $permission - ); - $container->set(PermissionInterface::class, $permission); - } - $this->permission = $permission; - if ($this->permission instanceof PermissionWrapper - && !$this->permission->getCapabilityEntityFactory() - ) { - $this->permission->setCapabilityEntityFactory(new CapabilityFactory()); - } - return $this; - } - - private function doAddMiddleware(): void - { - $container = $this->getContainer(); - ContainerHelper::use( - KernelInterface::class, - $container - )?->getHttpKernel()->addMiddleware( - new class($container, $this) extends AbstractMiddleware { - protected int $priority = PHP_INT_MAX - 10; - public function __construct( - ContainerInterface $container, - private readonly Users $auth - ) { - parent::__construct($container); - } - - protected function doProcess(ServerRequestInterface $request): ServerRequestInterface|ResponseInterface - { - $this->auth->setRequest($request); - return $request; - } - } - ); - } - - private bool $cookieResolved = false; - - /** - * @return self - */ - private function doResolveCookieName(): self - { - if ($this->cookieResolved) { - return $this; - } - - $this->cookieResolved = true; - $config = ContainerHelper::use(Config::class, $this->getContainer()); - $cookie = $config->get('cookie'); - if (!$cookie instanceof Config) { - $cookie = new Config(); - $config->set('cookie', $cookie); - } - foreach ($this->cookieNames as $key => $names) { - $cookieData = $cookie->get($key); - $cookieData = $cookieData instanceof Config - ? $cookieData - : new Config(); - // replace - $cookie->set($key, $cookieData); - $cookieName = $cookieData->get('name'); - $cookieName = is_string($cookieName) && trim($cookieName) !== '' - ? trim($cookieName) - : $names['name']; - $cookieName = preg_replace( - '~[^!#$%&\'*+-.^_`|\~a-z0-9]~i', - '', - $cookieName - ); - - $cookieName = $cookieName === '' ? $names['name'] : $cookieName; - $cookieLifetime = $cookieData->get('lifetime'); - $cookieLifetime = is_numeric($cookieLifetime) ? $cookieLifetime : 0; - $cookieLifetime = max((int) $cookieLifetime, 0); - $cookieWildcard = $cookieData->get('wildcard') === true; - $this->cookieNames[$key]['name'] = $cookieName; - $this->cookieNames[$key]['wildcard'] = $cookieWildcard; - $this->cookieNames[$key]['lifetime'] = $cookieLifetime; - } - - return $this; - } - - public function getRequest(): ?ServerRequestInterface - { - return $this->request; - } - - public function setRequest(ServerRequestInterface $request): void - { - $this->request = $request; - } - - public function isAuthProcessed(): bool - { - return $this->authProcessed; - } - - private function doProcessAuth(): self - { - if (!$this->request || $this->authProcessed) { - return $this; - } - - $this->authProcessed = true; - $container = $this->getContainer(); - $userAuth = ContainerHelper::service(UserAuth::class, $container); - - $request = $this->getManager()->dispatch('auth.request', $this->request); - $request = $request instanceof ServerRequestInterface - ? $request - : $this->request; - $userAuth->getHashIdentity()->setUserAgent( - $request->getHeaderLine('User-Agent') - ); - - $cookieNames = $this->getCookieNames(); - $cookieParams = $request->getCookieParams(); - $adminCookie = $cookieParams[$cookieNames[self::ADMIN_MODE]['name']]??null; - $adminCookie = !is_string($adminCookie) ? $adminCookie : null; - $userCookie = $cookieParams[$cookieNames[self::USER_MODE]['name']]??null; - $userCookie = is_string($userCookie) ? $userCookie : null; - - $this->userAccount = $userCookie ? $userAuth->getUser( - $userCookie, - $this->getUserEntityFactory() - ) : null; - $this->adminAccount = $adminCookie ? $userAuth->getUser( - $adminCookie, - $this->getAdminEntityFactory() - ) : null; - return $this; - } - - private function createEntityFactoryContainer(): self - { - $container = $this->getContainer(); - $hasUserEntity = $container->has(UserEntityFactory::class); - $hasAdminEntity = $container->has(AdminEntityFactory::class); - if ($hasUserEntity && $hasAdminEntity) { - return $this; - } - if ($container instanceof Container) { - if (!$hasUserEntity) { - $container->set(UserEntityFactory::class, UserEntityFactory::class); - } - if (!$hasUserEntity) { - $container->set(AdminEntityFactory::class, AdminEntityFactory::class); - } - return $this; - } - if (!$hasUserEntity) { - $container->set( - UserEntityFactory::class, - fn() => ContainerHelper::resolveCallable(UserEntityFactory::class, $container) - ); - } - if (!$hasAdminEntity) { - $container->set( - AdminEntityFactory::class, - fn() => ContainerHelper::resolveCallable(AdminEntityFactory::class, $container) - ); - } - return $this; - } - - public function getAdminEntityFactory() : AdminEntityFactory - { - return $this - ->createEntityFactoryContainer() - ->getContainer() - ->get(AdminEntityFactory::class); - } - - public function getUserEntityFactory() : UserEntityFactory - { - return $this - ->createEntityFactoryContainer() - ->getContainer() - ->get(UserEntityFactory::class); - } - - public function getPermission(): PermissionInterface - { - $container = $this->doResolvePermission()->getContainer(); - $permission = ContainerHelper::service(PermissionInterface::class, $container); - return $permission instanceof PermissionWrapper - ? $permission - : $this->permission; - } - - public function setAsAdminMode(): void - { - $this->currentMode = self::ADMIN_MODE; - } - - public function setAsUserMode(): void - { - $this->currentMode = self::ADMIN_MODE; - } - - public function getCurrentMode(): string - { - return $this->currentMode; - } - - public function isLoggedIn() : bool - { - return match ($this->getCurrentMode()) { - self::ADMIN_MODE => $this->isAdminLoggedIn(), - self::USER_MODE => $this->isUserLoggedIn(), - default => false - }; - } - - public function getAccount() : User|Admin|null - { - return match ($this->getCurrentMode()) { - self::ADMIN_MODE => $this->getAdminAccount(), - self::USER_MODE => $this->getUserAccount(), - default => null - }; - } - - public function getUserAccount(): ?User - { - return $this->doProcessAuth()->userAccount; - } - - public function getAdminAccount(): ?Admin - { - return $this->doProcessAuth()->adminAccount; - } - - /** - * @return array{ - * user: array{name:string, lifetime: int, wildcard: bool}, - * admin: array{name:string, lifetime: int, wildcard: bool} - * } - */ - public function getCookieNames(): array - { - return $this->doResolveCookieName()->cookieNames; - } - - /** - * @param string $type - * @return ?array{name:string, lifetime: int, wildcard: bool} - */ - public function getCookieNameData(string $type): ?array - { - return $this->getCookieNames()[$type]??null; - } - - public function sendAuthCookie( - AbstractUser $userEntity, - ResponseInterface $response - ) : ResponseInterface { - $container = $this->getContainer(); - $userAuth = ContainerHelper::service(UserAuth::class, $container); - - if (!$userAuth instanceof UserAuth) { - throw new RuntimeException( - 'Can not determine use auth object' - ); - } - $cookieName = $userEntity instanceof Admin - ? 'admin' - : ($userEntity instanceof User ? 'user' : null); - $settings = $cookieName ? $this->getCookieNameData($cookieName) : null; - if ($settings === null) { - throw new RuntimeException( - 'Can not determine cookie type' - ); - } - $request = $this->request??ServerRequest::fromGlobals( - $container->has(ServerRequestFactory::class) - ?$container->get(ServerRequestFactory::class) - : null - ); - $domain = $request->getUri()->getHost(); - $newDomain = $this->getManager()?->dispatch( - 'auth.cookieDomain', - $domain - ); - - $domain = is_string($newDomain) && filter_var( - $newDomain, - FILTER_VALIDATE_DOMAIN - ) ? $newDomain : $domain; - - if ($settings['wildcard']) { - $domain = DataNormalizer::splitCrossDomain($domain); - } - $cookie = new SetCookie( - name: $settings['name'], - value: $userAuth->getHashIdentity()->generate($userEntity->getId()), - expiresAt: $settings['lifetime'] === 0 ? 0 : $settings['lifetime'] + time(), - path: '/', - domain: $domain - ); - $cookieObject = $this->getManager()?->dispatch( - 'auth.cookieObject', - $cookie - ); - $cookie = $cookieObject instanceof SetCookie - ? $cookieObject - : $cookie; - return $cookie->appendToResponse($response); - } - - public function isAdminLoggedIn(): bool - { - return $this->getAdminAccount() !== null; - } - - public function isUserLoggedIn(): bool - { - return $this->getUserAccount() !== null; - } - - /** - * @param int $id - * @return ?Admin - */ - public function getAdminById(int $id): ?UserEntityInterface - { - return $this->getAdminEntityFactory()->findById($id); - } - - /** - * @param string $username - * @return ?Admin - */ - public function getAdminByUsername(string $username): ?UserEntityInterface - { - return $this->getAdminEntityFactory()->findByUsername($username); - } - - /** - * @param int $id - * @return ?User - */ - public function getUserById(int $id): ?UserEntityInterface - { - return $this->getUserEntityFactory()->findById($id); - } - - /** - * @param string $username - * @return ?User - */ - public function getUserByUsername(string $username) : ?UserEntityInterface - { - return $this->getUserEntityFactory()->findByUsername($username); - } -} diff --git a/composer.json b/composer.json index 2371e68..3d6616e 100644 --- a/composer.json +++ b/composer.json @@ -23,11 +23,6 @@ "squizlabs/php_codesniffer": "3.7.2", "slevomat/coding-standard": "^8.13" }, - "scripts": { - "post-create-project-cmd": [ - "ArrayAccess\\TrayDigita\\ComposerCreateProject::composerDoCreateProject" - ] - }, "config": { "optimize-autoloader": true, "allow-plugins": { diff --git a/phpcs.xml b/phpcs.xml index 147756d..480a216 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,4 +1,5 @@ + TrayDigita Core Module Coding Standard