Commit dc990e43 authored by Tino Goratsch's avatar Tino Goratsch

made it possible to override translation inside the themes

...this includes to theme inheritance feature too!
parent 46e4cef8
...@@ -33,6 +33,7 @@ abstract class AbstractBootstrap implements BootstrapInterface ...@@ -33,6 +33,7 @@ abstract class AbstractBootstrap implements BootstrapInterface
/** /**
* @param string $appMode * @param string $appMode
*
* @throws \Exception * @throws \Exception
*/ */
public function __construct($appMode) public function __construct($appMode)
......
...@@ -39,6 +39,7 @@ class ServiceContainerBuilder extends ContainerBuilder ...@@ -39,6 +39,7 @@ class ServiceContainerBuilder extends ContainerBuilder
/** /**
* ServiceContainerBuilder constructor. * ServiceContainerBuilder constructor.
*
* @param ApplicationPath $applicationPath * @param ApplicationPath $applicationPath
* @param SymfonyRequest $symfonyRequest * @param SymfonyRequest $symfonyRequest
* @param string $applicationMode * @param string $applicationMode
...@@ -103,6 +104,7 @@ class ServiceContainerBuilder extends ContainerBuilder ...@@ -103,6 +104,7 @@ class ServiceContainerBuilder extends ContainerBuilder
* @param \ACP3\Core\Environment\ApplicationPath $applicationPath * @param \ACP3\Core\Environment\ApplicationPath $applicationPath
* @param SymfonyRequest $symfonyRequest * @param SymfonyRequest $symfonyRequest
* @param string $applicationMode * @param string $applicationMode
*
* @return ContainerBuilder * @return ContainerBuilder
*/ */
public static function create( public static function create(
......
<?php
/**
* Copyright (c) by the ACP3 Developers.
* See the LICENSE file at the top-level module directory for licensing details.
*/
namespace ACP3\Core\Environment;
use ACP3\Core\Settings\SettingsInterface;
use ACP3\Core\XML;
use ACP3\Modules\ACP3\System\Installer\Schema;
class Theme
{
/**
* @var \ACP3\Core\Settings\SettingsInterface
*/
private $settings;
/**
* @var \ACP3\Core\XML
*/
private $xml;
/**
* @var array
*/
private $availableThemes = [];
/**
* @var array
*/
private $sortedThemeDependencies = [];
/**
* Design constructor.
*
* @param \ACP3\Core\Settings\SettingsInterface $settings
* @param \ACP3\Core\XML $xml
*/
public function __construct(
SettingsInterface $settings,
XML $xml
) {
$this->settings = $settings;
$this->xml = $xml;
}
public function getAvailableThemes(): array
{
if (empty($this->availableThemes)) {
$this->setAvailableThemes();
}
return $this->availableThemes;
}
private function setAvailableThemes(): void
{
$designs = \glob(ACP3_ROOT_DIR . '/designs/*/info.xml');
if ($designs === false) {
return;
}
foreach ($designs as $design) {
$designInfo = $this->xml->parseXmlFile($design, '/design');
if (!empty($designInfo)) {
$identifier = $this->getThemeInternalName($design);
$this->availableThemes[$identifier] = $designInfo;
}
}
}
/**
* @param string $file
*
* @return string
*/
private function getThemeInternalName(string $file): string
{
$path = \dirname($file);
$pathParts = \explode('/', $path);
return $pathParts[\count($pathParts) - 1];
}
/**
* @return array
*/
public function getCurrentThemeDependencies(): array
{
$settings = $this->settings->getSettings(Schema::MODULE_NAME);
return $this->getThemeDependencies($settings['design']);
}
/**
* @param string $themeName
*
* @return array
*/
public function getThemeDependencies(string $themeName): array
{
if (!isset($this->sortedThemeDependencies[$themeName])) {
$this->setThemeDependencies($themeName);
}
return $this->sortedThemeDependencies[$themeName];
}
private function setThemeDependencies(string $themeName): void
{
$availableThemes = $this->getAvailableThemes();
if (!isset($availableThemes[$themeName]['parent'])) {
$this->sortedThemeDependencies[$themeName] = [$themeName];
return;
}
$currentTheme = $themeName;
$parents = [$themeName];
do {
$currentTheme = $availableThemes[$currentTheme]['parent'];
$parents[] = $currentTheme;
} while (isset($availableThemes[$currentTheme]['parent']));
$this->sortedThemeDependencies[$themeName] = $parents;
}
}
...@@ -9,6 +9,8 @@ namespace ACP3\Core\I18n; ...@@ -9,6 +9,8 @@ namespace ACP3\Core\I18n;
use ACP3\Core\Cache; use ACP3\Core\Cache;
use ACP3\Core\Environment\ApplicationPath; use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\Environment\Theme;
use ACP3\Core\Modules;
use ACP3\Core\Modules\Vendor; use ACP3\Core\Modules\Vendor;
use Fisharebest\Localization\Locale; use Fisharebest\Localization\Locale;
...@@ -28,22 +30,36 @@ class DictionaryCache ...@@ -28,22 +30,36 @@ class DictionaryCache
* @var \ACP3\Core\Modules\Vendor * @var \ACP3\Core\Modules\Vendor
*/ */
protected $vendors; protected $vendors;
/**
* @var \ACP3\Core\Environment\Theme
*/
private $theme;
/**
* @var \ACP3\Core\Modules
*/
private $modules;
/** /**
* DictionaryCache constructor. * DictionaryCache constructor.
* *
* @param \ACP3\Core\Cache $cache * @param \ACP3\Core\Cache $cache
* @param \ACP3\Core\Environment\ApplicationPath $appPath * @param \ACP3\Core\Environment\ApplicationPath $appPath
* @param \ACP3\Core\Modules $modules
* @param \ACP3\Core\Modules\Vendor $vendors * @param \ACP3\Core\Modules\Vendor $vendors
* @param \ACP3\Core\Environment\Theme $theme
*/ */
public function __construct( public function __construct(
Cache $cache, Cache $cache,
ApplicationPath $appPath, ApplicationPath $appPath,
Vendor $vendors Modules $modules,
Vendor $vendors,
Theme $theme
) { ) {
$this->cache = $cache; $this->cache = $cache;
$this->appPath = $appPath; $this->appPath = $appPath;
$this->vendors = $vendors; $this->vendors = $vendors;
$this->theme = $theme;
$this->modules = $modules;
} }
/** /**
...@@ -52,8 +68,11 @@ class DictionaryCache ...@@ -52,8 +68,11 @@ class DictionaryCache
* @param string $language * @param string $language
* *
* @return array * @return array
*
* @throws \MJS\TopSort\CircularDependencyException
* @throws \MJS\TopSort\ElementNotFoundException
*/ */
public function getLanguageCache($language) public function getLanguageCache(string $language): array
{ {
if ($this->cache->contains($language) === false) { if ($this->cache->contains($language) === false) {
$this->saveLanguageCache($language); $this->saveLanguageCache($language);
...@@ -68,41 +87,84 @@ class DictionaryCache ...@@ -68,41 +87,84 @@ class DictionaryCache
* @param string $language * @param string $language
* *
* @return bool * @return bool
*
* @throws \MJS\TopSort\CircularDependencyException
* @throws \MJS\TopSort\ElementNotFoundException
*/ */
public function saveLanguageCache($language) public function saveLanguageCache(string $language): bool
{ {
$data = []; $locale = Locale::create($language);
$data = [
'info' => [
'direction' => $locale->script()->direction(),
],
'keys' => [],
];
foreach ($this->modules->getAllModulesTopSorted() as $module) {
$i18nFile = "{$this->appPath->getModulesDir()}{$module['vendor']}/{$module['dir']}/Resources/i18n/{$language}.xml";
if (\file_exists($i18nFile) === false) {
continue;
}
foreach ($this->vendors->getVendors() as $vendor) { $data['keys'] += $this->parseI18nFile($i18nFile, $module['dir']);
$languageFiles = \glob($this->appPath->getModulesDir() . $vendor . '/*/Resources/i18n/' . $language . '.xml'); }
if ($languageFiles !== false) { $themeDependenciesReversed = \array_reverse($this->theme->getCurrentThemeDependencies());
foreach ($languageFiles as $file) { foreach ($themeDependenciesReversed as $theme) {
if (isset($data['info']['direction']) === false) { $i18nFiles = \glob(ACP3_ROOT_DIR . "designs/{$theme}/*/i18n/{$language}.xml");
$locale = Locale::create($this->getLanguagePackIsoCode($file));
$data['info']['direction'] = $locale->script()->direction();
}
$module = $this->getModuleFromPath($file); if ($i18nFiles === false) {
continue;
}
// Iterate over all language keys foreach ($i18nFiles as $i18nFile) {
$xml = \simplexml_load_file($file); $data['keys'] = \array_merge(
foreach ($xml->keys->item as $item) { $data['keys'],
$data['keys'][\strtolower($module . (string) $item['key'])] = \trim((string) $item); $this->parseI18nFile($i18nFile, $this->getModuleNameFromThemePath($i18nFile))
} );
}
} }
} }
return $this->cache->save($language, $data); return $this->cache->save($language, $data);
} }
/**
* @param string $i18nFile
* @param string $moduleName
*
* @return array
*/
private function parseI18nFile(string $i18nFile, string $moduleName): array
{
$data = [];
$xml = \simplexml_load_file($i18nFile);
foreach ($xml->keys->item as $item) {
$data[\strtolower($moduleName . (string) $item['key'])] = \trim((string) $item);
}
return $data;
}
/**
* @param string $filePath
*
* @return string
*/
private function getModuleNameFromThemePath(string $filePath): string
{
$pathArray = \explode('/', $filePath);
return $pathArray[\count($pathArray) - 3];
}
/** /**
* Gets the cache for all registered languages. * Gets the cache for all registered languages.
* *
* @return array * @return array
*/ */
public function getLanguagePacksCache() public function getLanguagePacksCache(): array
{ {
if ($this->cache->contains('language_packs') === false) { if ($this->cache->contains('language_packs') === false) {
$this->saveLanguagePacksCache(); $this->saveLanguagePacksCache();
...@@ -116,21 +178,20 @@ class DictionaryCache ...@@ -116,21 +178,20 @@ class DictionaryCache
* *
* @return bool * @return bool
*/ */
protected function saveLanguagePacksCache() protected function saveLanguagePacksCache(): bool
{ {
$languagePacks = []; $languagePacks = [];
$languageFiles = \glob($this->appPath->getModulesDir() . '*/*/Resources/i18n/*.xml');
foreach ($this->vendors->getVendors() as $vendors) { if ($languageFiles !== false) {
$languageFiles = \glob($this->appPath->getModulesDir() . $vendors . '/*/Resources/i18n/*.xml'); foreach ($languageFiles as $file) {
$languagePack = $this->registerLanguagePack($file);
if ($languageFiles !== false) {
foreach ($languageFiles as $file) {
$languagePack = $this->registerLanguagePack($file);
if (!empty($languagePack)) { if (empty($languagePack)) {
$languagePacks += $languagePack; continue;
}
} }
$languagePacks += $languagePack;
} }
} }
...@@ -142,7 +203,7 @@ class DictionaryCache ...@@ -142,7 +203,7 @@ class DictionaryCache
* *
* @return array * @return array
*/ */
protected function registerLanguagePack($file) protected function registerLanguagePack(string $file): array
{ {
$languageIso = $this->getLanguagePackIsoCode($file); $languageIso = $this->getLanguagePackIsoCode($file);
......
...@@ -14,7 +14,7 @@ trait ExtractFromPathTrait ...@@ -14,7 +14,7 @@ trait ExtractFromPathTrait
* *
* @return string * @return string
*/ */
protected function getLanguagePackIsoCode($filePath) protected function getLanguagePackIsoCode(string $filePath): string
{ {
return \substr($filePath, \strrpos($filePath, '/') + 1, -4); return \substr($filePath, \strrpos($filePath, '/') + 1, -4);
} }
...@@ -24,7 +24,7 @@ trait ExtractFromPathTrait ...@@ -24,7 +24,7 @@ trait ExtractFromPathTrait
* *
* @return string * @return string
*/ */
protected function getModuleFromPath($filePath) protected function getModuleFromPath(string $filePath): string
{ {
$pathArray = \explode('/', $filePath); $pathArray = \explode('/', $filePath);
......
...@@ -35,6 +35,7 @@ class LoggerFactory ...@@ -35,6 +35,7 @@ class LoggerFactory
* @param string $level * @param string $level
* *
* @return LoggerInterface * @return LoggerInterface
*
* @throws \Exception * @throws \Exception
*/ */
public function create($channel, $level = LogLevel::DEBUG) public function create($channel, $level = LogLevel::DEBUG)
......
...@@ -39,6 +39,10 @@ class Modules ...@@ -39,6 +39,10 @@ class Modules
* @var array * @var array
*/ */
private $allModules = []; private $allModules = [];
/**
* @var array
*/
private $allModulesTopSorted = [];
/** /**
* @param \ACP3\Core\Environment\ApplicationPath $appPath * @param \ACP3\Core\Environment\ApplicationPath $appPath
...@@ -201,21 +205,25 @@ class Modules ...@@ -201,21 +205,25 @@ class Modules
* Returns an array with all modules which is sorted topologically. * Returns an array with all modules which is sorted topologically.
* *
* @return array * @return array
*
* @throws \MJS\TopSort\CircularDependencyException
* @throws \MJS\TopSort\ElementNotFoundException
*/ */
public function getAllModulesTopSorted(): array public function getAllModulesTopSorted(): array
{ {
$topSort = new StringSort(); if (empty($this->allModulesTopSorted)) {
$topSort = new StringSort();
$modules = $this->getAllModules(); $modules = $this->getAllModules();
foreach ($modules as $module) { foreach ($modules as $module) {
$topSort->add(\strtolower($module['dir']), $module['dependencies']); $topSort->add(\strtolower($module['dir']), $module['dependencies']);
} }
$topSortedModules = []; foreach ($topSort->sort() as $module) {
foreach ($topSort->sort() as $module) { $this->allModulesTopSorted[$module] = $modules[$module];
$topSortedModules[$module] = $modules[$module]; }
} }
return $topSortedModules; return $this->allModulesTopSorted;
} }
} }
...@@ -10,7 +10,6 @@ namespace ACP3\Core\Modules; ...@@ -10,7 +10,6 @@ namespace ACP3\Core\Modules;
use ACP3\Core\Cache; use ACP3\Core\Cache;
use ACP3\Core\Environment\ApplicationPath; use ACP3\Core\Environment\ApplicationPath;
use ACP3\Core\Filesystem; use ACP3\Core\Filesystem;
use ACP3\Core\I18n\Translator;
use ACP3\Core\Model\Repository\ModuleAwareRepositoryInterface; use ACP3\Core\Model\Repository\ModuleAwareRepositoryInterface;
use ACP3\Core\XML; use ACP3\Core\XML;
...@@ -26,10 +25,6 @@ class ModuleInfoCache ...@@ -26,10 +25,6 @@ class ModuleInfoCache
* @var \ACP3\Core\Environment\ApplicationPath * @var \ACP3\Core\Environment\ApplicationPath
*/ */
protected $appPath; protected $appPath;
/**
* @var \ACP3\Core\I18n\Translator
*/
protected $translator;
/** /**
* @var \ACP3\Core\Modules\Vendor * @var \ACP3\Core\Modules\Vendor
*/ */
...@@ -48,7 +43,6 @@ class ModuleInfoCache ...@@ -48,7 +43,6 @@ class ModuleInfoCache
* *
* @param Cache $cache * @param Cache $cache
* @param ApplicationPath $appPath * @param ApplicationPath $appPath
* @param Translator $translator
* @param Vendor $vendors * @param Vendor $vendors
* @param XML $xml * @param XML $xml
* @param ModuleAwareRepositoryInterface $systemModuleRepository * @param ModuleAwareRepositoryInterface $systemModuleRepository
...@@ -56,14 +50,12 @@ class ModuleInfoCache ...@@ -56,14 +50,12 @@ class ModuleInfoCache
public function __construct( public function __construct(
Cache $cache, Cache $cache,
ApplicationPath $appPath, ApplicationPath $appPath,
Translator $translator,
Vendor $vendors, Vendor $vendors,
XML $xml, XML $xml,
ModuleAwareRepositoryInterface $systemModuleRepository ModuleAwareRepositoryInterface $systemModuleRepository
) { ) {
$this->cache = $cache; $this->cache = $cache;
$this->appPath = $appPath; $this->appPath = $appPath;
$this->translator = $translator;
$this->vendors = $vendors; $this->vendors = $vendors;
$this->xml = $xml; $this->xml = $xml;
$this->systemModuleRepository = $systemModuleRepository; $this->systemModuleRepository = $systemModuleRepository;
...@@ -74,7 +66,7 @@ class ModuleInfoCache ...@@ -74,7 +66,7 @@ class ModuleInfoCache
*/ */
public function getCacheKey() public function getCacheKey()
{ {
return 'infos_' . $this->translator->getLocale(); return 'modules_info';
} }
/** /**
...@@ -153,10 +145,9 @@ class ModuleInfoCache ...@@ -153,10 +145,9 @@ class ModuleInfoCache
'installed' => (!empty($moduleInfoDb)), 'installed' => (!empty($moduleInfoDb)),
'active' => (!empty($moduleInfoDb) && $moduleInfoDb['active'] == 1), 'active' => (!empty($moduleInfoDb) && $moduleInfoDb['active'] == 1),
'schema_version' => !empty($moduleInfoDb) ? (int) $moduleInfoDb['version'] : 0, 'schema_version' => !empty($moduleInfoDb) ? (int) $moduleInfoDb['version'] : 0,
'description' => $this->getModuleDescription($moduleInfo, $moduleName),
'author' => $moduleInfo['author'], 'author' => $moduleInfo['author'],
'version' => $moduleInfo['version'], 'version' => $moduleInfo['version'],
'name' => $this->getModuleName($moduleInfo, $moduleName), 'name' => $moduleName,
'categories' => isset($moduleInfo['categories']), 'categories' => isset($moduleInfo['categories']),
'protected' => isset($moduleInfo['protected']), 'protected' => isset($moduleInfo['protected']),
'installable' => !isset($moduleInfo['no_install']), 'installable' => !isset($moduleInfo['no_install']),
...@@ -169,36 +160,6 @@ class ModuleInfoCache ...@@ -169,36 +160,6 @@ class ModuleInfoCache
return []; return [];
} }
/**
* @param array $moduleInfo
* @param string $moduleName
*
* @return string
*/
protected function getModuleDescription(array $moduleInfo, $moduleName)
{
if (isset($moduleInfo['description']['lang']) && $moduleInfo['description']['lang'] === 'true') {
return $this->translator->t($moduleName, 'mod_description');
}
return $moduleInfo['description'];
}
/**
* @param array $moduleInfo
* @param string $moduleName
*
* @return string
*/
protected function getModuleName(array $moduleInfo, $moduleName)
{
if (isset($moduleInfo['name']['lang']) && $moduleInfo['name']['lang'] === 'true') {
return $this->translator->t($moduleName, $moduleName);
}
return $moduleInfo['name'];
}
/** /**
* @return XML * @return XML
*/ */
......
...@@ -17,6 +17,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -17,6 +17,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* @param \ACP3\Core\Modules\Installer\SchemaInterface $schema * @param \ACP3\Core\Modules\Installer\SchemaInterface $schema
* *
* @return bool * @return bool
*
* @throws \Doctrine\DBAL\DBALException
*/ */
public function install(SchemaInterface $schema) public function install(SchemaInterface $schema)
{ {
...@@ -34,6 +36,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -34,6 +36,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* @param SchemaInterface $schema * @param SchemaInterface $schema
* *
* @return bool * @return bool
*
* @throws \Doctrine\DBAL\DBALException
*/ */
protected function moduleNeedsInstallation(SchemaInterface $schema) protected function moduleNeedsInstallation(SchemaInterface $schema)
{ {
...@@ -50,7 +54,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -50,7 +54,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* *
* @return bool * @return bool
*/ */
protected function addToModulesTable($moduleName, $schemaVersion) protected function addToModulesTable(string $moduleName, int $schemaVersion)
{ {
$insertValues = [ $insertValues = [
'id' => '', 'id' => '',
...@@ -72,7 +76,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -72,7 +76,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* *
* @throws \Doctrine\DBAL\ConnectionException * @throws \Doctrine\DBAL\ConnectionException
*/ */
protected function installSettings($moduleName, array $settings) protected function installSettings(string $moduleName, array $settings)
{ {
if (\count($settings) > 0) { if (\count($settings) > 0) {
$this->db->getConnection()->beginTransaction(); $this->db->getConnection()->beginTransaction();
...@@ -107,6 +111,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -107,6 +111,8 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* @param \ACP3\Core\Modules\Installer\SchemaInterface $schema * @param \ACP3\Core\Modules\Installer\SchemaInterface $schema
* *
* @return bool * @return bool
*
* @throws \Doctrine\DBAL\ConnectionException
*/ */
public function uninstall(SchemaInterface $schema) public function uninstall(SchemaInterface $schema)
{ {
...@@ -121,7 +127,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface ...@@ -121,7 +127,7 @@ class SchemaInstaller extends SchemaHelper implements InstallerInterface
* *
* @return bool