Skip to content

Support HTML content within templates #4502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 13, 2020
Merged

Support HTML content within templates #4502

merged 14 commits into from
May 13, 2020

Conversation

theseanything
Copy link
Contributor

@theseanything theseanything commented May 4, 2020

Here we aim to add support for more content types within named render_content_for blocks. Previously it wasn't possible to specify the content type within the template. e.g content_for :body is always rendered as Govspeak.

This PR adds the format option to render_content_for method within templates.

<% render_content_for :body, format: html %>
...content...
<% end %>

This new option supports :govspeak, :text and :html. Named blocks (e.g. :title, :body etc) have individual defaults, which are outlined in the docs. Otherwise all blocks default to :govspeak.

To enable this the following changes have been made:

  • Removed the ability to show unprocessed govspeak markdown by appending .txt on URLs. This was a debugging feature, however cannot be supported as content is pre-processed into the required format before being added to the main view. It also seemed to add unnecessary complexity when the source code is available to read.
  • Mandatory error_ prefix for error message names. This is to allow a default format of :text to apply to that error message content.

@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 4, 2020 15:48 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 4, 2020 17:30 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 5, 2020 10:52 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 5, 2020 11:01 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 5, 2020 11:02 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 5, 2020 11:09 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 5, 2020 12:18 Inactive
@theseanything theseanything marked this pull request as ready for review May 5, 2020 12:20
@theseanything theseanything requested a review from kevindew May 5, 2020 12:53
Copy link
Member

@kevindew kevindew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job Sean, looks like this opens up lots of opportunities to give Smart Answers a bit more polish and be more flexible for future endeavours.

