Showing posts with label visualforce. Show all posts
Showing posts with label visualforce. Show all posts

Sunday, 1 May 2016

Image mapping in Salesforce using jQuery

I had interesting requirement in one of my assignment couple of months back, thought I should share with the community.

The requirement came from construction industry client for managing the injuries of labors to track the health and safety. We used image-mapster jQuery library to demonstrate the human body so that support agent can create the injury details after clicking the body part and send the information directly to health and safety department.

The purpose of this solution is to track the common injuries and reasons to know which safety systems need more investment in order to make them better.

Technically, we have defined the HTML image maps for the body parts. I have used three images which are shown below and added them as static resource in Salesforce

This image is used to show the background which defines the structure of the body

This image is used to highlight the part of the body
This image is used to show the selected part when the mouse is hovered


All the images overlay each other and give the effect of single image. When the user hovers the body part it changes the color and display the name of the part, after clicking it shows the form with the auto populated values based on the selection, on submission of form it creates the injury detail record in Salesforce as shown below:




Code Snippet for Visualforce page:


<apex:page standardController="Case">

    <apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" />
    <apex:includeScript value="{!URLFOR($Resource.AbsoluteBeginners, '/AbsoluteBeginners/scripts/jquery.imagemapster.js')}"/>

<style>

@import "https://fonts.googleapis.com/css?family=Raleway";

#caseInjuryForm{
    width:100%;
    height:100%;
    opacity:.95;
    top:0;
    left:0;
    display:none;
    position:fixed;
    background-color:#313131;
    overflow:auto
}

img#close {
    position:absolute;
    right:-14px;
    top:-14px;
    cursor:pointer
}

div#popupCase {
    position:absolute;
    left:50%;
    top:17%;
    margin-left:-202px;
    font-family:'Raleway',sans-serif
}
#caseform {
    max-width:300px;
    min-width:250px; 
    padding:10px 50px;
    border:2px solid gray;
    border-radius:10px;
    font-family:raleway;
    background-color:#fff
}

#casep {
    margin-top:30px
}

#caseh2 {
    background-color:#FEFFED;
    padding:20px 35px;
    margin:-10px -50px;
    text-align:center;
    border-radius:10px 10px 0 0
}

#casehr {
    margin:10px -50px;
    border:0;
    border-top:1px solid #ccc
}

.caseinput {
    width:82%;
    padding:10px;
    margin-top:30px;
    border:1px solid #ccc;
    padding-left:40px;
    font-size:16px;
    font-family:raleway
}

#casename {
    background-image:url(../images/name.jpg);
    background-repeat:no-repeat;
    background-position:5px 7px
}

#caseemail {
    background-image:url(../images/email.png);
    background-repeat:no-repeat; 
    background-position:5px 7px
}

#casetextarea {
    background-image:url(../images/msg.png);
    background-repeat:no-repeat;
    background-position:5px 7px;
    width:82%;
    height:95px;
    padding:10px;
    resize:none;
    margin-top:30px;
    border:1px solid #ccc;
    padding-left:40px;
    font-size:16px;
    font-family:raleway;
    margin-bottom:30px
}


#casesubmit {
    text-decoration:none;
    width:100%;
    text-align:center;
    display:block;
    background-color:#FFBC00;
    color:#fff;
    border:1px solid #FFCB00;
    padding:10px 0;
    font-size:20px;
    cursor:pointer;
    border-radius:5px
}

#casespan {
    color:red;
    font-weight:700
}

#casebutton {
    width:10%;
    height:45px;
    border-radius:3px;
    background-color:#cd853f;
    color:#fff;
    font-family:'Raleway',sans-serif;
    font-size:18px;
    cursor:pointer
}


