From 3d3cb8d5a2facfb96383b2b934957c34a1851785 Mon Sep 17 00:00:00 2001 From: ArrayIterator Date: Thu, 19 Oct 2023 07:58:18 +0700 Subject: [PATCH] patch depends --- Core.php | 101 ++++++++++++++++ .../Controllers/RepairNeeded.php | 48 ++++++++ .../Views/contents/config-install.phtml | 24 ++++ .../Controllers/Views/install.phtml | 3 + .../Controllers/Views/repair.phtml | 53 +++++++++ .../Middlewares/ErrorMiddleware.php | 9 ++ .../Middlewares/InitMiddleware.php | 111 ++++++++++++++---- .../ServiceInitializer/ServiceInitializer.php | 9 +- SubModules/Sites/Sites.php | 2 +- .../Middlewares/TemplateMiddleware.php | 51 ++++++++ SubModules/Templates/Templates.php | 24 ++-- 11 files changed, 394 insertions(+), 41 deletions(-) create mode 100644 SubModules/ServiceInitializer/Controllers/RepairNeeded.php create mode 100644 SubModules/ServiceInitializer/Controllers/Views/contents/config-install.phtml create mode 100644 SubModules/ServiceInitializer/Controllers/Views/repair.phtml create mode 100644 SubModules/Templates/Middlewares/TemplateMiddleware.php diff --git a/Core.php b/Core.php index 5360868..0c870b9 100644 --- a/Core.php +++ b/Core.php @@ -20,6 +20,23 @@ use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Templates\Templates; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Translator\Translator; use ArrayAccess\TrayDigita\App\Modules\Media\Media; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Admin; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\AdminLog; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\AdminMeta; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\AdminOnlineActivity; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Attachment; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Capability; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\Role; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\RoleCapability; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\User as UserEntity; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserAttachment; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserLog; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserMeta; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserOnlineActivity; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserTerm; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserTermGroup; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserTermGroupMeta; +use ArrayAccess\TrayDigita\App\Modules\Users\Entities\UserTermMeta; use ArrayAccess\TrayDigita\App\Modules\Users\Users; use ArrayAccess\TrayDigita\Benchmark\Aggregator\EventAggregator; use ArrayAccess\TrayDigita\Benchmark\Injector\ManagerProfiler; @@ -32,6 +49,7 @@ use ArrayAccess\TrayDigita\Util\Filter\Consolidation; use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper; use ArrayAccess\TrayDigita\View\Interfaces\ViewInterface; +use Doctrine\DBAL\Exception; use Doctrine\ORM\Mapping\Driver\AttributeDriver; use function class_exists; use function strtolower; @@ -81,6 +99,55 @@ final class Core extends AbstractModule Translator::class, ]; + const ENTITY_CHECKING = [ + 'required' => [ + Entities\Option::class, + Entities\Post::class, + Entities\PostCategory::class, + Entities\PostMeta::class, + Entities\TaskScheduler::class, + Admin::class, + AdminLog::class, + AdminMeta::class, + AdminOnlineActivity::class, + Attachment::class, + Capability::class, + Role::class, + RoleCapability::class, + UserEntity::class, + UserAttachment::class, + UserLog::class, + UserMeta::class, + UserOnlineActivity::class, + UserTerm::class, + UserTermGroup::class, + UserTermGroupMeta::class, + UserTermMeta::class, + ], + 'optional' => [ + Entities\Announcement::class, + Entities\Book::class, + Entities\BookAuthor::class, + Entities\BookCategory::class, + Entities\BookPublisher::class, + Entities\Classes::class, + Entities\ClassMeta::class, + Entities\Department::class, + Entities\DepartmentMeta::class, + Entities\Faculty::class, + Entities\FacultyMeta::class, + Entities\Question::class, + Entities\QuestionCategory::class, + Entities\Quiz::class, + ], + 'additional' => [ + Entities\CacheItem::class, + Entities\LogItem::class, + Entities\Translation::class, + Entities\Site::class, + ] + ]; + /** * @var array */ @@ -370,4 +437,38 @@ private function doInitSubModules($modules) } return $modules; } + + /** + * @var ?array + */ + private ?array $entityChecking = null; + + /** + * @throws Exception + * @return array{ + * required: array, + * optionsl: array, + * additional: array, + * tables: array, + * } + */ + public function checkEntity(): array + { + if ($this->entityChecking !== null) { + return $this->entityChecking; + } + $schema = $this->getConnection()->createSchemaManager(); + $em = $this->getConnection()->getEntityManager(); + $schema = $schema->introspectSchema(); + $this->entityChecking = []; + foreach (self::ENTITY_CHECKING as $type => $entities) { + $this->entityChecking[$type] = []; + foreach ($entities as $entity) { + $metadata = $em->getClassMetadata($entity); + $this->entityChecking[$type][$entity] = $schema->hasTable($metadata->getTableName()); + $this->entityChecking['tables'][$entity] = $metadata->getTableName(); + } + } + return $this->entityChecking; + } } diff --git a/SubModules/ServiceInitializer/Controllers/RepairNeeded.php b/SubModules/ServiceInitializer/Controllers/RepairNeeded.php new file mode 100644 index 0000000..cb9e9c8 --- /dev/null +++ b/SubModules/ServiceInitializer/Controllers/RepairNeeded.php @@ -0,0 +1,48 @@ +render( + 'repair', + [ + 'request' => $request + ], + $response + )->withStatus(Code::SERVICE_UNAVAILABLE); + } +} diff --git a/SubModules/ServiceInitializer/Controllers/Views/contents/config-install.phtml b/SubModules/ServiceInitializer/Controllers/Views/contents/config-install.phtml new file mode 100644 index 0000000..52117fd --- /dev/null +++ b/SubModules/ServiceInitializer/Controllers/Views/contents/config-install.phtml @@ -0,0 +1,24 @@ + +
+
+

