Showing posts with label Code Sample. Show all posts
Showing posts with label Code Sample. Show all posts

Dec 27, 2012

Streaming APIs - Easy Code


Streaming API is useful when you want notifications to be pushed from the server to the client based on criteria that you define.

The sequence of events when using Streaming API is as follows: (explained in detail later)
1. Create a PushTopic based on a SOQL query. This defines the channel.
2. Clients subscribe to the channel.
3. A record is created or updated (an event occurs). The changes to that record are evaluated.
4. If the record changes match the criteria of the PushTopic query, a notification is generated by the server and received by the subscribed clients.

Before we get started, I recommend you to go through these topics:

1) Push Technology
2) Bayeux Protocol, CometD, and Long Polling


We will achieve this once we are done with all steps (given below):





Now follow these steps:

Prerequisites:
1) The “Streaming API” permission must be enabled -> "Your Name > Setup > Customize > User Interface"
2) The logged-in user must have “Read” permission on the PushTopic standard object to receive notifications.
3) The logged-in user must have “Create” permission on the PushTopic standard object to create and manage PushTopic records.
4) The logged-in user must have “Author Apex” permissions to create a PushTopic by using the Developer Console.


Step 1: Create an Object - In our case we will use Account

Step 2: Create a PushTopic
Use the Developer Console to create the PushTopic record. You can use either Developer Console or Workbench. If needed these records can be updated.

"Your Name > Developer Console"


PushTopic pushTopic = new PushTopic();
pushTopic.Name = 'RefreshAccounts';
pushTopic.Query = 'SELECT Id, Name FROM Account';
pushTopic.ApiVersion = 26.0;
pushTopic.NotifyForOperations = 'All';
pushTopic.NotifyForFields = 'Referenced';
insert pushTopic;


Step 3: Create Static resource

1. Download the CometD compressed archive (.tgz) file from http://download.cometd.org/cometd-2.2.0-distribution.tar.gz
2. Extract the following JavaScript files from cometd-2.2.0-distribution.tar.gz:
• cometd-2.2.0/cometd-javascript/common/target/org/cometd.js
• cometd-2.2.0/cometd-javascript/jquery/src/main/webapp/jquery/jquery-1.5.1.js
• cometd-2.2.0/cometd-javascript/jquery/src/main/webapp/jquery/json2.js
• cometd-2.2.0/cometd-javascript/jquery/src/main/webapp/jquery/jquery.cometd.js


File Name
Static Resource Name
cometd.js
Cometd
jquery-1.5.1.js
Jquery
json2.js
json2
jquery.cometd.js
jquery_cometd




Step 4: Test the PushTopic Channel

Here is the code sample

Visualforce Page


<apex:page id="PG" controller="StreamingAPIController">
<apex:form id="FRM">

    <apex:includeScript value="{!$Resource.cometd}"/>
    <apex:includeScript value="{!$Resource.jquery}"/>
    <apex:includeScript value="{!$Resource.json2}"/>
    <apex:includeScript value="{!$Resource.jquery_cometd}"/>

    <apex:actionFunction name="GetRefreshedAccounts" reRender="PB,PBT"/>

    <script type="text/javascript">
        (function($)
        {
            $(document).ready(function() {
                
                // Connect to the CometD endpoint
                $.cometd.init({
                    url: window.location.protocol+'//'+window.location.hostname+'/cometd/24.0/',
                    requestHeaders: { Authorization: 'OAuth {!$Api.Session_ID}'}
                });
                
                // Subscribe to a topic. JSON-encoded update will be returned in the callback
                // In this example we are using this only to track the generated event
                $.cometd.subscribe('/topic/RefreshAccounts', function(message)
                {
                    //You can use message as it will return you many attributes
                    //I am just using to track that event is generated
                    GetRefreshedAccounts();
                });

            });
        })(jQuery)
    </script>

    <apex:pageBlock id="PB">
        <apex:variable var="count" value="{!0}" />
        <apex:pageBlockTable id="PBT" value="{!getRefreshedAccount}" var="AllAcc">
            
            <apex:column headerValue="S.No.">
                <apex:variable var="count" value="{!count+1}" />
                {!count}
            </apex:column>
            <apex:column value="{!AllAcc.Name}" headerValue="Name"/>
            
        </apex:pageBlockTable>
    </apex:pageBlock>

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

Apex Class


public class StreamingAPIController
{
    //Everytime page is reRendered it will get refreshed values of account
    public List<Account> getRefreshedAccount
    {
        get
        {
            return [select Id, Name from Account LIMIT 50000] ;
        }
        set;
    }
    
    public StreamingAPIController()
    {
    }
}


This is very small and simple sample code, you can do a lot more with streaming APIs. Please refer these documents to learn more:

