Thursday, February 27, 2014

Binding a Grails date from params in a controller

Add the following lines in 'Config.groovy' file


grails.databinding.trimStrings=true
grails.databinding.convertEmptyStringsToNull=true
grails.databinding.dateFormats = ['yyyy-MM-dd HH:mm:ss.S', 'yyyy-MM-dd', "yyyy-MM-dd'T'hh:mm:ss'Z'"]

First line triming all string values on data binding to domain instance.
Second line convert all empty string to null.
And third line accept the date formats provided to convert it to date.

Or you can use those as following:


package com.pkm.test.domains

import org.grails.databinding.BindingFormat

class TestOne {
    Long id
    String name;
    String display;
    @BindingFormat("yyyy-mm-dd")
    Date created;

    static constraints = {
        display(nullable: true)
    }
}

And from controller


params.name = "YES-Pritom K Mondal";
params.display = " ";
params.created = "2014-02-27";
TestOne testOne = new TestOne();
DataBindingUtils.bindObjectToInstance(testOne, params)
testOne.save(failOnError: true);

Will create a following row in the table


Field 'display' is null because value from controller: ' ' first trim() and then convert empty string to null according to Config.groovy settings above.

Sunday, February 23, 2014

jQuery Change Event: Proper Binding


$("input[name='name']").on("input", function(event) {
    $("div").prepend("Text: " + $(this).val() + "<br/>");
});

// or

<input type='text' name='name' oninput='$(this).changeOccurs()'/>

and in jquery:
jQuery.fn.changeOccurs = function () {
    // do something you wish to do.
};

jsFiddle link

Wednesday, February 19, 2014

Groovy template engine

Included with Groovy are several template engines:
  • SimpleTemplateEngine - for basic templates
  • GStringTemplateEngine - stores the template as writable closures (useful for streaming scenarios)
  • XmlTemplateEngine - works well when the template and output are valid XML

SimpleTemplateEngine


import groovy.text.SimpleTemplateEngine

def text = 'Dear ${contact.firstName} ${contact.lastName}${contact.nickName? " " + contact.nickName: ""}.\n${time} is good.'

def binding = ["time":"this evening", month: "February", "contact": ["firstName": "Pritom", "lastName": "Mondal", age: 27]]

def engine = new SimpleTemplateEngine()

def template = engine.createTemplate(text).make(binding)

println template.toString();
Output
Dear Pritom Mondal.
this evening is good.

GStringTemplateEngine


import groovy.text.GStringTemplateEngine

File templateFile = new File("web-app/test.txt");
def templateEngine = new GStringTemplateEngine();
def templateObject = templateEngine.createTemplate(templateFile).make(binding);
println templateObject.toString();
File content
Dear ${contact.firstName} ${contact.lastName},
So nice to meet you in <% out << (time == "this evening" ? "\"${time}\"" : "\"this morning\"") %>.
So nice to meet you in <% out << (time == "this evening" ? "\"this morning\"" : time) %>.
See you in ${month?: ""}
Output
Dear Pritom Mondal,
So nice to meet you in "this evening".
So nice to meet you in "this morning".
See you in February

Tuesday, February 18, 2014

Check the validity of an HTML5 form that does not contain a submit button


/* Check if html5 validation enabled */
function hasHtml5Validation () {
    return typeof document.createElement('input').checkValidity === 'function';
}


var $form = $("form");
$form.find("input.save-and-next").click(function() {
    $form.find(":input").each(function() {
        //if(!$(this).checkValidity()) {
        if(!$(this)[0].checkValidity()) {            
            $form.find("input[type='submit']").click();
            return false;
        }
    });
});

When you select  $(this) you get a collection of nodes, to access actual DOM properties you must select the numbered one such $(this)[0].

Or you can bind validation process to a form field such as:

form.find("input[type='text']").keyup(function() {
    if(!$(this)[0].checkValidity()) {
        form.find("input[type='submit']").click();
    }
});

Hooking into GORM custom event listener from plugin in Grails

In our custom listener, the method ‘onPersistenceEvent’ is called on all GORM events that we filter to what we interested – PreInsert, PreUpdate
Create a groovy file under src/groovy suppose named 'FieldValidationListener.groovy' with the following contents. And create required method in your domain class, suppose one method can be named: 'doValidationPreInsert' or 'doValidationPreUpdate'.

package com.groovy.fieldSetting

import grails.util.Holders
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.grails.datastore.mapping.core.Datastore
import org.grails.datastore.mapping.engine.event.*
import org.springframework.context.ApplicationEvent