translateContext('Installation', 'installation', 'core-module');?>

+
+
diff --git a/SubModules/ServiceInitializer/Controllers/Views/install.phtml b/SubModules/ServiceInitializer/Controllers/Views/install.phtml index 2ee9325..fcff2d5 100644 --- a/SubModules/ServiceInitializer/Controllers/Views/install.phtml +++ b/SubModules/ServiceInitializer/Controllers/Views/install.phtml @@ -47,6 +47,9 @@ $translator = $core->getTranslator(); case KernelInterface::CONFIG_NOT_ITERABLE: require __DIR__ . '/contents/config-not-iterable.phtml'; break; + default: + require __DIR__ . '/contents/config-install.phtml'; + break; } ?> diff --git a/SubModules/ServiceInitializer/Controllers/Views/repair.phtml b/SubModules/ServiceInitializer/Controllers/Views/repair.phtml new file mode 100644 index 0000000..a54741e --- /dev/null +++ b/SubModules/ServiceInitializer/Controllers/Views/repair.phtml @@ -0,0 +1,53 @@ +getView()->getContainer()) + ->get(Core::class); +$entities = $core->checkEntity(); +$tables = $entities['tables']; +$requiredModules = array_keys(array_filter($entities['required'], static fn($e) => $e === false)); +?> + + + + + + + <?= htmlentities($core->translateContext('Incomplete Database Schema', 'module-not-found', 'core-module'));?> + + + +
+
+

503

+

translateContext( + 'Database schema are incomplete', + 'incomplete-schema', + 'core-module' + ); + ?>

+
    + +
  • + + +