1) http://www.salesforce.com/us/developer/docs/api_streaming/api_streaming.pdf
2) http://www.salesforce.com/us/developer/docs/api_streaming/index.htm
3)http://wiki.developerforce.com/page/Getting_Started_with_the_Force.com_Streaming_API

Dec 22, 2012

Uploading Multiple Attachments into Salesforce - Simple Code

Okay, so here is the simple code to upload multiple attachments into Salesforce. Please note that I've used standard controller ("Account" in the code) so you need to pass the record Id in parameter. You can also change this according to your need as code is dynamic.

So first option allow you to select how many files you want to upload. Say, you have selected "5", then it will give you 5 options to select files and once you click on "Upload" it will upload the files as attachments to the associated record.



Visualforce Page Code:


<apex:page standardController="Account" extensions="MultipleUploadController">
    <apex:form>
        <apex:pageBlock title="Upload Multiple Attachment to Object">
            
            <apex:pageBlockButtons>
                <apex:commandButton value="Upload"  action="{!SaveAttachments}"/>
            </apex:pageBlockButtons>
            
            <apex:pageMessages id="MSG"/>
            <apex:actionFunction name="ChangeCount" action="{!ChangeCount}"/>
            
            <apex:pageblocksection>
                            
                <apex:pageBlockSectionItem>
                    <apex:outputLabel value="How many files you want to upload?"/>
                    <apex:selectList onchange="ChangeCount() ;" multiselect="false" size="1" value="{!FileCount}">
                        <apex:selectOption itemLabel="--None--" itemValue=""/>
                        <apex:selectOptions value="{!filesCountList}"/>
                    </apex:selectList>
                </apex:pageBlockSectionItem>
            
            </apex:pageblocksection>
            
            <apex:pageBlockSection title="Select Files" rendered="{!IF(FileCount != null && FileCount != '', true , false)}">
                <apex:repeat value="{!allFileList}" var="AFL">
                    <apex:inputfile value="{!AFL.body}" filename="{!AFL.Name}" />
                    <br/>
                </apex:repeat>
            </apex:pageBlockSection>
            
        </apex:pageBlock>
    </apex:form>
</apex:page>


Apex Code:


public class MultipleUploadController
{   
    //Picklist of tnteger values to hold file count
    public List<SelectOption> filesCountList {get; set;}
    //Selected count
    public String FileCount {get; set;}
    
    public List<Attachment> allFileList {get; set;}
    
    public MultipleUploadController(ApexPages.StandardController controller)
    {
        //Initialize  
        filesCountList = new List<SelectOption>() ;
        FileCount = '' ;
        allFileList = new List<Attachment>() ;
        
        //Adding values count list - you can change this according to your need
        for(Integer i = 1 ; i < 11 ; i++)
            filesCountList.add(new SelectOption(''+i , ''+i)) ;
    }
    
    public Pagereference SaveAttachments()
    {
        String accId = System.currentPagereference().getParameters().get('id');
        if(accId == null || accId == '')
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'No record is associated. Please pass record Id in parameter.'));
        if(FileCount == null || FileCount == '')
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'Please select how many files you want to upload.'));

        List<Attachment> listToInsert = new List<Attachment>() ;
        
        //Attachment a = new Attachment(parentId = accid, name=myfile.name, body = myfile.body);
        for(Attachment a: allFileList)
        {
            if(a.name != '' && a.name != '' && a.body != null)
                listToInsert.add(new Attachment(parentId = accId, name = a.name, body = a.body)) ;
        }
        
        //Inserting attachments
        if(listToInsert.size() > 0)
        {
            insert listToInsert ;
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.INFO, listToInsert.size() + ' file(s) are uploaded successfully'));
            FileCount = '' ;
        }
        else
            ApexPages.addmessage(new ApexPages.message(ApexPages.Severity.ERROR,'Please select at-least one file'));
            
        return null;
    }
    
    public PageReference ChangeCount()
    {
        allFileList.clear() ;
        //Adding multiple attachments instance
        for(Integer i = 1 ; i <= Integer.valueOf(FileCount) ; i++)
            allFileList.add(new Attachment()) ;
        return null ;
    }
}


You can enhance the code if you like, as I've provided the basic code which is re-usable. Also you can change the object passed in standard controller and code will handle the rest. URL should have ID else you will get error on UI :-)

Say, I've saved my VFP code as "MultipleUpload", so my URL will be https://...../apex/MultipleUpload?id=XXXXXXXXXXXXXXX (account id in this code).

Happy Coding.

Nov 23, 2012

Page Block Table With Dynamic Columns In Salesforce

This is something extensively used, but every time I need to look for some easy code.

