Saturday, October 20, 2012

Prevent inserting duplicate records without coding!!!

Hi Friends,

Today implemented this simple technique to prevent inserting  duplicate records ( records with same name)

In one of my customer's org... there was a trigger written on a campaign object to prevent inserting campaign with same name...some developer wrote it few months back... though its a simple trigger but there was a logical bug in it.... however when our system admin informed me about this issue....i thought of the following solution:-


1- Create a new text field called "custom_name__c"
2- Check "Unique(do not allow dups)" checkbox while you are creating this field
3- Create a workflow rule that updates this field with the standard name field of the object every time record is inserted or updated.....

simple right?????
 so there is no need of that trigger anymore!!!!!!!!!!!1 :)


Thursday, May 24, 2012

dashboard auto refresh


Auto refreshing dashboard:


This is the feature many sfdc admins have been asking for.... Currently it is not possible to refresh the dashboard automatically when the sfdc user logs in to salesforce.com.


use the following steps to refresh the dashboard( automatically...after 5 seconds) when the user logs in to the salesforce OR the user clicks the home tab:- 


1) Create a new custom Home Page Component( type:HTML)


2) Add the following code:-(click the show html button before pasting the code)
<script type="text/javascript"> function refreshDashboardTimeout() {var dashboardButton = document.getElementById("db_ref_btn");dashboardButton.click()}window.setTimeout("refreshDashboardTimeout();", 5*1000); </script>


3) save and Add this component to your Home Page Layout.


quiet simple....right?? :)

Thursday, April 12, 2012

Object sharing using email approval


This solution gives record access to a Salesforce user via email approval. Here we are providing access to the Accounts but this can be generalized for any standard or custom object.
 In order to grant access to the Account, an approval workflow request would be sent to the record owner and upon approval Apex code would be executed to add the requested user in the Account Team/Share objects.


1    1.    Provide a Request for Access button against each Account record on the Visual force page.
a.      Display a pop-up to the end user: A Request Email has been sent for Access.
b.      Create a Custom Object “Account Sharing Request” and add an Entry in this object:
AccountID, OwnerID, RequestedBy(UserID),IsApproved(False) for the request.
 
2.      Create an Approval process for Account Sharing Request in the controller class and send an approval request email to the record owner. 
 
3.      Enable email approval response so that the user can approve or reject the request by replying to the email: approve, approved, yes, reject, rejected, or no.
 
4.      Update IsApproved field to True upon approval: This is the Final approval action.
 
5.      Update trigger on the Account_Sharing_Request__c object would add the requested User to the Account Team and Account share Object.


Saturday, April 7, 2012

Query User access Level in Apex

     With API 24 salesforce has given access to UserRecordAccess table. This gives us more power in querying whether user have access and upto what level so that we can build our VF and apex accordingly.
http://www.salesforce.com/us/developer/docs/api/index_Left.htm#StartTopic=Content/sforce_api_objects_userrecordaccess.htm

I was writing a VF page in which i needed to have a table of sobject records with Edit and del links for each line item. I was using{!URL for} function which would take care of the link automatically. It would even prevent users who do not have permission to edit or delete the record to take it to the page 'you do not have sufficient permission'.

 I thought it would be nice to know if the loggedin user or profile permission can be queried based on which i could display or hide these links instead of user clicking the link and finding out. Thats when i searched i figured it could be handled with Api 24.0

SELECT RecordId, HasReadAccess, HasTransferAccess, MaxAccessLevel
     FROM UserRecordAccess
     WHERE UserId = [single ID]
     AND RecordId = [single ID]      //or Record IN [list of IDs]

so i needed to get info on hasEditAccess and hasDeleteAccess for loggedin user and based on which i displayed or hid the link. It worked like charm.

Wednesday, March 28, 2012

Sort an Apex class Object List

[Edit : Now there is an interface natively provided by salesforce to sort the class object lists]

I recently needed to sort an apex class object List which was retrieved through a webservice callout as it could not be sorted at the service end.