+
+
+ + diff --git a/SubModules/ServiceInitializer/Middlewares/ErrorMiddleware.php b/SubModules/ServiceInitializer/Middlewares/ErrorMiddleware.php index 629b610..a5bc477 100644 --- a/SubModules/ServiceInitializer/Middlewares/ErrorMiddleware.php +++ b/SubModules/ServiceInitializer/Middlewares/ErrorMiddleware.php @@ -3,7 +3,9 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Middlewares; +use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\ServiceInitializer; use ArrayAccess\TrayDigita\Middleware\ErrorMiddleware as CoreErrorMiddleware; +use Psr\Container\ContainerInterface; use const PHP_INT_MAX; class ErrorMiddleware extends CoreErrorMiddleware @@ -12,4 +14,11 @@ class ErrorMiddleware extends CoreErrorMiddleware * @var int set as highest priority */ protected int $priority = PHP_INT_MAX; + + public function __construct( + ContainerInterface $container, + public readonly ServiceInitializer $serviceInitializer + ) { + parent::__construct($container); + } } diff --git a/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php b/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php index ccc4dea..0ec5525 100644 --- a/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php +++ b/SubModules/ServiceInitializer/Middlewares/InitMiddleware.php @@ -4,58 +4,127 @@ namespace ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Middlewares; use ArrayAccess\TrayDigita\App\Modules\Core\Core; +use ArrayAccess\TrayDigita\App\Modules\Core\Entities\Option as OptionEntity; +use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Option\Option; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Controllers\InstallController; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\Controllers\RequireModuleController; +use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\ServiceInitializer\ServiceInitializer; use ArrayAccess\TrayDigita\Http\Interfaces\HttpExceptionInterface; +use ArrayAccess\TrayDigita\Http\SetCookie; +use ArrayAccess\TrayDigita\L10n\Languages\Locale; use ArrayAccess\TrayDigita\L10n\Translations\Interfaces\TranslatorInterface; use ArrayAccess\TrayDigita\Middleware\AbstractMiddleware; -use ArrayAccess\TrayDigita\Module\Modules; use ArrayAccess\TrayDigita\Routing\Interfaces\RouterInterface; use ArrayAccess\TrayDigita\Routing\MatchedRoute; use ArrayAccess\TrayDigita\Util\Filter\Consolidation; use ArrayAccess\TrayDigita\Util\Filter\ContainerHelper; +use Doctrine\DBAL\Exception; +use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use function array_filter; +use function is_string; use const PHP_INT_MAX; class InitMiddleware extends AbstractMiddleware { - protected int $priority = PHP_INT_MAX - 10000; + protected int $priority = PHP_INT_MAX - 1000; + + public function __construct( + ContainerInterface $container, + public readonly ServiceInitializer $serviceInitializer + ) { + parent::__construct($container); + } /** - * @throws HttpExceptionInterface + * @throws HttpExceptionInterface|Exception */ protected function doProcess(ServerRequestInterface $request): ServerRequestInterface|ResponseInterface { - // enhance views - $container = $this->getContainer(); - $core = ContainerHelper::service(Modules::class) - ->get(Core::class); - $translator = $core->getTranslator()??ContainerHelper::use(TranslatorInterface::class, $container); - $view = $core->getView(); - if ($translator) { - $view->setRequest($request); - $view->setParameter('language', $translator->getLanguage()); - } - if (Consolidation::isCli()) { return $request; } // REGISTER CONTROLLER IF CONFIG ERRORS - $router = $core->getKernel()->getHttpKernel()->getRouter(); - if ($core->getKernel()->getConfigError()) { + $router = $this->serviceInitializer->getKernel()->getHttpKernel()->getRouter(); + foreach ($this->serviceInitializer->core->getRequiredModules() as $module) { + if (!$this->serviceInitializer->core->getModules()->has($module)) { + $router->addRouteController(RequireModuleController::class); + return $this->doHandle($router, $request); + } + } + + if ($this->serviceInitializer->core->getKernel()->getConfigError()) { $router->addRouteController(InstallController::class); return $this->doHandle($router, $request); } + $countRequired = count(Core::ENTITY_CHECKING['required']); + $entities = array_filter( + $this->serviceInitializer->core->checkEntity()['required'], + static fn ($e) => $e === false + ); + if ($countRequired === count($entities)) { + $router->addRouteController(InstallController::class); + return $this->doHandle($router, $request); + } + $option = $this->serviceInitializer->getModule(Option::class); + $translator = $this + ->serviceInitializer + ->core + ->getTranslator() ?? ContainerHelper::use( + TranslatorInterface::class, + $this->getContainer() + ); + $optionLanguage = $option?->get('language'); + $language = $optionLanguage?->getValue(); + $language = $option && $language ? Locale::normalizeLocale($language) : null; + if (!$language) { + $optionLanguage ??= $option + ?->createNewOptionEntityObject('language') + ??new OptionEntity(); + $language = $translator?->getLanguage()??$this + ->serviceInitializer + ->core + ->getView()?->getParameter('language'); + $optionLanguage->setName('language'); + $language = is_string($language) ? Locale::normalizeLocale($language) : 'en'; + $optionLanguage->setValue($language); + $em = $this->serviceInitializer->core->getConnection()->getEntityManager(); + $optionLanguage->setEntityManager($em); + $em->persist($optionLanguage); + $em->flush(); + } - foreach ($core->getRequiredModules() as $module) { - if (!$core->getModules()->has($module)) { - $router->addRouteController(RequireModuleController::class); - return $this->doHandle($router, $request); - } + // set user language + $originSelectedLanguage = $request->getCookieParams()[ServiceInitializer::LANGUAGE_COOKIE]??null; + $selectedLanguage = $language; + if ($originSelectedLanguage) { + $selectedLanguage = Locale::normalizeLocale($originSelectedLanguage)??$language; + } + if ($originSelectedLanguage !== $selectedLanguage) { + $this->getManager()?->attach( + 'response.final', + static function ($response) use ($language) { + if (!$response instanceof ResponseInterface) { + return $response; + } + return $response->withAddedHeader( + 'Set-Cookie', + (string) new SetCookie( + ServiceInitializer::LANGUAGE_COOKIE, + $language, + path: '/' + ) + ); + } + ); } + $translator?->setLanguage($selectedLanguage); + $view = $this->serviceInitializer->core->getView(); + $view->setRequest($request); + $view->setParameter('language', $selectedLanguage); return $request; } diff --git a/SubModules/ServiceInitializer/ServiceInitializer.php b/SubModules/ServiceInitializer/ServiceInitializer.php index 524b623..8092c00 100644 --- a/SubModules/ServiceInitializer/ServiceInitializer.php +++ b/SubModules/ServiceInitializer/ServiceInitializer.php @@ -23,6 +23,8 @@ final class ServiceInitializer extends CoreSubmoduleAbstract protected string $name = 'Service Initializer'; + const LANGUAGE_COOKIE = 'language'; + /** * @var array> */ @@ -107,11 +109,7 @@ private function doRegisterMiddlewares(): void return; } foreach ($this->middlewares as $middleware) { - try { - $middleware = ContainerHelper::resolveCallable($middleware, $this->getContainer()); - $kernel->addMiddleware($middleware); - } catch (Throwable) { - } + $kernel->addDeferredMiddleware(new $middleware($this->getContainer(), $this)); } } @@ -134,5 +132,6 @@ private function doSetLanguage(): void return; } $this->core->getTranslator()?->setLanguage($language); + $this->core->getView()?->setParameter('language', $language); } } diff --git a/SubModules/Sites/Sites.php b/SubModules/Sites/Sites.php index 37424b1..6ac3994 100644 --- a/SubModules/Sites/Sites.php +++ b/SubModules/Sites/Sites.php @@ -51,7 +51,7 @@ protected function doInit(): void private function doAddMiddleware(): void { $container = $this->getContainer(); - $this->getKernel()?->getHttpKernel()->addMiddleware( + $this->getKernel()?->getHttpKernel()->addDeferredMiddleware( new class($container, $this) extends AbstractMiddleware { protected int $priority = PHP_INT_MAX - 10; public function __construct( diff --git a/SubModules/Templates/Middlewares/TemplateMiddleware.php b/SubModules/Templates/Middlewares/TemplateMiddleware.php new file mode 100644 index 0000000..867cd63 --- /dev/null +++ b/SubModules/Templates/Middlewares/TemplateMiddleware.php @@ -0,0 +1,51 @@ +templates->getModule(Option::class); + $active = $option?->get(Templates::ACTIVE_TEMPLATE_KEY)?->getValue(); + if (is_string($active)) { + $this->templates->getTemplateRule()->setActive($active); + } + + $template = $this->templates->getTemplateRule()->getActive(); + if ($template) { + if ($template->getBasePath() !== $active && $option) { + $option->set(Templates::ACTIVE_TEMPLATE_KEY, $template->getBasePath(), true); + } + $this->templates->core->getView()->setViewsDirectory([]); + } + + return $request; + } +} diff --git a/SubModules/Templates/Templates.php b/SubModules/Templates/Templates.php index 3c0dee0..75c21e9 100644 --- a/SubModules/Templates/Templates.php +++ b/SubModules/Templates/Templates.php @@ -5,6 +5,7 @@ use ArrayAccess\TrayDigita\App\Modules\Core\Abstracts\CoreSubmoduleAbstract; use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Option\Option; +use ArrayAccess\TrayDigita\App\Modules\Core\SubModules\Templates\Middlewares\TemplateMiddleware; use ArrayAccess\TrayDigita\Kernel\Interfaces\KernelInterface; use ArrayAccess\TrayDigita\Templates\Middlewares\TemplateLoaderMiddleware; use ArrayAccess\TrayDigita\Templates\Wrapper; @@ -57,6 +58,7 @@ private function initSetTemplate($module, KernelInterface $kernel) if ($kernel->getConfigError()) { return $module; } + $view = $this->core->getView(); $templateRule = $view->getTemplateRule(); $wrapper = $templateRule?->getWrapper() @@ -64,30 +66,24 @@ private function initSetTemplate($module, KernelInterface $kernel) if (!$wrapper) { return $module; } + $this->templateRule = $templateRule instanceof TemplateRule ? $templateRule : new TemplateRule($wrapper); $this->templateRule->initialize(); $view->setTemplateRule($this->templateRule); - - $option = $this->getModules()->get(Option::class); - $active = $option?->get(self::ACTIVE_TEMPLATE_KEY)?->getValue(); - if (is_string($active)) { - $this->templateRule->setActive($active); - } - $template = $this->templateRule->getActive(); - if ($template) { - if ($template->getBasePath() !== $active && $option) { - $option->set(self::ACTIVE_TEMPLATE_KEY, $template->getBasePath(), true); - } - $view->setViewsDirectory([]); - } + $kernel->getHttpKernel()->addDeferredMiddleware( + new TemplateMiddleware( + $view->getContainer()??$this->getContainer(), + $this + ) + ); /** * add middleware to load template.php * @see TemplateLoaderMiddleware::doProcess() */ - $kernel->getHttpKernel()->addMiddleware( + $kernel->getHttpKernel()->addDeferredMiddleware( new TemplateLoaderMiddleware( $view->getContainer()??$this->getContainer() )