Xintong Zhang
Technology. Evangelism. Salesforce. Give Back.
Sunday, May 22, 2016
Thursday, March 3, 2016
Lovely Coding Club
You think you are looking at a crazy group? Nope, we are Girls Who Code Millbrae Club. We play hard and work hard! We break things and fix them quick!
I’m a engineer at Salesforce and my dream of becoming a teacher led me to teach at Girls Who Code Club. First time getting into the classroom with 20+ middle and high school girls was intimidating. I was so worried about whether the girls will hate me and walk away. This ended up not happening. We had a great time exploring variables, loops and other computer knowledge by building little fun python projects together. We meet every Saturday and now we are halfway through the semester. The girls are still eager to learn and code!
I can’t express in words how exciting I feel to work with the girls. They are so smart and passionate. I enjoy every aha moment when they shout out excitedly “Aha. I’ve got it!”. That makes me rethink why I got into engineering field at the first place and cherish my current job more.
I have no doubt that there will be a future “Grace Hopper” and “Marissa Mayer” among the girls. Way to go girls. Halfway though. You got it!
I have no doubt that there will be a future “Grace Hopper” and “Marissa Mayer” among the girls. Way to go girls. Halfway though. You got it!
Thursday, February 25, 2016
Things to watch out for when using Visualforce in Lightning Experience
Lightning Experience is a revolutionary and exciting change for Salesforce. Because Lightning Experience has some differences when compared to the Classic UI, you will need to account for certain things when switch. Here are a couple of things to watch out for to make your Visualforce implementations work in Lightning Experience.
- After the 2016 Spring release, inline Visualforce not only shows on View pages, but also shows on Create/Edit/Clone pages in Lightning Experience. This is a feature many Customers have asked for. It’s finally live! But will it work with your implementation?
If your current implementation relies on data from a View page, then it might break. For example, if your VF assumes recordId exists and doesn’t do null checks for recordId, then Create/Edit/Clone pages would fail.
Here’s a code example.
1. Create an Apex class to update the description field of the current record.
public class test{
public String currentRecordId {get;set;}
public test(ApexPages.StandardController controller) {
currentRecordId = ApexPages.CurrentPage().getparameters().get('id');
}
public void updatecurrentrecord(){
try {
Case caseToUpdate =
[SELECT Description FROM Case
WHERE id = :currentRecordId
LIMIT 1];
caseToUpdate.Description = 'test description';
update caseToUpdate;
} catch(DmlException e) {
System.debug('An unexpected error has occurred: ' + e.getMessage());
}
}
}
2. Create a VF page to display the currentrecordid and call updatecurrentrecord function from above apex class.
<apex:page standardController="Case" extensions="test" action="{!updatecurrentrecord}">
<h2 style="background-color:rgb(255,0,0)">
This is your new Case VF Page
<apex:form >
<apex:pageBlock >
<apex:pageBlockSection title="Current case record Id is : {!currentRecordId}" collapsible="false">
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</h2>
</apex:page>
3. Add the VF page to the Case object layout.
4. Make sure you are in Lightning Experience.
On the View page, you will see the inline VF page generated correctly.
However on the Create page, you will see the following error, because currentRecordId is null
The takeaway is: always write “safe” code with error handling and empty checks. Please be aware of this change and check your existing implementations.
- Another change for Lightning Experience is that you can no longer override standard/custom Tabs with the same object's list action. In other words, you can not override Account Tab with Account List action.
1. Create a test Apex class with the following code for redirection.
public class test {
public test(ApexPages.StandardSetController controller) {}
public test(ApexPages.StandardController controller) {}
public PageReference init(){
String url='/006';
PageReference pr = new PageReference(url);
pr.setRedirect(true);
return pr;
}
}
<apex:page standardController="Opportunity" extensions="test" recordSetVar="opportunities" action="{!init}"> </apex:page>
3. Override Opportunity Tab with the above VF page.4. Switch to Lightning Experience, click on Opportunity Tab. You will see this error.
“This page isn't available in Salesforce Lightning Experience or Salesforce1.”
The reason behind this behavior is that in Lightning Experience, Object Tab and Object List actions are combined into one page. After clicking on the Opportunity Tab, you are already at the Opportunity List page. So you are not allowed to redirect to the same page again.
Interestingly, you can override Opportunity Tab with the Account List or override any other Opportunity actions with the Opportunity List action. For example, if you change the PageReference in Apex class to “/001”, clicking on the Opportunity Tab will take you to the Account List page.
Routing in Lightning Experience is different than Classic UI. Even though your implementation works in Classic, make sure you still test it out in Lightning Experience. Branch off code between Classic and Lightning Experience with sforce.one if necessary. Make sure you read through limitations of Visualforce in Lightning Experience.
Wednesday, December 2, 2015
Salesforce Activities in a Nutshell
What is Activity?
Activities in Salesforce are the actions you take, for example calls, emails, to-dos.
Activities consist of tasks and events.
Tasks are your to-dos. It has a due date, but not a due time.
Events are events on your calendar. It has both due date and due time. All-day event is an exception, because it doesn’t have a due time.
Although tasks and events are two different standard objects, they are mostly the same behind the scene. You can take them as different record types of activities.
"Who" and "What"
"Who" is the "Name" field of an activity. It refers to a person, ex. contact or lead. What of an activity is the “related to” field. It refers to an object type things, ex. account, opportunity, product ...
If Mary has a task "Grab coffee with David from Coca Cola." Then activity "Owner" is Mary. "Who" is contact David. "What” is the account David coming from "Coca Cola".
Activity’s Parent
Activities have "Who", "What" and "Owner" fields. Which one is activity's parent?
The short answer is both "Who" and "What".
Please note that the "parent" relation here is not "master-detail" or "lookup" relationship. Definition of "parent" here is the following:
1. Deleting the "parent" objects will cascade delete the activity.
2. Parent field is not mandatory. An activity can have null "Who" and "What".
3. Sharings of child activities are determined by parents.
Most other Salesforce standard objects have 1 and only 1 parent. However, activity can have multiple parents.
Activities Sharing
Sharing for activities is different from sharing of all other entities. (Thanks Peter Wu for insights about activities sharing.)
When "Org Wide Sharing Settings" is set to the following:
"Controlled by Parent"
1. 1 "What", 0 "Who": User must have access to the What to see the activity
2. 0 "What", 1 "Who": User must have access to at least 1 Who to see the activity
3. 1 "What", n "Who": User must have access to both the What and at least 1 Who to see the activity.
"Private"
Private sharing for Activities, is actually better named as "Has read-only access if have access to Parents." It's not true private sharing.
“Shared Activities” Perm
Out of the box, one activity can only be associated with 1 "Who". However, turning on "shared activities" allows an activity being associated with multiple "Who"s. For example, if "Mary grab coffee with David and Peter". Then this task is associated with two contacts "David" and "Peter".
When shared activity is enabled, an activity can be “parented” to 1 “What” and up to 50 “Who”s at the same time.
Keep in mind that disable "Shared Activities" is a destructive process. It only keeps the "primary Who" association and removes all extra "who" relationships. Please be very careful about whether you wanna turn on "Shared activities". Never turn off "shared activities" by yourself. Inquire Salesforce support about the risk.
Recurring Events
Creating a recurring event will create a event series and multiple occurrence records. To modify the series events, you must modify the series rather than an occurrence of the event.
When "Advanced Calendaring" perm is on, orgs will see two new columns added to the standard event object. "IsException" and "OriginalInstanceDateTime". Once an occurrence’s "IsException" field is set to true, modify the series will not modify this occurrence. Keep in mind that an occurrence is marked as exception if you modify the subject of this occurrence.
API Tables
In workbench, there are Task and Event tables.
If you turned on “Shared Activities” preference, two new tables "TaskRelation" "EventRelation" will be added to keep track of the "What", "Who" and "Invitee" relationships.
Activities Archiving
Closed activities due one year ago are automatically archived by Salesforce. Once activities are archived, users should not see them from UI or API. ( System Admin is an exception.) However, Customer can still access these activities via Data export files. A side note for API, query() call doesn’t return archived activities, queryAll() call does.
Saturday, October 31, 2015
Wanna Sync Google Calendar Event to Salesforce? DIY!
Have you ever wanted to push your Google Calendar event to Salesforce? Do you want to save at least $10 per user per month on purchasing apps? Here’s the recipe to build a simple solution yourself.
Prerequisite:
- A login to Salesforce org with Admin access privilege. Your profile needs to be API enabled.
- Create a security token to be used in the app. (You can use an existing token if you have one.) Your name > My Settings > Personal > Reset My Security Token.
The tutorial includes 4 steps:
- Create a Google Standalone App Script following. https://developers.google.com/apps-script/guides/standalone
- Connect your script to your Salesforce account.
- You need to configure Connected apps in setup menu to create a new remote access application. ( See details in tutorial below.)
- OAuth (Username and Password Flow) will be implemented for API authorization. Keep in mind that embed your username and password in application is not a good practice. OAuth(Refresh Token Flow) is more secure. We are not able to demonstrate this in the tutorial because, refresh token flow will prompt a page for user to approve Google accessing Salesforce Data. Google Standalone App Script doesn’t allow prompting a dialog/page.
- Pull calendar event from Google Calendar via Calendar API.
- Create event record in Salesforce via REST API.
Tutorial:
1. Create a standalone app script with a simple hello world function. Publish to web app. Copy the “Current Web App URL”. We will paste this to Salesforce later.
2. Now we want to whitelist the app you just created and let Salesforce know where to make callback to. Create a new Connected App from Salesforce > Setup > Connected Apps. Paste the “Current Web App URL” in OAuth “Callback URL” section. Configure Selected OAuth Scopes according to your own need.
Wait for 2-10 mins, go back to the app you just created. You will find “Consumer Key” (client_id) and “Consumer Secret” (client_secret). They will later be embeded in your request to Salesforce.
Manage OAuth policies of this app. Set “Relaxation” to “Relax IP Restrictions”.
Then copy and paste the source code to your own project. Put your User Settings in.
SalesforceConnectUtil.gs handles authorization with Salesforce.
//Salesforce Authorizatin Endpoint
var AUTHORIZE_URL = 'https://login.salesforce.com/services/oauth2/token';
//PUT YOUR USER SETTINGS HERE
var CLIENT_ID = 'REPLACE_WITH_YOUR_CLIENT_ID';//Consumer Key
var CLIENT_SECRET='REPLACE_WITH_YOUR_CLIENT_SECRET';//Consumer Secret
var USERNAME = 'REPLACE_WITH_YOUR_USERNAME';
var PASSWORD = 'REPLACE_WITH_YOUR_PASSWORD_SECURITY_TOKEN';//password+securitytoken
function loginToSalesforce(){
var token = PropertiesService.getScriptProperties().getProperty('token');
//If user doesn't have a token
if(!token){
//Request access token using username and password
var response = requestService().getContentText();
//Parse and store access token, instance_url
parseResponse(response);
}
}
function requestService(){
var payload = {
"grant_type" : "password",
"client_id" : CLIENT_ID,
"client_secret" : CLIENT_SECRET,
"username" : USERNAME,
"password": PASSWORD
};
var options = {
"method": "post",
"payload": payload
};
return postToURL(AUTHORIZE_URL, options);
}
function parseResponse(r){
//Parse Response
var tokenResponse = JSON.parse(r);
//store token and instsanceURL
PropertiesService.getUserProperties().setProperty( 'instance_url', tokenResponse.instance_url);
PropertiesService.getUserProperties().setProperty( 'token', tokenResponse.access_token);
}
3. Retrieve calendar event from Google Calendar using Calendar API. Note that to keep it simple, the sample code only fetches 1 calendar event.(The 1st event in the future) It’s a simple modification to retrieve multiple events at a time. Be aware if you want to send multiple records to Salesforce, REST API will not be the best choice. Consider using SOAP API or Bulk API in step 4.
//Google Calendar API: https://developers.google.com/google-apps/calendar/v3/reference/events/list
function retrieveCalendarEventFromGoogle() {
var calendarId = 'primary';
var optionalArgs = {
timeMin: (new Date()).toISOString(),//Lower bound (inclusive) for an event's end time to filter by.
showDeleted: false,
singleEvents: true,
maxResults: 1,//Maximum number of events returned on one result page.
orderBy: 'startTime'
};
var eventList = Calendar.Events.list(calendarId, optionalArgs).items;
if(eventList.length > 0 ){
return eventList[0];
}
}
4. Now, it’s time to create Event in Salesforce with REST API.
function sendEventToSalesforce(event){
var url = getRestAPIEndpoint()+'/sobjects/Event/';
var payload = {
"Subject" : event.summary,
"Location" : event.location,
"Description" : event.description,
"StartDateTime" : Utilities.formatDate(new Date(), "GMT", "yyyy-mm-dd'T'hh:mm:ss'Z'"),
"EndDateTime" : Utilities.formatDate(new Date(), "GMT", "yyyy-mm-dd'T'hh:mm:ss'Z'")
};
var options = {
"method": "post",
"contentType" : "application/json",
"headers" : {
"Authorization" : "Bearer " + PropertiesService.getUserProperties().getProperty('token')
},
"payload" : JSON.stringify(payload)
};
var res = postToURL(url, options);
Logger.log(res.getContentText());
}
Finally, it’s time to celebrate your success. Create a future event on your Google Calendar, run the script and go back to Salesforce. Hurrah! A brand new event just get created!
Reference:
https://www.youtube.com/watch?v=9SEAmNDtlcA&list=PL68F511F6E3C122EB
Subscribe to:
Posts (Atom)