</style>
    
    <script type="text/javascript" src="/soap/ajax/28.0/connection.js"></script>
    <script type="text/javascript" src="/soap/ajax/28.0/apex.js"></script>
    <script language="JavaScript">
        
        sforce.connection.sessionId = "{!$Api.Session_ID}";
        $(document).ready(function(){
            
            document.getElementById('caseInjuryForm').style.display = "none";
            
            $('#human-body-map').mapster({
                singleselect : true,
                render_highlight: {
                    fillColor: '2aff00',
                    stroke: false,
                    fade: false,
                    altImage: '{!URLFOR($Resource.HumanBodyMap, '/HumanBodyMap2.gif')}'
                    
                },
                render_select: {
                    fillColor: 'ff000c',
                    stroke: false,
                    altImage: '{!URLFOR($Resource.HumanBodyMap, '/HumanBodyMap1.gif')}'  
                },
                mapKey:'datapart',
                fillOpacity : 1,
                areas: [
                {
                    key: 'RightHandPalm',
                    selected: true
                },
                {
                    key: 'LeftHandPalm',
                    selected: true
                },
                {
                    key: 'RightLegKnee',
                    selected: true
                }]
            });
        
        });
        
        function div_show(title) {
            document.getElementById('caseInjuryForm').style.display = "block";
            var res = title.split("-");
            document.getElementById('partOfBody').value=res[0];
            document.getElementById('side').value=res[1];
            document.getElementById('detailPartBody').value=res[2];
        }
        
        function div_hide() {
            document.getElementById('caseInjuryForm').style.display = "none";
        }
        
        
        function InjurySubmit(){
            var createRecord = new Array();     
            
            var createInjuryDetail = new sforce.SObject("Injury_Detail__c");
            createInjuryDetail.Name = 'SalesforceZone Demo'     
            createInjuryDetail.Case__c= '{!Case.Id}';    
            createInjuryDetail.Part_of_body_injured__c = document.getElementById('partOfBody').value;
            createInjuryDetail.Side__c = document.getElementById('side').value;
            createInjuryDetail.Detail_of_part_of_body_injured__c = document.getElementById('detailPartBody').value;
            createInjuryDetail.Nature_of_injury__c = document.getElementById('natureInjury').value;
            createInjuryDetail.Aggravation_Description__c = document.getElementById('description').value;
            
            createRecord.push(createInjuryDetail);        
            sforce.connection.create(createRecord);
            document.getElementById('caseInjuryForm').style.display = "none";
            window.open("/"+'{!Case.id}','_parent');
            
        }
    </script>
        
    <div id="image">
        <div id="mapster_wrap_0" style="display: block; position: relative; padding: 0px; width: 400px; height: 400px;">
            <img class="mapster_el" src="{!URLFOR($Resource.HumanBodyMap,'/HumanBodyMap1.gif')}" style="display: none;"/>
            <img class="mapster_el" src="{!URLFOR($Resource.HumanBodyMap,'/HumanBodyMap1.gif')}"  style="display: none;"/>
            <img class="mapster_el" src="{!URLFOR($Resource.HumanBodyMap,'/HumanBodyMap1.gif')}" style="display: none;"/>
            <img id="human-body-map"  src="{!URLFOR($Resource.HumanBodyMap,'/HumanBodyMap.gif')}" border="0" width="800" height="600" orgWidth="800" orgHeight="600" usemap="#human-body-map" alt="human-body-map" />
        </div>
    </div>
    
    <map name="human-body-map">
        <area href="#" shape="poly" datapart="RightHandPalm" title="Hand-Right-Palm of Hand"  coords="81,332,94,315,106,305,114,307,124,310,125,324,119,336,117,344,114,351,111,357,105,363,103,362,100,368,93,364" onClick="div_show(this.title);return false" alt="" />
        <area href="#" shape="poly" datapart="LeftHandPalm" title="Hand-Left-Palm of Hand"  coords="304,312,322,307,333,311,341,317,346,327,345,333,337,332,336,341,337,356,336,361,334,364,332,366,328,367,325,368,317,361,311,345,307,333"  onClick="div_show(this.title);return false"  alt=""/>
        <area href="#" shape="poly" datapart="RightLegKnee" title="Leg-Right-Knee"  coords="170,398,180,410,192,414,201,418,205,433,203,442,194,441,184,440,175,435,175,434,171,433" onClick="div_show(this.title);return false" alt=""/>
    </map>
    
    <div id="caseInjuryForm">
        
        <div id="popupCase">
        <!-- Case Injury Details Form -->
        <form action="#" id="caseform" method="post" name="caseform">
        <img id="close" src="{!$Resource.close}" onclick ="div_hide()" />
        <h2 id="caseh2">Case Injury Details</h2>
        <hr id="casehr"/>
        <input id="partOfBody" class="caseinput" name="casename" placeholder="Part of body injured" type="text"/>
        <input id="side" class="caseinput" name="caseemail" placeholder="Side" type="text"/>
        <input id="detailPartBody" class="caseinput" name="caseemail" placeholder="Detail of part of body injured" type="text"/>
        <input id="natureInjury" class="caseinput" list="injuryList" name="caseemail" placeholder="Nature of injury" />
        <datalist id="injuryList">
            <option value="Abrasion" />
            <option value="Aggravation" />  
            <option value="Allergy"  />
            <option value="Amputation" />
            <option value="Animal" />
            <option value="Bruise" />
            <option value="Burn" />
            <option value="Chemical" />
            <option value="Choking/Suffocation" />  
            <option value="Concussion" />
            <option value="Crush/Bruise" />
            <option value="Dehydration" />  
            <option value="Dislocation" />      
            <option value="Electric shock" />
            <option value="Foreign body" />
            <option value="Fracture" />
            <option value="Hearing loss" />
            <option value="Heat stroke" />
            <option value="Intercranial Injury" />
        </datalist> 
        
        <input id="description" class="caseinput" name="caseemail" placeholder="Aggravation Description" type="text"/>
        <a onClick="InjurySubmit()" id="casesubmit">Submit Case Injury</a>
        
        </form>
        </div>
        <!-- Popup Div Ends Here -->
    </div>
    