/**
 * Created by pritom on 17/02/14.
 */
class FieldValidationListener extends AbstractPersistenceEventListener {
    public FieldValidationListener(Datastore datastore) {
        super(datastore)
    }

    @Override
    protected void onPersistenceEvent(AbstractPersistenceEvent event) {
        String operation;
        switch (event.eventType) {
            case EventType.PreInsert:
                operation = "PreInsert"
                break;
            case EventType.PreUpdate:
                operation = "PreUpdate"
                break;
        }
        if(operation) {
            String methodName = "doValidation${operation}"
            println "Searching for method: ${methodName}"
            if (event.entityObject.respondsTo(methodName)) {
                try {
                    event.entityObject."${methodName}"()
                } catch (Exception exp) {
                    event.cancel();
                    throw exp;
                }
            }
        }
    }

    @Override
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        if ([PreInsertEvent, PreUpdateEvent].any { def supportedType ->
            if (supportedType.isAssignableFrom(eventType)) {
                return true
            } else {
                return false
            }
        }) {
            return true
        } else {
            return false
        }
    }

    public static void initialize(GrailsApplication application) {
        application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
            Holders.applicationContext.addApplicationListener(new FieldValidationListener(datastore))
        }
    }
}

And initialize start listening, insert the following code block into 'BootStrap.groovy' [in 'init' method]:

FieldValidationListener.initialize(grailsApplication)

Saturday, February 15, 2014

Accessing i18n Messages from exception (validation errors) in Grails in a Service

To access your message, inject the MessageSource into your service:
def messageSource  

And to get the error messages from exception based on your i18n properties file, use the following code block in your service:
 
def messageSource;

def printAllMessage(Exception ex) {
    ex.errors.allErrors.eachWithIndex { ObjectError objectError, Integer index ->
        println "Error #${index}: " + messageSource.getMessage(objectError, Locale.default);
    }
}


Will output something like this based on you i18n messages.properties file:

Error #0: Property [accountId] cannot be null
Error #1: Property [displayName] cannot be null
Error #2: Property [emailAddress] with value [pritom@bitmascot.com] must be unique

If your i18n file contains something like:

default.null.message=Property [{0}] cannot be null
default.not.unique.message=Property [{0}] with value [{2}] must be unique

Thursday, February 13, 2014

timezone get list, time zone id, time zone name, time offset using java


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package time.zone;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 *
 * @author Pritom Kumar Mondal
 */
public class TimeZoneList {
    public static void main(String[] args) {
        String supportedPattern = "^(Africa|America|Asia|Atlantic|Australia|"
                + "Europe|Indian|Pacific)/.*";
        String[] timeZoneIdList = TimeZone.getAvailableIDs();
        List<TimeZone> timeZoneList = new ArrayList<TimeZone>();
        for(String id : timeZoneIdList) {
            if(id.matches(supportedPattern)) {
                timeZoneList.add(TimeZone.getTimeZone(id));
            }
        }
        Collections.sort(timeZoneList, new Comparator<TimeZone>() {
            public int compare(final TimeZone a, final TimeZone b) {
                return a.getID().compareTo(b.getID());
            }
        });
        for(TimeZone timeZone : timeZoneList) {
            String zoneId = timeZone.getID();
            String displayName = timeZone.getDisplayName();
            double offset = (double) timeZone.getRawOffset() / 1000 / 60 / 60;
            String minute = ((offset - (int) offset) * 60) > 0 ? (" & " + (int) 
                    ((offset - (int) offset) * 60) + " minute") : "";
            System.out.println("Zone id: " + zoneId + ", name: " + displayName + ""
                    + ", offset: " + (int) offset + " hour" + minute);
        }
    }
}

And output would something like this...

.
.
.
Zone id: Asia/Bangkok, name: Indochina Time, offset: 7 hour
Zone id: Asia/Beirut, name: Eastern European Time, offset: 2 hour
Zone id: Asia/Bishkek, name: Kirgizstan Time, offset: 6 hour
Zone id: Asia/Brunei, name: Brunei Time, offset: 8 hour
Zone id: Asia/Calcutta, name: India Standard Time, offset: 5 hour & 30 minute
Zone id: Asia/Choibalsan, name: Choibalsan Time, offset: 8 hour
Zone id: Asia/Chongqing, name: China Standard Time, offset: 8 hour
Zone id: Asia/Chungking, name: China Standard Time, offset: 8 hour
Zone id: Asia/Colombo, name: India Standard Time, offset: 5 hour & 30 minute
Zone id: Asia/Dacca, name: Bangladesh Time, offset: 6 hour
Zone id: Asia/Damascus, name: Eastern European Time, offset: 2 hour
Zone id: Asia/Dhaka, name: Bangladesh Time, offset: 6 hour
.
.
.