A few things come to mind:

  • We've got a whole ton of files with govspeak.erb extensions that can now be mixed media, shall we change all of those to .erb
  • There is this somewhat quirky feature in Smart Answers where it takes the text of answers and puts them into the content item as hidden search terms (For example, the details->hidden_search_terms array in: https://www.gov.uk/api/content/business-coronavirus-support-finder) I'm not sure how that works and/or if it uses the text handling
  • Another quirky thing we've got is the MarriageAbroadOutcomeFlattener, which I believe Platform Health may use, I don't know if that'll be negatively impacted at all? It's super weird so would be great to remove it

@@ -14,22 +14,22 @@ def initialize(template_directory:, template_name:, locals: {}, helpers: [])
@template_directory = template_directory
@template_name = template_name
@locals = locals
lookup_context = ActionView::LookupContext.new([@template_directory, FlowRegistry.instance.load_path])
default_view_paths = ActionController::Base.view_paths.paths.map(&:to_s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks familiar 😉 - I do wonder if there's a less cryptic way to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahah yup, definitely pinched that. Had a look and can't see an obvious nicer solution. Essentially what we are trying to do is get the govuk_publishing_components views in the view path (which is system dependent). So could do something like:

lookup_context = ActionView::LookupContext.new([
    @template_directory,
    FlowRegistry.instance.load_path,
    File.expand_path("app/views", GovukPublishingComponents::Config.gem_directory),
])

But felt like this is making assumptions about where the views live inside GovukPublishingComponents

@@ -0,0 +1,64 @@
module SmartAnswer
module Helpers::FormatCaptureHelper
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a slightly strange namespace for this to be - as this is a component of the ErbRenderer it might make sense under that. Under helpers gives me the impression this is shared around whereas this is only used in one context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah good point! I'll try and re-organise this better - I think I was just trying to the code in to another file, because the erb_renderer.rb was getting super long.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved them under the ErbRenderer namespace.

text: [/^title$/, /^meta_description$/, /^hint$/, /^label$/, /^suffix_label$/, /^error_*./],
}.freeze

def content_for(name, content = nil, options = {}, &block)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered whether it'd be good to rename this so that it didn't clobber the existing content_for function that ActiveView provides. Maybe it's too much of a pain in this repo at this point but it feels like we're breaking the L in SOLID that the subclassing of this function stops the original use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup agree, I was trying limit breaking changes how existing templates are currently written, but probably safer to give it a new name and not try to deal with Rails internals.


should "render govspeak by default" do
assert_captured_content("content", nil, "<g><p>content</p></g>")
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to also make clear that it's wrapped in govspeak component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully the larger refactor of the tests makes this clear now?

Copy link
Contributor

@benthorner benthorner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I've been beaten to the review here! I do like the end result - it's neat ✏️. I'm content with the overall approach, and we should just address a few things before this goes in.

@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 12:27 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 13:27 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 13:34 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 15:34 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 16:38 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 6, 2020 18:40 Inactive
@theseanything
Copy link
Contributor Author

@kevindew @benthorner Thanks for the review and all the comments! Appreciate this becoming quite large and there's a lot going on - if its helpful, I can try split off smaller PRs? (The commits may be easier to follow)

  • We've got a whole ton of files with govspeak.erb extensions that can now be mixed media, shall we change all of those to .erb

Yup - happy for me to change that in a separate PR?

  • There is this somewhat quirky feature in Smart Answers where it takes the text of answers and puts them into the content item as hidden search terms (For example, the details->hidden_search_terms array in: https://www.gov.uk/api/content/business-coronavirus-support-finder) I'm not sure how that works and/or if it uses the text handling

We should be good! The hidden_search_terms array is generated here - looks like it strips out HTML and is getting strings from the Outcome and Question Presenters, which were always rendered Govspeak anyway.

  • Another quirky thing we've got is the MarriageAbroadOutcomeFlattener, which I believe Platform Health may use, I don't know if that'll be negatively impacted at all? It's super weird so would be great to remove it

Should be good too! - it just generates partials from a yaml file, but the views that use them e.g here them has been updated. The templates from which the partials are generated in the MarriageAbroadOutcomeFlattener aren't effected.

@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 10:24 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 10:34 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 11:23 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 12:08 Inactive
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 14:07 Inactive
This feature was used for debugging by allowing a user to append .txt
to a url to show the govspeak unrendered. This adds complexity to the
view rendering logic with little value as a user can reference the
source code instead.
This parameter provided the option to retrieve content without
processing it as govspeak i.e `html: false` prevents the block being
processed as govspeak and returns plain text. This is no longer needed
as it was used for a debugging feature to user unprocessed govspeak,
which was removed in earlier commits.
This prefix's error messages with `error_` so they conform to
conventions outlined in the documentation.
The FormatCaptureHelper was added to render content_for blocks within
smart answer templates. Previously it was not possible to specify the
content type for particular sections within the template, as they were
all post-processed in the same way. e.g. content for the body was
always passed through the Govspeak processor before being rendered in
the main views (result, question, start). Therefore there was no way to
specify a content format within the node template. e.g. html content for
the body, not processed by govspeak. This helper enables the option to
specify a content format with the content_for function within the node
template.

To enable this we are also expanding the view paths of ErbRenderer to
include those from Rail's ActionController::Base. This is because we
need access GovukPublishingComponents to fully render govspeak.
As rendering of the content_for block is done at view render time. There
is no need to have individual methods to access types of content from
the ErbRenderer. Access can be simplified to simply delegating to the
ActiveView::Base#content_for method.
Govspeak is rendered and wrapped with the govspeak component when the
content_for is called in the template. Therefore there are redundant
wrapping of govspeak within the main views (i.e. results, questions and
landing views)
This adds the :html format for content_for block. It should render the
block as is.
For consistency wih FormatCaptureHelper, this code was moved to a
seperated file under the helpers folder.
The FormatCaptureHelper and QuestionOptionHelper were under the general
helper module namespace. To make it more clear their purpose, they have
been moved under the ErbRenderer namespace.
@bevanloon bevanloon temporarily deployed to smart-answer-html-templ-3bjbmf May 7, 2020 15:11 Inactive
@theseanything theseanything requested a review from kevindew May 12, 2020 10:10
Copy link
Member

@kevindew kevindew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wowsers - this is a huge PR (> 900 files 😵). I've only really given a lot of them a scroll through.

I've put just a minor comment on that can be fixed without a re-review.

I do find myself a bit baffled by the render_content_for method, the naming seems a bit confusing outside of govspeak. Though I understand this naming has been what has dragged on this PR and I don't want to block your team over it. What's next for the naming discussion once this is merged?

Previously this method was intented to override the `content_for`
within ActiveView::Helpers:CaptureHelper. This changed the expected
behaviour of content_for within template and could lead to confusion. It
also put dependency on undocumented behaviour of the content_for within
Rails, which could be fragile and confusing to support in the future.

This change requires an update to all templates to use the renamed method.
As this option is only used by render_content_for, it is removed before
being passed on to content_for to prevent any potential side effects.
No longer used.
Previously render_content_for marked strings of text format as
html_safe. However this lead to the possibility of character's not being
properly escaped. e.g. you shouldn't be able to write HTML in the text
field.
@theseanything
Copy link
Contributor Author

Thanks @kevindew!

Wowsers - this is a huge PR (> 900 files 😵). I've only really given a lot of them a scroll through.

Yeah sorry, most of its the renaming from content_for.

I do find myself a bit baffled by the render_content_for method, the naming seems a bit confusing outside of govspeak. Though I understand this naming has been what has dragged on this PR and I don't want to block your team over it. What's next for the naming discussion once this is merged?

Yup, so I've opened issue #4510 to continue discussion. I was initial hesitant to merge this, but think it's fine given the previous behaviour had the same issues (i.e. content_for implicitly rendered Govspeak for some sections and plaintext for others.) But there is definitely room for improvement on the syntax for these templates.

@kevindew
Copy link
Member

Great stuff - looks like this is good to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants