Customization

Configuration File Options

The settings of the config/config.yml file can be set to override the settings of the config/config.defaults.yml file.

email_enrollment_submitted_notification

Set this variable to true to send the email in app/views/user_mailer/enrollment_submitted_notification.text.erb. See below (in the section on Overriding Default Views) for instructions on how to safely alter the contents of this email.

Enabling and Disabling "Sections" of Tapestry

Tapestry has been divided into a handful of logical "sections". To date, they are:

  • Section::SIGNUP

Removing this Section will make it impossible to create a new login account.

  • Section::ENROLL

Removing this Section will display the contents of the file app/views/pages/_enrollment_disabled_notice.html.erb instead of allowing the user to go through the enrollment process.

  • Section::PUBLIC_DATA

Removing this Section will hide and disallow access to the various pages normally seen in the "Public Data" application menu.

  • Section::PUBLIC_PROFILE

Removing this Section will make all access to Public Profiles, by anyone disallowed, and links to these hidden.

  • Section::GOOGLE_SURVEYS

Removing this Section makes all mention of and access to the Google Surveys disappear.

  • Section::SAMPLES

Removing this Section specifically hides mention of and disallows access to the "Samples" part of Public Data.

  • Section::CCR

Removing this Section specifically hides mention of and disallows access to the functionality for uploading CCR XML files.

  • Section::REAL_NAMES

Enabling this section makes it possible for a user to add or remove the display of their Real Name from their Public Profile.

  • Section::SHIPPING_ADDRESS

Enabling this section makes it possible for a user to specify their Shipping Address.

In your config.yml file you can specify an array of these and assign it to the enabled_sections config parameter. An example is in the config.defaults.yml, where only Section::SIGNUP is enabled by default.

Furthermore, in your overridden views, partials, etc. (see section below) you can use embedded Ruby to access which sections are enabled with the include_section? helper method. Search in the source code for examples.

Overriding Default Views, Partials, Templates, eMails etc.

It is possible to override any Rails view in the application by mimicking the directory structure in app/views but under another folder site_specific/app/views. For example, to use your own version of _dashboard.html.erb in app/views/pages you would put it in site_specific/app/views/pages.

You can override the #{Rails.root}/site_specific folder itself with the environment variable TAPESTRY_OVERRIDE_PATH, so that the folder can be left entirely outside of the Tapestry code base.

It is important to understand that including this folder, any subfolders, and all files is optional. If you do not wish to override a particular view, leave it out of the override folder.

Caveat

This statement to be followed up after more investigation.

It is my impression that when using multiple paths that the technique of using explicitly_unloadable_constants for having files reload without restarting the server will not work properly. This could mean that while developing these site-specific files that the server has to be restarted after each change.

Overriding lib Files

The same logic works for files in the override path under the lib subfolder, i.e. either #{Rails.root}/site_specific/lib or #{ENV['TAPESTRY_OVERRIDE_PATH']}/lib.

Adding Custom Questions to the "Participation Consent" Form

Currently this text and form are found in views/participation_consents/show.html.erb. This currently saves the user's responses in the InformedConsentResponse model. There is in this model a field called "other_answers" that is a serialized Hash where any number of "dynamically defined" answers can be saved with keys of your choosing. To accomplish this you have to add form inputs that end up with a name attribute that looks like (e.g. to record "age"): other_answers[age] and it will be recorded in the "other_answers" Hash in the model under the :age key.

There is a view helper for creating radio boxes for the participation concept form using this "other_answers" field. Example:

  <div class="consent-form-question">
    <p>
      Would you judge yourself to be sane?
      <%= radio_answers( 'sanity', [['0', 'No'],
                                    ['1', 'Sometimes'],
                                    ['2', 'Yes']] ) %>
    </p>
  </div>

There are also helpers for text areas (text_area_answer) and standard input texts (text_field_answer). For example:

  <div class="consent-form-question">
    <p>
      If sane only sometimes, please explain when and for what reason this occurs:
    </p>
    <p>
      <%= text_area_answer 'reason_sometimes_sane', { :cols => 60, :rows => 3 } %>
    </p>
  </div>

Adding Custom Validation of Your Custom Question