Wednesday, February 12, 2014

Get list of all country with iso3 and code from locale using java


package pritom;

import java.util.Locale;

/**
 * Created by pritom on 12/02/14.
 */
public class CheckCountry {
    public static void main(String[] args) {
        String[] isoCountries = Locale.getISOCountries();
        for (String country : isoCountries) {
            Locale locale = new Locale("en", country);
            String code = locale.getISO3Country();
            String name = locale.getCountry();
            String displayName = locale.getDisplayCountry();
            System.out.println("ISO3 Code: " + code + " , Name: " + name + " , Display: " + displayName);
        }
    }
}

Some example output of this code:


.
.
.
ISO3 Code: AUT , Name: AT , Display: Austria
ISO3 Code: AUS , Name: AU , Display: Australia
ISO3 Code: ABW , Name: AW , Display: Aruba
ISO3 Code: ALA , Name: AX , Display: Ã…land Islands
ISO3 Code: AZE , Name: AZ , Display: Azerbaijan
ISO3 Code: BIH , Name: BA , Display: Bosnia and Herzegovina
ISO3 Code: BRB , Name: BB , Display: Barbados
ISO3 Code: BGD , Name: BD , Display: Bangladesh
.
.
.

Monday, February 10, 2014

Use grails service from java class


package com.groovy.exception

import com.services.TestService
import org.springframework.context.ApplicationContext
import org.codehaus.groovy.grails.web.util.WebUtils

/**
 * Created by pritom on 9/02/14.
 */
class CustomException extends RuntimeException {
    private static TestService testService;
    String message;
    List args = [];

    public CustomException(String message, List args) {
        this.message = message;
        this.args = args;
    }

    public String getMessage() {
        return this.toString();
    }

    public List getArgs() {
        return this.args;
    }

    public String toString() {
        String _message = testService.getMessage(this.message, this.args);
        return _message
    }

    static {
        if(!testService) {
            ApplicationContext applicationContext = WebUtils.retrieveGrailsWebRequest().getApplicationContext();
            testService = applicationContext.getBean("testService");
        }
    }
}

Friday, February 7, 2014

Get gmt time and gmt diferrence using jQuery


<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script>
    $(document).ready(function() {
        var rightNow = new Date();
        $("div").append("Local time: " + rightNow + "<br/>");
        var temp = rightNow.toGMTString();
        $("div").append("GMT time: " + temp + "<br/>");
        var visitorTimeZone = "GMT " + -(rightNow.getTimezoneOffset() / 60);
        $("div").append(visitorTimeZone + "<br/>");
    });    
</script>
<div class="div"></div>

Thursday, February 6, 2014

Place auto complete address form using google api and jQuery

Google api link || Live example || Download autocomplete-address-form.js

Example screen shots:
Html Code:

<!DOCTYPE html>
<html>
    <head>
        <title>Place Auto Complete Address Form</title>
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
        <meta charset="utf-8">
        <style>
            html, body, #map-canvas {
                height: 100%;
                margin: 0px;
                padding: 0px
        }
        </style>
        <link type="text/css" rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
        <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
        <script src="//code.jquery.com/jquery-1.10.2.js"></script>
        <script src="autocomplete-address-form.js"></script>
        <script type="text/javascript">
            $(document).ready(function() {
                app.autoCompleteAddress.initialize($(this).find("#html-body"));
            });
        </script>

        <style>
            #locationField, #controls {
                position: relative;
                width: 480px;
            }
            #autocomplete {
                position: absolute;
                top: 0px;
                left: 0px;
                width: 99%;
            }
            .label {
                text-align: right;
                font-weight: bold;
                width: 100px;
                color: #303030;
            }
            #address {
                border: 1px solid #000090;
                background-color: #f0f0ff;
                width: 480px;
                padding-right: 2px;
            }
            #address td {
                font-size: 10pt;
            }
            .field {
                width: 99%;
            }
            .slimField {
                width: 80px;
            }
            .wideField {
                width: 200px;
            }
            #locationField {
                height: 20px;
                margin-bottom: 2px;
            }
        </style>
    </head>

    <body style="padding-left: 50px; padding-top: 50px;" id="html-body">
        <div id="locationField">
            <input id="autocomplete" class="auto-complete-address"
                   placeholder="Enter your address" type="text"></input>
        </div>

        <table id="address">
            <tr>
                <td class="label">Street address</td>
                <td class="slimField"><input class="field" id="street_number"/></td>
                <td class="wideField" colspan="2"><input class="field" id="route"/></td>
            </tr>
            <tr>
                <td class="label">City</td>
                <td class="wideField" colspan="3"><input class="field" id="locality"/></td>
            </tr>
            <tr>
                <td class="label">State</td>
                <td class="slimField"><input class="field"/></td>
                <td class="label">Zip code</td>
                <td class="wideField"><input class="field" id="postal_code"/></td>
            </tr>
            <tr>
                <td class="label">Country</td>
                <td class="wideField" colspan="3"><input id="country" class="field"/></td>
            </tr>
            </tr>
                <td class="label">Other Address Type</td>
                <td class="wideField" colspan="3"><div id="otherType" class="field"></div></td>
            </tr>
        </table>
    </body>
