Pagelets

A mechanism for extending Foreman ERB from plugins.

Pagelet is basically a named partial that can mount to an extension point (a symbol that holds list of pagelets) of individual controller/action pairs. Pagelets are rendered onto pages in simple loops as various HTML elements like arbitrary HTML, tabs or buttons.

In Foreman >= 1.13, it is also possible to use controller/partial instead of controller/action. This removes the necessity to register a pagelet for each action if a partial is reused for multiple actions.

Usage

To put some HTML code into a mountpoint, pagelet must be registered in a plugin initializer:

Foreman::Plugin.register :foreman_colly do
  requires_foreman '>= 1.11'

  extend_page "smart_proxies/show" do |cx|
    cx.add_pagelet :main_tabs, :name => N_("My Tab"), :partial => "smart_proxies/show/tab_contents" 
    cx.add_pagelet :tab_contents, :partial => "smart_proxies/show/tabs_pagelet", :my_variable => "blah", :priority => 500, :id => 'custom-html-id', :onlyif => Proc.new { |subject| subject.show_pagelet? }
  end
end

The @extend_page" method requires controller and action pair and creates a pagelet context. All mountpoints and pagelets share the same context within Rails rendering system.

The add_pagelet method accepts mountpoint name (symbol that uniquely identifies mountpoint within context), partial and optional name that is used by some mountpoints (usually new tab name or menu item label). It is a good practice to mark name of your pagelet for translation with N_. More on translations can be found on Translating page. Each pagelet has a priority (number) for each individual controller/action. If no priority is given, pagelets receive default priority numbers from 100 increasing by 100. Pagelet with lowest priority number gets to be rendered first for a mountpoint within context. You can also specify an optional html id for your pagelet. This is significant if pagelet serves as a tab. If no id is provided, a default one is created from pagelet's name. Conditional rendering of a pagelet can be specified with :onlyif, which is helpful for hiding tab headers. This option expects a proc which gets evaluated in the context of a view and a pagelet is displayed when the result is true. In cases where no :onlyif option is supplied, pagelet is always visible.
Other values can be provided that are all visible via the "pagelet" variable from the partial:

Pagelet examples

Writing pagelet partials is straightforward, the ERB context is shared, variables passed in along with the pagelet attribute:

<div class="col-md-6">
  Hello world, this is pagelet <%= pagelet.name %> and it has variable <%= pagelet.my_variable %>
</div>

Mountpoint examples

Typical usage is to allow extension of some arbitrary HTML component. This will loop through all pagelets mounted to a particular mountpoint and render them. Variables can be added to the page context:

<div class="row">
  <%= render_pagelets_for(:mountpoint, :page_variable => @xyz) %>
</div>

It is important to document on this page the expected content. Typical usage for extending a page with existing content is to create Bootstrap Grid environment that can be reused by other plugins. For instance, new class "row" is allocated for the content and each plugin should add column of expected size (e.g. 6).

Pagelet name can be used when creating menu or tab items:

<% pagelets_for(:mountpoint).each do |pagelet|
  <li><a href='#<%= pagelet.name %>' data-toggle='tab'><%= pagelet.name %></a></li>" 
<% end %>

Since tabs are often extended, two helper methods are available: render_tab_header_for and render_tab_content_for. For usage example, see "smart_proxies/show" page.

Remember that pagelets themselves can add new mountpoints, so plugins can extend other plugins.

Best practice

Each pagelet has a priority. In order to give enough space for other plugins, it is good practice to add pagelets with houndrets increments, e.g. pagelet1 = 100, pagelet2 = 200 etc. If plugin author is not interested in putting the item in particular order, priority is not required (and items are placed in order of initialization).

Each pagelet should have a valid name. The name is not used in all contexts, but it is required in order to render things like menu or tab items.

Each extension point must be documented on this page.

Available extension points

This is a complete list of extension points for Foreman development branch.

smart_proxies/show - main_tabs (Foreman 1.11+)

Tabs in Smart Proxies - Show page. Name option is required for the item to show up. Partial represents the contents of the tab.

smart_proxies/show - details_content (Foreman 1.11+)

Additional content on the Details page on the right side within the 6-sized column.

smart_proxies/show - overview_content (Foreman 1.11+)

Additional content on the Details page on the left side, under existing details within the 6-sized column.

smart_proxies/show - smart_proxy_title_actions

Additional entries in the title action dropdown menu.

Other ideas

- We can define mountpoint :title_actions which will render buttons via title_actions method, if available.
- Conditional rendering (#13079)