Customised User Management

Goldberg’s user and self-registration system is functional but fairly basic. Most real live sites would want to customise the information they collect about their users, as well as the appearance (the login page, the emails sent for self-registration etc.). Fortunately this is all possible, and without altering the code in RAILS_ROOT/vendor/plugins/goldberg.

The examples in this page are based on a recent project, where I helped a community group set up a site to allow parents to register their children for an event. These were the customisations we required:

  • We decided to make another model to supplement Goldberg::User, to contain more detailed name and address information for the parents.
  • The parents were named by their email addresses (for the purposes of logging in etc.). It was decided that the concept of a screen name would be too complex and unnecessary for random members of the public, especially considering they would only be using the site for one purpose.
  • There were various aspects of the user-registration process that we wanted to customise, including the content of the self-registration emails.

Note that there’s always more than one way to do it. This is the approach that suited us at the time — the specific techniques you use might differ, but hopefully this will give you some ideas.

Extending the User Model

A good approach is to create a separate model in a belongs-to relationship with Goldberg::User, to contain any supplementary details and functionality that Goldberg::User doesn’t offer.

RAILS_ROOT/app/models/parent.rb

class Parent < ActiveRecord::Base
  belongs_to :user, :class_name => 'Goldberg::User'

  protected

  def validate_on_update
    # Check for empty fields
    errors.add_on_empty %w(given_names surname address suburb postcode)

    # Check that at least one phone number has been provided
    (
     (self.home_phone && self.home_phone.length > 0) ||
     (self.mobile_phone && self.mobile_phone.length > 0)
     ) || errors.add_to_base("You need to provide either your home or mobile phone number")
  end
end

Goldberg::User.class_eval do
  has_one :parent, :dependent => :destroy

  validates_format_of :name, 
  :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
end

One potential issue is that Goldberg::User’s association with Member won’t come into effect until this file is loaded. To avoid any problems it would be a good idea to require this model on startup. Add the following line to your environment.rb:

RAILS_ROOT/config/environment.rb

# Put this somewhere near the end:
require 'parent'

User Self-registration

We wanted to use self-registration, but to customise how it worked in terms of the views and the fields required. The approach we took was to manage the parents and their corresponding Goldberg::User records through the parents controller.

RAILS_ROOT/app/views/parents/register.rhtml

<% form_tag :controller => '/parents', :action => 'register_submit' do %>
  <table>
    <tr>
      <th class="required">Email address</th>
      <td><%= text_field 'user', 'email' %></td>
    </tr>

    <tr>
      <th class="required">Password</th>
      <td><%= password_field 'user', 'clear_password' %></td>
    </tr>

    <tr>
      <th class="required">Password, again</th>
      <td><%= password_field 'user', 'confirm_password' %></td>
    </tr>

  </table>
  <br/>
  <%= submit_tag 'Register' %>
  <br/>
<% end %>

Note the use of form_tag instead of form_for. Not every Rails user is aware of the distinction because form_for is so ubiquitous, but these two methods offer two very different approaches to form design:

  • form_for creates a form that is bound to a specific object. This is very convenient if you are only dealing with one model object because then you can use other methods such as text_field that automatically populate the value of the form field.
  • form_tag creates a form that isn’t bound to a specific object. This approach offers a high degree of flexibility: you can create form fields for multiple different objects within the one form. For example you could create a text_field_tag called “user[fullname]”, and another called “parent[home_phone]”. Rails would unpack these into differnt parts of the params hash: all the user fields under params[:user], and all the parent fields under params[:parent].

So form_for is easy, but form_tag is highly flexible, allowing you to gather all the details for both the Goldberg::User and the other associated model all in one form. Then in the controller, you can create both the records and associate them.

Customising the Login Page

If you want to customise the the appearance of Goldberg’s login page, you can alter its templates. But rather than altering any files in vendor/plugins/goldberg/app, it’s recommended that you make a copy of Goldberg’s login templates and alter those.

Start by copying RAILS_ROOT/vendor/plugins/goldberg/app/views/goldberg/auth to RAILS_ROOT/app/views/goldberg/auth. But then how do you tell Goldberg to use your customised login templates, rather than the provided ones? Add the following to the end of your application.rb file:

RAILS_ROOT/app/controllers/application.rb

# Add this at the end:

Goldberg::AuthController.class_eval do
  self.template_root = File.join("#{File.dirname(__FILE__)}/../views")
  self.layout "layouts/application" 
end

Customising the Self-registration Emails

Similarly to customising the login page, you can copy Goldberg’s templates into your application’s space and modify them.

For our project, we actually copied Goldbergs UserMailer templates and made a new mailer class called ParentMailer. This is because we not only wanted to customise the appearance of the emails: we also wanted to expand the functionality of the mailer model because there were other email messages we wanted to be able to send.


Login