Teach Me Salesforce

A community approach to learning salesforce.com

Archive for the ‘Visualforce’ Category

Integrating Google Maps in Salesforce

with one comment

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>
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”.

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

 

Original Post by Forceguru : http://forceguru.blogspot.com/2012/01/integrating-google-maps-in-salesforce.html

Advertisements

Written by Ankit Arora (forceguru)

January 16, 2012 at 3:18 am

Displaying Role Hierarchy on Visualforce Page – Tree View

leave a comment »

This is a cross post from my Blog.

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 ListgetAllSubordinates (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  roleUsersMap;

    // map to hold child roles with parentRoleId as the key
    private static Map > parentChildRoleMap;

    // List holds all subordinates
    private static List 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 gd;

    // map to store objects and their prefixes
    private static Map keyPrefixMap;

    // to hold set of all sObject prefixes
    private static Set 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  */
    private static void init1() {
        // get all objects from the org
        gd = Schema.getGlobalDescribe();

        // to store objects and their prefixes
        keyPrefixMap = new Map{};

        //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  */
    private static void init2() {

        // Create a blank list
        allSubordinates = new List();

        // Get role to users mapping in a map with key as role id
        roleUsersMap = new Map([select Id, Name, parentRoleId, (select id, name from users) from UserRole order by parentRoleId]);

        // populate parent role - child roles map
        parentChildRoleMap = new Map >();        
        for (UserRole r : roleUsersMap.values()) {
            List tempList;
            if (!parentChildRoleMap.containsKey(r.parentRoleId)){
                tempList = new List();
                tempList.Add(r);
                parentChildRoleMap.put(r.parentRoleId, tempList);
            }
            else {
                tempList = (List)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 tempUsrList = new List();
            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 lst = new List();
            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 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 myUsers {get; set;}

        public List 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.

Written by Ankit Arora (forceguru)

January 16, 2012 at 2:59 am

JavaScript Remoting

with 2 comments

One of the major enhancements from visualforce this release(Summer 11) and my favorite is JavaScript remoting.

JavaScript Remoting is the ability to invoke apex class method from javascript embedded in a visualforce page.JavaScript Remoting is now available after summer 11 release.

Here is a quick example : we have a Apex class which defines a get Account method.

global with sharing class MyJSRemoting
{
    public static Account account { get; set; }

    @RemoteAction
    global static Account getAccount(String accountName) {
        account = [select id, name, phone, type, numberofemployees from
             Account where name = :accountName limit 1];
        return account;
    }
}

This method is having @RemoteAction annotation making it available to be called from a visualforce page. Now in our visualforce page we have a script tag set which is how we embed javascript within visualforce pages.

<apex:page controller="MyJSRemoting">

<script type="text/javascript">
function getAccountJS() {
    var accountNameJS = document.getElementById('accountname').value;

    ankit.MyJSRemoting.getAccount( accountNameJS, function(result, event)
    {
        if (event.status)
        {
           document.getElementById("{!$Component.theBlock.pbs.pbsi2.accId}")
                                  .innerHTML = result.Id;
           document.getElementById("{!$Component.theBlock.pbs.pbsi1.name}")
                                  .innerHTML = result.Name;
        }
    }, {escape:true});
}
</script>

    <input id="accountname" type="text">

    <button onclick="getAccountJS();">Get Account</button>

    <apex:pageblock id="theBlock">
        <apex:pageblocksection columns="2" id="pbs">

            <apex:pageblocksectionitem id="pbsi1">
                 <apex:outputtext id="name">
            </apex:outputtext></apex:pageblocksectionitem>

            <apex:pageblocksectionitem id="pbsi2">
                <apex:outputtext id="accId">
            </apex:outputtext></apex:pageblocksectionitem>

        </apex:pageblocksection>
    </apex:pageblock>
</apex:page>

This JavaScript code then invokes the getAccount method in the apex class to take action.

Please note I have used namespace “ankit.MyJSRemoting” as there is a namespace registered on my organization.

JavaScript Remoting is the feature which is primarily be used by developers and will allow them to create richer and more interactive user interfaces that everyone will benefit from.

In particular, lets developers create rich javascript based user interfaces by invoking apex controller methods within JavaScript code. JavaScript is typically used to render small parts of user interface. Result from a Javascript remoting call are much faster then using a typical visualforce reRender model, developers can provide more instant feedback to the user. This is because remoting calls are stateless. Means only data is set back and forth instead a lot of bulky HTML.

Additional Visualforce enhancements :

1) Filtered Lookup via Visualforce.
2) Inline editing for rich text area field.
3) Field Set property accessors.

This is a cross post to my Blog.

Written by Ankit Arora (forceguru)

June 19, 2011 at 4:33 pm

Visualforce Code Generator

with 9 comments

This is a cross post from my Blog Post.

We know how much important role visualforce plays in our application. There are many instance where we need to create a visualforce page and apex class. Salesforce provides powerful tools like workflows, approvals, validation etc. from which we can implement our business functionalities with just button clicks.

Now I was having a requirement where I need to create many visualforce pages, some are custom and some are cloning native page with native layouts with some small changes. This just clicked me to create a tool from which we can create a visualforce page code with just clicking buttons. Yes!! Visualforce page code with button click.

This saved a lot of time, as I do not need to write any thing to create a heavy visualforce page and amazingly it is done just using button clicks.

Install the package : https://login.salesforce.com/packaging/installPackage.apexp?p0=04t90000000Pqos

Just append “/apex/vfgenerator__codegenerator” in URL.

Now let me jump into the explanation what exactly I have done. A simple UI will be displayed, where user can select the desired type of page, object name, record type, email field.

Object Name : Valid object API name (include namespace if any)

Record Type : Record type Id of object selected in Object Name
Type of Page :

  1. Edit : Code will be generated for the edit page of the object (according to “record type” layout if selected any).
  2. Detail : Code will be generated for the detail page of the object (according to “record type” layout if selected any)
  3. Custom Detail : Code will be generated for detail page according to the fields selected from UI
  4. Custom Edit : Code will be generated for edit page according to the fields selected from UI

In the above screen I have selected “Edit” as the type of page and “Account” in object, also provided my email. When I click “Generate Code” it displays like this :

Just copy the code and paste it in new visualforce page. Why I have given my email id because when you paste the code in visualforce page it will loose all the formatting and will display in one single line, but code sent on email will come with formatting. So we can copy the generated code from email also. Now when you hit the newly created visualforce page it will display all fields in edit mode which are displayed on native edit page. Isn’t that good?

Now lets try creating some custom visualforce page. Select “Custom Edit” in “Type of Page” and “Account” in “Object Name”. It will display a section “Select Fields”. Click on “Display Fields”, it will display all fields which are up datable by present user.

Select some fields and click on  “Generate Code”. Again copy paste the code in your visualforce page, it will display selected fields in edit mode.

Now heavy visualforce pages are created with just button clicks.

Package provided is managed because the tool is under testing and I would like to have feedback from you all. Once it is free from all bugs I will provide the code.

Declaration: You may find similar post related to “Visualforce Code Generator”. The end result may be similar but there is a considerable difference in the approaches being followed here. So I, hereby declare that my project is entirely my own creation and has not been copied from any other person/organization’s words or idea. Please feel free to drop me an email at “arora.salesforce@gmail.com” if there is any disagreement.

Cheers

Written by Ankit Arora (forceguru)

June 15, 2011 at 2:32 pm

Visualforce- how to “stretch” columns in a table inside an apex:pageblocksection

with one comment

Using apex:pageblocksections are a great way to tidy up a page, and also a great look and feel for a vf page.

One issue in an apex:pageblocksection, is that the columns in a apex:datatable or apex:pageblocktable only seem to stretch halfway across the page, even if you specify the width.

<apex:pageBlockSection title="Diner Details">
 <apex:dataTable value="{!Mystery_Diner_Report__c}" var="mdr" width="100%" 
       columns="2" columnsWidth="60%,40%">
   <apex:column id="c7" headerValue="Account Name">
     <apex:outputField value="{!mdr.Account_Name__c}" />
   </apex:column>
 </apex:dataTable>
</apex:pageBlockSection>

The solution is to add a Column attribute into the apex:pageblocksection, and add a width attribute into the datatable or pageblocktable, e.g.::

<apex:pageBlockSection title="Diner Details" columns="1">
   <apex:outputPanel >
      <apex:dataTable value="{!Mystery_Diner_Report__c}" var="mdr" width="100%" 
            columns="2" columnsWidth="60%,40%">
        <apex:column id="c7" headerValue="Account Name">
           <apex:outputField value="{!mdr.Account_Name__c}" />
         </apex:column>
       </apex:dataTable>
    </apex:outputPanel>
 </apex:pageBlockSection>

Also posted on SalesCheck's blog http://salescheck.wordpress.com/

Written by SalesCheck (Charly)

May 7, 2011 at 9:24 am