1

I have a Rails app that uses activescaffold and I would like to hide some fields on the update for some of the records.

I have been trying to do it using helper methods, but I can not seem to get that to work.

What is the best way to do this?

nroose
  • 1,689
  • 2
  • 21
  • 28

4 Answers4

2

The best way to do this is by using one of the security method templates (depending on your need) provided by activescaffold plugin.

Pasted from activescaffold wiki:

Model Methods: Restricting Anything Else

On your model object you may define methods (none of which accept any arguments) in any of four formats, depending on your need for granularity.

The formats are:

* #{column_name}_authorized_for_#{crud_type}?

For example if you have an activescaffold based controller called user:

class Admin::UsersController < ApplicationController
  active_scaffold do |config|
    config.columns = [:username, :name, :email]
  end
end

And you only want to allow the user to be able to update the username if they are admin then you can do something like this:

User Model:

class User < ActiveRecord::Base

  # ActiveScaffold security template: #{column_name}_authorized_for_#{crud_type}?
  def username_authorized_for_update?
    # As soon as this method will return false 
    # the username field will not be available on the update form
    return true # Write logic to decide if username field should be visible
  end
end

Active Scaffold wiki link: https://github.com/activescaffold/active_scaffold/wiki/Security

whizcreed
  • 2,700
  • 3
  • 22
  • 35
  • That's pretty good. However, I want to do it per record, not per user. I don't think this solution provides record-level security, does it? – nroose May 02 '11 at 16:45
2

If you just want to hide some columns in the update view, then to configure this in the controller is quite easy.

Either you can specify the columns you want to see:

class DocumentsController < ApplicationController
  active_scaffold :document do |config|
    config.columns = [ :id, :product, :title, :document_type, :author, :organization, :document_approver, :document_location ]
    config.list.columns = [ :id, :product, :title, :document_type, :author ]
    config.show.columns = [ :product, :title, :document_type, :author, :organization, :document_approver, :document_location ]
    config.create.columns = [ :product, :title, :document_type, :document_approver, :document_location ]
    config.update.columns = [ :product, :title, :document_type, :organization, :document_approver, :document_location ]
  end
end

Or you can exclude the ones you want to hide:

class DocumentsController < ApplicationController
  active_scaffold :document do |config|
    config.columns = [ :id, :product, :title, :document_type, :author, :organization, :document_approver, :document_location ]
    config.list.columns.exclude :organization, :document_approver, :document_location
    config.show.columns.exclude :id
    config.create.columns.exclude :id, :author, :organization
    config.update.columns.exclude :id, :author
  end
end

Note that the 'config.columns' is used to define the total number of columns for the controller, and if any of 'list', 'show', 'create' or 'update' are not specifically defined, then 'config.columns' is used per default.

This also means, that if you want the same columns visible for all views except 'update', then you can just define it like:

class DocumentsController < ApplicationController
  active_scaffold :document do |config|
    config.columns = [ :id, :product, :title, :document_type, :author, :organization, :document_approver, :document_location ]
    config.update.columns = [ :product, :title, :document_type, :organization, :document_approver, :document_location ]
  end
end

Or:

class DocumentsController < ApplicationController
  active_scaffold :document do |config|
    config.columns = [ :id, :product, :title, :document_type, :author, :organization, :document_approver, :document_location ]
    config.update.columns.exclude :id, :author
  end
end
Jonas Bang Christensen
  • 1,041
  • 2
  • 10
  • 18
0

The security model methods as mentioned in other answers are a good option, however in my case I wanted to allow the field to be shown or hidden depending on the data entered into other columns, by chaining form fields. When using the security model methods, nothing at all is rendered for the field whose security method returned false, which prevents the ActiveScaffold update_column javascript from re-rendering that field when the related column gets updated.

As a simplified example, if my WritingUtensilsController has

config.columns[:type].form_ui = :select
config.columns[:type].options = { options: %w( pencil pen crayon ) }
config.columns[:type].update_columns = [ :ink_color ]
config.columns[:type].send_form_on_update_column = true

And I want the ink_color field to only show up if the type dropdown is set to "pen", using the security method would not work here, because when the type dropdown is changed, we can't locate the ink_color column to update.

Solution

Override the form column partial for the ink_color column (_ink_color_form_column.erb) to conditionally render the normal field, OR a hidden input (with no name attribute but the correct class) inside a dl tag depending on whether or not the writing utensil is a pen:

writing_utensils/_ink_color_form_column.erb:

<% if record.is_pen? %>
  <%= form_attribute(column, record, scope, false) %>
<% else %>
  <dl><input type="hidden" class="<%= column.name %>-input"></dl>
<% end %>

Calling the form_attribute method will cause the column to be rendered as normal. When the record is not a pen however, the hidden input will allow the javascript to target the parent dl of the input with class ink_color-input for replacement when the type column gets updated by the user.

Bonus:

add an after_render_field method in your controller to manipulate your record when the update_column process happens:

def after_render_field(record, column)
  if column.name == :type
    if record.type == 'pen'
      record.last_sharpened = nil
    end
  end
end

be sure to add last_sharpened to the update_columns array if you want it to be re-rendered with the new value as well

Lannar
  • 143
  • 1
  • 6
0

whizcreed's answer is correct, and these ActiveScaffold security model methods are in fact evaluated per-record, so you could do something like this in the model:

def username_authorized_for_update?
  return true unless existing_record_check?
  return false if userrights != 'admin'
  return true
end

where userrights is a string field on this record (admittedly poor example) - but replace this conditional with whatever you want to check on that existing model object.

Ryan Barton
  • 313
  • 3
  • 12