From 03aa4c9ddcc84e682c9ad5373ed3f206bf841663 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sun, 24 Feb 2019 15:54:38 +0100 Subject: [PATCH] Plugins support (#592) --- .gitignore | 7 ++- README.md | 46 +++++++------- composer.json | 3 +- src/Controller/EventController.php | 67 ++++++++++++++++++++ src/DataFixtures/CustomerFixtures.php | 12 ++-- src/DependencyInjection/AppExtension.php | 4 ++ src/Event/ThemeEvent.php | 78 ++++++++++++++++++++++++ src/Kernel.php | 30 +++++++++ templates/admin/activity.html.twig | 4 +- templates/base.html.twig | 3 + var/plugins/.gitkeep | 1 + 11 files changed, 220 insertions(+), 35 deletions(-) create mode 100644 src/Controller/EventController.php create mode 100644 src/Event/ThemeEvent.php create mode 100644 var/plugins/.gitkeep diff --git a/.gitignore b/.gitignore index f7ddb843..7bccbd11 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,8 @@ templates/invoice/renderer/.~lock* -/var/data/*.sqlite -/var/data/*.sqlite-journal +/var/data/* +!/var/data/.gitkeep !/var/data/kimai_test.sqlite /var/coverage/ /var/cache/* @@ -17,6 +17,9 @@ templates/invoice/renderer/.~lock* /var/invoices/* +/var/plugins/* +!var/plugins/.gitkeep + /var/log/* !var/log/.gitkeep diff --git a/README.md b/README.md index db7be3ac..194ba407 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Kimai 2 - Time-tracking made easy -Kimai - the open source time-tracker application with a mobile-first approach (read more at the [official website](http://v2.kimai.org)). +Kimai - the open source time-tracker application with a mobile-first approach (read more at the [official website](https://www.kimai.org)). [![Latest Stable Version](https://poser.pugx.org/kevinpapst/kimai2/v/stable)](https://packagist.org/packages/kevinpapst/kimai2) [![License](https://poser.pugx.org/kevinpapst/kimai2/license)](https://packagist.org/packages/kevinpapst/kimai2) @@ -30,53 +30,51 @@ Kimai is a [multi-language application](var/docs/translations.md) and already tr ## Documentation -Looking for more information about Kimai 2? Check out our detailed [documentation](var/docs/). +Looking for more information about Kimai 2? Check out our detailed [documentation](https://www.kimai.org/documentation/). ### Installation -There are multiple ways to install Kimai, all of them described in the [installation docu](var/docs/installation.md): +There are multiple ways to install Kimai, all of them described in the [installation docu](https://www.kimai.org/documentation/installation.html): -- [Recommended installation with GIT and Composer](var/docs/installation.md#recommended-setup) -- [Development setup](var/docs/installation.md#development-installation) -- [Docker](var/docs/docker.md) -- [1-click installations](var/docs/installation.md#hosting-and-1-click-installations) -- [FTP](var/docs/installation.md#ftp-installation) +- [Recommended installation with GIT and Composer](https://www.kimai.org/documentation/installation.html#recommended-setup) +- [Development setup](https://www.kimai.org/documentation/installation.html#development-installation) +- [Docker](https://www.kimai.org/documentation/docker.html) +- [1-click installations](https://www.kimai.org/documentation/installation.html#hosting-and-1-click-installations) +- [FTP](https://www.kimai.org/documentation/installation.html#ftp-installation) ### Updating Kimai Read the following documentations before you start your upgrade: -- The [update documentation](var/docs/updates.md) +- The [update documentation](https://www.kimai.org/documentation/updates.html) - The version specific [UPGRADING guide](UPGRADING.md) - The [release information](https://github.com/kevinpapst/kimai2/releases) ## Roadmap and releases -You can see our development roadmap in the [Milestones](https://github.com/kevinpapst/kimai2/milestones) sections. +You can see a rough development roadmap in the [Milestones](https://github.com/kevinpapst/kimai2/milestones) sections. It is open for changes and input from the community, your [ideas and questions](https://github.com/kevinpapst/kimai2/issues) are welcome! > Kimai 2 uses a rolling release concept for delivering updates. > You can upgrade Kimai at any time, you don't need to wait for the next official release. Read the [upgrade docs](UPGRADING.md) first! -Release versions will be created on a regular base and you can use these tags if you are familiar with Git, -but we will not provide support for any specific version. +Release versions will be created on a regular base (approx. 1 per month) and you can use these tags if you are familiar with Git, +but we cannot provide support for any specific version (especially older ones). Every code change, whether it's a new feature or a bug fix, will be done on the master branch. I have to do it this way, as I develop Kimai in my free time and want to put most effort into the software instead of backporting changes for old versions. -## Extensions for Kimai 2 +## Plugins for Kimai 2 -As Kimai 2 was built on top of Symfony, it can be extended like every other Symfony application. -We call these extensions bundles, but you might also know them as add-ons, extensions or plugins. +Kimai 2 was built on top of the Symfony framework and can be extended in various ways: -All available Kimai 2 bundles can be found at the [Kimai recipes](https://github.com/kimai/recipes) repository. +- Users looking for existing plugins go to our [plugin marketplace](https://www.kimai.org/store/) +- Developer start with the [developer documentation](https://www.kimai.org/documentation/developers.html) -## Developer +## Credits -Kimai 2 is developed with modern frameworks like [Symfony v4](https://github.com/symfony/symfony), [Doctrine](https://github.com/doctrine/), -[AdminLTE](https://github.com/kevinpapst/AdminLTEBundle/) and [many](composer.json) [more](package.json). - -If you want to start developing for Kimai 2, please read the following documentation: - -- an example on how to extend Kimai 2 can be found in this [GitHub repository](https://github.com/kevinpapst/kimai2-invoice) -- the [developer documentation](var/docs/developers.md) is available both on GitHub and your local installation +Kimai 2 is developed with modern frameworks like +[Symfony v4](https://github.com/symfony/symfony), +[Doctrine](https://github.com/doctrine/), +[AdminLTEBundle](https://github.com/kevinpapst/AdminLTEBundle/) (based on [AdminLTE theme](https://github.com/almasaeed2010/AdminLTE)) and +[many](composer.json) [more](package.json). diff --git a/composer.json b/composer.json index 5db8fc25..eb4bdb19 100644 --- a/composer.json +++ b/composer.json @@ -68,7 +68,8 @@ }, "autoload": { "psr-4": { - "App\\": "src/" + "App\\": "src/", + "KimaiPlugin\\": "var/plugins/" } }, "autoload-dev": { diff --git a/src/Controller/EventController.php b/src/Controller/EventController.php new file mode 100644 index 00000000..80fd2af7 --- /dev/null +++ b/src/Controller/EventController.php @@ -0,0 +1,67 @@ +eventDispatcher = $dispatcher; + } + + /** + * @return EventDispatcherInterface + */ + protected function getDispatcher() + { + return $this->eventDispatcher; + } + + /** + * @param string $eventName + * + * @return bool + */ + protected function hasListener($eventName) + { + return $this->getDispatcher()->hasListeners($eventName); + } + + /** + * @param Request $request + * @param string $eventName + * @return ThemeEvent|Response + */ + public function trigger(Request $request, string $event) + { + if (!$this->hasListener($event)) { + return new Response(); + } + + $themeEvent = new ThemeEvent($request, $this->getUser()); + $this->getDispatcher()->dispatch($event, $themeEvent); + + return new Response($themeEvent->getContent()); + } +} diff --git a/src/DataFixtures/CustomerFixtures.php b/src/DataFixtures/CustomerFixtures.php index 66761254..56da29da 100644 --- a/src/DataFixtures/CustomerFixtures.php +++ b/src/DataFixtures/CustomerFixtures.php @@ -32,10 +32,10 @@ class CustomerFixtures extends Fixture public const MAX_BUDGET = 100000; public const MIN_GLOBAL_ACTIVITIES = 5; public const MAX_GLOBAL_ACTIVITIES = 50; - public const MIN_PROJECTS_PER_CUSTOMER = 10; - public const MAX_PROJECTS_PER_CUSTOMER = 50; + public const MIN_PROJECTS_PER_CUSTOMER = 1; + public const MAX_PROJECTS_PER_CUSTOMER = 25; public const MIN_ACTIVITIES_PER_PROJECT = 0; - public const MAX_ACTIVITIES_PER_PROJECT = 15; + public const MAX_ACTIVITIES_PER_PROJECT = 25; /** * @param ObjectManager $manager @@ -46,18 +46,18 @@ class CustomerFixtures extends Fixture $amountCustomers = rand(self::MIN_CUSTOMERS, self::MAX_CUSTOMERS); for ($c = 1; $c <= $amountCustomers; $c++) { - $visibleCustomer = 0 != $c % 3; + $visibleCustomer = 0 != $c % 5; $customer = $this->createCustomer($faker, $visibleCustomer); $projectForCustomer = rand(self::MIN_PROJECTS_PER_CUSTOMER, self::MAX_PROJECTS_PER_CUSTOMER); for ($p = 1; $p <= $projectForCustomer; $p++) { - $visibleProject = 0 != $p % 3; + $visibleProject = 0 != $p % 7; $project = $this->createProject($faker, $customer, $visibleProject); $manager->persist($project); $activityForProject = rand(self::MIN_ACTIVITIES_PER_PROJECT, self::MAX_ACTIVITIES_PER_PROJECT); for ($a = 1; $a <= $activityForProject; $a++) { - $visibleActivity = 0 != $a % 3; + $visibleActivity = 0 != $a % 6; $activity = $this->createActivity($faker, $project, $visibleActivity); $manager->persist($activity); } diff --git a/src/DependencyInjection/AppExtension.php b/src/DependencyInjection/AppExtension.php index 4f2e3dcb..202a3184 100644 --- a/src/DependencyInjection/AppExtension.php +++ b/src/DependencyInjection/AppExtension.php @@ -33,6 +33,10 @@ class AppExtension extends Extension implements PrependExtensionInterface $config = []; } + // safe alternatives to %kernel.project_dir% + $container->setParameter('kimai.data_dir', $container->getParameter('kernel.project_dir') . '/var/data'); + $container->setParameter('kimai.plugin_dir', $container->getParameter('kernel.project_dir') . '/var/plugins'); + $container->setParameter('kimai.languages', $config['languages']); $container->setParameter('kimai.calendar', $config['calendar']); $container->setParameter('kimai.dashboard', $config['dashboard']); diff --git a/src/Event/ThemeEvent.php b/src/Event/ThemeEvent.php new file mode 100644 index 00000000..52f57e0e --- /dev/null +++ b/src/Event/ThemeEvent.php @@ -0,0 +1,78 @@ +request = $request; + $this->user = $user; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @return Request + */ + public function getRequest(): Request + { + return $this->request; + } + + /** + * @return string + */ + public function getContent(): string + { + return $this->content; + } + + /** + * @param string $content + * @return ThemeEvent + */ + public function addContent(string $content) + { + $this->content .= $content; + return $this; + } +} diff --git a/src/Kernel.php b/src/Kernel.php index d6fde4d4..6dde12cc 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -23,6 +23,8 @@ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; @@ -64,6 +66,30 @@ class Kernel extends BaseKernel yield new $class(); } } + + $pluginsDir = $this->getProjectDir() . '/var/plugins'; + if (!file_exists($pluginsDir)) { + return; + } + + $finder = new Finder(); + $finder->ignoreUnreadableDirs()->directories()->name('*Bundle'); + /** @var SplFileInfo $bundleDir */ + foreach ($finder->in($pluginsDir) as $bundleDir) { + $bundleName = $bundleDir->getRelativePathname(); + + $bundleFilename = $bundleDir->getRealPath() . '/' . $bundleName . '.php'; + if (!file_exists($bundleFilename)) { + continue; + } + + $pluginClass = 'KimaiPlugin\\' . $bundleName . '\\' . $bundleName; + if (!class_exists($pluginClass)) { + continue; + } + + yield new $pluginClass; + } } protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) @@ -106,6 +132,10 @@ class Kernel extends BaseKernel // load application routes $routes->import($confDir . '/routes' . self::CONFIG_EXTS, '/', 'glob'); + + // load plugin routes + $pluginsDir = $this->getProjectDir() . '/var/plugins'; + $routes->import($pluginsDir . '/*Bundle/Resources/config/routes'. self::CONFIG_EXTS, '/', 'glob'); } protected function configureFosUserRoutes(RouteCollectionBuilder $routes) diff --git a/templates/admin/activity.html.twig b/templates/admin/activity.html.twig index b922847b..3509a4f5 100644 --- a/templates/admin/activity.html.twig +++ b/templates/admin/activity.html.twig @@ -42,14 +42,14 @@ {% if entry.project and entry.project.customer %} {{ widgets.label_customer(entry.project.customer) }} {% else %} - {#{ widgets.label('x', 'success') }#} + {# GLOBAL ACTIVTITY DOES NOT HAVE A CUSTOMER #} {% endif %} {% if entry.project %} {{ widgets.label_project(entry.project) }} {% else %} - {#{ widgets.label('x', 'success') }#} + {# GLOBAL ACTIVTITY DOES NOT HAVE A PROJECT #} {% endif %} {{ entry.comment }} diff --git a/templates/base.html.twig b/templates/base.html.twig index 08346afa..f13c948a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -89,12 +89,14 @@ {% block stylesheets %} {# we do not call parent() as we use a custom built for the frontend assets and don't want the default #} + {{ render(controller('App\\Controller\\EventController::trigger', {'request':app.request, 'event':constant('App\\Event\\ThemeEvent::STYLESHEET')})) }} {% endblock %} {% block head %} {{ parent() }} {% include 'partials/head.html.twig' %} + {{ render(controller('App\\Controller\\EventController::trigger', {'request':app.request, 'event':constant('App\\Event\\ThemeEvent::HTML_HEAD')})) }} {% endblock %} {% block javascripts %} @@ -119,4 +121,5 @@ {# $.kimai.pauseRecord('li.messages-menu ul.menu li'); #} }); + {{ render(controller('App\\Controller\\EventController::trigger', {'request':app.request, 'event':constant('App\\Event\\ThemeEvent::JAVASCRIPT')})) }} {% endblock %} diff --git a/var/plugins/.gitkeep b/var/plugins/.gitkeep new file mode 100644 index 00000000..21d13dc6 --- /dev/null +++ b/var/plugins/.gitkeep @@ -0,0 +1 @@ +This directory can store as Kimai plugin/bundle folder. \ No newline at end of file