This is the apex method which was used to build it

 public void testsort(){
        map<string,List<accClass>> stracc = new map<string,list<accClass>>();
        for(accClass a:accClassobj ){
            if(stracc.containsKey(a.name))
                stracc.get(a.name).add(a);           
            else
              stracc.put(a.name,new accclass[]{a});          
        }
        accclassobj= new list<accclass>();
        list<string> nameList = new list<string>();
        nameList.addAll(stracc.keyset());
        nameList.sort();       
        for(string s:nameList){
           accclassobj.addAll(stracc.get(s));          
        }

Steps involved in sorting

1. build a map with object parameter as the key by which we gonna sort and List<object> as the value.
2. sort the keyset
3. arrange the values based on the sorted keyset, so that object gets sorted by that parameter

It is a map of parameter and list of object but not parameter and just object to handle the records which will have same parameters. This sorting handles null values automatically. This sorting can never be dynamic and you will need to create more methods based on the object parameter by which sorting is needed.

Friday, March 23, 2012

Filter list alphabetically



There was a requirement where i needed to build a list which can be filtered based on the starting alphabet. And also the List needs to remember the selections made as we move through other pages.

<apex:page controller="Pagin_alpha">
<style type="text/css">
      .loadingIcon {
            background-image: url(/img/loading.gif);
            width: 16px;
            height: 16px;
        }
     </style>
<script type="text/javascript">
function checkAll(cb,cbid)
        {
            var inputElem = document.getElementsByTagName("input");                     
            for(var i=0; i<inputElem.length; i++)
            {  
               
                 if(inputElem[i].id.indexOf(cbid)!=-1){                                        
                inputElem[i].checked = cb.checked;
                }
            }
        }
</script>
<apex:form id="form">
<br/>
<div style="align:right;">
<apex:repeat value="{!alphabet}" var="a">
<apex:commandLink value="{!a}" action="{!refreshList2}" rerender="form" style="{!if($CurrentPage.parameters.alpha=a,'font-weight:bold','')}" status="stat">
<apex:param name="alpha" value="{!a}"/>
</apex:commandLink>
&nbsp;|&nbsp;
</apex:repeat>
</div>
<br/>
<apex:pageBlock id="block">
<apex:pageBlockButtons >
<apex:commandButton rendered="{!standardsetcon.hasprevious}" value="Previous" action="{!standardsetcon.previous}" rerender="block,block2" status="stat"/>
<apex:commandButton rendered="{!standardsetcon.hasnext}" value="Next" action="{!standardsetcon.next}" rerender="block,block2" status="stat"/>
</apex:pageBlockButtons>
<apex:actionStatus id="stat">
<apex:facet name="start">
<apex:outputPanel layout="block" styleClass="message infoM4">
<apex:panelGrid columns="2" styleClass="messageTable" columnClasses="messageCell" style="padding:0px;margin:0px;">
<apex:panelGroup >
<img class="loadingIcon" src="/s.gif"/>
</apex:panelGroup>
<apex:panelGroup >
<div class="messageText">Please wait...</div>
</apex:panelGroup>
</apex:panelGrid>
</apex:outputPanel>
</apex:facet>
<apex:facet name="stop">
<apex:pageBlockTable value="{!CurrentList}" var="c" id="table">
<apex:column >
<apex:facet name="header"><apex:inputcheckbox onclick="checkAll(this,'check')" value="{!fals}" /></apex:facet>
<apex:inputcheckbox value="{!c.checked}" id="check">
</apex:inputcheckbox>
</apex:column>
<apex:column value="{!c.ocontact.name}" headerValue="Name"/>
</apex:pageBlockTable>
</apex:facet>
</apex:actionStatus>
</apex:pageBlock>
<apex:pageBlock title="Selected Contacts" id="block2">
<apex:commandButton value="Display Selected" rerender="block2"/>
<apex:pageBlockTable value="{!DisplaySelectedList}" var="c">
<apex:column value="{!c.name}" headerValue="Name"/>
<apex:column value="{!c.phone}" headerValue="Phone"/>
</apex:pageBlockTable>
</apex:pageBlock>

</apex:form> 
</apex:page>

In the VF page i am displaying the List 'CurrentLsit' which would hold the contact list with checkbox which is paginated and 'DisplaySelectedList' which would remember the selected contact. Array Alphabet is used in repeat to build the link of alphabets at the top of page

Controller method



public with sharing class Pagin_alpha {

    
    private List<contactSet> contactSetList{get;set;}
    private string contactListQuery;
    private set<contact> selectedContact;
   
    public List<string> alphabet{get;set;}
    public boolean fals{get;set;}  
   
    public Pagin_alpha(){
      fals=false;
      alphabet=new string[]{'All','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; 
      contactSetList = new List<contactSet>();
      selectedContact = new set<contact>();
      contactListQuery= 'select id,name,phone from contact limit 1000';   
    }
   
     Private void updateSelectedContact(){
        for(contactSet cs:contactSetList ){
           if(cs.checked)
               selectedcontact.add(cs.ocontact);
           else{
               if(selectedcontact.contains(cs.ocontact))
                   selectedcontact.remove(cs.ocontact);
               }
       }  
    }
   
      public ApexPages.StandardSetController standardSetCon {
        get {
            if(standardSetCon == null) {
                standardSetCon = new ApexPages.StandardSetController(Database.getQueryLocator(contactListQuery));
                // sets the number of records in each page set
                standardSetCon .setPageSize(20);
            }
            return standardSetCon ;
        }
        set;
    }
 
    public List<contactSet> getCurrentList() {
       updateSelectedContact();
       contactSetList = new List<contactSet>();      
        for (contact category : (List<contact>)standardSetCon.getRecords()){       
            if(selectedContact.contains(category))         
            contactSetList.add(new contactSet(category,'true'));
            else
            contactSetList.add(new contactSet(category));
        }
        fals=false;
        return contactSetList;
    }
   
    public PageReference refreshList2() {      
       standardSetCon = null;    
       string s;
       if(apexpages.currentpage().getparameters().get('alpha') == 'All')
           s='%';
       else
           s= apexpages.currentpage().getparameters().get('alpha')+'%';
      
       contactListQuery= 'select id,name,phone from contact where name like' +'\''+s +'\''+ 'limit 5000';          
        return null;
    }
   
  
   
    public List<contact> getDisplaySelectedList(){
        updateSelectedContact();
        List<contact> displaycon = new list<contact>();
        displaycon.addall(selectedcontact);
        return displaycon;
    }
      
    public class contactSet {
        public Boolean checked{ get; set; }
        public contact ocontact { get; set;}

        public contactSet(){
            ocontact = new contact();
            checked = false;
        }
        public contactSet(contact c){
            ocontact = c;
            checked = false;

        }
        public contactSet(contact c,string s){
            ocontact = c;
            checked = true;

        }
    }
   
 
}
getCurrentList populates the currentList in the page. refreshList2 will refresh the list when the link on top of the page to filter the contact is clicked.

This concept can be extended to multiple fields in the List .

Saturday, March 10, 2012

Hierarchy in VisualForce

This is an example of how to build a hierarchy Lists in VF page with select checkboxes. It will have a checkbox at parent level with which you can select all the child automatically. It has a neat link with which you can show or hide children under a parent row. It uses jquery libraries for achieving this. It has a list of accounts with each accounts displaying all its contacts in a hierarchy view.

Apex Class

public class accconhierarchy {
    Private list<account> acc;
   
    public List<accountWrap> accountList { get; set; }
    public accconhierarchy (){
        List<contactWrap> cw;
        accountList = new List<accountWrap>();
        acc = [select id,name,industry,billingcountry,createdbyid,(select id,name,email,phone from contacts) from account limit 20];
        for(account a:acc){
            cw = new list<contactWrap>();
            For(contact co : a.contacts){
               cw.add(new contactWrap(co));
            }
            accountList.add(new accountWrap(a,false,cw));
        }
    }
   
    public class accountWrap{
        public account oAccount{get;set;}
        public boolean isSelected{get;set;}
        public List<contactWrap> contactset{get;set;}
        public accountWrap(account a,boolean b, List<contactWrap> c){
            oAccount=a;
            isSelected=b;
            contactset =c;           
        }
    }
   
    public class contactWrap{
        public contact oContact{get;set;}
        public boolean isSelected{get;set;}       
        public contactWrap(contact a){
            oContact=a;
            isSelected=false;                      
        }
    }
   
}


Visualforce Page

<apex:page controller="accconhierarchy">
  <apex:form >
      <apex:pageBlock >
      <style type="text/css">
        .showContacts {
            background: transparent url('/img/alohaSkin/twisty_sprite.png') 0 0 no-repeat;
        }
        .hideContacts {
            background: transparent url('/img/alohaSkin/twisty_sprite.png') 0 -11px no-repeat;
        }
    </style>
                  <apex:outputPanel layout="none"
                        rendered="{!accountList.size!=0}">
                        <table id="tableAccount" class="list" border="0" cellpadding="0" cellspacing="0">
                            <thead>
                                <tr class="headerRow">
                                    <th class="actionColumn">Action</th>
                                    <th>Account Name</th>
                                    <th>Industry</th>
                                    <th>Billing Country</th>
                                    <th>Createdby</th>
                                </tr>
                            </thead>
                            <tbody>
                            <apex:repeat id="repeatAccount"
                                value="{!accountList}"
                                var="item">
                                <apex:variable var="acc" value="{!item.oAccount}" />  
                                <tr id="{!$Component.repeatAccount}:account" class="dataRow">
                                    <td class="actionColumn">
                                        <apex:inputCheckbox id="selectAccountRow"
                                            value="{!item.IsSelected}"
                                            onclick="javascript:toggleSelectAll(this, '{!$Component.repeatAccount}');"/>
                                    </td>
                                    <td class="dataCell">
                                        <apex:outputLink id="linkShowHide" value="javascript:toggleContactRow('{!$Component.repeatAccount}')">
                                            <img id="{!$Component.repeatAccount}:Img" src="/s.gif" class="showContacts" border="0" height="11px" width="11px" style="padding-right: 4px;"/>
                                            <apex:outputField value="{!acc.Name}" />
                                        </apex:outputLink>
                                    </td>
                                    <td class="dataCell"><apex:outputField value="{!acc.Industry}" /></td>
                                    <td class="dataCell"><apex:outputField value="{!acc.billingcountry}" /></td>
                                    <td class="dataCell"><apex:outputField value="{!acc.CreatedById}" /></td>
                                </tr>

                                <tr id="{!$Component.repeatAccount}:Contact" class="dataRow" style="display:none;">
                                    <td colspan="100" style="padding:10px; padding-left: 45px;">
                                        <h3>Contacts</h3>
                                        <br/>                                       
                                        <apex:outputPanel layout="none">                                           
                                            <table id="tableAccountContacts" class="list" border="0" cellpadding="0" cellspacing="0">
                                                <thead>
                                                    <tr class="headerRow">
                                                        <th class="actionColumn">Action</th>
                                                        <th>Name</th>
                                                        <th>Email</th>
                                                        <th>Phone</th>
                                                    </tr>
                                                </thead>
                                                <tbody>
                                                <apex:repeat id="repeatAccountContacts"
                                                    value="{!item.contactSet}"
                                                    var="subitem">
                                                    <apex:variable value="{!subitem.oContact}" var="con" />
                                                    <tr class="dataRow">
                                                        <td class="actionColumn"><apex:inputCheckbox id="selectContactRow" styleclass="ContactCheckbox" value="{!subitem.IsSelected}"/></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.Name}" /></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.email}"/></td>
                                                        <td class="dataCell"><apex:outputField value="{!con.phone}" /></td>                                                       
                                                    </tr>
                                                </apex:repeat>
                                                </tbody>
                                            </table>
                                        </apex:outputPanel>
                                    </td>
                                </tr>
                            </apex:repeat>
                            </tbody>
                        </table>
                    </apex:outputPanel>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
 <script>
        jQuery.noConflict();
        function toggleContactRow(rowId) {
            try {
                var AccountImg = jQuery(document.getElementById(rowId + ":Img"));
                var ContactsRow = jQuery(document.getElementById(rowId + ":Contact"));
                if (AccountImg.hasClass("hideContacts")) {
                    AccountImg.attr("class", "showContacts");
                    ContactsRow.hide();
                }
                else {
                    AccountImg.attr("class", "hideContacts");
                    ContactsRow.show();
                }
            }
            catch (e) { alert(e); }
        } 

        function toggleSelectAll(elCheckbox, rowId) {
            try {
                var SelectAll = jQuery(elCheckbox);
                var ContactsRow = jQuery(document.getElementById(rowId + ":Contact"));
                ContactsRow.find(".ContactCheckbox").each(function() {
                    jQuery(this).prop("checked", SelectAll.is(":checked"));
                });
            }
            catch (e) { alert(e); }
        }

    </script>
    </apex:pageBlock>
  </apex:form>
</apex:page>

JS function toggleContactRow is used to show or hide contacts and toggleSelectAll is for header level checkbox at the parent Level.

Wednesday, February 22, 2012

SFDC API 24.0 test failure issue

Yesterday I was experiencing a weird issue.

For a new requirement we did development in our DEV environment…We wrote new test methods and we thought the work is done :)... 
However, there were Null Pointer Exceptions when we ran the test methods.
And it was pointing to the custom settings….we doubled checked and confirmed that the custom setting had the default values. Also, it was accessible to anyone (public).

