8 Insanely Useful ActiveAdmin Customizations

Mike Ackerman, Former Senior Developer

Article Category: #Code

Posted on

Here at Viget, we've successfully used ActiveAdmin on a number of custom CMS projects. ActiveAdmin is a great help in providing a sensible set of features out-of-the-box, while still allowing heavy customization for great justice. It also has a very opinionated way of doing things, which can make customization a bit tricky (eg. layouts via Arbre, Custom Pages, etc.)

After working with ActiveAdmin for a couple of years, here are 8 customizations that I find myself using often:

1. Adding a custom behavior to ActiveAdmin::BaseController:

Often times, you'll want to add a before_filter to something like your typical ApplicationController, but scoped to the ActiveAdmin engine. In this case, you can add custom behavior by following this pattern:

# config/initializers/active_admin_extensions.rb:
ActiveAdmin::BaseController.send(:include, ActiveAdmin::SiteRestriction)

# lib/active_admin/site_restriction.rb:
module ActiveAdmin
  module SiteRestriction
    extend ActiveSupport::Concern

    included do
      before_filter :restrict_to_own_site
    end

    private

    def restrict_to_own_site
      unless current_site == current_admin_user.site
        render_404
      end
    end
  end
end

2. Conditionally add a navigation menu item:

When you have multiple admin types, you may want to only show certain menu items to a specific type of admin user. You can conditionally show menu items:

# app/admin/resource.rb:
ActiveAdmin.register Resource do
  menu :parent => "Super Admin Only", :if => proc { current_admin_user.super_admin? }
end

3. To display an already uploaded image on the form:

ActiveAdmin uses Formtastic behind the scenes for forms. Persisting uploads between invalid form submissions can be accomplished via f.input :image_cache, :as => :hidden. However you may want to display the already uploaded image upon visiting the form for editing an existing item. You could use set one using a hint (f.input :image, :hint => (f.template.image_tag(f.object.image.url) if f.object.image?)), but this won't allow you to set any text as a hint. Instead, you could add some custom behavior to the Formtastic FileInput:

# app/admin/inputs/file_input.rb
class FileInput < Formtastic::Inputs::FileInput
  def to_html
    input_wrapping do
      label_html <<
        builder.file_field(method, input_html_options) <<
        image_preview_content
    end
  end

  private

  def image_preview_content
    image_preview? ? image_preview_html : ""
  end

  def image_preview?
    options[:image_preview] && @object.send(method).present?
  end

  def image_preview_html
    template.image_tag(@object.send(method).url, :class => "image-preview")
  end
end

# app/admin/my_class.rb
ActiveAdmin.register MyClass do

  form do |f|
    f.input :logo_image, :image_preview => true
  end
end

4. Scoping queries:

ActiveAdmin uses Inherited Resources behind the scenes. Inherited Resources uses the concept of resource and collection. ActiveAdmin uses a controller method called scoped_collection which we can override to add our own scope.
In your ActiveAdmin resource definition file:

# app/admin/my_class.rb
ActiveAdmin.register MyClass do 

  controller do
    def scoped_collection
      Post.for_site(current_site)
    end
  end
end

Similarly, you can override the resource method to customize how the singular resource is found.

5. Customizing the method by which a resource is found by the URL:

Often times we add a "slug" to a resource for prettier URLs. You can use your friendly URL parameters in the CMS as well:

# app/admin/page.rb
ActiveAdmin.register Page do

  controller do
    defaults :finder => :find_by_slug!
  end
end

I found that when a user submits the form while changing the slug value and the form is invalid, the next form submission will attempt to POST to the invalid slug. To fix this, I updated my model's to_param method from:

def to_param
  slug
end

To:

def to_param
  if invalid? && slug_changed?
    # return original slug value
    changes["slug"].first
  else
    slug
  end
end

6. Inserting arbitrary content into the form:

It can be handy to insert some explaining text, or even images, to within the Formtastic form itself. You'll need to insert the content into the form buffer:
Text:

form do |f|
  f.form_buffers.last << "<li>My Text</li>".html_safe
end

Image:

form do |f|
  f.form_buffers.last << f.template.image_tag('http://doge.com/image.png', :height => 350)
end

7. Dynamic Site Title:

It's possible you have multiple user types or sites that are managed via a single CMS. In this case, you may want to update the title displayed in the Navigation Menu.

# config/initializers/active_admin.rb
config.site_title = proc { "#{current_site.name} CMS" }

8. Manually defining sort order of top level navigation menu items:

It's easy to define the priority (sort order) of menu items when they belong to a parent, however if you are trying to set the sort order of top level parent items, it's a bit trickier.

# config/initializers/active_admin.rb
config.namespace :admin do |admin|
  admin.build_menu do |menu|
    menu.add :label => "First Item", :priority => 1
    menu.add :label => "Second Item", :priority => 2
    menu.add :label => "Third Item", :priority => 3
  end
end

What customizations have you found that you think are worth sharing? Please leave a note in the comments below!

Related Articles