Sunday, December 11, 2011

Project Portfolios engine for Refinery CMS

Just added a new kind of portfolio to get it running in RefineryCMS. Here is demo.
Project portfolio
Project details popup

Wednesday, June 16, 2010

Faqs plugin for Refinery CMS

Refinery is one of the rails CMS that allows people to extend the CMS and build custom plugin on that. The plugins are easy to include and maintain.
Faqs are one of the essential part for most of the web applications. So I build a plugin for refinery. The project is kept on github. Follow the following scripts to include the plugin.

script/plugin install git://github.com/ashrafuzzaman/refinerycms-faqs.git
rake refinery:faqs:install

Thursday, December 24, 2009

Implementing Payson API Integration 1.0 in Ruby

To do payment using Payson API integration 1.0 you need to follow 3 steps.
  1. Request to initiate a pay request (in return it will send you back a token)
  2. Redirect client to Payson site using that token
  3. After client completes the payment, Payson will redirect the client back to your site and you can check the payment status and update status of payment on your application
Payson api cycle

Plugin used

RestClient is nice API to handle http request and response.

Step 1 (Request to initiate a pay request)

To be able to use Payson API integration you must have,
AgentID: XXXX
MD5-key: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
For a pay initiate request you can see the specification here. Before you integrate the API in your application you can make a curl http call just to check if every thing works.
curl -H "PAYSON-SECURITY-USERID:XXXX" \
-H "PAYSON-SECURITY-PASSWORD:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" \
-d "receiverList.receiver(0).email=test@gmail.com&receiverList.receiver(0).amount=10&returnUrl=http://localhost:3000/payson_return/56&cancelUrl=http://localhost:3000/payson_cancel/56&memo=test&senderEmail=sender@gmail.com&senderFirstName=A.K.M.&senderLastName=Ashrafuzzaman" https://api.payson.se/1.0/Pay/ -o payson.out
Here PAYSON-SECURITY-USERID is AgentID and PAYSON-SECURITY-PASSWORD MD5-key. And as you can see PAYSON-SECURITY-USERID and PAYSON-SECURITY-PASSWORD (the authentication fields) will have to be send in the headers of the request. Now it should return a message which looks like,
responseEnvelope.ack=SUCCESS&responseEnvelope.timestamp=2009-11-15T17%3a14%3a03&responseEnvelope.version=1.0&TOKEN=4ecce14b-a4c7-4990-9814-db8aa2e97553
The response will contain a token which will be used to do the pay request.
Now let us see the ruby version of the curl request,
params_list = { 'receiverList.receiver(0).email' => reciever_email,
  'receiverList.receiver(0).amount' => recieving_amount,
  'receiverList.receiver(1).email' => more_receiver_email, #you can send money to more then one account
  'receiverList.receiver(1).amount' => more_receive_amount,
  'returnUrl' => callback_url,
  'cancelUrl' => cancel_url,
  'memo' => description,
  'senderEmail' => sender_email,
  'senderFirstName' => first_name,
  'senderLastName' => last_name }

header_list = {'PAYSON-SECURITY-USERID' => 'XXXX', 'PAYSON-SECURITY-PASSWORD' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'}

response = RestClient.post 'https://api.payson.se/1.0/Pay/', params_list, header_list

Step 2 (Redirect client to Payson site)

Now parse the response and redirect to Payson site for client to complete the payment.
response_hash = parse_response(response)

if response_hash['responseEnvelope.ack'] == 'SUCCESS'
  logger.info "Payson pay response #{response}"
  @signup.payson_token = response_hash['TOKEN']
  @signup.save!
  redirect_to "https://www.payson.se/PaySecure?token=#{response_hash['TOKEN']}"
elsif response_hash['responseEnvelope.ack'] == 'FAILURE'
  redirect_to @signup.event.home_path
end
This will redirect to Payson paysecure page. And client will complete the payment which will redirect the client back to returnUrl. If the client cancels the payment then he will be redirected to cancel url.

Step 3 (complete the process)

Now it is time to nail it ;)
In the return action you should check that the payment is done.
response = RestClient.get "https://api.payson.se/1.0/PaymentDetails?token=#{signup.payson_token}",
              {'PAYSON-SECURITY-USERID' => 'XXXX', 'PAYSON-SECURITY-PASSWORD' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'}
response_hash = parse_response(response)
@signup.payson_params = response_hash
@signup.payson_status = response_hash['responseEnvelope.status']
@signup.save!
if @signup.payson_status == Payment::PaysonClient::COMPLETED
  confirm_signup
else
  @signup.fail!
  ErrorNotifier.deliver_notification("Payment failed with status #{@signup.payson_status} for signup #{@signup.inspect}")
end
You can check the reference for pay details request here.

Saturday, August 8, 2009

Tips on writing browser compatible css

Writing browser compatible css is a difficult job for people with less experience in css. Specially Internet Explorer make things interesting(or gives you headache, what ever you prefer ;) ). As an application grows it becomes more and more difficult to manage browser compatibility. And as Internet explorer continues to release new versions, it leaves a few time for developers to spare. Some times different browser is needed to tweak differently. It may be the size of the font, padding floating problems etc.

People then started to use Conditional Comments like bellow to give browser compatibility,

<!--[if lte IE 6]>
<link rel="stylesheet" type="text/css" href="ie_hacks.css" />
<![endif]—>

And there is the !important hack