After some time I realized that the test method API version was 24.0.. I changed it to 23.0 and there were no errors :)

Edit - SFDC has introduced test classes not to access data directly with API 24. Even though it is a good move , I would like to access certain info by default like custom settings, and Pricebook. But there is always an option to use @istest(seealldata=true) to access live data.


http://boards.developerforce.com/t5/General-Development/Custom-setting-not-accessible-from-test-class/m-p/399669#M66636

Saturday, February 4, 2012

Account address to be displayed as single block

Small code snippet to display Account address as a single block just like in standard page

<apex:outputText label="Shipping Address"
                    escape="false"
                    value="{!IF(NOT(ISNULL(Account.ShippingStreet)),'{0}<br/>{1}, {2} {3}<br/>{4}', '&nbsp;')}">
                   
                    <apex:param value="{!Account.ShippingStreet}" />
                    <apex:param value="{!Account.ShippingCity}" />
                    <apex:param value="{!Account.ShippingState}" />
                    <apex:param value="{!Account.ShippingPostalCode}" />
                    <apex:param value="{!Account.ShippingCountry}" />
                </apex:outputText>

Wednesday, January 25, 2012

Identifying SobjectType Dyanamically

getKeyPrefix of Schema.getGlobalDescribe can be used to identify dynamically the SobjectType in Apex.

