Merge pull request #3837 from Icinga/fix/dont-translate-pane-and-dashlet-names-in-configs-3542

Don't translate pane and dashlet names in configs
This commit is contained in:
Johannes Meyer 2019-07-18 08:45:36 +02:00 committed by GitHub
commit 8d09279e15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 232 additions and 50 deletions

View File

@ -106,29 +106,22 @@ class DashboardController extends ActionController
$action = $this;
$form->setOnSuccess(function (Form $form) use ($dashboard, $action) {
try {
$pane = $dashboard->getPane($form->getValue('pane'));
$pane = $dashboard->getPane($form->getValue('org_pane'));
$pane->setTitle($form->getValue('pane'));
} catch (ProgrammingError $e) {
$pane = new Dashboard\Pane($form->getValue('pane'));
$pane->setUserWidget();
$dashboard->addPane($pane);
}
try {
$dashlet = $pane->getDashlet($form->getValue('dashlet'));
$dashlet = $pane->getDashlet($form->getValue('org_dashlet'));
$dashlet->setTitle($form->getValue('dashlet'));
$dashlet->setUrl($form->getValue('url'));
} catch (ProgrammingError $e) {
$dashlet = new Dashboard\Dashlet($form->getValue('dashlet'), $form->getValue('url'), $pane);
$pane->addDashlet($dashlet);
}
$dashlet->setUserWidget();
// Rename dashlet
if ($form->getValue('org_dashlet') && $form->getValue('org_dashlet') !== $dashlet->getTitle()) {
$pane->removeDashlet($form->getValue('org_dashlet'));
}
// Move
if ($form->getValue('org_pane') && $form->getValue('org_pane') !== $pane->getTitle()) {
$oldPane = $dashboard->getPane($form->getValue('org_pane'));
$oldPane->removeDashlet($dashlet->getTitle());
}
$dashboardConfig = $dashboard->getConfig();
try {
$dashboardConfig->saveIni();
@ -269,7 +262,7 @@ class DashboardController extends ActionController
$action = $this;
$form->setOnSuccess(function (Form $form) use ($dashboard, $pane, $action) {
$pane = $dashboard->getPane($pane);
$dashboard->removePane($pane->getTitle());
$dashboard->removePane($pane->getName());
$dashboardConfig = $dashboard->getConfig();
try {
$dashboardConfig->saveIni();

View File

@ -161,10 +161,10 @@ class DashletForm extends Form
public function load(Dashlet $dashlet)
{
$this->populate(array(
'pane' => $dashlet->getPane()->getName(),
'pane' => $dashlet->getPane()->getTitle(),
'org_pane' => $dashlet->getPane()->getName(),
'dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getTitle(),
'org_dashlet' => $dashlet->getName(),
'url' => $dashlet->getUrl()->getRelativeUrl()
));
}

View File

@ -59,7 +59,7 @@
<?= $this->qlink(
$dashlet->getTitle(),
'dashboard/update-dashlet',
array('pane' => $pane->getName(), 'dashlet' => $dashlet->getTitle()),
array('pane' => $pane->getName(), 'dashlet' => $dashlet->getName()),
array('title' => sprintf($this->translate('Edit dashlet %s'), $dashlet->getTitle()))
); ?>
</td>
@ -75,10 +75,10 @@
<?= $this->qlink(
'',
'dashboard/remove-dashlet',
array('pane' => $pane->getName(), 'dashlet' => $dashlet->getTitle()),
array('pane' => $pane->getName(), 'dashlet' => $dashlet->getName()),
array(
'icon' => 'trash',
'title' => sprintf($this->translate('Remove dashlet %s from pane %s'), $dashlet->getTitle(), $pane->getName())
'title' => sprintf($this->translate('Remove dashlet %s from pane %s'), $dashlet->getTitle(), $pane->getTitle())
)
); ?>
</td>

View File

@ -14,6 +14,9 @@ v2.3 to v2.5 requires to follow the instructions for v2.4 too.
for log messages emitted by jquery-migrate. (https://github.com/jquery/jquery-migrate) Your javascript code will still
work, though jquery-migrate will notify you if you're utilizing deprecated/removed functions. jquery-migrate will be
removed with Icinga Web v2.8 and code not adjusted accordingly will stop working.
* If you're using a language other than english and you've adjusted or disabled module dashboards, you'll need to
update all of your `dashboard.ini` files. A CLI command exists to assist you with this task. Enable the `migrate`
module and run the following on the host where these files exist: `icingacli migrate dashboard sections --verbose`
## Upgrading to Icinga Web 2 2.6.x <a id="upgrading-to-2.6.x"></a>

View File

@ -314,9 +314,12 @@ class Module
$navigation = new Navigation();
foreach ($panes as $pane) {
/** @var DashboardContainer $pane */
$dashlets = array();
$dashlets = [];
foreach ($pane->getDashlets() as $dashletName => $dashletUrl) {
$dashlets[$this->translate($dashletName)] = $dashletUrl;
$dashlets[$dashletName] = [
'label' => $this->translate($dashletName),
'url' => $dashletUrl
];
}
$navigation->addItem(
@ -326,7 +329,7 @@ class Module
array(
'label' => $this->translate($pane->getName()),
'type' => 'dashboard-pane',
'dashlets' => $dashlets
'children' => $dashlets
)
)
);

View File

@ -5,6 +5,9 @@ namespace Icinga\Legacy;
use Icinga\Application\Config;
use Icinga\User;
use Icinga\Web\Navigation\DashboardPane;
use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Navigation\NavigationItem;
/**
* Legacy dashboard config class for case insensitive interpretation of dashboard config files
@ -76,6 +79,51 @@ class DashboardConfig extends Config
*/
public function saveIni($filePath = null, $fileMode = 0660)
{
// Preprocessing start, ensures that the non-translated names are used to save module dashboard changes
// TODO: This MUST NOT survive the new dashboard implementation (yes, it's still a thing..)
$dashboardNavigation = new Navigation();
$dashboardNavigation->load('dashboard-pane');
$getDashboardPane = function ($label) use ($dashboardNavigation) {
foreach ($dashboardNavigation as $dashboardPane) {
/** @var DashboardPane $dashboardPane */
if ($dashboardPane->getLabel() === $label) {
return $dashboardPane;
}
foreach ($dashboardPane->getChildren() as $dashlet) {
/** @var NavigationItem $dashlet */
if ($dashlet->getLabel() === $label) {
return $dashlet;
}
}
}
};
foreach (clone $this->config as $name => $options) {
if (strpos($name, '.') !== false) {
list($dashboardLabel, $dashletLabel) = explode('.', $name, 2);
} else {
$dashboardLabel = $name;
$dashletLabel = null;
}
$dashboardPane = $getDashboardPane($dashboardLabel);
if ($dashboardPane !== null) {
$dashboardLabel = $dashboardPane->getName();
}
if ($dashletLabel !== null) {
$dashletItem = $getDashboardPane($dashletLabel);
if ($dashletItem !== null) {
$dashletLabel = $dashletItem->getName();
}
}
unset($this->config[$name]);
$this->config[$dashboardLabel . ($dashletLabel ? '.' . $dashletLabel : '')] = $options;
}
// Preprocessing end
parent::saveIni($filePath, $fileMode);
if ($filePath === null) {
$filePath = $this->configFile;

View File

@ -62,19 +62,6 @@ class DashboardPane extends NavigationItem
$this->setUrl(Url::fromPath('dashboard', array('pane' => $this->getName())));
}
/**
* {@inheritdoc}
*/
public function merge(NavigationItem $item)
{
parent::merge($item);
$this->setDashlets(array_merge(
$this->getDashlets(false),
$item->getDashlets(false)
));
}
/**
* Set disabled state for pane
*

View File

@ -77,15 +77,15 @@ class Dashboard extends AbstractWidget
foreach ($navigation as $dashboardPane) {
/** @var DashboardPane $dashboardPane */
$pane = new Pane($dashboardPane->getLabel());
foreach ($dashboardPane->getDashlets(false) as $title => $url) {
$pane->addDashlet($title, $url);
foreach ($dashboardPane->getChildren() as $dashlet) {
$pane->addDashlet($dashlet->getLabel(), $dashlet->getUrl());
}
$panes[] = $pane;
}
$this->mergePanes($panes);
$this->loadUserDashboards();
$this->loadUserDashboards($navigation);
return $this;
}
@ -103,7 +103,7 @@ class Dashboard extends AbstractWidget
}
foreach ($pane->getDashlets() as $dashlet) {
if ($dashlet->isUserWidget()) {
$output[$pane->getName() . '.' . $dashlet->getTitle()] = $dashlet->toArray();
$output[$pane->getName() . '.' . $dashlet->getName()] = $dashlet->toArray();
}
}
}
@ -114,10 +114,10 @@ class Dashboard extends AbstractWidget
/**
* Load user dashboards from all config files that match the username
*/
protected function loadUserDashboards()
protected function loadUserDashboards(Navigation $navigation)
{
foreach (DashboardConfig::listConfigFilesForUser($this->user) as $file) {
$this->loadUserDashboardsFromFile($file);
$this->loadUserDashboardsFromFile($file, $navigation);
}
}
@ -128,7 +128,7 @@ class Dashboard extends AbstractWidget
*
* @return bool
*/
protected function loadUserDashboardsFromFile($file)
protected function loadUserDashboardsFromFile($file, Navigation $dashboardNavigation)
{
try {
$config = Config::fromIni($file);
@ -143,8 +143,12 @@ class Dashboard extends AbstractWidget
$dashlets = array();
foreach ($config as $key => $part) {
if (strpos($key, '.') === false) {
if ($this->hasPane($part->title)) {
$panes[$key] = $this->getPane($part->title);
$dashboardPane = $dashboardNavigation->getItem($key);
if ($dashboardPane !== null) {
$key = $dashboardPane->getLabel();
}
if ($this->hasPane($key)) {
$panes[$key] = $this->getPane($key);
} else {
$panes[$key] = new Pane($key);
$panes[$key]->setTitle($part->title);
@ -155,6 +159,14 @@ class Dashboard extends AbstractWidget
}
} else {
list($paneName, $dashletName) = explode('.', $key, 2);
$dashboardPane = $dashboardNavigation->getItem($paneName);
if ($dashboardPane !== null) {
$paneName = $dashboardPane->getLabel();
$dashletItem = $dashboardPane->getChildren()->getItem($dashletName);
if ($dashletItem !== null) {
$dashletName = $dashletItem->getLabel();
}
}
$part->pane = $paneName;
$part->dashlet = $dashletName;
$dashlets[] = $part;
@ -175,6 +187,7 @@ class Dashboard extends AbstractWidget
$dashletData->url,
$pane
);
$dashlet->setName($dashletData->dashlet);
if ((bool) $dashletData->get('disabled', false) === true) {
$dashlet->setDisabled(true);
@ -200,7 +213,7 @@ class Dashboard extends AbstractWidget
{
/** @var $pane Pane */
foreach ($panes as $pane) {
if ($this->hasPane($pane->getTitle()) === true) {
if ($this->hasPane($pane->getName()) === true) {
/** @var $current Pane */
$current = $this->panes[$pane->getName()];
$current->addDashlets($pane->getDashlets());

View File

@ -22,6 +22,8 @@ class Dashlet extends UserWidget
*/
private $url;
private $name;
/**
* The title being displayed on top of the dashlet
* @var
@ -91,11 +93,23 @@ EOD;
*/
public function __construct($title, $url, Pane $pane)
{
$this->name = $title;
$this->title = $title;
$this->pane = $pane;
$this->url = $url;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
/**
* Retrieve the dashlets title
*

View File

@ -22,7 +22,6 @@ class Pane extends UserWidget
/**
* The title of this pane, as displayed in the dashboard tabs
* @TODO: Currently the same as $name, evaluate if distinguishing is needed
*
* @var string
*/
@ -229,7 +228,7 @@ class Pane extends UserWidget
public function addDashlet($dashlet, $url = null)
{
if ($dashlet instanceof Dashlet) {
$this->dashlets[$dashlet->getTitle()] = $dashlet;
$this->dashlets[$dashlet->getName()] = $dashlet;
} elseif (is_string($dashlet) && $url !== null) {
$this->createDashlet($dashlet, $url);
} else {
@ -248,15 +247,15 @@ class Pane extends UserWidget
{
/* @var $dashlet Dashlet */
foreach ($dashlets as $dashlet) {
if (array_key_exists($dashlet->getTitle(), $this->dashlets)) {
if (preg_match('/_(\d+)$/', $dashlet->getTitle(), $m)) {
$name = preg_replace('/_\d+$/', $m[1]++, $dashlet->getTitle());
if (array_key_exists($dashlet->getName(), $this->dashlets)) {
if (preg_match('/_(\d+)$/', $dashlet->getName(), $m)) {
$name = preg_replace('/_\d+$/', $m[1]++, $dashlet->getName());
} else {
$name = $dashlet->getTitle() . '_2';
$name = $dashlet->getName() . '_2';
}
$this->dashlets[$name] = $dashlet;
} else {
$this->dashlets[$dashlet->getTitle()] = $dashlet;
$this->dashlets[$dashlet->getName()] = $dashlet;
}
}

View File

@ -0,0 +1,122 @@
<?php
/* Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
namespace Icinga\Module\Migrate\Clicommands;
use Exception;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Modules\DashboardContainer;
use Icinga\Cli\Command;
use Icinga\Application\Logger;
use Icinga\Util\Translator;
use ReflectionClass;
class DashboardCommand extends Command
{
/**
* Rename translated dashboard sections
*
* Migrates all locally found dashboard configurations so that the effects of
* https://github.com/Icinga/icingaweb2/issues/3542 are reversed.
*
* USAGE
*
* icingacli migrate dashboard sections
*/
public function sectionsAction()
{
$moduleReflection = new ReflectionClass('Icinga\Application\Modules\Module');
// There's no direct way to invoke this method
$launchConfigScriptMethod = $moduleReflection->getMethod('launchConfigScript');
$launchConfigScriptMethod->setAccessible(true);
// Calling getDashboard() results in Url::fromPath() getting called as well == the CLI's death
$paneItemsProperty = $moduleReflection->getProperty('paneItems');
$paneItemsProperty->setAccessible(true);
// Again, no direct way to access this nor to let the module setup its own translation domain
$localeDirProperty = $moduleReflection->getProperty('localedir');
$localeDirProperty->setAccessible(true);
$locales = Translator::getAvailableLocaleCodes();
$modules = Icinga::app()->getModuleManager()->loadEnabledModules()->getLoadedModules();
foreach ($this->listDashboardConfigs() as $path) {
Logger::info('Migrating dashboard config: %s', $path);
$config = Config::fromIni($path);
foreach ($modules as $module) {
$localePath = $localeDirProperty->getValue($module);
if (! is_dir($localePath)) {
// Modules without any translations are not affected
continue;
}
$launchConfigScriptMethod->invoke($module);
Translator::registerDomain($module->getName(), $localePath);
foreach ($locales as $locale) {
if ($locale === 'en_US') {
continue;
}
Translator::setupLocale($locale);
foreach ($paneItemsProperty->getValue($module) as $paneName => $container) {
/** @var DashboardContainer $container */
foreach ($config->toArray() as $section => $options) {
if (strpos($section, '.') !== false) {
list($paneTitle, $dashletTitle) = explode('.', $section, 2);
} else {
$paneTitle = $section;
$dashletTitle = null;
}
if (isset($options['disabled']) && mt($module->getName(), $paneName) !== $paneTitle) {
// `disabled` is checked because if it's a module's pane that's the only reason
// why it's in there. If a user utilized the same label though for a custom pane,
// it remains as is.
continue;
}
$dashletName = null;
if ($dashletTitle !== null) {
foreach ($container->getDashlets() as $name => $url) {
if (mt($module->getName(), $name) === $dashletTitle) {
$dashletName = $name;
break;
}
}
}
$newSection = $paneName . ($dashletName ? '.' . $dashletName : '');
$config->removeSection($section);
$config->setSection($newSection, $options);
Logger::info('Migrated section "%s" to "%s"', $section, $newSection);
}
}
}
}
$config->saveIni();
}
}
protected function listDashboardConfigs()
{
$dashboardConfigPath = Config::resolvePath('dashboards');
try {
$dashboardConfigDir = opendir($dashboardConfigPath);
} catch (Exception $e) {
Logger::error('Cannot access dashboard configuration: %s', $e);
exit(1);
}
while ($entry = readdir($dashboardConfigDir)) {
$userDashboardPath = join(DIRECTORY_SEPARATOR, [$dashboardConfigPath, $entry, 'dashboard.ini']);
if (is_file($userDashboardPath)) {
yield $userDashboardPath;
}
}
}
}