diff --git a/src/EventSubscriber/MenuSubscriber.php b/src/EventSubscriber/MenuSubscriber.php index d97f38c9..3381fa6e 100644 --- a/src/EventSubscriber/MenuSubscriber.php +++ b/src/EventSubscriber/MenuSubscriber.php @@ -10,6 +10,7 @@ namespace App\EventSubscriber; use App\Event\ConfigureMainMenuEvent; +use App\Twig\IconExtension; use KevinPapst\AdminLTEBundle\Event\SidebarMenuEvent; use KevinPapst\AdminLTEBundle\Model\MenuItemModel; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -18,20 +19,21 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; /** * Menu event subscriber is creating the Kimai default menu structure. */ -class MenuSubscriber implements EventSubscriberInterface +final class MenuSubscriber implements EventSubscriberInterface { /** * @var AuthorizationCheckerInterface */ private $security; - /** - * MenuSubscriber constructor. - * @param AuthorizationCheckerInterface $security + * @var IconExtension */ + private $icons; + public function __construct(AuthorizationCheckerInterface $security) { $this->security = $security; + $this->icons = new IconExtension(); } /** @@ -63,28 +65,28 @@ class MenuSubscriber implements EventSubscriberInterface /** * @param SidebarMenuEvent $menu */ - protected function configureMainMenu(SidebarMenuEvent $menu) + private function configureMainMenu(SidebarMenuEvent $menu) { $auth = $this->security; if ($auth->isGranted('view_own_timesheet')) { $menu->addItem( - new MenuItemModel('timesheet', 'menu.timesheet', 'timesheet', [], 'fas fa-clock') + new MenuItemModel('timesheet', 'menu.timesheet', 'timesheet', [], $this->getIcon('timesheet')) ); $menu->addItem( - new MenuItemModel('calendar', 'calendar.title', 'calendar', [], 'far fa-calendar-alt') + new MenuItemModel('calendar', 'calendar.title', 'calendar', [], $this->getIcon('calendar')) ); } if ($auth->isGranted('view_invoice')) { $menu->addItem( - new MenuItemModel('invoice', 'menu.invoice', 'invoice', [], 'fas fa-file-invoice') + new MenuItemModel('invoice', 'menu.invoice', 'invoice', [], $this->getIcon('invoice')) ); } if ($auth->isGranted('view_export')) { $menu->addItem( - new MenuItemModel('export', 'menu.export', 'export', [], 'fas fa-file-export') + new MenuItemModel('export', 'menu.export', 'export', [], $this->getIcon('export')) ); } } @@ -92,31 +94,31 @@ class MenuSubscriber implements EventSubscriberInterface /** * @param MenuItemModel $menu */ - protected function configureAdminMenu(MenuItemModel $menu) + private function configureAdminMenu(MenuItemModel $menu) { $auth = $this->security; if ($auth->isGranted('view_other_timesheet')) { $menu->addChild( - new MenuItemModel('timesheet_admin', 'menu.admin_timesheet', 'admin_timesheet', [], 'fas fa-user-clock') + new MenuItemModel('timesheet_admin', 'menu.admin_timesheet', 'admin_timesheet', [], $this->getIcon('timesheet-team')) ); } if ($auth->isGranted('view_customer')) { $menu->addChild( - new MenuItemModel('customer_admin', 'menu.admin_customer', 'admin_customer', [], 'fas fa-user-tie') + new MenuItemModel('customer_admin', 'menu.admin_customer', 'admin_customer', [], $this->getIcon('customer')) ); } if ($auth->isGranted('view_project')) { $menu->addChild( - new MenuItemModel('project_admin', 'menu.admin_project', 'admin_project', [], 'fas fa-project-diagram') + new MenuItemModel('project_admin', 'menu.admin_project', 'admin_project', [], $this->getIcon('project')) ); } if ($auth->isGranted('view_activity')) { $menu->addChild( - new MenuItemModel('activity_admin', 'menu.admin_activity', 'admin_activity', [], 'fas fa-tasks') + new MenuItemModel('activity_admin', 'menu.admin_activity', 'admin_activity', [], $this->getIcon('activity')) ); } @@ -130,26 +132,31 @@ class MenuSubscriber implements EventSubscriberInterface /** * @param MenuItemModel $menu */ - protected function configureSystemMenu(MenuItemModel $menu) + private function configureSystemMenu(MenuItemModel $menu) { $auth = $this->security; if ($auth->isGranted('view_user')) { $menu->addChild( - new MenuItemModel('user_admin', 'menu.admin_user', 'admin_user', [], 'fas fa-users') + new MenuItemModel('user_admin', 'menu.admin_user', 'admin_user', [], $this->getIcon('user')) ); } if ($auth->isGranted('plugins')) { $menu->addChild( - new MenuItemModel('plugins', 'menu.plugin', 'plugins', [], 'fas fa-plug') + new MenuItemModel('plugins', 'menu.plugin', 'plugins', [], $this->getIcon('plugin')) ); } if ($auth->isGranted('system_configuration')) { $menu->addChild( - new MenuItemModel('system_configuration', 'menu.system_configuration', 'system_configuration', [], 'fas fa-cogs') + new MenuItemModel('system_configuration', 'menu.system_configuration', 'system_configuration', [], $this->getIcon('configuration')) ); } } + + private function getIcon(string $icon) + { + return $this->icons->icon($icon, $icon); + } } diff --git a/src/Twig/Extensions.php b/src/Twig/Extensions.php index d2bd4f7d..8632c81a 100644 --- a/src/Twig/Extensions.php +++ b/src/Twig/Extensions.php @@ -45,61 +45,6 @@ class Extensions extends AbstractExtension */ protected $moneyFormatter; - /** - * @var string[] - */ - protected static $icons = [ - 'activity' => 'fas fa-tasks', - 'admin' => 'fas fa-wrench', - 'calendar' => 'far fa-calendar-alt', - 'customer' => 'fas fa-user-tie', - 'copy' => 'far fa-copy', - 'create' => 'far fa-plus-square', - 'dashboard' => 'fas fa-tachometer-alt', - 'delete' => 'far fa-trash-alt', - 'download' => 'fas fa-download', - 'duration' => 'far fa-hourglass', - 'edit' => 'far fa-edit', - 'filter' => 'fas fa-filter', - 'help' => 'far fa-question-circle', - 'invoice' => 'fas fa-file-invoice', - 'list' => 'fas fa-list', - 'logout' => 'fas fa-sign-out-alt', - 'manual' => 'fas fa-book', - 'money' => 'far fa-money-bill-alt', - 'print' => 'fas fa-print', - 'project' => 'fas fa-project-diagram', - 'repeat' => 'fas fa-redo-alt', - 'start' => 'fas fa-play-circle', - 'start-small' => 'far fa-play-circle', - 'stop' => 'fas fa-stop', - 'stop-small' => 'far fa-stop-circle', - 'timesheet' => 'fas fa-clock', - 'trash' => 'far fa-trash-alt', - 'user' => 'fas fa-users', - 'visibility' => 'far fa-eye', - 'settings' => 'fas fa-cog', - 'export' => 'fas fa-file-export', - 'pdf' => 'fas fa-file-pdf', - 'csv' => 'fas fa-table', - 'ods' => 'fas fa-table', - 'xlsx' => 'fas fa-file-excel', - 'on' => 'fas fa-toggle-on', - 'off' => 'fas fa-toggle-off', - 'audit' => 'fas fa-history', - 'home' => 'fas fa-home', - 'shop' => 'fas fa-shopping-cart', - 'about' => 'fas fa-info-circle', - 'debug' => 'far fa-file-alt', - 'profile-stats' => 'far fa-chart-bar', - 'profile' => 'fas fa-user-edit', - 'warning' => 'fas fa-exclamation-triangle', - 'permissions' => 'fas fa-user-lock', - 'back' => 'fas fa-long-arrow-alt-left', - 'tag' => 'fas fa-tags', - 'avatar' => 'fas fa-user' - ]; - /** * @param LocaleSettings $localeSettings */ @@ -119,7 +64,6 @@ class Extensions extends AbstractExtension new TwigFilter('money', [$this, 'money']), new TwigFilter('currency', [$this, 'currency']), new TwigFilter('country', [$this, 'country']), - new TwigFilter('icon', [$this, 'icon']), new TwigFilter('docu_link', [$this, 'documentationLink']), ]; } @@ -204,16 +148,6 @@ class Extensions extends AbstractExtension return Intl::getRegionBundle()->getCountryName($country); } - /** - * @param string $name - * @param string $default - * @return string - */ - public function icon($name, $default = '') - { - return self::$icons[$name] ?? $default; - } - /** * @param string $url * @return string diff --git a/src/Twig/IconExtension.php b/src/Twig/IconExtension.php new file mode 100644 index 00000000..edbdce86 --- /dev/null +++ b/src/Twig/IconExtension.php @@ -0,0 +1,89 @@ + 'fas fa-tasks', + 'admin' => 'fas fa-wrench', + 'calendar' => 'far fa-calendar-alt', + 'customer' => 'fas fa-user-tie', + 'copy' => 'far fa-copy', + 'create' => 'far fa-plus-square', + 'dashboard' => 'fas fa-tachometer-alt', + 'delete' => 'far fa-trash-alt', + 'download' => 'fas fa-download', + 'duration' => 'far fa-hourglass', + 'edit' => 'far fa-edit', + 'filter' => 'fas fa-filter', + 'help' => 'far fa-question-circle', + 'invoice' => 'fas fa-file-invoice', + 'list' => 'fas fa-list', + 'logout' => 'fas fa-sign-out-alt', + 'manual' => 'fas fa-book', + 'money' => 'far fa-money-bill-alt', + 'print' => 'fas fa-print', + 'project' => 'fas fa-briefcase', + 'repeat' => 'fas fa-redo-alt', + 'start' => 'fas fa-play-circle', + 'start-small' => 'far fa-play-circle', + 'stop' => 'fas fa-stop', + 'stop-small' => 'far fa-stop-circle', + 'timesheet' => 'fas fa-clock', + 'trash' => 'far fa-trash-alt', + 'user' => 'fas fa-users', + 'visibility' => 'far fa-eye', + 'settings' => 'fas fa-cog', + 'export' => 'fas fa-file-export', + 'pdf' => 'fas fa-file-pdf', + 'csv' => 'fas fa-table', + 'ods' => 'fas fa-table', + 'xlsx' => 'fas fa-file-excel', + 'on' => 'fas fa-toggle-on', + 'off' => 'fas fa-toggle-off', + 'audit' => 'fas fa-history', + 'home' => 'fas fa-home', + 'shop' => 'fas fa-shopping-cart', + 'about' => 'fas fa-info-circle', + 'debug' => 'far fa-file-alt', + 'profile-stats' => 'far fa-chart-bar', + 'profile' => 'fas fa-user-edit', + 'warning' => 'fas fa-exclamation-triangle', + 'permissions' => 'fas fa-user-lock', + 'back' => 'fas fa-long-arrow-alt-left', + 'tag' => 'fas fa-tags', + 'avatar' => 'fas fa-user', + 'timesheet-team' => 'fas fa-user-clock', + 'plugin' => 'fas fa-plug', + 'configuration' => 'fas fa-cogs', + ]; + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return [ + new TwigFilter('icon', [$this, 'icon']), + ]; + } + + public function icon(string $name, string $default = ''): string + { + return self::$icons[$name] ?? $default; + } +} diff --git a/tests/Twig/ExtensionsTest.php b/tests/Twig/ExtensionsTest.php index 25ec4b9a..9fd8bb6c 100644 --- a/tests/Twig/ExtensionsTest.php +++ b/tests/Twig/ExtensionsTest.php @@ -50,7 +50,7 @@ class ExtensionsTest extends TestCase public function testGetFilters() { - $filters = ['duration', 'money', 'currency', 'country', 'icon', 'docu_link']; + $filters = ['duration', 'money', 'currency', 'country', 'docu_link']; $sut = $this->getSut($this->localeDe); $twigFilters = $sut->getFilters(); $this->assertCount(count($filters), $twigFilters); @@ -213,28 +213,6 @@ class ExtensionsTest extends TestCase return $record; } - public function testIcon() - { - $icons = [ - 'user', 'customer', 'project', 'activity', 'admin', 'invoice', 'timesheet', 'dashboard', 'logout', 'trash', - 'delete', 'repeat', 'edit', 'manual', 'help', 'start', 'start-small', 'stop', 'stop-small', 'filter', - 'create', 'list', 'print', 'visibility', 'calendar', 'money', 'duration', 'download', 'copy', 'settings', - 'export', 'pdf', 'csv', 'ods', 'xlsx', 'on', 'off', 'audit', 'home', 'shop', 'about', 'debug', 'profile-stats' - ]; - - // test pre-defined icons - $sut = $this->getSut($this->localeEn); - foreach ($icons as $icon) { - $result = $sut->icon($icon); - $this->assertNotEmpty($result, 'Problem with icon definition: ' . $icon); - $this->assertIsString($result); - } - - // test fallback will be returned - $this->assertEquals('', $sut->icon('foo')); - $this->assertEquals('bar', $sut->icon('foo', 'bar')); - } - public function testDocuLink() { $data = [ diff --git a/tests/Twig/IconExtensionTest.php b/tests/Twig/IconExtensionTest.php new file mode 100644 index 00000000..7bccc0fe --- /dev/null +++ b/tests/Twig/IconExtensionTest.php @@ -0,0 +1,58 @@ +getFilters(); + $this->assertCount(count($filters), $twigFilters); + $i = 0; + /** @var TwigFilter $filter */ + foreach ($twigFilters as $filter) { + $this->assertInstanceOf(TwigFilter::class, $filter); + $this->assertEquals($filters[$i++], $filter->getName()); + } + } + + public function testIcon() + { + $icons = [ + 'user', 'customer', 'project', 'activity', 'admin', 'invoice', 'timesheet', 'dashboard', 'logout', 'trash', + 'delete', 'repeat', 'edit', 'manual', 'help', 'start', 'start-small', 'stop', 'stop-small', 'filter', + 'create', 'list', 'print', 'visibility', 'calendar', 'money', 'duration', 'download', 'copy', 'settings', + 'export', 'pdf', 'csv', 'ods', 'xlsx', 'on', 'off', 'audit', 'home', 'shop', 'about', 'debug', 'profile-stats', + 'profile', 'warning', 'permissions', 'back', 'tag', 'avatar', 'timesheet-team', 'plugin', 'configuration' + ]; + + // test pre-defined icons + $sut = new IconExtension(); + + foreach ($icons as $icon) { + $result = $sut->icon($icon); + $this->assertNotEmpty($result, 'Missing icon definition: ' . $icon); + $this->assertIsString($result); + } + + // test fallback will be returned + $this->assertEquals('', $sut->icon('foo')); + $this->assertEquals('bar', $sut->icon('foo', 'bar')); + } +}