So here is the solution (apologies as I've not covered the negative cases in code, and I hope you all are smart enough to take care of that). Requirement is to build the dynamic page block table with dynamic columns, but this is not it. It should be so dynamic that user can select the object and it's fields by himself and nothing should be hard-coded. So first of all I've provided a picklist with all objects (not all objects... why? Take a look at it Please Remove The System Objects From My sObject List)

Visualforce Page Code:

<apex:page controller="DynamicTableController">
<apex:pageBlock >
    <apex:form >
        <apex:actionFunction name="ObjectFileds" action="{!ObjectFields}"/>
        
        <apex:commandButton  value="Show Table" action="{!ShowTable}"/>
        
        <apex:pageBlockSection >
            <apex:pageBlockSectionItem >
                <apex:outputLabel value="Select Object"/>
                <apex:selectList multiselect="false" size="1" value="{!SelectedObject}" onchange="ObjectFileds();">
                    <apex:selectOption itemLabel="--None--" itemValue="--None--"/>
                    <apex:selectoptions value="{!supportedObject}" />
                </apex:selectlist>
            </apex:pageBlockSectionItem>
            
            <apex:pageBlockSectionItem >
                <apex:outputLabel value="Select Field"/>
                <apex:selectList multiselect="true" size="5" value="{!SelectedFields}">
                    <apex:selectOption itemLabel="--None--" itemValue="--None--"/>
                    <apex:selectoptions value="{!fieldLableAPI}" />
                </apex:selectlist>
            </apex:pageBlockSectionItem>
            
            <apex:pageBlockTable rendered="{!IF(ObjectList.size > 0 , true , false)}" value="{!ObjectList}" var="rec">
                <apex:column value="{!rec.Id}" rendered="{!IF(SelectedFields.size == 0 , true, false)}"/>
                <apex:repeat value="{!SelectedFields}" var="FieldLable">
                    <apex:column value="{!rec[FieldLable]}" rendered="{!IF(FieldLable != '--None--' , true, false)}"/>
                </apex:repeat>
            </apex:pageBlockTable>
            
            <apex:outputPanel rendered="{!IF(ObjectList.size < 1 , true , false)}">
                <apex:pageMessage severity="ERROR" summary="No records to display"/>
            </apex:outputPanel>
            
        </apex:pageBlockSection>
        
    </apex:form>
</apex:pageBlock>
</apex:page>


Apex Code (Class):

public class DynamicTableController
{
    //List displayed on UI
    public List<selectoption> supportedObject {get; set;}
    
    //Selected Object
    public String SelectedObject {get; set;}
    
    //Global describe
    Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
    Set<String> objectKeys = gd.keySet();
    
    //Field Select List
    public List<SelectOption> fieldLableAPI {get; set;}
    
    //Selected fields to be displayed in table
    public List<String> SelectedFields {get; set;}
    
    //List to maintain dynamic query result
    public List<sObject> ObjectList {get; set;}
    
    
    //Constructor
    public DynamicTableController()
    {
        //Initialize
        supportedObject = new List<selectoption>() ;
        SelectedObject = '' ;
        fieldLableAPI = new List<SelectOption>() ;
        SelectedFields = new List<String>() ;
        ObjectList = new List<sObject>() ;
        
        //Get only reference to objects
        for(Schema.SObjectType item : ProcessInstance.TargetObjectId.getDescribe().getReferenceTo())
        {
            //Excluding custom setting objects
            if(!item.getDescribe().CustomSetting)
            {
                //Adding to list
                supportedObject.add(new SelectOption(item.getDescribe().getLocalName().toLowerCase() , item.getDescribe().getLabel() ));
            }
        }
        
    }
    
    //Get fields of selected object
    public void ObjectFields()
    {
        if(SelectedObject != '--None--')
        {
            //Creating sObject for dynamic selected object
            Schema.SObjectType systemObjectType = gd.get(SelectedObject);
            //Fetching field results
            Schema.DescribeSObjectResult r = systemObjectType.getDescribe();
                
            Map<String, Schema.SObjectField> M = r.fields.getMap();
            //Creating picklist of fields
            for(Schema.SObjectField fieldAPI : M.values())
            {
                fieldLableAPI.add(new SelectOption(fieldAPI.getDescribe().getName() , fieldAPI.getDescribe().getLabel())) ;
            }
        }
    }
    
    public void ShowTable()
    {
        //Creating dynamic query with selected field
        String myQuery = 'Select Id ' ;
        
        for(String field : SelectedFields)
        {
            if(field.toLowerCase() != 'id' && field.toLowerCase() != '--none--')
            myQuery += ','+ field + ' ' ;
        }
        
        //Limit is 100 for now you can change it according to need
        myQuery += ' from ' + SelectedObject + ' LIMIT 100' ;
        
        //Executing the query and fetching results
        ObjectList = Database.query(myQuery) ;
    }
}

Once user selects any object from the first picklist, filed picklist will be populated with all the field labels related to that object. Note, it's a multi-select picklist so user can select multiple fields. Once fields are selected and "Show Table" is clicked, page block table will display the field values (selected in field picklist) of all records (limit is 100 right now, you can change it according to your need).

There are some extra checks that needs to be implemented in code like "isQueryable" etc..

Jan 15, 2012

Integrating Google Maps in Salesforce

Requirement is to show the location of my Account on Google Maps. It looks difficult but actually it is very simple to implement.


Just create a new visualforce page with this code
<apex:page standardController="Account">

<script src="http://maps.google.com/maps?file=api">
</script>

<script type="text/javascript">

var map = null;
var geocoder = null;

var address = "{!Account.BillingStreet}, {!Account.BillingPostalCode} {!Account.BillingCity}, {!Account.BillingState}, {!Account.BillingCountry}";

function initialize() {
if(GBrowserIsCompatible())
{
  map = new GMap2(document.getElementById("MyMap"));
  map.addControl(new GMapTypeControl());
  map.addControl(new GLargeMapControl3D());
  
  geocoder = new GClientGeocoder();
  geocoder.getLatLng(
    address,
    function(point) {
      if (!point) {
        document.getElementById("MyMap").innerHTML = address + " not found";
      } else {
        map.setCenter(point, 13);
        var marker = new GMarker(point);
        map.addOverlay(marker);
        marker.bindInfoWindowHtml("Account Name : <b><i> {!Account.Name} </i></b> <br/> Address : "+address);
      }
    }
  );
}
}
</script>


<div id="MyMap" style="width:100%;height:300px"></div>
<script>
    initialize() ;
</script>

</apex:page>
As you can see that I have used standard controller so you need to pass the account Id in URL. Let's say if the page name is "GoogleMaps" then the URL will look something like this : ".../apex/GoogleMaps?id=YOUR_ACCOUNT_ID".






When you click on the balloon it will show you the Account name and the address, you can change it according to your need by changing the code of line "marker.bindInfoWindowHtml"


Use this page as in line visualforce page on native layout and enjoy the Google maps with Salesforce.


References
http://code.google.com/apis/maps/documentation/webservices/
http://code.google.com/apis/maps/index.html

Dec 31, 2011

Displaying Role Hierarchy on Visualforce Page - Tree View

Ever thought of building a tree like data structure for the users based on role hierarchy and displaying it in the form of a JavaScript tree with node selection capability on the Visualforce page?

So recently I came across a functionality where a third party javascript calendar was used on the VisualForce page and all events were fetched programmatically through Apex and plotted on the calendar. The UI looked good along with other custom developed functionality. All was fine until client asked if it was possible to select some of the logged in user's subordinates through custom VF page and plot events on the calendar for the selected users only. In other words, fetch and display only those events which were owned by users who worked below the logged in user in the role hierarchy.

It got me thinking and I did some research to check if there was an easier way to get this done, but soon realized that this required custom  and tricky Apex/VF code. There's a nice little script written by Jeff Douglas that was closest to what I actually wanted.

So I came up with this handy utility which fulfils my requirements. Getting user IDs of subordinates could also be useful in situations where, for example, you want to do a comparative analysis of performance for all users reporting to a manager.



There are mainly two parts to the solution I designed:

1. RoleUtil (Apex Class): Utility class which exposes the following API

a. public static RoleNodeWrapper getRootNodeOfUserTree (Id userOrRoleId) - function creates the tree data structure for the requested user or role ID and returns the root node to the caller
b. public static List<User> getAllSubordinates (Id userId) - function returns the list of all subordinate users for the requested user ID
c. public static String getTreeJSON (Id userOrRoleId) - function returns the JSON string for the requested user or role ID
d. public static String getSObjectTypeById(Id objectId) - general utility function to return the string representation of the object type for the requested object ID
e. public static Boolean isRole (Id objId) - internally uses the getSObjectTypeById (#d above) to check whether the requested object ID is of UserRole type
f. public class RoleNodeWrapper (inner class) - wrapper for user role, represents a node in the tree data structure mentioned above and exposes boolean properties like hasChildren, hasUsers, isLeafNode, etc

public class RoleUtil {

    /********************* Properties used by getRootNodeOfUserTree function - starts **********************/
    // map to hold roles with Id as the key
    private static Map <Id, UserRole> roleUsersMap;

    // map to hold child roles with parentRoleId as the key
    private static Map <Id, List<UserRole>> parentChildRoleMap;

    // List holds all subordinates
    private static List<User> allSubordinates {get; set;}
        
    // Global JSON generator
    private static JSONGenerator gen {get; set;}

    /********************* Properties used by getRootNodeOfUserTree function - ends **********************/
    
    
    /********************* Properties used by getSObjectTypeById function - starts ********************* */
    // map to hold global describe data
    private static Map<String,Schema.SObjectType> gd;
    
    // map to store objects and their prefixes
    private static Map<String, String> keyPrefixMap;

    // to hold set of all sObject prefixes
    private static Set<String> keyPrefixSet;
    /********************* Properties used by getSObjectTypeById function - ends **********************/
    
    /* // initialize helper data */ 
    static {
        // initialize helper data for getSObjectTypeById function
        init1();
        
        // initialize helper data for getRootNodeOfUserTree function
        init2();
    }
    
    /* // init1 starts <to initialise helper data> */
    private static void init1() {
        // get all objects from the org
        gd = Schema.getGlobalDescribe();
        
        // to store objects and their prefixes
        keyPrefixMap = new Map<String, String>{};
        
        //get the object prefix in IDs
        keyPrefixSet = gd.keySet();
        
        // fill up the prefixes map
        for(String sObj : keyPrefixSet) {
            Schema.DescribeSObjectResult r =  gd.get(sObj).getDescribe();
            String tempName = r.getName();
            String tempPrefix = r.getKeyPrefix();
            keyPrefixMap.put(tempPrefix, tempName);
        }
    }
    /* // init1 ends */

    /* // init2 starts <to initialise helper data> */
    private static void init2() {
        
        // Create a blank list
        allSubordinates = new List<User>();
        
        // Get role to users mapping in a map with key as role id
        roleUsersMap = new Map<Id, UserRole>([select Id, Name, parentRoleId, (select id, name from users) from UserRole order by parentRoleId]);
        
        // populate parent role - child roles map
        parentChildRoleMap = new Map <Id, List<UserRole>>();        
        for (UserRole r : roleUsersMap.values()) {
            List<UserRole> tempList;
            if (!parentChildRoleMap.containsKey(r.parentRoleId)){
                tempList = new List<UserRole>();
                tempList.Add(r);
                parentChildRoleMap.put(r.parentRoleId, tempList);
            }
            else {
                tempList = (List<UserRole>)parentChildRoleMap.get(r.parentRoleId);
                tempList.add(r);
                parentChildRoleMap.put(r.parentRoleId, tempList);
            }
        }
    } 
    /* // init2 ends */

    /* // public method to get the starting node of the RoleTree along with user list */
    public static RoleNodeWrapper getRootNodeOfUserTree (Id userOrRoleId) {
        return createNode(userOrRoleId);
    }
    
    /* // createNode starts */
    private static RoleNodeWrapper createNode(Id objId) {
        RoleNodeWrapper n = new RoleNodeWrapper();
        Id roleId;
        if (isRole(objId)) {
            roleId = objId;
            if (!roleUsersMap.get(roleId).Users.isEmpty()) {
                n.myUsers = roleUsersMap.get(roleId).Users;
                allSubordinates.addAll(n.myUsers);
                n.hasUsers = true;
            }
        }
        else {
            List<User> tempUsrList = new List<User>();
            User tempUser = [Select Id, Name, UserRoleId from User where Id =: objId];
            tempUsrList.add(tempUser);
            n.myUsers = tempUsrList;
            roleId = tempUser.UserRoleId;
        }
        n.myRoleId = roleId;
        n.myRoleName = roleUsersMap.get(roleId).Name;
        n.myParentRoleId = roleUsersMap.get(roleId).ParentRoleId;

        if (parentChildRoleMap.containsKey(roleId)){
            n.hasChildren = true;
            n.isLeafNode = false;
            List<RoleNodeWrapper> lst = new List<RoleNodeWrapper>();
            for (UserRole r : parentChildRoleMap.get(roleId)) {
                lst.add(createNode(r.Id));
            }           
            n.myChildNodes = lst;
        }
        else {
            n.isLeafNode = true;
            n.hasChildren = false;
        }
        return n;
    }
    
    public static List<User> getAllSubordinates(Id userId){
        createNode(userId);
        return allSubordinates;
    }
    
    public static String getTreeJSON(Id userOrRoleId) {
        gen = JSON.createGenerator(true);
        RoleNodeWrapper node = createNode(userOrRoleId);
        gen.writeStartArray();
            convertNodeToJSON(node);
        gen.writeEndArray();
        return gen.getAsString();
    }
    
    private static void convertNodeToJSON(RoleNodeWrapper objRNW){
        gen.writeStartObject();
            gen.writeStringField('title', objRNW.myRoleName);
            gen.writeStringField('key', objRNW.myRoleId);
            gen.writeBooleanField('unselectable', false);
            gen.writeBooleanField('expand', true);
            gen.writeBooleanField('isFolder', true);
            if (objRNW.hasUsers || objRNW.hasChildren)
            {
                gen.writeFieldName('children');
                gen.writeStartArray();
                    if (objRNW.hasUsers)
                    {
                        for (User u : objRNW.myUsers)
                        {
                            gen.writeStartObject();
                                gen.writeStringField('title', u.Name);
                                gen.writeStringField('key', u.Id);
                            gen.WriteEndObject();
                        }
                    }
                    if (objRNW.hasChildren)
                    {
                        for (RoleNodeWrapper r : objRNW.myChildNodes)
                        {
                            convertNodeToJSON(r);
                        }
                    }
                gen.writeEndArray();
            }
        gen.writeEndObject();
    }
    
    /* // general utility function to get the SObjectType of the Id passed as the argument, to be used in conjunction with */ 
    public static String getSObjectTypeById(Id objectId) {
        String tPrefix = objectId;
        tPrefix = tPrefix.subString(0,3);
        
        //get the object type now
        String objectType = keyPrefixMap.get(tPrefix);
        return objectType;
    }
    /* // utility function getSObjectTypeById ends */
    
    /* // check the object type of objId using the utility function getSObjectTypeById and return 'true' if it's of Role type */
    public static Boolean isRole (Id objId) {
        if (getSObjectTypeById(objId) == String.valueOf(UserRole.sObjectType)) {
            return true;
        }
        else if (getSObjectTypeById(objId) == String.valueOf(User.sObjectType)) {
            return false;
        } 
        return false;
    }
    /* // isRole ends */
    
    public class RoleNodeWrapper {
    
        // Role info properties - begin
        public String myRoleName {get; set;}
        
        public Id myRoleId {get; set;}
        
        public String myParentRoleId {get; set;}
        // Role info properties - end
        
        
        // Node children identifier properties - begin
        public Boolean hasChildren {get; set;}
        
        public Boolean isLeafNode {get; set;}
        
        public Boolean hasUsers {get; set;}
        // Node children identifier properties - end
        
        
        // Node children properties - begin
        public List<User> myUsers {get; set;}
    
        public List<RoleNodeWrapper> myChildNodes {get; set;}
        // Node children properties - end   
        
        public RoleNodeWrapper(){
            hasUsers = false;
            hasChildren = false;
        }
    }
 }

2. TreeView (Visualforce component): Dynatree based reusable VF component that exposes input parameters like

a. roleOrUserId - required string type input attribute
b. selectable - boolean attribute to indicate whether you want to display checkboxes against nodes in the tree for user selection
c. JsonData - optional string type input attribute, if supplied to the component ignores the "roleOrUserId" attribute and displays the tree structure for the input JSON string
d. value - a string type output attribute which returns the IDs/Keys of the selected nodes in the CSV format, which can then be utilised by the page controller

<apex:component controller="TreeViewController">
    <apex:attribute name="roleOrUserId" required="true" type="String" assignTo="{!roleOrUserId}" description="Enter Role or User Id to build the hierarchy. Pass null if you are passing JSON data as a parameter" />
    <apex:attribute name="selectable" type="Boolean" assignTo="{!selectable}" description="Do you want nodes to be selectable?" />
    <apex:attribute name="value" type="String" description="IDs of selected Nodes in CSV format" />
    <apex:attribute name="JsonData" type="String" assignTo="{!JsonData}" description="JSON input for the tree component" />
    <apex:inputHidden id="selectedKeys" value="{!value}" />
    <apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery.js' )}" />
    <apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery-ui.custom.js' )}" />
    <apex:includeScript value="{!URLFOR($Resource.DynaTree, 'jquery/jquery.cookie.js' )}" />
    <apex:includeScript value="{!URLFOR($Resource.DynaTree, 'src/jquery.dynatree.js' )}" />
    
    <apex:stylesheet value="{!URLFOR($Resource.DynaTree, 'src/skin/ui.dynatree.css')}" />

    <!-- Add code to initialize the tree when the document is loaded: -->
    <script type="text/javascript">
    $(function(){
        // Attach the dynatree widget to an existing <div id="tree"> element
        // and pass the tree options as an argument to the dynatree() function:
        $("#tree").dynatree({
            onActivate: function(node) {
                // A DynaTreeNode object is passed to the activation handler
                // Note: we also get this event, if persistence is on, and the page is reloaded.
                //alert("You activated " + node.data.key);
            },
            persist: false,
            checkbox: {!selectable},
            generateIds: false,
            classNames: {
                checkbox: "dynatree-checkbox",
                expanded: "dynatree-expanded"
            },
            selectMode: 3,
            children: {!JsonString},
            onSelect: function(select, node) {
                // Get a list of all selected nodes, and convert to a key array:
                var selKeys = $.map(node.tree.getSelectedNodes(), function(node){
                    return node.data.key;
                });
                jQuery(document.getElementById("{!$Component.selectedKeys}")).val(selKeys.join(", "));
                
                // Get a list of all selected TOP nodes
                var selRootNodes = node.tree.getSelectedNodes(true);
                // ... and convert to a key array:
                var selRootKeys = $.map(selRootNodes, function(node){
                    return node.data.key;
                });
            },
        });
    });
    </script>
    
    <!-- Add a <div> element where the tree should appear: -->
    <div id="tree"> </div>

</apex:component>

You can see a working demo of the functionality here: http://treeview-developer-edition.ap1.force.com/

The code is available as unmanaged package (https://login.salesforce.com/packaging/installPackage.apexp?p0=04t90000000LlqQ) if you want to use it in your org. The code has been written assuming positive use cases and exceptional situations have not much been handled. It is advised to review and tweak the code before you use it in your org.

Dec 28, 2011

Fetching sObject Type From Record Id

"How I can get the sObject Type from Record Id?". In many requirements I do have 15-digit or 18-digit Id and I want to know the sObject to which the Id belongs to. Answer is using Global Describe we can get the sObject type.


Am getting too many emails from folks from Community Forums regarding this, so here is the generic method which will help all.


public class KeyPrefix
{
    // map to hold global describe data
    private static Map<String,Schema.SObjectType> gd;
    
    // map to store objects and their prefixes
    private static Map<String, String> keyPrefixMap;

    // to hold set of all sObject prefixes
    private static Set<String> keyPrefixSet;
    
    private static void init() {
        // get all objects from the org
        gd = Schema.getGlobalDescribe();
        
        // to store objects and their prefixes
        keyPrefixMap = new Map<String, String>{};
        
        //get the object prefix in IDs
        keyPrefixSet = gd.keySet();
        
        // fill up the prefixes map
        for(String sObj : keyPrefixSet)
        {
            Schema.DescribeSObjectResult r =  gd.get(sObj).getDescribe();
            String tempName = r.getName();
            String tempPrefix = r.getKeyPrefix();
            keyPrefixMap.put(tempPrefix, tempName);
        }
    }
    
    public static String GetKeyPrefix(String ObjId)
    {
        init() ;
        String tPrefix = ObjId;
        tPrefix = tPrefix.subString(0,3);
        
        //get the object type now
        String objectType = keyPrefixMap.get(tPrefix);
        return objectType;
    }
}


Now how you can use this? Simply save the class and pass your object Id in method "GetKeyPrefix" like this 


//a0090000002QGKu will be your object Id
System.debug('::::::: '+ KeyPrefix.GetKeyPrefix('a0090000002QGKu') );


And it will return you the object API name.

Dec 8, 2011

Case Assignment Rules From Apex - Database.DMLOptions

This is really very important for users who use native cases with assignment rules.


I have assignment rule defined on Case which says if the "Status" of case is "New" then assign that case to "System Admin" (User in my organization)




Now when I insert a case manually with "Assignment using active assignment rules" checkbox checked then assignment rule works fine, and "System Admin" is assigned as the Case Owner.


Now I want to create the case from my apex code and also want assignment rules to be triggered. But the problem is we are not able to access the "Assignment using active assignment rules" checkbox in apex. Now am stuck because there is a lot of code already written for case insertion, so what is the solution for this?


This code :
Case newCase = new Case(Status = 'New') ;
insert newCase ;


inserts the case but assignment rules are not triggered so case owner in my scenario remains "Ankit Arora" and expected was "System Admin"






Solution is using Database.DMLOptions. Now before inserting the case I use Database.DMLOptions and with my case insertion assignment rules are also triggered, this is all what I want.


When I use this code :
//Fetching the assignment rules on case
AssignmentRule  AR = new AssignmentRule();
AR = [select id from AssignmentRule where SobjectType = 'Case' and Active = true limit 1];

//Creating the DMLOptions for "Assign using active assignment rules" checkbox
Database.DMLOptions dmlOpts = new Database.DMLOptions();
dmlOpts.assignmentRuleHeader.assignmentRuleId= AR.id;

Case newCase = new Case(Status = 'New') ;
newCase.setOptions(dmlOpts);
insert newCase ;


It inserts the case and my assignment rule which is in active state get fired and Case Owner becomes "System Admin"



Jun 27, 2011

Display Picture in Contact

What if I want to display my image on contact, and I frequently change it also. The answer in general is, create a visualforce page page and override it to native contact detail page.


This doesn't sounds good to me, am not happy overriding native pages provided by Salesforce. So I finally decided to put my head together and think how I can do this in another way.


Idea came in mind that what if I use Notes and Attachments on Contact object. Yes! I have uploaded some of my pictures in Notes and Attachment and provided a button on detail page to select one of them.




When you click on the "Set Profile Picture" a pop up gets open where all valid images are shown with radio option as shown in above screen. Selected image is displayed on contact like this,



How can you implement this?? Follow some simple steps.

Make sure you copy all Labels, Names etc correctly.


1) Create field of Type : "Text" with Field Label : "StoreImageUrl" and Field Name : "StoreImageUrl" (Lenght - 255) -> Make it visible to all profiles but do not show this on any page layout. This is for internal use only.


2) Create another field of Type : "Formula" with Field Label : "&nbsp" and Field Name : "Image" with return type "Text"


Formula will contain
IF( StoreImageUrl__c != null , IMAGE( StoreImageUrl__c , '' , 100 , 100 ) , IMAGE('' , '' , 0 , 0))
Make it visible for all profiles and show it at top on all layouts.


3) Create an Apex Class
public class DisplayImagesController
{
    //Parent Contact
    private String ContactId ;
    //Image selected from UI
    public String selectedImage {get; set;}
    
