Skip to content

Commit

Permalink
Merge pull request #563 from bolt/working-on-extensions
Browse files Browse the repository at this point in the history
Working on Extensions
  • Loading branch information
bobdenotter authored Aug 5, 2019
2 parents e39a043 + 64cbc45 commit 789414e
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 77 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"require-dev": {
"ext-curl": "*",
"ext-pdo_sqlite": "*",
"acmecorp/reference-extension": "^1.0",
"behat/behat": "^3.5",
"behat/mink": "dev-master@dev",
"behat/mink-extension": "^2.3",
Expand Down
3 changes: 3 additions & 0 deletions config/extensions/acmecorp-reference.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Reference extension configuration file

foo: Bar
7 changes: 7 additions & 0 deletions config/extensions/bobdenotter-weatherwidget.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Weather widget configuration file

# Leave the location blank for a guesstimate of your current location. Otherwise
# Set it to the preferred name of your country / city.

location: Paris
#location:
11 changes: 8 additions & 3 deletions src/Event/Subscriber/ExtensionSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;

class ExtensionSubscriber implements EventSubscriberInterface
{
Expand All @@ -26,27 +27,31 @@ class ExtensionSubscriber implements EventSubscriberInterface
/** @var Config */
private $config;

public function __construct(ExtensionRegistry $extensionRegistry, Widgets $widgets, Config $config)
/** @var Environment */
private $twig;

public function __construct(ExtensionRegistry $extensionRegistry, Widgets $widgets, Config $config, Environment $twig)
{
$this->extensionRegistry = $extensionRegistry;
$this->widgets = $widgets;
$this->config = $config;
$this->twig = $twig;
}

/**
* Kernel response listener callback.
*/
public function onKernelResponse(ControllerEvent $event): void
{
$this->extensionRegistry->initializeAll($this->widgets, $this->config);
$this->extensionRegistry->initializeAll($this->widgets, $this->config, $this->twig);
}

/**
* Command response listener callback.
*/
public function onConsoleResponse(ConsoleCommandEvent $event): void
{
$this->extensionRegistry->initializeAll($this->widgets, $this->config);
$this->extensionRegistry->initializeAll($this->widgets, $this->config, $this->twig);
}

/**
Expand Down
89 changes: 28 additions & 61 deletions src/Extension/BaseExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Bolt\Configuration\Config;
use Bolt\Event\Subscriber\ExtensionSubscriber;
use Bolt\Widget\WidgetInterface;
use Bolt\Widgets;
use Cocur\Slugify\Slugify;
use Composer\Package\CompletePackage;
Expand All @@ -14,17 +15,13 @@
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Parser;
use Tightenco\Collect\Support\Collection;
use Twig\Environment;
use Twig\Extension\ExtensionInterface as TwigExtensionInterface;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;

/**
* BaseWidget can be used as easy starter pack or as a base for your own extensions.
*/
abstract class BaseExtension implements ExtensionInterface, TwigExtensionInterface
abstract class BaseExtension implements ExtensionInterface
{
/** @var Widgets */
protected $widgets;
Expand All @@ -35,6 +32,9 @@ abstract class BaseExtension implements ExtensionInterface, TwigExtensionInterfa
/** @var Config */
protected $boltConfig;

/** @var Environment */
protected $twig;

/**
* Returns the descriptive name of the Extension
*/
Expand Down Expand Up @@ -136,81 +136,48 @@ public function initialize(): void
*
* @see ExtensionSubscriber
*/
public function injectObjects(Widgets $widgets, Config $boltConfig): void
public function injectObjects(Widgets $widgets, Config $boltConfig, Environment $twig): void
{
$this->widgets = $widgets;
$this->boltConfig = $boltConfig;
}

public function getComposerPackage(): ?CompletePackage
{
$className = $this->getClass();

$finder = static function (PackageInterface $package) use ($className) {
return array_key_exists('entrypoint', $package->getExtra()) && ($className === $package->getExtra()['entrypoint']);
};
$package = Packages::find($finder);

return $package->current();
$this->twig = $twig;
}

/**
* Twig: Returns the token parser instances to add to the existing list.
*
* @return TokenParserInterface[]
* Shortcut method to register a widget and inject the extension into it
*/
public function getTokenParsers(): array
public function registerWidget(WidgetInterface $widget): void
{
return [];
}
$widget->injectExtension($this);

/**
* Twig: Returns the node visitor instances to add to the existing list.
*
* @return NodeVisitorInterface[]
*/
public function getNodeVisitors(): array
{
return [];
$this->widgets->registerWidget($widget);
}

/**
* Twig: Returns a list of filters to add to the existing list.
*
* @return TwigFilter[]
* Shortcut method to register a TwigExtension.
*/
public function getFilters(): array
public function registerTwigExtension(TwigExtensionInterface $extension): void
{
return [];
}
if ($this->twig->hasExtension(\get_class($extension))) {
return;
}

/**
* Twig: Returns a list of tests to add to the existing list.
*
* @return TwigTest[]
*/
public function getTests(): array
{
return [];
$this->twig->addExtension($extension);
}

/**
* Twig: Returns a list of functions to add to the existing list.
*
* @return TwigFunction[]
* Get the ComposerPackage, that contains information about the package,
* version, etc.
*/
public function getFunctions(): array
public function getComposerPackage(): ?CompletePackage
{
return [];
}
$className = $this->getClass();

/**
* Twig: Returns a list of operators to add to the existing list.
*
* @return array<array> First array of unary operators, second array of binary operators
*/
public function getOperators(): array
{
return [];
$finder = static function (PackageInterface $package) use ($className) {
return array_key_exists('entrypoint', $package->getExtra()) && ($className === $package->getExtra()['entrypoint']);
};
$package = Packages::find($finder);

return $package->current();
}
}
3 changes: 2 additions & 1 deletion src/Extension/ExtensionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Bolt\Configuration\Config;
use Bolt\Widgets;
use Twig\Environment;

interface ExtensionInterface
{
Expand All @@ -15,7 +16,7 @@ public function getName(): string;

public function getClass(): string;

public function injectObjects(Widgets $widgets, Config $config): void;
public function injectObjects(Widgets $widgets, Config $config, Environment $twig): void;

public function initialize(): void;
}
7 changes: 4 additions & 3 deletions src/Extension/ExtensionRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
use Bolt\Widgets;
use Composer\Package\PackageInterface;
use ComposerPackages\Types;
use Twig\Environment;

class ExtensionRegistry
{
/** @var ExtensionInterface[] * */
/** @var ExtensionInterface[] */
protected $extensions = [];

/** @var array */
Expand Down Expand Up @@ -55,13 +56,13 @@ public function getExtensions(): array
return $this->extensions;
}

public function initializeAll(Widgets $widgets, Config $config): void
public function initializeAll(Widgets $widgets, Config $config, Environment $twig): void
{
$this->addComposerPackages();

foreach ($this->getExtensionClasses() as $extensionClass) {
$extension = new $extensionClass();
$extension->injectObjects($widgets, $config);
$extension->injectObjects($widgets, $config, $twig);
$extension->initialize();

$this->extensions[$extensionClass] = $extension;
Expand Down
4 changes: 2 additions & 2 deletions src/Twig/HtmlExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function __construct(Markdown $markdown)
/**
* {@inheritdoc}
*/
public function getFunctions()
public function getFunctions(): array
{
$safe = ['is_safe' => ['html']];

Expand All @@ -36,7 +36,7 @@ public function getFunctions()
/**
* {@inheritdoc}
*/
public function getFilters()
public function getFilters(): array
{
$safe = ['is_safe' => ['html']];

Expand Down
18 changes: 16 additions & 2 deletions src/Widget/BaseWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Bolt\Widget;

use Bolt\Extension\ExtensionInterface;
use Bolt\Widget\Exception\WidgetException;
use Cocur\Slugify\Slugify;
use Symfony\Bundle\TwigBundle\Loader\NativeFilesystemLoader;
Expand All @@ -27,6 +28,9 @@ abstract class BaseWidget implements WidgetInterface
/** @var string from RequestZone */
protected $zone;

/** @var ExtensionInterface */
protected $extension;

/** @var int */
protected $priority = 0;

Expand Down Expand Up @@ -88,6 +92,11 @@ public function getPriority(): int
return $this->priority;
}

public function injectExtension(ExtensionInterface $extension): void
{
$this->extension = $extension;
}

/**
* Method to 'invoke' the widget. Simple wrapper around the 'run' method,
* which can be overridden in a custom Widget or trait
Expand All @@ -107,13 +116,18 @@ protected function run(array $params = []): ?string
$this->setTemplate($params['template']);
}

// Extension is set, and needs to be available in the template
$params['extension'] = $this->extension;

if ($this instanceof TwigAware) {
$this->addTwigLoader();
try {
$output = $this->getTwig()->render($this->getTemplate(), $params);
} catch (LoaderError $e) {
$output = sprintf("<mark><strong>Could not render extension '%s'.</strong></mark><br>", $this->getName());
$output .= sprintf('<mark><code>%s</code></mark>', $e->getMessage());
$output = "<div style='border: 1px solid #666; background-color: #FCF8E3; padding: 0.5rem;'><mark><strong>";
$output .= sprintf("Could not render extension '%s'.</strong></mark><br>", $this->getName());
$output .= sprintf('<code>%s</code><br>', $e->getMessage());
$output .= sprintf('Did you mean to use <code>@%s/%s</code> instead?</mark></div>', $this->getSlug(), basename($this->getTemplate()));
}
} else {
$output = $this->getTemplate();
Expand Down
9 changes: 9 additions & 0 deletions src/Widget/BoltHeaderWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

namespace Bolt\Widget;

use Bolt\Extension\ExtensionInterface;
use Bolt\Widget\Injector\RequestZone;
use Bolt\Widget\Injector\Target;

class BoltHeaderWidget implements WidgetInterface, ResponseAware
{
use ResponseTrait;

/** @var ExtensionInterface */
protected $extension;

public function __invoke(array $params = []): ?string
{
$this->getResponse()->headers->set('X-Powered-By', 'Bolt', false);
Expand All @@ -37,4 +41,9 @@ public function getZone(): string
{
return RequestZone::FRONTEND;
}

public function injectExtension(ExtensionInterface $extension): void
{
$this->extension = $extension;
}
}
4 changes: 4 additions & 0 deletions src/Widget/WidgetInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Bolt\Widget;

use Bolt\Extension\ExtensionInterface;

/**
* Every widget must implement this interface.
*/
Expand All @@ -24,4 +26,6 @@ public function getPriority(): int;
public function getZone(): string;

public function __invoke(array $params = []): ?string;

public function injectExtension(ExtensionInterface $extension): void;
}
3 changes: 3 additions & 0 deletions symfony.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"acmecorp/reference-extension": {
"version": "1.0.0"
},
"api-platform/core": {
"version": "2.1",
"recipe": {
Expand Down
Loading

0 comments on commit 789414e

Please sign in to comment.