</apex:page>


Download jQuery plugin from here
http://www.outsharked.com/imagemapster/

Define your HTML image maps here
http://www.image-mapper.com/

Useful link to setup the page
http://members.shaw.ca/sites/AbsoluteBeginners/beginner1.htm

Feel free comment below if anything is over pedantic. Cheers

Sunday, 4 October 2015

Workaround for removing the Save&New button from Standard pages after Winter'16 release

As you all know earlier Salesforce replaced the rich text editor in HTML Area home page components with a new version that supports more markup but doesn’t allow HTML to be manually entered.
So as a workaround, we used custom links in home page components to invoke our scripts, See the below link for details.

http://salesforce.stackexchange.com/questions/38918/end-of-javascript-sidebar-workarounds

Since this workaround was only supported till Summer'15 and we are now in Winter'16 so this wont work because now sidebar script does not automatically invoke and requires a manual click on the link to invoke it. See this link for details http://docs.releasenotes.salesforce.com/en-us/winter16/release-notes/rn_forcecom_general_requirescript.htm

With the new UI, the sidebar is going to go away. So, the less reliance we have on the sidebar components, the better it would be for our transition.

However, there isn't any other way to hide the "Save & New" button but to replace it by using a Visualforce page. Salesforce has been taking security as the highest priority and been striving to avoid any kind of hacking activities (including Javascript workarounds) which could incur potential security thread to customers. That's the reason why Javascript is no longer supported within a home page component.

After discussing with Salesforce, the workaround I've found is to override the "New" button of an object using a Visualforce page which has a Controller to check if the "Save & New" is clicked. If it's clicked then ignore the "New" action and just do "Save". So even the "Save & New" button is still visible but it would just behave like the "Save" button.
The following are the sample code which works on the Account Object. To see it in action simply goto Setup | Customize | Accounts | Buttons, Links and Actions | Edit the "New" button by the following Visualforce page.

Visualforce Page:

