MuckProfiles

Muck profiles adds profiles to muck users. This implements a photo for users as well as an easy way to add any number of properties to enable total customization of the user’s profile information including privacy settings.

Installation

Muck profile relies upon the muck-engine and muck-users gems as well as paperclip. Add the following lines to your Gemfile:

gem 'paperclip'
gem 'muck-engine'
gem 'muck-users'
gem 'muck-profiles'
gem 'geokit' # Only needed if you want to add location to your profiles. MuckProfiles uses geokit to determine a users
             # location and to make it possible to find users that are within a given proximity.

Install the GeoKit Rails Plugin:

script/plugin install git://github.com/andre/geokit-rails.git

Be sure to get api keys from Google an Yahoo. Instructions for doing so can be found in config/initializers/geokit_config.rb after installing the plugin.

We recommend moving the keys into secrets.yml:

google_geo_key: 'some key'  # API key for Google.  Get it here: http://www.google.com/apis/maps/signup.html
yahoo_geo_key: 'some key'   # API key for Yahoo.  Get it here: http://developer.yahoo.com/faq/index.html#appid

Then change the lines in config/initializers/geokit_config.rb to:

Geokit::Geocoders::yahoo = Secrets.yahoo_geo_key
Geokit::Geocoders::google = Secrets.google_geo_key

If you used the muck template to create your rails application you will have a secrets.yml file. If not then you will need to create a secrets.yml file and then add the following to environment.rb right above Rails::Initializer.run do |config|

require 'ostruct'
require 'yaml'
::Secrets = OpenStruct.new(YAML.load_file(File.expand_path('../secrets.yml', __FILE__))[Rails.env])

Omit secrets.yml from your version control system and use it to keep sensitive data like email server credentials

email_user_name: 'TODO_admin@#{domain_name}'    # Email server username
email_password = 'TODO_password'                # Email server password

production:
  <<: *DEFAULT
  # Add production only secrets
staging:
  <<: *DEFAULT
  # Add staging only secrets
development:
  <<: *DEFAULT
  # Development specific
test:
  <<: *DEFAULT
  # Test specific

Usage

Create a model called profile.rb as show below. This mixes in the muck profile functionality but also permits further customization of the profile in your application.

class Profile < ActiveRecord::Base
  include MuckProfiles::Models::MuckProfile
end

Modify your user model so that it has a profile:

class User < ActiveRecord::Base
  acts_as_authentic do |c|
    c.crypto_provider = Authlogic::CryptoProviders::BCrypt
  end
  include MuckUsers::Models::MuckUser
  include MuckProfiles::Models::MuckUser
end

Your user model will now appear to have a ‘photo’ which is delegated to the profile model:

@user.photo # returns a photo object from paperclip

Configuration

Create an initializer to configure muck profiles:

MuckProfiles.configure do |config|
  config.enable_sunspot = false       # This enables or disables sunspot for profiles. Only use acts_as_solr or sunspot not both. Sunspot does not include multicore support.
  config.enable_solr = true           # This enables or disables acts as solr for profiles.
  config.enable_guess_location = true # If true the profile system will attempt to determine the user's location via IP and populated with the location, lat and lon fields.
end

Acts as Solr

Muck Profiles works with solr to allow searching users.

If you enable acts_as_solr or sunspot you’ll also want to specify a default policy in your configuration. This policy is used to determine which fields are public and private. The default policy looks like this:

MuckProfiles.configure do |config|
  config.enable_sunspot = false       # This enables or disables sunspot for profiles. Only use acts_as_solr or sunspot not both. Sunspot does not include multicore support.
  config.enable_solr = false          # This enables or disables acts as solr for profiles. You will need to include muck-solr in your gemfile.
  config.enable_geokit = false        # Turn geokit functionality on/off. You will need to include geokit in your gemfile.
  config.enable_guess_location = true # If true the profile system will attempt to determine the user's location via IP and populated with the location, lat and lon fields.
  config.policy => { :public => [:login, :first_name, :last_name, :about],
                     :authenticated => [:location, :city, :state_id, :country_id, :language_id],
                     :friends => [:email],
                     :private => [] }

end

If you add attributes to the profile as show below then you will want to specify which fields fall into each privacy setting. Note that it is also possible to add new privacy levels simply by adding new values to the hash. For example:

MuckProfiles.configure do |config|
  config.enable_sunspot = false       # This enables or disables sunspot for profiles. Only use acts_as_solr or sunspot not both. Sunspot does not include multicore support.
  config.enable_solr = true           # This enables or disables acts as solr for profiles.
  config.enable_guess_location = true # If true the profile system will attempt to determine the user's location via IP and populated with the location, lat and lon fields.
  config.policy => { :public => [:login, :first_name, :last_name, :about],
                     :authenticated => [:location, :city, :state_id, :country_id, :language_id],
                     :friends => [:email],
                     :private => [] ,
                     :instructors => [:grades]}

end

For each top level key a method will be auto generated that returns the values from each attribute concatenated together. Assuming we have a profile with fields populated calling each method might return something like this:

@profile.public_fields         # returns 'testguy test guy I am the test guy'
@profile.authenticated_fields  # returns 'somewhere USA English'
@profile.friends_fields        # returns '[email protected]'
@profile.private_fields        # returns ' '

Solr will index these fields and then permit you to search based on each privacy level.

Profile Attributes

The profile comes preconfigured with a basic set of common profile options. It is easy to add new fields. Simply add the fields to the profile using a migration:

class AddMoreFieldsToProfiles < ActiveRecord::Migration
  def self.up
    add_column :profiles, :occupation, :string
    add_column :profiles, :gender, :string
    add_column :profiles, :birthday, :datetime
    add_column :profiles, :company, :string
    add_column :profiles, :zip, :string
    add_column :profiles, :mobile_phone, :string
    add_column :profiles, :home_phone, :string
    add_column :profiles, :alumni_of, :string
    add_column :profiles, :relationship_status, :string
  end

  def self.down
    add_column :profiles, :occupation
    add_column :profiles, :gender
    add_column :profiles, :birthday
    add_column :profiles, :company
    add_column :profiles, :zip
    add_column :profiles, :mobile_phone
    add_column :profiles, :home_phone
    add_column :profiles, :alumni_of
    add_column :profiles, :relationship_status
  end
end

Next create a new views/profiles/edit.html.erb file. The built in file is very basic and makes it easy to add additional fields. The ‘profile_form’ method will create form elements for the built in fields. Add extra fields after that.

<div id="edit_content" class="span-24 colborder column">
  <%= output_errors(t('muck.profiles.problem_editing_profile'), {:class => 'help-box'}, @user) %>
  <%= profile_form(@user) do |f| -%>
    <%# can add form fields as desired here -%>
  <% end -%>
</div>

Last create view/profiles/show.html.erb. There is a built in show page however it is assumed that most applications will implement a custom show page to hightlight the focus of the system.

<div class="span-24 colborder column">
  <%= icon @user, :thumb %>
  <p><%= @user.full_name %></p>
  <p><%= link_to t('muck.profiles.edit_profile'), edit_user_profile_path(@user) if @profile.can_edit?(current_user) %></p>
  <!-- Add more fields and customize the profile. -->
</div>

It’s important to sanitize user input. If you add fields to the profile then be sure to prevent cross site scripting. Override the ‘sanitize_attributes’ method in your model and add your new fields. Note that you can also override ‘sanitize_level’ to determine how sanitization occurs.

# Sanitize content before saving.  This prevents XSS attacks and other malicious html.
def sanitize_attributes
  if self.sanitize_level
    self.about = Sanitize.clean(self.about, self.sanitize_level)
    self.location = Sanitize.clean(self.location, self.sanitize_level)
    # add your fields
  end
end

# Override this method to control sanitization levels.
# Currently a user who is an admin will not have their content sanitized.  A user
# in any role 'editor', 'manager', or 'contributor' will be given the 'RELAXED' settings
# while all other users will get 'BASIC'.
#
# Options are from sanitze:
# nil - no sanitize
# Sanitize::Config::RELAXED
# Sanitize::Config::BASIC
# Sanitize::Config::RESTRICTED
# for more details see: http://rgrove.github.com/sanitize/
def sanitize_level
  return Sanitize::Config::BASIC if self.user.nil?
  return nil if self.user.admin?
  return Sanitize::Config::RELAXED if self.user.any_role?('editor', 'manager', 'contributor')
  Sanitize::Config::BASIC
end

Copyright © 2009-2010 Tatemae, released under the MIT license