String prefix = recordId.substring(0,3);
String objectType;
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
for(Schema.SObjectType s : gd.values()){
    if(prefix == s.getDescribe().getKeyPrefix()){
        objectType = s.getDescribe().Name;
    }
}

recordId consists the Id of the sobject Record for whose SobjectType needs to be identified. Above piece of code does it well.

Wednesday, January 11, 2012

Build your Own Standard set controller

Standard set controller can be used for many objects . Recently i found that it cannot be used with activity objects(Open activity and activity history). Also there are many other Restrictions placed on these objects. You cannot query on these objects directly. It has to be a nested SOQL within an object using a relationship name.

So coming back to my requirement i needed to roll up related list for all the activities in the account hierarchy for the topmost parent. For other related list i achieved it through standardset controller. But for activity alone i created a class which would behave like a standard set controller.

public class SetActivityClass {
        public integer pagenumber{get;set;}
        public integer totalpages{get;set;}
        public integer pagesize{get;set;}
        public list<sobject> sobjectList{get;set;}       
 public SetActivityClass(list<sobject> sobjectList){
        pagenumber =1;
        pagesize = 60;
        this.sobjectList=sobjectList;
        totalpages= Integer.valueof(math.ceil((double)sobjectList.size()/(double)pagesize ));
        }
        public boolean gethasprevious(){
          return (pagenumber != 1);
        }
        public boolean gethasnext(){
            return (pagenumber != totalpages);
        }
        public pagereference Previous(){
        if(gethasprevious())
            pagenumber--;
            return null;
        }
        public pagereference next(){
        if(gethasnext())
            pagenumber++;
            return null;
        }
        public list<sobject> getRecords(){
        list<sobject> Sactivity = new list<sobject>();       
            for(integer counter2=(pagenumber-1)*pagesize;counter2<(math.min(pagenumber*pagesize,sobjectList.size()));counter2++){
                Sactivity.add(sobjectList.get(counter2));
            }
            return Sactivity;
        }
          
    } 

