diff --git a/framework/core/js/src/common/models/Post.js b/framework/core/js/src/common/models/Post.js index 5941ffe5e..29a122cb9 100644 --- a/framework/core/js/src/common/models/Post.js +++ b/framework/core/js/src/common/models/Post.js @@ -10,9 +10,11 @@ Object.assign(Post.prototype, { createdAt: Model.attribute('createdAt', Model.transformDate), user: Model.hasOne('user'), + contentType: Model.attribute('contentType'), content: Model.attribute('content'), contentHtml: Model.attribute('contentHtml'), + renderFailed: Model.attribute('renderFailed'), contentPlain: computed('contentHtml', getPlainContent), editedAt: Model.attribute('editedAt', Model.transformDate), diff --git a/framework/core/js/src/forum/components/CommentPost.js b/framework/core/js/src/forum/components/CommentPost.js index f7433bc0b..730a54969 100644 --- a/framework/core/js/src/forum/components/CommentPost.js +++ b/framework/core/js/src/forum/components/CommentPost.js @@ -100,6 +100,7 @@ export default class CommentPost extends Post { ' ' + classList({ CommentPost: true, + 'Post--renderFailed': post.renderFailed(), 'Post--hidden': post.isHidden(), 'Post--edited': post.isEdited(), revealContent: this.revealContent, diff --git a/framework/core/less/forum/Post.less b/framework/core/less/forum/Post.less index e29a2b409..aa6994104 100644 --- a/framework/core/less/forum/Post.less +++ b/framework/core/less/forum/Post.less @@ -185,6 +185,10 @@ } } +.Post--renderFailed { + background-color: @control-danger-bg; +} + .Post--hidden { .Post-header, .Post-header a, .PostUser h3, .PostUser h3 a { color: @muted-more-color; diff --git a/framework/core/locale/core.yml b/framework/core/locale/core.yml index 40356cdb1..8b7e1c820 100644 --- a/framework/core/locale/core.yml +++ b/framework/core/locale/core.yml @@ -527,6 +527,7 @@ core: payload_too_large_message: The request payload was too large. permission_denied_message: You do not have permission to do that. rate_limit_exceeded_message: You're going a little too quickly. Please try again in a few seconds. + render_failed_message: Sorry, we encountered an error while displaying this content. If you're a user, please try again later. If you're an administrator, take a look in your Flarum log files for more information. # These translations are used in the loading indicator component. loading_indicator: diff --git a/framework/core/src/Api/Serializer/BasicPostSerializer.php b/framework/core/src/Api/Serializer/BasicPostSerializer.php index f687ca59c..55f04533a 100644 --- a/framework/core/src/Api/Serializer/BasicPostSerializer.php +++ b/framework/core/src/Api/Serializer/BasicPostSerializer.php @@ -9,12 +9,30 @@ namespace Flarum\Api\Serializer; +use Exception; +use Flarum\Foundation\ErrorHandling\LogReporter; use Flarum\Post\CommentPost; use Flarum\Post\Post; use InvalidArgumentException; +use Symfony\Contracts\Translation\TranslatorInterface; class BasicPostSerializer extends AbstractSerializer { + /** + * @var LogReporter + */ + protected $log; + + /** + * @var TranslatorInterface + */ + protected $translator; + + public function __construct(LogReporter $log, TranslatorInterface $translator) + { + $this->log = $log; + $this->translator = $translator; + } /** * {@inheritdoc} */ @@ -41,7 +59,14 @@ class BasicPostSerializer extends AbstractSerializer ]; if ($post instanceof CommentPost) { - $attributes['contentHtml'] = $post->formatContent($this->request); + try { + $attributes['contentHtml'] = $post->formatContent($this->request); + $attributes['renderFailed'] = false; + } catch (Exception $e) { + $attributes['contentHtml'] = $this->translator->trans('core.lib.error.render_failed_message'); + $this->log->report($e); + $attributes['renderFailed'] = true; + } } else { $attributes['content'] = $post->content; } diff --git a/framework/core/tests/integration/api/posts/ShowTest.php b/framework/core/tests/integration/api/posts/ShowTest.php new file mode 100644 index 000000000..632553af5 --- /dev/null +++ b/framework/core/tests/integration/api/posts/ShowTest.php @@ -0,0 +1,82 @@ +prepareDatabase([ + 'discussions' => [ + ['id' => 1, 'title' => 'Discussion with post', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1, 'is_private' => 0], + ], + 'posts' => [ + ['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '

valid

'], + ['id' => 2, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => ' [ + $this->normalUser(), + ] + ]); + } + + /** + * @test + */ + public function properly_formatted_post_rendered_correctly() + { + $response = $this->send( + $this->request('GET', '/api/posts/1', [ + 'authenticatedAs' => 2, + ]) + ); + + $this->assertEquals(200, $response->getStatusCode()); + + $body = (string) $response->getBody(); + $this->assertJson($body); + + $data = json_decode($body, true); + + $this->assertEquals($data['data']['attributes']['contentHtml'], '

valid

'); + } + + /** + * @test + */ + public function malformed_post_caught_by_renderer() + { + $response = $this->send( + $this->request('GET', '/api/posts/2', [ + 'authenticatedAs' => 2, + ]) + ); + + $this->assertEquals(200, $response->getStatusCode()); + + $body = (string) $response->getBody(); + $this->assertJson($body); + + $data = json_decode($body, true); + + $this->assertEquals("Sorry, we encountered an error while displaying this content. If you're a user, please try again later. If you're an administrator, take a look in your Flarum log files for more information.", $data['data']['attributes']['contentHtml']); + } +}