Category Archives: Apex

Sortable, searchable table with pagination using knockout.js in Salesforce

This is a demo using knockout.js to create a table for listing records of any object custom, standard or combinations of data from multiple objects.  The table is sortable and searchable using a typeahead search field.  It also includes a dropdown to limit the number of records visible as well as pagination.

This is what it looks like in its most basic styling:

This is the link to the working demo

 

This is the component that holds the knockout code:

This is the controller to retrieve the records using a remote action:

The visualforce page:

 

Get and pass date from Controller and pass to knockout

Javascript remote on site:

Visualforce.remoting.Manager.invokeAction(
  	'{!$RemoteAction.ApplicationController.getStudentInfo}',    
  	    function(student){                        	                        	                       	                                                                              		
    		Application.studentLastName(student.LastName); 
    		Application.studentFirstName(student.FirstName);
    		Application.studentPhone(student.Phone);
    		Application.studentAltPhone(student.OtherPhone);
    		Application.studentEmail(student.Email);   		
    		Application.studentHomeAddress(student.MailingStreet);			    		
    		Application.studentCity(student.MailingCity);			    		
    		Application.studentZip(student.MailingPostalCode);
    		Application.studentCountry(student.MailingCountry);
    		if(student.Birthdate != null){
    			var dob = normalizeDate(student.Birthdate);
	    		var dobAlt = normalizeDate2(student.Birthdate);
	    		Application.studentDOB(dob);
	    		document.getElementById('studentDOB_alternate').value = dobAlt;
	    }
  	},{escape : false});

Script to convert date from controller to mm-dd-yyyy

function normalizeDate(dateVal){ 
	mydate = new Date(dateVal);
    data = new Date(mydate -  mydate.getTimezoneOffset() * 60000);
    var d = mydate.getDate()+1;
    var m = mydate.getMonth()+1;
    var y = mydate.getFullYear();
    return (m<=9?'0'+m:m) +'-' + (d<=9?'0'+d:d)+'-' + y;		
}

Script to convert date from controller to yyy-mm-dd to send back to controller

function normalizeDate2(dateVal){ 
	mydate = new Date(dateVal);
    data = new Date(mydate -  mydate.getTimezoneOffset() * 60000);
    var d = mydate.getDate()+1;
    var m = mydate.getMonth()+1;
    var y = mydate.getFullYear();		    
    return y + '-' + (m<=9?'0'+m:m) +'-' + (d<=9?'0'+d:d);		
}

Knockout send to controller

self.goToParentSection = function(){
    			if (studentInfoValidation().length === 0) {		
					var parentIsEdit = self.parentIsEdit();
					var studentDetails = {
						representativeId : self.representativeId(),						
						applicationId : self.applicationId(),										
						studentFirstName : self.studentFirstName(),
						studentLastName : self.studentLastName(),
						studentDOB : (j$('#studentDOB_alternate').val()),
						studentPhone : self.studentPhone(),
						studentOtherPhone : self.studentAltPhone(),
						studentEmail : self.studentEmail(),
						studentCountry : self.studentCountry(),
						studentHomeAddress : self.studentHomeAddress(),
						studentCity : self.studentCity(),
						studentState : self.studentState(),
						studentZip : self.studentZip(),
						studentId : self.studentId(),
						studentInfoComplete : self.studentDetailsComplete(),
						householdId : self.householdId()																																	
					}
					Visualforce.remoting.Manager.invokeAction(
	                	'{!$RemoteAction.ApplicationController2.studentInfo}',studentDetails,   
	                        function(results){                        	
	                        	self.studentId(results.Id);	                            				                
	                        },{escape : false});
																
				}
				else {
		            alert('Please check your submission.');
		            studentInfoValidation.showAllMessages();
		        }		        					
			}			

Controller

@RemoteAction
    public static object studentInfo(Map<string, string> studentDetails){
		Date dt = Date.valueOf(studentDetails.get('studentDOB'));  
        studentId = studentDetails.get('studentId');
		Contact studentToUpdate = [SELECT id,AccountId, Name FROM Contact WHERE Id =: studentId];
		string contactRecordTypeId;    		
		RecordType rt = [select Id, Name from RecordType where DeveloperName = 'Student_Master' and SobjectType = 'Contact' limit 1];
		contactRecordTypeId = rt.Id;
		studentToUpdate.RecordTypeId = contactRecordTypeId;
		studentToUpdate.LastName = studentDetails.get('studentLastName');
		studentToUpdate.FirstName = studentDetails.get('studentFirstName');
		studentToUpdate.Phone = studentDetails.get('studentPhone');
		studentToUpdate.OtherPhone = studentDetails.get('studentOtherPhone');
		studentToUpdate.Email = studentDetails.get('studentEmail');   		
		studentToUpdate.MailingStreet = studentDetails.get('studentHomeAddress');  		
		studentToUpdate.MailingCity = studentDetails.get('studentCity');
		studentToUpdate.MailingState = studentDetails.get('studentState');
		studentToUpdate.MailingPostalCode = studentDetails.get('studentZip');
		studentToUpdate.MailingCountry = studentDetails.get('studentCountry');    		
		studentToUpdate.BirthDate = dt;    			
		
		update studentToUpdate;

		return studentToUpdate;    
	}