oActivityHistory= new SetActivityClass(ActivityHistoryList);
        oOpenActivity= new SetActivityClass(OpenActivityList); 


public List<OpenActivity> getGoToListOpenActivities() {
    return oOpenActivity.getRecords();       
    }

public List<ActivityHistory> getGoToListActivityHistories() {
    return oActivityHistory.getRecords();       
    }

Just like the standard set controller, this SetActivityClass has methods to return next set or previous set of records. These can be added to command links as they are pagereference method. Also Hasprevious and Hasnext can be called from the page to check if the previous or next set of records exist.Pagenumber denotes the current page number. Pagesize determines no of records to be displayed in the page. I would recommend a size of 40 -60 in a page for better readability by Users(I have hardcoded it as 60). Total pages is calculated based on this pagesize.

Also note that getRecord function accepts and returns generic sobject. I used the same class for both activityhistory and openactivity.This Class can be slightly tweaked to handle list of objects of your custom class  as well.

Here is how i would display it in the page

<apex:panelGrid columns="2">                 
 <apex:commandLink action="{!oOpenActivity.previous}" rendered={!oOpenActivity.hasPrevious}"> Previous </apex:commandLink>
  <apex:commandLink action="{!oOpenActivity.next}" rendered="{!oOpenActivity.hasNext}" >Next </apex:commandLink>           
  </apex:panelGrid>