1
2
<apex:page standardController="Account" extensions="SObjectNewActionOverrideController" action="{!onLoadPage}">
</apex:page>
Controller:
public class SObjectNewActionOverrideController {

    public String debug{get;Set;}
    private String SObjectPrefix = null;
    public SObjectNewActionOverrideController(ApexPages.StandardController controller){
        this.SObjectPrefix = controller.getRecord().getSObjectType().getDescribe().getKeyPrefix();
    }
    
    public PageReference onLoadPage(){
        this.debug = JSON.serializePretty(ApexPages.currentPage().getParameters());
        String retURL = ApexPages.currentPage().getParameters().get('retURL');

        if(ApexPages.currentPage().getParameters().get('save_new')=='1'
            && retURL != null
            && retURL.startsWith('/'+SObjectPrefix)
            && retURL.indexOf('/', 4) < 0
            && !retURL.contains('?')
            && retURL.length() >= 15){
            PageReference pg = new PAgeReference(retURL);
            pg.setRedirect(true);
            return pg;
        }else{
            PageReference pg = new PAgeReference('/'+this.SObjectPrefix+'/e');
            for(String key : ApexPages.currentPage().getParameters().keyset()){
            if(key == 'save_new' || key == 'sfdc.override') continue;
                pg.getParameters().put(key, ApexPages.currentPage().getParameters().get(key));
        }

        pg.getParameters().put('nooverride','1');
        pg.setRedirect(true);
        return pg;
        }
    }
}

Saturday, 6 June 2015

Using Comparable Interface to sort by Date

I have come across the requirement in which I have to sort the list of Apex Class based on Date.

If you want to add sorting support for Lists that contain non-primitive types, that is, lists of user-defined types. you must implement the Comparable interface with its compareTo method in your class.

To implement the Comparable interface, you must first declare a class with the implements keyword.
For sorting by date, we can use daysBetween(Date) which returns the number of days between that called the method and specified date.

Code Snippet of Apex Class:  
global class PaymentPlanList implements Comparable{
    
    public static List <PaymentPlanList> lstPaymentPlans {get;set;}
    public String Type {get;set;}
    public Decimal Amount {get;set;}
    public Date ToDate {get;set;}
    public Date FromDate {get;set;}
    
    public PaymentPlanList(PaymentPlan__c pp){
        Type = pp.Name;
        Amount = pp.Amount__c;
        ToDate = pp.To_Date__c;
        FromDate = pp.From_Date__c;
    }
    
    public static List<PaymentPlanList> GetPayments(){
        if(lstPaymentPlans == null ){
            lstPaymentPlans = new List<PaymentPlanList>();
            for(PaymentPlan__c payment : [Select Name,Amount__c,From_Date__c,To_Date__c from PaymentPlan__c]){
                lstPaymentPlans.add(new PaymentPlanList(payment));
            }            
        }
       return lstPaymentPlans;
    }
    
    global Integer compareTo(Object compareTo) {
       //assuming if their is no date, it would be TODAY
       Date fromDatee = compareTo != null ? ((PaymentPlanList)compareTo).FromDate : System.today();           
       return fromDatee.daysBetween(this.FromDate); // Sorting by Date
    }
}
Sorting list of Apex Class and binded to VF page:
public class PaymentPlan{
    public List <PaymentPlanList> PaymentPlans {get;set;}
    public PaymentPlan(){
        PaymentPlans = PaymentPlanList.GetPayments(); // Calling Method of Outer Class for populating List of user-defined type
        PaymentPlans.sort(); // Adds sorting support for Lists that contain non-primitive types, that is, Lists of user-defined types.
    }
}
Visuaforce Page:
<apex:page controller="PaymentPlan" >
    <apex:pageBlock >
        <apex:pageBlockTable value="{!PaymentPlans}" var="pp">
            <apex:column headerValue="Payment Type" value="{!pp.Type}" style="width:25%"/>
            <apex:column headerValue="Amount" value="{!pp.Amount}" style="width:25%"/>
            <apex:column headerValue="From Date" style="width:25%">
                <apex:outputText value="{0,date,MM/dd/yy}"> 
                    <apex:param value="{!pp.FromDate}"/> 
                </apex:outputText> 
            </apex:column>
            <apex:column headerValue="To Date" style="width:25%">
                <apex:outputText value="{0,date,MM/dd/yy}"> 
                    <apex:param value="{!pp.ToDate}"/> 
                </apex:outputText> 
            </apex:column>
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>
If you notice the list is sorted by Date.
Feel free to post comments if you have any questions. Cheers !!!