</html>

Tuesday, February 4, 2014

Secure pay xml api credit card payment


<?php 
#Test Merchant ID: abc0001
#Test Merchant Password: acb123
#Test URL (no SSL):     http://test.securepay.com.au/xmlapi/payment
#Test URL (SSL):        https://test.securepay.com.au/xmlapi/payment
#Live URL:              https://api.securepay.com.au/xmlapi/payment
$xml = '<?xml version="1.0" encoding="UTF-8"?>
    <SecurePayMessage>
        <MessageInfo>
            <messageID>8af793f9af34bea0cf40f5fb5c630c</messageID>
            <messageTimestamp>20041803161306527000+660</messageTimestamp>
            <timeoutValue>60</timeoutValue>
            <apiVersion>xml-4.2</apiVersion>
        </MessageInfo>
        <MerchantInfo>
            <merchantID>abc0001</merchantID>
            <password>abc123</password>
        </MerchantInfo>
        <RequestType>Payment</RequestType>
        <Payment>
            <TxnList count="1">
                <Txn ID="1">
                    <txnType>0</txnType>
                    <txnSource>0</txnSource>
                    <amount>1000</amount>
                    <currency>AUD</currency>
                    <purchaseOrderNo>test</purchaseOrderNo>
                    <CreditCardInfo>
                        <cardNumber>4444333322221111</cardNumber>
                        <expiryDate>09/15</expiryDate>
                    </CreditCardInfo>
                </Txn>
            </TxnList>
        </Payment>
    </SecurePayMessage>';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "http://test.securepay.com.au/xmlapi/payment");
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); // Follow redirects
curl_setopt($curl, CURLOPT_POST, TRUE);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); // Return the HTTP response from the curl_exec function

$response = curl_exec($curl);
curl_close($curl);

header('Content-type: text/xml');
echo $response;

$responseIsLikeThis = '
<SecurePayMessage>
    <MessageInfo>
        <messageID>8af793f9af34bea0cf40f5fb5c630c</messageID>
        <messageTimestamp>20140402171039522000+660</messageTimestamp>
        <apiVersion>xml-4.2</apiVersion>
    </MessageInfo>
    <RequestType>Payment</RequestType>
    <MerchantInfo>
        <merchantID>ABC0001</merchantID>
    </MerchantInfo>
    <Status>
        <statusCode>000</statusCode>
        <statusDescription>Normal</statusDescription>
    </Status>
    <Payment>
        <TxnList count="1">
            <Txn ID="1">
                <txnType>0</txnType>
                <txnSource>0</txnSource>
                <amount>1000</amount>
                <currency>AUD</currency>
                <purchaseOrderNo>test</purchaseOrderNo>
                <approved>Yes</approved>
                <responseCode>00</responseCode>
                <responseText>Approved</responseText>
                <thinlinkResponseCode>100</thinlinkResponseCode>
                <thinlinkResponseText>000</thinlinkResponseText>
                <thinlinkEventStatusCode>000</thinlinkEventStatusCode>
                <thinlinkEventStatusText>Normal</thinlinkEventStatusText>
                <settlementDate>20140204</settlementDate>
                <txnID>576566</txnID>
                <CreditCardInfo>
                    <pan>444433...111</pan>
                    <expiryDate>09/15</expiryDate>
                    <cardType>6</cardType>
                    <cardDescription>Visa</cardDescription>
                </CreditCardInfo>
            </Txn>
        </TxnList>
    </Payment>
</SecurePayMessage>';
?>