diff --git a/src/Extend/Formatter.php b/src/Extend/Formatter.php
index b185a12cd..98041514c 100644
--- a/src/Extend/Formatter.php
+++ b/src/Extend/Formatter.php
@@ -10,38 +10,104 @@
namespace Flarum\Extend;
use Flarum\Extension\Extension;
-use Flarum\Formatter\Event\Configuring;
use Flarum\Formatter\Formatter as ActualFormatter;
use Illuminate\Contracts\Container\Container;
-use Illuminate\Events\Dispatcher;
class Formatter implements ExtenderInterface, LifecycleInterface
{
- private $callback;
+ private $configurationCallbacks = [];
+ private $parsingCallbacks = [];
+ private $renderingCallbacks = [];
+ /**
+ * Configure the formatter. This can be used to add support for custom markdown/bbcode/etc tags,
+ * or otherwise change the formatter. Please see documentation for the s9e text formatter library for more
+ * information on how to use this.
+ *
+ * @param callable|string $callback
+ *
+ * The callback can be a closure or invokable class, and should accept:
+ * - \s9e\TextFormatter\Configurator $configurator
+ */
public function configure($callback)
{
- $this->callback = $callback;
+ $this->configurationCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Prepare the system for parsing. This can be used to modify the text that will be parsed, or to modify the parser.
+ * Please note that the text to be parsed must be returned, regardless of whether it's changed.
+ *
+ * @param callable|string $callback
+ *
+ * The callback can be a closure or invokable class, and should accept:
+ * - \s9e\TextFormatter\Parser $parser
+ * - mixed $context
+ * - string $text: The text to be parsed.
+ *
+ * The callback should return:
+ * - string $text: The text to be parsed.
+ */
+ public function parse($callback)
+ {
+ $this->parsingCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Prepare the system for rendering. This can be used to modify the xml that will be rendered, or to modify the renderer.
+ * Please note that the xml to be rendered must be returned, regardless of whether it's changed.
+ *
+ * @param callable|string $callback
+ *
+ * The callback can be a closure or invokable class, and should accept:
+ * - \s9e\TextFormatter\Rendered $renderer
+ * - mixed $context
+ * - string $xml: The xml to be rendered.
+ * - ServerRequestInterface $request
+ *
+ * The callback should return:
+ * - string $xml: The xml to be rendered.
+ */
+ public function render($callback)
+ {
+ $this->renderingCallbacks[] = $callback;
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
- $events = $container->make(Dispatcher::class);
-
- $events->listen(
- Configuring::class,
- function (Configuring $event) use ($container) {
- if (is_string($this->callback)) {
- $callback = $container->make($this->callback);
- } else {
- $callback = $this->callback;
+ $container->extend('flarum.formatter', function ($formatter, $container) {
+ foreach ($this->configurationCallbacks as $callback) {
+ if (is_string($callback)) {
+ $callback = $container->make($callback);
}
- $callback($event->configurator);
+ $formatter->addConfigurationCallback($callback);
}
- );
+
+ foreach ($this->parsingCallbacks as $callback) {
+ if (is_string($callback)) {
+ $callback = $container->make($callback);
+ }
+
+ $formatter->addParsingCallback($callback);
+ }
+
+ foreach ($this->renderingCallbacks as $callback) {
+ if (is_string($callback)) {
+ $callback = $container->make($callback);
+ }
+
+ $formatter->addRenderingCallback($callback);
+ }
+
+ return $formatter;
+ });
}
public function onEnable(Container $container, Extension $extension)
diff --git a/src/Formatter/Event/Configuring.php b/src/Formatter/Event/Configuring.php
index a09c558e7..9f5993ce4 100644
--- a/src/Formatter/Event/Configuring.php
+++ b/src/Formatter/Event/Configuring.php
@@ -11,6 +11,9 @@ namespace Flarum\Formatter\Event;
use s9e\TextFormatter\Configurator;
+/**
+ * @deprecated beta 15, removed beta 16. Use the Formatter extender instead.
+ */
class Configuring
{
/**
diff --git a/src/Formatter/Event/Parsing.php b/src/Formatter/Event/Parsing.php
index de72035cf..b0fcb3fd1 100644
--- a/src/Formatter/Event/Parsing.php
+++ b/src/Formatter/Event/Parsing.php
@@ -11,6 +11,9 @@ namespace Flarum\Formatter\Event;
use s9e\TextFormatter\Parser;
+/**
+ * @deprecated beta 15, removed beta 16. Use the Formatter extender instead.
+ */
class Parsing
{
/**
diff --git a/src/Formatter/Event/Rendering.php b/src/Formatter/Event/Rendering.php
index 4ec2360f9..16c17d80c 100644
--- a/src/Formatter/Event/Rendering.php
+++ b/src/Formatter/Event/Rendering.php
@@ -12,6 +12,9 @@ namespace Flarum\Formatter\Event;
use Psr\Http\Message\ServerRequestInterface;
use s9e\TextFormatter\Renderer;
+/**
+ * @deprecated beta 15, removed beta 16. Use the Formatter extender instead.
+ */
class Rendering
{
/**
diff --git a/src/Formatter/Formatter.php b/src/Formatter/Formatter.php
index d8cfe62da..f2d6b7c5b 100644
--- a/src/Formatter/Formatter.php
+++ b/src/Formatter/Formatter.php
@@ -20,6 +20,12 @@ use s9e\TextFormatter\Unparser;
class Formatter
{
+ protected $configurationCallbacks = [];
+
+ protected $parsingCallbacks = [];
+
+ protected $renderingCallbacks = [];
+
/**
* @var Repository
*/
@@ -47,6 +53,21 @@ class Formatter
$this->cacheDir = $cacheDir;
}
+ public function addConfigurationCallback($callback)
+ {
+ $this->configurationCallbacks[] = $callback;
+ }
+
+ public function addParsingCallback($callback)
+ {
+ $this->parsingCallbacks[] = $callback;
+ }
+
+ public function addRenderingCallback($callback)
+ {
+ $this->renderingCallbacks[] = $callback;
+ }
+
/**
* Parse text.
*
@@ -58,8 +79,13 @@ class Formatter
{
$parser = $this->getParser($context);
+ // Deprecated in beta 15, remove in beta 16
$this->events->dispatch(new Parsing($parser, $context, $text));
+ foreach ($this->parsingCallbacks as $callback) {
+ $text = $callback($parser, $context, $text);
+ }
+
return $parser->parse($text);
}
@@ -75,8 +101,13 @@ class Formatter
{
$renderer = $this->getRenderer();
+ // Deprecated in beta 15, remove in beta 16
$this->events->dispatch(new Rendering($renderer, $context, $xml, $request));
+ foreach ($this->renderingCallbacks as $callback) {
+ $xml = $callback($renderer, $context, $xml, $request);
+ }
+
return $renderer->render($xml);
}
@@ -122,8 +153,13 @@ class Formatter
$configurator->Autolink;
$configurator->tags->onDuplicate('replace');
+ // Deprecated in beta 15, remove in beta 16
$this->events->dispatch(new Configuring($configurator));
+ foreach ($this->configurationCallbacks as $callback) {
+ $callback($configurator);
+ }
+
$this->configureExternalLinks($configurator);
return $configurator;
diff --git a/tests/integration/extenders/FormatterTest.php b/tests/integration/extenders/FormatterTest.php
new file mode 100644
index 000000000..722f83783
--- /dev/null
+++ b/tests/integration/extenders/FormatterTest.php
@@ -0,0 +1,145 @@
+app()->getContainer()->make(Formatter::class);
+ $formatter->flush();
+
+ return $formatter;
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_config_doesnt_work_by_default()
+ {
+ $formatter = $this->getFormatter();
+
+ $this->assertEquals('[B]something[/B]', $formatter->parse('[B]something[/B]'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_config_works_if_added_with_closure()
+ {
+ $this->extend((new Extend\Formatter)->configure(function ($config) {
+ $config->BBCodes->addFromRepository('B');
+ }));
+
+ $formatter = $this->getFormatter();
+
+ $this->assertEquals('something', $formatter->render($formatter->parse('[B]something[/B]')));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_config_works_if_added_with_invokable_class()
+ {
+ $this->extend((new Extend\Formatter)->configure(InvokableConfig::class));
+
+ $formatter = $this->getFormatter();
+
+ $this->assertEquals('something', $formatter->render($formatter->parse('[B]something[/B]')));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_parsing_doesnt_work_by_default()
+ {
+ $this->assertEquals('Text<a>', $this->getFormatter()->parse('Text'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_parsing_works_if_added_with_closure()
+ {
+ $this->extend((new Extend\Formatter)->parse(function ($parser, $context, $text) {
+ return 'ReplacedText';
+ }));
+
+ $this->assertEquals('ReplacedText<a>', $this->getFormatter()->parse('Text'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_parsing_works_if_added_with_invokable_class()
+ {
+ $this->extend((new Extend\Formatter)->parse(InvokableParsing::class));
+
+ $this->assertEquals('ReplacedText<a>', $this->getFormatter()->parse('Text'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_rendering_doesnt_work_by_default()
+ {
+ $this->assertEquals('Text', $this->getFormatter()->render('Text
'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_rendering_works_if_added_with_closure()
+ {
+ $this->extend((new Extend\Formatter)->render(function ($renderer, $context, $xml, $request) {
+ return 'ReplacedText';
+ }));
+
+ $this->assertEquals('ReplacedText', $this->getFormatter()->render('Text'));
+ }
+
+ /**
+ * @test
+ */
+ public function custom_formatter_rendering_works_if_added_with_invokable_class()
+ {
+ $this->extend((new Extend\Formatter)->render(InvokableRendering::class));
+
+ $this->assertEquals('ReplacedText', $this->getFormatter()->render('Text'));
+ }
+}
+
+class InvokableConfig
+{
+ public function __invoke($config)
+ {
+ $config->BBCodes->addFromRepository('B');
+ }
+}
+
+class InvokableParsing
+{
+ public function __invoke($parser, $context, $text)
+ {
+ return 'ReplacedText';
+ }
+}
+
+class InvokableRendering
+{
+ public function __invoke($renderer, $context, $xml, $request)
+ {
+ return 'ReplacedText';
+ }
+}