Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions ProcessMaker/Managers/ControllerAddonsRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace ProcessMaker\Managers;

class ControllerAddonsRegistry
{
private $addons = [];

/**
* Register a controller addon.
*/
public function register(string $scope, array $config): void
{
$config['scope'] = $scope;
$this->addons[] = $config;
}

/**
* Get configured addons for a controller.
*/
public function getAddons(string $scope, string $method, array $data): array
{
$addons = [];

foreach ($this->addons as $addon) {
if ($addon['method'] !== $method || $addon['scope'] !== $scope) {
continue;
}

if (isset($addon['data']) && is_callable($addon['data'])) {
$data = call_user_func($addon['data'], $data);
}

$addon['content'] = isset($addon['view']) && !isset($addon['content'])
? view($addon['view'], $data)->render()
: (isset($addon['content']) ? $addon['content'] : '');
$addon['script'] = isset($addon['script']) && is_string($addon['script'])
? view($addon['script'], $data)->render()
: '';
$addons[] = $addon;
}

return $addons;
}
}
4 changes: 4 additions & 0 deletions ProcessMaker/Providers/ProcessMakerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public function register(): void
return new Managers\LoginManager();
});

$this->app->singleton(Managers\ControllerAddonsRegistry::class, function () {
return new Managers\ControllerAddonsRegistry();
});

/*
* Maps our Index Manager as a singleton. The Index Manager is used
* to manage customizations to the search indexer.
Expand Down
29 changes: 4 additions & 25 deletions ProcessMaker/Traits/HasControllerAddons.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace ProcessMaker\Traits;

use ProcessMaker\Managers\ControllerAddonsRegistry;

trait HasControllerAddons
{
private static $addons = [];

/**
* Get configured addons for this controller
*
Expand All @@ -16,26 +16,7 @@ trait HasControllerAddons
*/
protected function getPluginAddons($method, array $data)
{
if (!isset(static::$addons)) {
return;
}

$addons = [];
foreach (static::$addons as $addon) {
// The addon must have the requested method and must be associated to the current controller
if ($addon['method'] === $method && $addon['scope'] === get_class($this)) {
if (isset($addon['data']) && is_callable($addon['data'])) {
$data = call_user_func($addon['data'], $data);
}
$addon['content'] = isset($addon['view']) && !isset($addon['content'])
? view($addon['view'], $data)->render() : (isset($addon['content'])
? $addon['content'] : '');
$addon['script'] = isset($addon['script']) && is_string($addon['script']) ? view($addon['script'], $data)->render() : '';
$addons[] = $addon;
}
}

return $addons;
return app(ControllerAddonsRegistry::class)->getAddons(static::class, $method, $data);
}

/**
Expand All @@ -47,8 +28,6 @@ protected function getPluginAddons($method, array $data)
*/
public static function registerAddon(array $config)
{
// Add the controller to which the addon is attached
$config['scope'] = static::class;
static::$addons[] = $config;
app(ControllerAddonsRegistry::class)->register(static::class, $config);
}
}
136 changes: 136 additions & 0 deletions tests/unit/ProcessMaker/Managers/ControllerAddonsRegistryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

declare(strict_types=1);

namespace Tests\Unit\ProcessMaker\Managers;

use Illuminate\Container\Container;
use PHPUnit\Framework\TestCase as BaseTestCase;
use ProcessMaker\Http\Controllers\Admin\UserController;
use ProcessMaker\Managers\ControllerAddonsRegistry;

class ControllerAddonsRegistryTest extends BaseTestCase
{
private ControllerAddonsRegistry $registry;

private ?Container $previousContainer = null;

protected function setUp(): void
{
parent::setUp();

$this->registry = new ControllerAddonsRegistry();
$this->bindRegistryInContainer();
}

protected function tearDown(): void
{
Container::setInstance($this->previousContainer);

parent::tearDown();
}

public function test_register_addon_is_retrieved_for_matching_scope_and_method(): void
{
$this->registry->register(UserController::class, [
'id' => 'test-addon',
'method' => 'edit',
'title' => 'Test Addon',
'content' => 'addon-content',
]);

$addons = $this->registry->getAddons(UserController::class, 'edit', []);

$this->assertCount(1, $addons);
$this->assertSame('test-addon', $addons[0]['id']);
$this->assertSame('addon-content', $addons[0]['content']);
}

public function test_addons_from_other_controllers_are_not_returned(): void
{
$this->registry->register(UserController::class, [
'id' => 'user-addon',
'method' => 'edit',
'content' => 'user-content',
]);
$this->registry->register('Other\\Controller', [
'id' => 'other-addon',
'method' => 'edit',
'content' => 'other-content',
]);

$addons = $this->registry->getAddons(UserController::class, 'edit', []);

$this->assertCount(1, $addons);
$this->assertSame('user-addon', $addons[0]['id']);
}

public function test_get_plugin_addons_does_not_mutate_registered_addons(): void
{
$this->registry->register(UserController::class, [
'id' => 'test-addon',
'method' => 'edit',
'content' => 'original-content',
]);

$this->registry->getAddons(UserController::class, 'edit', []);
$this->registry->getAddons(UserController::class, 'edit', []);

$addons = $this->registry->getAddons(UserController::class, 'edit', []);

$this->assertCount(1, $addons);
$this->assertSame('original-content', $addons[0]['content']);
}

public function test_register_addon_static_method_delegates_to_registry(): void
{
UserController::registerAddon([
'id' => 'static-addon',
'method' => 'edit.settings',
'content' => 'settings-content',
]);

$controller = new UserController();
$addons = $this->invokeGetPluginAddons($controller, 'edit.settings', []);

$this->assertCount(1, $addons);
$this->assertSame('static-addon', $addons[0]['id']);
}

public function test_callable_data_modifier_is_applied_when_resolving_addons(): void
{
$this->registry->register(UserController::class, [
'id' => 'callable-addon',
'method' => 'edit',
'content' => 'content',
'data' => fn (array $data) => array_merge($data, ['extra' => 'value']),
]);

$addons = $this->registry->getAddons(UserController::class, 'edit', ['base' => 'data']);

$this->assertCount(1, $addons);
}

private function bindRegistryInContainer(): void
{
$this->previousContainer = Container::getInstance();

$container = new Container();
$container->singleton(ControllerAddonsRegistry::class, fn () => $this->registry);
Container::setInstance($container);

if (!function_exists('app')) {
require_once dirname(__DIR__, 4) . '/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php';
}
}

/**
* @return array<int, array<string, mixed>>
*/
private function invokeGetPluginAddons(object $controller, string $method, array $data): array
{
$reflection = new \ReflectionMethod($controller, 'getPluginAddons');

return $reflection->invoke($controller, $method, $data);
}
}
Loading