    public DisplayImagesController()
    {
        //Fetching contact Id
        ContactId = ApexPages.CurrentPage().getParameters().get('Id') ;
        selectedImage = '' ;
    }
    
    public boolean validateImage(String image)
    {
        String Regex = '([^\\s]+(\\.(?i)(jpg|png|gif|bmp))$)';
        Pattern MyPattern = Pattern.compile(Regex);
        Matcher MyMatcher = MyPattern.matcher(image);
        return MyMatcher.matches() ;
    }
    
    public List<SelectOption> getItems()
    {
        List<SelectOption> options = new List<SelectOption>(); 
        
        //All attachments related to contact
        List<Attachment> attachLst = [SELECT Id , Name FROM Attachment WHERE ParentId =: ContactId] ;
        
        //Creating option list
        for(Attachment att : attachLst)
        {
            String imageName = att.name.toLowerCase() ;
            if(validateImage(imageName))
            {
                options.add(new SelectOption(att.Id , att.Name));
            }
        }
        
        return options ;
    }
    
    public PageReference SaveImage()
    {
        //Contact to update
        List<Contact> conToUpdate = new List<Contact>() ;
        conToUpdate = [select id,StoreImageUrl__c from contact where id =: ContactId] ;

        //Inserting image parth
        if(conToUpdate.size() > 0)
        {
            conToUpdate[0].StoreImageUrl__c = 'https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=' + selectedImage ;
            update conToUpdate[0] ;
        }
        return null ;
    }
}


