pluginId = $pluginId; $this->registerManifestFolder(); } public function registerMiddleware(array $middleware) { EventDispatcher::add_filter_listener('leantime.core.middleware.loadplugins.handle.pluginsEvents', function (array $existing) use ($middleware) { return array_merge($existing, $middleware); } ); EventDispatcher::add_filter_listener('leantime.core.http.httpkernel.*.plugins_middleware', function (array $existing) use ($middleware) { return array_merge($existing, $middleware); } ); } public function registerLanguageFiles(array $languages = []) { $pluginId = $this->pluginId; if (empty($languages)) { $languages = $this->findLanguageFiles(); } EventDispatcher::add_event_listener('leantime.core.middleware.loadplugins.handle.pluginsEvents', function () use ($languages) { $language = app()->make(Language::class); $config = app()->make(Environment::class); $currentUserLanguage = session('usersettings.language'); // At this point in the stack localization has already determined user language and set up the core language // array in the language of the users choice. First we register english if it is in the array and then we // override with the user language if (in_array('en-US', $languages)) { $pluginLangArray = $this->loadPluginLanguage('en-US'); $language->mergeLanguageArray($pluginLangArray); } // Now check the user language and override if needed if (in_array($currentUserLanguage, $languages)) { $pluginLangArray = $this->loadPluginLanguage($currentUserLanguage); $language->mergeLanguageArray($pluginLangArray); } }, 5); } private function findLanguageFiles(): array { $pluginPath = APP_ROOT.'/app/Plugins/'; $languageDir = '/Language/'; // Check both possible locations for language files $pharPath = "phar://{$pluginPath}{$this->pluginId}/{$this->pluginId}.phar".$languageDir; $regularPath = "{$pluginPath}{$this->pluginId}".$languageDir; $languageFiles = []; // Check regular directory first if (is_dir($regularPath)) { $files = scandir($regularPath); foreach ($files as $file) { if (substr($file, -4) === '.ini') { $languageFiles[] = substr($file, 0, -4); } } } // Check phar if no files found in regular directory if (empty($languageFiles) && file_exists($pharPath)) { $files = scandir($pharPath); foreach ($files as $file) { if (substr($file, -4) === '.ini') { $languageFiles[] = substr($file, 0, -4); } } } return ! empty($languageFiles) ? $languageFiles : []; } private function loadPluginLanguage($language): array|false { if (Cache::store('installation')->has($this->pluginId.'.language.'.$language)) { return Cache::store('installation')->get($this->pluginId.'.language.'.$language); } $pluginPath = APP_ROOT.'/app/Plugins/'; $pharPath = "phar://{$pluginPath}{$this->pluginId}/{$this->pluginId}.phar"; $regularPath = "{$pluginPath}{$this->pluginId}"; $languagePath = "/Language/{$language}.ini"; // Check phar first if (file_exists($pharPath.$languagePath)) { $completeLanguagePath = $pharPath.$languagePath; } elseif (file_exists($regularPath.$languagePath)) { $completeLanguagePath = $regularPath.$languagePath; } else { // Language file doesn't exist Cache::store('installation')->set($this->pluginId.'.language.'.$language, false, new \DateInterval('P7D')); return false; } $languageArray = parse_ini_file($completeLanguagePath, true); // We're caching the results no matter what, language file is not going to magically appear. // So even a false is valid as parse_ini_is too expensive to run every time Cache::store('installation')->set($this->pluginId.'.language.'.$language, $languageArray, new \DateInterval('P7D')); return $languageArray; } private function getPluginBasePath() { $pluginPath = APP_ROOT.'/app/Plugins/'; $pharPath = "phar://{$pluginPath}{$this->pluginId}/{$this->pluginId}.phar"; $regularPath = "{$pluginPath}{$this->pluginId}"; if (file_exists($pharPath)) { return $pharPath; } if (file_exists($regularPath)) { return $regularPath; } return '/'; } public function addMenuItem(array $item, string $section, array $location) { $pluginId = $this->pluginId; EventDispatcher::add_filter_listener('leantime.domain.menu.repositories.menu.getMenuStructure.menuStructures.'.$section, function ($menu) use ($item, $location, $pluginId) { // Prepare $item['title'] = " ".__($item['title']); $item['tooltip'] = __($item['tooltip']); $item['type'] = 'item'; $item['module'] = $pluginId; $primaryLocationKey = $location[0] ?? $menu[count($menu)]; if (count($location) <= 1) { $menu[$primaryLocationKey] = $item; } if (count($location) == 2) { $submenuLocationKey = $location[1] ?? $menu[$primaryLocationKey]['submenu'][count( $menu[$primaryLocationKey]['submenu'] )]; $menu[$primaryLocationKey]['submenu'][$submenuLocationKey] = $item; } return $menu; }, 50 ); EventDispatcher::add_filter_listener('leantime.domain.menu.repositories.menu.getSectionMenuType.menuSections', function ($routes) use ($section, $item) { $route = str_replace('/', '.', $item['href']); $array = explode('.', $route); if ($route[0] == '.') { array_shift($array); } $route = implode('.', $array); return array_merge($routes, [$route => $section]); }, ); } public function addHeaderJs(array $jsFiles) { EventDispatcher::add_event_listener('leantime.*.afterLinkTags', function () use ($jsFiles) { $mix = app()->make(\Leantime\Core\Support\Mix::class); $basePath = $this->getPluginBasePath(); // Add jQuery UI for draggable and resizable functionality $files = $mix->getManifest()[$basePath.'/dist']; foreach ($jsFiles as $jsFile) { if (isset($files[$jsFile])) { echo ''; } } }); } protected function registerManifestFolder() { if ($this->distFolderRegistered === false) { $distPath = ''; $basePath = $this->getPluginBasePath(); if (file_exists($basePath.'/dist')) { $distPath = $basePath.'/dist'; } if ($distPath !== '') { EventDispatcher::add_filter_listener( 'leantime.core.support.mix.__construct.mix_manifest_directories', function (array $directories) use ($distPath) { return array_merge($directories, [$distPath]); } ); $this->distFolderRegistered = true; } } } public function addFooterJs(array $paths) { EventDispatcher::add_event_listener('leantime.*.beforeBodyClose', function () use ($paths) { $mix = app()->make(\Leantime\Core\Support\Mix::class); $basePath = $this->getPluginBasePath(); // Add jQuery UI for draggable and resizable functionality $files = $mix->getManifest()[$basePath.'/dist']; foreach ($paths as $jsFile) { if (isset($files[$jsFile])) { echo ''; } } }); } public function addCss(array $paths) { EventDispatcher::add_event_listener('leantime.*.afterLinkTags', function () use ($paths) { $mix = app()->make(\Leantime\Core\Support\Mix::class); $basePath = $this->getPluginBasePath(); // Add jQuery UI for draggable and resizable functionality $files = $mix->getManifest()[$basePath.'/dist']; foreach ($paths as $cssFile) { if (isset($files[$cssFile])) { echo ''; } } }); } }