Normally in CSS whichever rule is specified last takes precedence. However if you use !important after a command then this CSS command will take precedence regardless of what appears after it. This is true for all browsers except IE. Here you can find more.

h1 { font-size: 1.6em !important; font-size: 1.4em; }

Here IE still takes 1.4em as the font size.

Tantek box model hack

Tantek introduced a hack for ie and opera regarding the box model problem.

Hybrid Hacking defined css as,

#wrapper {
width: 770px;
wid\th: 750px;
}

It's a legal 'escape' backslash and should be ignored, but when it is inside the property name it is not ignored by IE5 and IE5.5 for Windows. Instead it causes those browsers to ignore the following "t" character in this case, thus making the property name unreadable to them. Which was explained here.

Then there was the The Child and Adjacent Sibling Hacks, as IE does not understands html>body. Which looks like,

#wrapper {
min-height: 500px; /* IEwin does not support min-height */
height: 500px;
}

html>body #wrapper {
height: auto;
}

This is discussed more elaborately here.

Here is a list of rule compatibility of browsers which is really useful.

An easy solution to write css which can easily support different browser

It is a combination of Javascript and CSS. Here is the Javascript that detects the browser and add a dummy class to body tag.

if ( $.browser.msie && $.browser.version == "6.0" ) {
$("body").addClass("ie6");
}
Although it is written in JQuery, there are plain Javascript available.

So now if your clients browser is IE6 then you already have a dummy class added to you body tag. Now you can write IE6 specific hack easily.

h1 { font-size: 1.6em; }
.ie6 h1 { font-size: 1.4em; }

It is more readable and you can provide different hack for different browser and versions with this hack.

References

http://www.positioniseverything.net/articles/ie7-dehacker.html

http://www.evolt.org/ten-css-tricks-you-may-not-know

Thursday, August 6, 2009

Using acts_as_auditable for keeping history for model activities in rails

Keeping change history for important models are crucial for some application. And acts_as_auditable provides a clean implementation for keeping history.

Install

1. Download acts_as_auditable and put in your vendor folder

2. run

ruby script\generate audit

from your application directory. Which will generate a migration script to create audits table and a model named Audit.

Expectation

1. User model that inherits from ActiveRecord::Base with an instance method of auditor_name(it is required as it stores the auditors name as well as the auditor’s reference for keeping history).

2. A property named auditor(which will be injected in the model where you are using audit) should be prefilled with the user object who is changing the model.

3. Now you can use audit in your model as,

audit :when => :before_update,
      :if => lambda {|work_item| work_item.changed? },
      :with_message => lambda { |post| 'Something changed :)' }

Here

:when         => any active record callbacks (like :before_create)
:if           => provide a condition which will be checked to create the audit
:with_message => the message to be stored

Well that’s about it to use acts_as_auditable.

Implementing in real project

The challenge that I faced is to create a human readable message for model and make it maintainable.

I actually used audit like,

audit :when => :before_update,
      :if => lambda {|work_item| work_item.changed? },
      :with_message => lambda { |work_item| work_item.audit_message }

Now here is the code to generate the message.

def audit_message
  history_text = ''
  self.changes.each do |field, change|
    history_text += "#{field.humanize} changed from '#{change[0]}' to '#{change[1]}'\n" unless field.ends_with?('_id')
  end

  history_text += audit_message_for('project_id', Project, :name, 'project')
  history_text += audit_message_for('sprint_id', Sprint, :name, 'sprint')
  history_text += audit_message_for('responsible_person_id', User, :full_name, 'responsible_person', 'Responsible Person')
  history_text += audit_message_for('release_id', Release, :name, 'release')
  history_text += audit_message_for('point_id', Point, :name, 'point')

  return history_text
end

And here is the nasty part that at least cleans the audit message implementation. It helps to generate audit message for reference objects.

private
# Used for making the audit text readable
#
# ==== Parameters
#
# * +field+ - The database field to inspect changes
# * +klass+ - The class for the referenced object
# * +klass_property+ - The property of the object which will be readable to store audit data
# * +readable_name+ - readable name for the class. By default it is klass.to_s
#
# ==== Returns
# The audit text for that field.
# If there is no change in the field then empty string is returned
def audit_message_for(field, klass, klass_property, belongs_to, readable_name = klass.to_s)
  history_text = ''
  seperator = '\n'
  #as this is done with reflaction, catching exeptions for protection
  begin
    if self.send("#{field}_changed?")
      field_was = "#{field}_was"

      changed_from = klass.find(self.send(field_was)).send(klass_property) unless self.send(field_was) == nil
      changed_to   = self.send(belongs_to).send(klass_property) unless self.send(field) == nil

      if self.send(field_was) == nil
        history_text = "#{readable_name} assigned to '#{changed_to}'#{seperator}"
      elsif self.send(field) == nil
        history_text = "Removed #{readable_name} from '#{changed_from}'#{seperator}"
      else
        history_text = "#{readable_name} changed from '#{changed_from}' to '#{changed_to}'#{seperator}"
      end
    end
  rescue Exception => ex
    logger.error "Error #{ex.to_s}"
  end

  return history_text
end

Another thing to consider is assigning the auditor before saving the history. It is cleaner if you define a method named auditor. That is how you can avoid assigning to that attribute.

def auditor
  return self.new_record? ? self.creator : self.updator
end

But for implementing like this you need to modify some code in the library. Open the vendor\plugins\acts_as_auditable\lib\shooter\acts\auditable.rb and remove the line,

attr_accessor :auditor

Otherwise your method will be overwritten.