Working with .erb templates in a plugin

I’ve been giving some advice on how to work with .erb templates in Discourse recently and noticed there isn’t much detail on this here on meta, so I thought I’d start this wiki on the subject. This wiki assumes you know what .erb templates are and what their function is in Discourse, and Rails more broadly. If you want more details on that background please read this first.

Server plugin outlets

Server plugin outlets work in a similar fashion to client plugin outlets, albeit there are only a few of them. They let you insert your own .erb template into a discourse/discourse .erb template. They are defined like this:

<%= server_plugin_outlet [name] %>

For an example see the use of the unsubscribe_options outlet in the chat plugin:

HTML builders

HTML builders are designed for standard HTML that is dynamically built, instead of using templates and plugin outlets. To use HTML builders in .erb templates look for these insertion points:

<%= build_plugin_html [name] %>

You can insert HTML at these points by returning it to a register_html_builder block in your plugin.rb, i.e.

register_html_builder([name]) do |controller|
   ## build and return your custom HTML
end

For an example see the use of server:before-head-close-crawler in the solved plugin:

You can actually register html builders to add client-side html as well, however this is not recommended. Add client-side html using the client-side plugin outlets and templates.

In a similar vein there is also a server-side plugin api method called register_custom_html, which can be used for similar purposes, however unless you have specific reason to do so, and you know what you’re doing, I wouldn’t use it.

Overriding a template

You can override .erb templates from a plugin by adding your plugin’s views to a controller’s view_paths. This is just Rails, and is not Discourse specific. How this works is covered in the rails guides here:

https://guides.rubyonrails.org/v5.1/action_view_overview.html#view-paths

For example, if you want to override app/views/email/unsubscribed.html.erb, you should to do two things:

Structure your plugin views using the same namespace

Your template should be here:

your-plugin-folder/app/views/email/unsubscribed.html.erb

Prepend your plugin views path to the relevant controller

In this case the relevant controller is EmailController. Add the following to your plugin.rb file:

EmailController.prepend_view_path File.expand_path("../views", __FILE__)

You could add your plugin’s views to the global view paths, however there’s no need to in most cases.

6 Likes