From 395d8d489413288fa3635b727822f4b0f7f13f5d Mon Sep 17 00:00:00 2001 From: Bob den Otter Date: Mon, 5 Aug 2019 16:56:13 +0200 Subject: [PATCH 1/3] Working on Extensions --- src/Event/Subscriber/ExtensionSubscriber.php | 11 ++- src/Extension/BaseExtension.php | 85 ++++++-------------- src/Extension/ExtensionInterface.php | 3 +- src/Extension/ExtensionRegistry.php | 7 +- src/Twig/HtmlExtension.php | 4 +- src/Widget/BaseWidget.php | 18 ++++- src/Widget/BoltHeaderWidget.php | 9 +++ src/Widget/WidgetInterface.php | 4 + 8 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/Event/Subscriber/ExtensionSubscriber.php b/src/Event/Subscriber/ExtensionSubscriber.php index dc1f58c42..5c5288dad 100644 --- a/src/Event/Subscriber/ExtensionSubscriber.php +++ b/src/Event/Subscriber/ExtensionSubscriber.php @@ -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 { @@ -26,11 +27,15 @@ 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; } /** @@ -38,7 +43,7 @@ public function __construct(ExtensionRegistry $extensionRegistry, Widgets $widge */ public function onKernelResponse(ControllerEvent $event): void { - $this->extensionRegistry->initializeAll($this->widgets, $this->config); + $this->extensionRegistry->initializeAll($this->widgets, $this->config, $this->twig); } /** @@ -46,7 +51,7 @@ public function onKernelResponse(ControllerEvent $event): void */ public function onConsoleResponse(ConsoleCommandEvent $event): void { - $this->extensionRegistry->initializeAll($this->widgets, $this->config); + $this->extensionRegistry->initializeAll($this->widgets, $this->config, $this->twig); } /** diff --git a/src/Extension/BaseExtension.php b/src/Extension/BaseExtension.php index 726e2ed7e..5df49f753 100644 --- a/src/Extension/BaseExtension.php +++ b/src/Extension/BaseExtension.php @@ -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; @@ -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; @@ -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 */ @@ -136,81 +136,44 @@ 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 []; + $this->twig->addExtension($extension); } /** - * Twig: Returns a list of tests to add to the existing list. - * - * @return TwigTest[] + * Get the ComposerPackage, that contains information about the package, + * version, etc. */ - public function getTests(): array + public function getComposerPackage(): ?CompletePackage { - return []; - } + $className = $this->getClass(); - /** - * Twig: Returns a list of functions to add to the existing list. - * - * @return TwigFunction[] - */ - public function getFunctions(): array - { - return []; - } + $finder = static function (PackageInterface $package) use ($className) { + return array_key_exists('entrypoint', $package->getExtra()) && ($className === $package->getExtra()['entrypoint']); + }; + $package = Packages::find($finder); - /** - * Twig: Returns a list of operators to add to the existing list. - * - * @return array First array of unary operators, second array of binary operators - */ - public function getOperators(): array - { - return []; + return $package->current(); } } diff --git a/src/Extension/ExtensionInterface.php b/src/Extension/ExtensionInterface.php index dd333ec2b..02695a03d 100644 --- a/src/Extension/ExtensionInterface.php +++ b/src/Extension/ExtensionInterface.php @@ -6,6 +6,7 @@ use Bolt\Configuration\Config; use Bolt\Widgets; +use Twig\Environment; interface ExtensionInterface { @@ -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; } diff --git a/src/Extension/ExtensionRegistry.php b/src/Extension/ExtensionRegistry.php index 956feceb0..39e3054e8 100644 --- a/src/Extension/ExtensionRegistry.php +++ b/src/Extension/ExtensionRegistry.php @@ -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 */ @@ -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; diff --git a/src/Twig/HtmlExtension.php b/src/Twig/HtmlExtension.php index 49c37f2b4..b6a7029ea 100644 --- a/src/Twig/HtmlExtension.php +++ b/src/Twig/HtmlExtension.php @@ -24,7 +24,7 @@ public function __construct(Markdown $markdown) /** * {@inheritdoc} */ - public function getFunctions() + public function getFunctions(): array { $safe = ['is_safe' => ['html']]; @@ -36,7 +36,7 @@ public function getFunctions() /** * {@inheritdoc} */ - public function getFilters() + public function getFilters(): array { $safe = ['is_safe' => ['html']]; diff --git a/src/Widget/BaseWidget.php b/src/Widget/BaseWidget.php index d4c05d802..ce39c0e86 100644 --- a/src/Widget/BaseWidget.php +++ b/src/Widget/BaseWidget.php @@ -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; @@ -27,6 +28,9 @@ abstract class BaseWidget implements WidgetInterface /** @var string from RequestZone */ protected $zone; + /** @var ExtensionInterface */ + protected $extension; + /** @var int */ protected $priority = 0; @@ -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 @@ -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("Could not render extension '%s'.
", $this->getName()); - $output .= sprintf('%s', $e->getMessage()); + $output = "
"; + $output .= sprintf("Could not render extension '%s'.
", $this->getName()); + $output .= sprintf('%s
', $e->getMessage()); + $output .= sprintf('Did you mean to use @%s/%s instead?
', $this->getSlug(), basename($this->getTemplate())); } } else { $output = $this->getTemplate(); diff --git a/src/Widget/BoltHeaderWidget.php b/src/Widget/BoltHeaderWidget.php index ab5c0197f..bdd64019b 100644 --- a/src/Widget/BoltHeaderWidget.php +++ b/src/Widget/BoltHeaderWidget.php @@ -4,6 +4,7 @@ namespace Bolt\Widget; +use Bolt\Extension\ExtensionInterface; use Bolt\Widget\Injector\RequestZone; use Bolt\Widget\Injector\Target; @@ -11,6 +12,9 @@ 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); @@ -37,4 +41,9 @@ public function getZone(): string { return RequestZone::FRONTEND; } + + public function injectExtension(ExtensionInterface $extension): void + { + $this->extension = $extension; + } } diff --git a/src/Widget/WidgetInterface.php b/src/Widget/WidgetInterface.php index fbdd27db8..52a605f68 100644 --- a/src/Widget/WidgetInterface.php +++ b/src/Widget/WidgetInterface.php @@ -4,6 +4,8 @@ namespace Bolt\Widget; +use Bolt\Extension\ExtensionInterface; + /** * Every widget must implement this interface. */ @@ -24,4 +26,6 @@ public function getPriority(): int; public function getZone(): string; public function __invoke(array $params = []): ?string; + + public function injectExtension(ExtensionInterface $extension): void; } From a90988d81086335666942e9a14b4992265024582 Mon Sep 17 00:00:00 2001 From: Bob den Otter Date: Mon, 5 Aug 2019 17:23:44 +0200 Subject: [PATCH 2/3] Working --- composer.json | 1 + config/extensions/bobdenotter-weatherwidget.yaml | 7 +++++++ symfony.lock | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 config/extensions/bobdenotter-weatherwidget.yaml diff --git a/composer.json b/composer.json index 4e7f75649..6e5a1047a 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/config/extensions/bobdenotter-weatherwidget.yaml b/config/extensions/bobdenotter-weatherwidget.yaml new file mode 100644 index 000000000..43ca75992 --- /dev/null +++ b/config/extensions/bobdenotter-weatherwidget.yaml @@ -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: \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index 35070e526..744a85626 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,7 @@ { + "acmecorp/reference-extension": { + "version": "1.0.0" + }, "api-platform/core": { "version": "2.1", "recipe": { From 64cbc458d950f3571003e7f8f747efe206300e43 Mon Sep 17 00:00:00 2001 From: Bob den Otter Date: Mon, 5 Aug 2019 17:54:15 +0200 Subject: [PATCH 3/3] Fixing tests --- config/extensions/acmecorp-reference.yaml | 3 +++ src/Extension/BaseExtension.php | 4 ++++ templates/pages/extensions.html.twig | 13 +++++++++---- tests/e2e/pages/dashboard.js | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 config/extensions/acmecorp-reference.yaml diff --git a/config/extensions/acmecorp-reference.yaml b/config/extensions/acmecorp-reference.yaml new file mode 100644 index 000000000..ca0e2017e --- /dev/null +++ b/config/extensions/acmecorp-reference.yaml @@ -0,0 +1,3 @@ +# Reference extension configuration file + +foo: Bar diff --git a/src/Extension/BaseExtension.php b/src/Extension/BaseExtension.php index 5df49f753..4f9d24cb5 100644 --- a/src/Extension/BaseExtension.php +++ b/src/Extension/BaseExtension.php @@ -158,6 +158,10 @@ public function registerWidget(WidgetInterface $widget): void */ public function registerTwigExtension(TwigExtensionInterface $extension): void { + if ($this->twig->hasExtension(\get_class($extension))) { + return; + } + $this->twig->addExtension($extension); } diff --git a/templates/pages/extensions.html.twig b/templates/pages/extensions.html.twig index 08c3a404e..743b9be1d 100644 --- a/templates/pages/extensions.html.twig +++ b/templates/pages/extensions.html.twig @@ -6,7 +6,9 @@ {% for extension in extensions %} {% set package = extension.composerPackage() %} - {% set config = extension.hasConfigFilenames()|first %} + {% set config = extension.config() %}{# Note: we do this, because then we trigger copying the dist file, if needed #} + {% set configFile = extension.hasConfigFilenames()|first %} + {% set configLink = path('bolt_file_edit', {'location': 'config', 'file': configFile }) %}
@@ -23,6 +25,10 @@
{% for author in package.authors %}{{ author.name }}{% if not loop.last %}, {% endif %}{% endfor %}
Package / Class name:
{{ package.prettyName }} - {{ extension.class }}
+ {% if configFile %} +
Configuration filename:
+
{{ configFile }}
+ {% endif %}
Version:
{{ package.prettyVersion }}
@@ -39,9 +45,8 @@ {% endif %}
- {% if config %} - {% set link = path('bolt_file_edit', {'location': 'config', 'file': config }) %} - + {% if configFile %} + Configuration {% endif %} diff --git a/tests/e2e/pages/dashboard.js b/tests/e2e/pages/dashboard.js index 560b92314..6e2ee943a 100644 --- a/tests/e2e/pages/dashboard.js +++ b/tests/e2e/pages/dashboard.js @@ -12,7 +12,7 @@ class DashboardPage extends BasePage { this.first_record = $('.listing__row'); // Test for widget - this.widget_title = $('#widget-news-widget h5'); + this.widget_title = $('#widget-news-widget .card-header'); } }