4) Create a visualforce page with Label and Name "DisplayImages"
<apex:page id="pg" controller="DisplayImagesController" sidebar="false" showHeader="false">
<script>
    function validateRadio()
    {
        var elLength = document.getElementById('pg:frm:pb:pbs:pbsi:selectR');
        var childInputElements = elLength.getElementsByTagName('input');
        var flag = false ;
        for(var i=0; i < childInputElements.length; i++)
        {
            if(childInputElements[i].checked == true)
                {
                    flag = true ;
                    break
                }
        }
        if(!flag)
        {
            alert('Please select one Picture') ;
            return false ;
        }
        else
        {
            Validate() ;
        }
    }
</script>
<apex:form id="frm">
    <apex:actionFunction name="Validate" action="{!SaveImage}" onComplete="alert('Picture is Applied'); window.close();" reRender=""/>
    <apex:pageBlock id="pb" title="Contact" tabStyle="Contact">

        <apex:pageBlockButtons >
            <apex:commandButton rendered="{!IF(Items.size > 0 , true , false)}" value="Save" onClick="return validateRadio() ;"/>
            <apex:commandButton value="Cancel" onClick="window.close() ;"/>
        </apex:pageBlockButtons>

        <apex:pageBlockSection id="pbs" title="All Images" rendered="{!IF(Items.size > 0 , true , false)}">
            <apex:pageBlockSectionItem id="pbsi">
                <apex:selectRadio id="selectR" value="{!selectedImage}">
                    <apex:selectOptions id="MyRadio" value="{!Items}" />
                </apex:selectRadio>
            </apex:pageBlockSectionItem>
        </apex:pageBlockSection>

        <apex:pageBlockSection rendered="{!IF(Items.size > 0 , false , true)}">
            <apex:outputLabel value="No attachments to display or there is no valid image in attachments."/>
        </apex:pageBlockSection>

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


5) Create a Detail Page button with Label : "Set Profile Picture" and Name : "Set_Profile_Picture", Behavior : "Execute Java Script" and Content Source : "OnClick JavaScript"
window.showModalDialog('/apex/DisplayImages?Id={!Contact.Id}','') ; 
window.location.href = window.location.href ;


6) Edit the contact layout and add "Set Profile Picture" button.


7) Create a contact and add some images in notes and attachments, click on "Set Profile Picture". Select the image and click save.


*Note : I hate using JS with IE.


Also you may have to change a line of the Apex code above : "https://c.ap1.content.force.com/servlet/servlet.FileDownload?file=' + selectedImage ;"


in "SaveImage()" method.


Above path can be different on different organizations, so you just need to make sure that this is correct by :
View the attachment uploaded and copy the path from the URL except the ID.You can also create a custom setting to store this path.


Once above steps are done we are all set to go.


Cheers.