<apex:pageBlockTable id="tableMasterAccountOpenActivities" var="openact"  value="{!GoToListOpenActivities}">
 <apex:column .....
.......
</apex:pageblocktable>

And whenever the next and previous is click make sure atleast the pageblock tabele is rerendered if not the whole page based on your logic. I  have shown the VF for openactivity. similarly u can use it for activity history. Also you can use the same logic in building your own custom class wrapper object standard set controller this way.

Saturday, January 7, 2012

Javascript for Ghost Text in Salesforce

There will be a need to add ghost text which will be like a help text or format in which the value needs to be there for a particular input field in visual force. I would suggest use visual force help text for it. But if ghost text is required use the following script.



Trick here use the script after all the component is loaded preferably before </apex:page> tag.

        <script type="text/javascript">  
            var txtContent  = document.getElementById("{!$Component.theform.thepb.thepbs.mp}");
            var defaultText = '+91 - *** *** ****';
            txtContent.value = defaultText;           
            txtContent.style.color = '#CCC';           
            txtContent.onfocus = function() {
              if (this.value == defaultText) {   
                this.value = '';
                this.style.color = '#000';
              }
            }
            txtContent.onblur = function() { 
              if (this.value == '') {   
                this.value = defaultText;
                this.style.color = '#CCC';
              }
            }
        </script>  

Also Make sure to handle the save if the field is filled with ghost text(Default text here).