Visualforce page with datepicker


	

Datepicker date ranges with alt date to pass to controller

<script type="text/javascript">      
    j$ = jQuery.noConflict();       
    j$(document).ready(function() {
        j$( "#visaExpirationDate" ).datepicker({
            yearRange: "c:+10",//today plus 10 years
            //yearRange: "-25:c",//today minus 25 years
            changeMonth: true,
            changeYear: true,
            numberOfMonths: 1,
            dateFormat:"mm-dd-yy",
            altField: "#visaExpirationDate_alternate",
            altFormat: "yy-mm-dd",
            showAnim: "slide"
        })
    });                     
</script> 

SFDC Randomizer

public class Randomizer {

     //returns a random Integer
     public static Integer getRandomNumber(Integer size){
          Double d = math.random() * size;
          return d.intValue();
     }

     //returns either true or false randomly
     public static Boolean getRandomBoolean(){
          if(math.mod(getRandomNumber(10),2) == 0){
               return true;
          }
          else{
               return false;
          }
     }

     //Get's a random value from a list of strings
     public static String getRandomString(List<String> strings){
          List<Double> ranks = new List<Double>();
          Map<Double,String> rankMap = new Map<Double,String>();

          for(String s : strings){
               Boolean isDup = true;
               Double rank;

               While(isDup){
                    Double x = getRandomNumber(100000);
                    if(!rankMap.containsKey(x)){
                         rank = x;
                         isDup = false;
                    }
               }

               ranks.add(rank);
               rankMap.put(rank,s);
          }

          ranks.sort();
          return rankMap.get(ranks.get(0));
     }

     //Returns a random picklist value 
     public static string getRandomPickListValue(Sobject s_object, String field_name, Boolean allow_blank){
          List<String> Strings = new List<String>();
          if(allow_blank){
               String b = '';
               Strings.add(b);
          }
            Schema.sObjectType sobject_type = s_object.getSObjectType();
            Schema.DescribeSObjectResult sobject_describe = sobject_type.getDescribe();
            Map<String, Schema.SObjectField> field_map = sobject_describe.fields.getMap();
            List<Schema.PicklistEntry> pick_list_values = field_map.get(field_name).getDescribe().getPickListValues();
            for (Schema.PicklistEntry a : pick_list_values) {
                Strings.add(a.getValue());
            }
            return getRandomString(Strings);
     }

     //returns a map of all picklists and multiselect picklists for a givien object
     //the keyset is the field name using proper case
     public static Map<String,List<String>> getPicVals(sObject s_object){
          Map<String,List<String>> valueMap = new Map<String,List<String>>();
          Schema.sObjectType sobject_type = s_object.getSObjectType();
          Schema.DescribeSObjectResult r = sobject_type.getDescribe();
          Map<String, Schema.SObjectField> field_map = r.fields.getMap();

          for(String s : field_map.keyset()){
               List<String> strings = new List<String>();
               Schema.DescribeFieldResult F = field_map.get(s).getDescribe();
               if(f.GetType() == Schema.DisplayType.Picklist || f.GetType() == Schema.DisplayType.MultiPicklist){
                    List<Schema.PicklistEntry> pick_list_values = field_map.get(s).getDescribe().getPickListValues();
                    for (Schema.PicklistEntry a : pick_list_values) {
                         strings.add(a.getValue());
                   }
                   valueMap.put(String.valueOf(field_map.get(s)),strings);
               }
          }
          return valueMap;
     }