This is done by adding a site-specific validations file in the lib "override" folder, in the lib/site_specific/validations.rb module. First place the following code in the file:

module SiteSpecific
  module Validations
    extend ActiveSupport::Concern

    # *Do not remove this +included+ block!* It is what works the magic.
    included do
      method_name = "#{self.name.to_s.underscore}_validations" 
      validate method_name if method_defined? method_name
    end

  end
end

Then after this insert a method called informed_consent_response_validations (after the name of the model relevant to the Participation Consent form). You can check other_answers and add errors in the standard ActiveRecord way. For example:

    def informed_consent_response_validations
      case self.other_answers[:sanity]
      when '2'
        errors.add( :other_answers, :sanity_not_permitted)
      when '1'
        if self.other_answers[:reason_sometimes_sane].blank?
          errors.add( :other_answers, :explain_occasional_sanity )
        end
      end
    end

The messages should be placed in your locale file under:

en:
  activerecord:
    errors:
      models:
        informed_consent_response:
          attributes:
            other_answers:
              sanity_not_permitted:
                You are not permitted to be sane.
              explain_occasional_sanity:
                Please explain when and why you are sometimes sane.

See the documentation on Internationalization for where to put this.

Overriding the Validations on Any Model

The above section explaining how to override validations for the InformedConsentResponse model can serve as an example for the general case. There is only one change required to the Tapestry source code base. In general, this is discouraged, so you should consider contacting the Tapestry development team and letting them know that you've found a need to override validation on a particular model, but the change is slight and easy to deal with in future merges. Basically, you must insert, after any model validations, the following line:

  include SiteSpecific::Validations rescue {}

If you do not insert this line after any of the model validations already present in the model class, you will not be able to override them. An example is already in the model for ShippingAddress (app/models/shipping_address.rb), where a site may want to allow specifying "State" to be optional:

  validates_presence_of     :user_id
  validates_presence_of     :address_line_1
  validates_presence_of     :city
  validates_presence_of     :state
  validates_presence_of     :zip
  validates_presence_of     :phone

  include SiteSpecific::Validations rescue {}

Note that the include is after the list of validates_presence_of directives. This allows any of those validations to be effectively reversed. So, in your site-specific override folder, in your lib/site_specific/validations.rb file (also see example above for Consent Questions), you would simply add the following method:

    def shipping_address_validations
      # allow invalid "state" field
      errors.delete(:state)
    end

To remove the State field from the user interface entirely you'l also want to remove it from the relevant view, which for ShippingAddress is the partial in your site-override folder under app/views/shipping_addresses/_form.html.erb. Simply copy the _form.html.erb file from the original source code tree and then remove the following lines:

  <div class="field">
    <span style="color: red"> * </span><%= f.label :state %><br />
    <%= f.state_select(:state, 'US', {:include_blank => "Select a State", :selected => @shipping_address.state }, { :style => "width: 230px;"}) -%>
  </div>

Or remove the <span style="color: red"> * </span> to show it as non-mandatory. (For information on the state_select method, see the documentation for the carmen gem.)

Automatic Reloading of the Validations Override During Development

As shown in the development.rb.example file, you can add the following line to your development.rb to have validation override changes automatically reload without having to restart your Rails server:

ActiveSupport::Dependencies.explicitly_unloadable_constants << 'SiteSpecific::Validations'

Custom Validation of Postal Codes ("Zip" Codes)

Currently for the "Geographic Information Survey" and the "Residency Response" question there are input for a Postal Code. There is a central validation for these Postal Codes. To change it, in your config.yml you can override the config.defaults.yml file key with your own regular expression. Note that you should leave the YAML data type directive in, and also the single quotes (although if they get in the way there are many different ways to do quoting in YAML: check the specifications at http://www.yaml.org/

  zip_validation: !ruby/regexp '/^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/'

Next you'll want to change the validation error message. For the "Geographic Information Survey", the Postal Code is saved in the User model in the zip attribute, and this message is currently meant to be used as the default and central message for any Postal Code validation problems. To override, in the locales file location described in Internationalization add the following key:

en:
  activerecord:
    errors:
      models:
        user:
          attributes:
            zip:
              invalid:
                'should be in Canadian Postal Code format (A1A-1A1)'