Monday, 24 June 2013

Conditioning in Visualforce Page

If you want to override Opportunity detail page and show OpportunityLineItem's detail page underneath the Opportunity page, you can do it easily by using <apex:detail>,<apex:repeat> and <apex:variable> with some conditioning in VF page.


<apex:detail> shows the standard detail page for a particular object, as defined by the associated page layout for the object in Setup. So in order to show Opportunity detail page, we can use <apex:detail> and bind VF page by Opportunity standard controller.

Use custom controller as an extension to get the related OpportunityLineItems of that particular Opportunity in list and bind it with <apex:detail>.But here is a tricky part, We have a list of OpportunityLineItems and <apex:detail> includes 'subject' attribute which takes ID of the record that should provide data to the component. To set ID of record in <apex:detail> we can give the name of the list with the element's index position in square brackets
<apex:detail subject="{!oppLineItemList[cnt]}"/>.

Iterate the list with incrementing the list index(local variable 'cnt').<apex:variable> is a local variable that can be used as a replacement for a specified expression within the body of the component.
<apex:variable var="cnt" value="{!0}"/>.

To iterate and increment local variable value we can use <apex:repeat>. It is an iteration component that allows you to output the contents of a collection according to a structure that you specify and in our case it is iterating to size of OpportunityLineItem list.
<apex:repeat value="{!oppLineItemList}">.

If we increment list index(local variable) like <apex:variable var="cnt" value="{!cnt+1}"/> then and it will throw us an error "List index out of bounds" because in last iteration list index(local variable) will be equal to list size and we know list index is zero based and it should be less than the size of the collection. To avoid this error, we can give condition that when [list index(local variable) value] is less than [one less than list size] then increment list index(local variable) otherwise keep it as it is.
value="{!IF(cnt < oppLineItemList.size-1,cnt+1,cnt)}".

Below is the code snippet of Controller and Page. 

Controller:


 public class contOppLineItem{  
  public list<OpportunityLineItem> oppLineItemList{get;set;}  
  public Opportunity opp;  
  public contOppLineItem(ApexPages.StandardController stdController){  
     //Constructor  
     this.opp = (Opportunity)stdController.getRecord();  
     oppLineItemList = [Select Id,OpportunityId From OpportunityLineItem Where OpportunityId =: opp.Id];  
  }  
 }  


VisualForce Page:


 <apex:page standardController="Opportunity" extensions="contOppLineItem">  
   <apex:detail relatedList="false" title="true"/>  
   <apex:pageBlock mode="detail">  
     <apex:pageBlockSection title="OpportunityLineItem">  
       <div align="center">  
         <apex:variable var="cnt" value="{!0}" />   
         <apex:repeat value="{!oppLineItemList}">  
              <apex:detail subject="{!oppLineItemList[cnt]}"/>    
              <apex:variable var="cnt" value="{!IF(cnt<oppLineItemList.size-1,cnt+1,cnt)}"/>  
         </apex:repeat>   
       </div>    
     </apex:pageBlockSection>  
   </apex:pageBlock>  
 </apex:page>  

Thursday, 3 May 2012

Inline scripting in Vf pages

If you want to get id of any element and set certain property to it you can do that by inline scripting. Below is the code snippet which is getting inputtext id and set the disabled property to it.
 <apex:inputText id="testid" style="width:40pt;text-align:center;" styleClass="testclass"></apex:inputText>  
 <script>document.getElementById('{!$Component.testid}').disabled = true; </script>