     //Returns Lorem Ipsum placeholder text.
     public static String getPlaceholderText(Integer length){
          String firstSentence = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
          List<String> sentenceList = new List<String>();
          sentenceList.add('Vivamus nec lacus eget massa cursus pulvinar. ');
          sentenceList.add('Morbi vel odio eget nunc auctor posuere eget eget ante. ');
          sentenceList.add('Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. ');
          sentenceList.add('Pellentesque lacus eros. ');
          sentenceList.add('Sed suscipit tristique varius. ');
          sentenceList.add('Mauris ultricies, nibh eu fermentum accumsan, justo quam pulvinar tellus, sed tempor quam eros sit amet ante. ');
          sentenceList.add('Duis mi libero, cursus nec facilisis ut, commodo eget nunc. ');
          sentenceList.add('Nulla eros augue, iaculis sed volutpat et, sagittis quis sem. ');
          sentenceList.add('Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla placerat accumsan vulputate. ');
          sentenceList.add('Fusce placerat tellus eget tellus faucibus a gravida sapien fermentum. ');

          String s = firstSentence;

          while (s.length() < length+1){
               s += getRandomString(sentenceList);
          }

          s = s.trim();

          while (s.length() >= length-1){
               s = s.substring(0,s.length()-1).trim();
          } 

          s = s.substring(0,s.length()-1).trim();
          s += '.';
          return s;
     }

     static testMethod void testRandomizer() {
          test.startTest();
          Integer testInt = getRandomNumber(10);
          Boolean testBool = getRandomBoolean();
          List<String> testStringList = new List<String>();
          testStringList.add('one');
          testStringList.add('two');
          testStringList.add('three');
          String testString = getRandomString(testStringList);
          String testString2 = getRandomPickListValue(new Account(), 'Industry', true);
          String testString3 = getRandomPickListValue(new Account(), 'Industry', false);
          Map<String,List<String>> testMap = getPicVals(new Account());
          test.stopTest();
          String testString4 = getPlaceholderText(300);
     }
}

Override a page for some, but not all, users

Some of your users should use a custom Visualforce page, while others should use a standard Salesforce.com page.To override the Account tab with a Visualforce page for most of your users, but send users with the “System Administrator” profile to the standard Salesforce.com Account home page:

  1. Create a Visualforce page called conditionalAccountOverride.
    • Click Setup | Develop | Pages.
    • Click New.
    • In the label field, enter Override the Account Page for Most Users.
    • In the name field, enter accountOverride.
    • In the description, enter This page will display for all users, except System Administrators, when they click the Account tab.
    • In the editor, enter the following markup:
<apex:page action=
  "{!if($Profile.Name !='System Administrator', 
    null, 
	urlFor($Action.Account.Tab, $ObjectType.Account,
	null, true))}"
  standardController="Account" 
  recordSetVar="accounts" 
  tabStyle="Account">

  <!-- Replace with your markup -->  
	
  This page replaces your Account home page for 
  all users except Administrators.
</apex:page>
    • Click Save.

2. Set the page level security to allow all users to view the page.

    • Click Security for the page you just created.
    • Select all the profiles that will be using this page in the Available Profiles list and click Add to add them to the Enabled Profiles list.
    • Click Save.

3. Create an override that directs users to your new page.

    • Click Setup | Customize | Accounts | Buttons and Links.
    • In the Standard Buttons and Links list, click Override for the Accounts Tab.
    • Set the content type to Visualforce Page.
    • From the Content Name drop-down list, select conditionalAccountOverride.
    • Click Save.

This solution uses the action attribute on the apex:page component to test the user’s profile. Using the action attribute is a good way to ensure an action is taken when the page loads. If instead of limiting the standard page to a particular group of users, you want to limit the override to a particular group of users, such as all “Marketing Users” and “Solution Managers,” you need to create a controller extension. To override the Account tab with a custom Visualforce page only for users with the “Marketing User” or the “Solution Manager” profile:

  1. Create a Visualforce page called standardAcctPage.
    • Click Setup | Develop | Pages.
    • Click New.
    • In the label field, enter Override the Account Page for Most Users.
    • In the name field, enter standardAcctPage.
    • In the description, enter This page will display for all users, except Marketing Users and Solution Managers, when they click the Account tab.
    • In the editor, enter the following markup:
<apex:page standardController="account" extensions="overrideCon" action="{!redirect}">
  <apex:detail/>
</apex:page>

This page overrides your current Account home page. It uses a controller extension to test the user profile. If the user is a “Marketing User” or “Solution Manager,” they are redirected to a different page.

2. Using the procedure in step 1, create a second Visualforce page called customAcctPage:

<apex:page standardController="account">
  <h1>Override Account page for two profiles</h1>
  <apex:detail />
</apex:page>

This is the page that only the “Marketing Users” and “Solutions Managers” will see. They only get to this page through redirection.

3. Grant access to both pages for all profiles.

    • Click Security for the each of the pages you just created.
    • Select all the profiles that will be using this page in the Available Profiles list and click Add to add them to the Enabled Profiles list.
    • Click Save.

4. Create a controller extension called overrideCon.

    • Click Setup | Develop | Apex Classes.
    • Click New.
    • In the editor, add the following content:
public class overrideCon {
   String recordId;
   
public overrideCon(ApexPages.StandardController 
       controller) {recordId = controller.getId();}

public PageReference redirect() {
  Profile p = [select name from Profile where id = 
			   :UserInfo.getProfileId()];
  if ('Marketing User'.equals(p.name) 
	  || 'Solution Manager'.equals(p.name)) 
	  {
	   PageReference customPage =  Page.customAccountPage;
	   customPage.setRedirect(true);
	   customPage.getParameters().put('id', recordId);
	   return customPage;
	  } else {
		  return null; //otherwise stay on the same page  
	
	  }
   }
}
    • Click Save

5.  Create an override that directs users to the standardAcctPage page when they click on the Accounts tab.

    • Click Setup | Customize | Accounts | Buttons and Links.
    • In the Standard Buttons and Links list, click Override for the Accounts Tab.
    • Set the content type to Visualforce Page.
    • From the Content Name drop-down list, select standardAcctPage.
    • Click Save.

When a user clicks on the Account tab, if their profile is “Marketing User” or “Solution Manager” the controller extension will automatically redirect them to the customAcctPage.

 

Add support:clickToDial tag for Salesforce CTI integration

The support:clickToDial component lets you add a phone field to custom Visualforce detail and edit pages. When users in organizations with Open CTI or Salesforce CRM Call Center click on the phone field, the phone number is dialed automatically and connects to the SoftPhone.

  • The User must be logged into the CTI to use
  • The showHeader=”true” attribute must be set in the <apex:page> tag or ommitted altogether to enable
<div class="customer">
    <apex:outputLabel value="Phone: ">
        <support:clickToDial number="{!Lead.phone}" entityId="{!Lead.Id}" /> 
    </apex:outputLabel>            					
</div>	

Results:

Capture

Custom button to change record type and status in Salesforce

In Salesforce, “Record Types” are a way to arrange and classify data in an object. An example of howRecord Types can be particularly helpful is when you want to assign different sets of data to different groups of sales teams.

Whenever you’re working to customize or change something in Salesforce and need to reference Record Types, you must have the appropriate Record-Type ID. There are two ways to find them:

  • Go to the Record Type (Setup> Customize> (object)> Record Types). Click on the record type. Find the Record Type ID in the URL between id= and &type.

Alternatively, you can create a custom/formula field with the value Record-Type ID.

  • Go to Your Name> (appropriate object) Customize> Fields> under custom fields,
  • Click “New“.
  • Make sure you pick Formula as the type and then Formula Text and click Next.
  • You can label the field “Record Type“.
  • Finally, enter “RecordTypeid” under Advanced Formula tab.
  • Don’t forget to hit “Save“.
/* 
Created by: Mark Miller 
Last Update: 18 September 2015 

NOTES: 
- Updates selected quote record with a status of "PO Received" 
- Updates selected quote record with a record type of "PO Received" 
- Display Type is "List Button" 
- Behavior is "Execute JavaScript" 
- Content Source is "OnClick JavaScript" 
*/ 
{!REQUIRESCRIPT('/soap/ajax/34.0/connection.js')} //adds the proper code for inclusion of AJAX toolkit 

var q = new sforce.SObject('Quote'); 
q.id = '{!Quote.Id}'; 

q.Status = "PO Received"; 

q.RecordTypeId = '012j0000000zNVn' 
result = sforce.connection.update([q]); 

if(result[0].success=='false'){ 
alert(result[0].errors.message); 
}else{ 
location.reload(true); 
}

Using the !Case Logic Function

In the previous post I changed the image on one page depending on the referring page but that was an all or one situation.   I had a situation where there were mutiple orgs referring to one Visualforce page that needed a different logo image for each.  Instead of using an IF/ELSE statement I used the CASE logic function. The documentation states this for the use of this function:

CASE(expression,​value1, result1, value2,​result2,…,​ else_result) and replace expression with the field or value you want compared to each specified value. Replace each value and result with the value that must be equivalent to return the result entry. Replace else_result with the value you want returned when the expression does not equal any values.

This was a little tricky because it doesn’t state specifically if the values needed double quotes or single quotes(it didn’t work until I put in double quotes btw).  This is what it finally looked like:

<img  src="{!CASE($CurrentPage.parameters.SrcOrg, "orgOne", URLFOR($Resource.sourceZip, '/images/orgOne.png'),
            "orgTwo", URLFOR($Resource.sourceZip,'/images/orgTwo.gif'),
            "orgThree", URLFOR($Resource.sourceZip,'/images/orgThree.jpg'), 
            "orgFour", URLFOR($Resource.sourceZip,'/images/orgFour.gif'), 
            "orgFive",URLFOR($Resource.eFNOLpage,'/images/orgFive.jpeg'), 
            URLFOR($Resource.sourceZip,'/images/default.jpg'))}"   />