Development

Here is an example of
1) how to query fields of an object and fields on records of a related object
2) how to loop over the queried records and the records of the related object in Apex

In the below example the Opportunity is the master of a master-detail relationship where Sub_Object__c is the detail. The task is to query fields and set these to values on both the opportunity and to the related Sub_Object__c records.

public void resetStates() {

// Create map of a list of opportunities.
Map theMap = getTheMap();

// List all opportunities except the selected one,
// i.e. the map excludes the selected opp.
List allOpps = [SELECT Id, Selected__c,
(SELECT id, Selected__c FROM Sub_Object__r) FROM Opportunity
WHERE Id IN: theMap.keySet()];

// Loop over the opportunities and set values
for (Opportunity o : allOpps) {
  o.Selected__c = false;

  // Loop over the records retrieved in the nested SELECT statement and set values
  // Do this for each opportunity
  for (Sub_Object__c so : o.Sub_Object__r) {
    so.Selected__c = false;
  }
}

// Update the db with the new values
update allOpps;

}

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

Modify multiple profiles in Salesforce

by Niklas Waller on January 26, 2012

in Salesforce

When you are working with profiles and access rights in a Salesforce organization or when you are developing new functionality or modifying existing, you will sooner or later need to modify the profiles.

Example:
Assume you have created new functionality which includes a new custom object. You will need to give the profiles access to this new object.

If you have a large organization there could be quite a lot of profiles. So start by creating a profile view that lists the profile settings that you want to modify. See this post for more info on how to do that.

So let’s look at the same screen shot as in the referred post.

Create profile view

 

Let’s say want to modify the delete access for all custom profiles

1. Start by selecting the check boxes for the custom profiles.

2. Then double-click in the column “Opportunity: Delete” in any of the selected rows.

3. You will see this screen

Modify profiles settings
The check box “Opportunity: Delete” is unchecked since this is what we want to do. In the middle you see what this change means, i.e. settings that will be disabled as a consequence.

At the bottom you can choose to perform the action for the row that you clicked on only or check “All 3 selected records” to perform the action on all custom profiles.

You will be redirected back to the profile view and can instantly see that the custom profiles have lost their delete access to the Opportunity object.

I have written about this before, but I think this is more detailed and easier to read.

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

Creating profile views in Salesforce

by Niklas Waller on January 19, 2012

in Salesforce

It happens quite often both as an admin and as a developer that you need to check profile access. One way to do this is to open each profile one at a time to check for for example each profile’s access to a specific object. This could be a quite tiresome work especially if you have a large and complex org with many profiles.

At these times I always create a profile view which gives me a view of exactly what I want to see all at once. And its very simple to do too.

So here’s how to do it:

1. First make sure that you can create profile list views. Goto Administration Setup -> Customize -> User Interface. The checkbox ‘Enable Enhanced Profile List Views’ should be checked.

Enable Enhanced Profile List Views

2. Open up the User Profiles view (Administration Setup -> Manage Users -> Profiles). All profiles are listed here and you can see the default view ‘All Profiles’.

3. Now, if you want to see the profile’s access rights for the opportunity object for example you start by creating a new view. Click ‘Create New View’.

4. Name the view something, e.g. ‘Opportunity Access’.

5. Filters can be added, this is optional though. But assume you have a lot of custom profiles that are prefixed with “MyCompany: “. If I only want to see them I can add the filter with Setting “Profile Name” starts with “MyCompany: “.

6. In step 3 you specify what you want to see in the view. In this case add:
Opportunity: Read
Opportunity: Create
Opportunity: Edit
Opportunity: Delete

7. Now choose the new view ‘Opportunity Access’ in the select list of the profile page. As you can see you get a good overview and can make further decisions based on this.

Create profile view

 

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

On a visualforce page I want to display notes  from an opportunity. To accomplish this I need a VF-page and an extension class (apex).

Part of VF-page that displays the notes:

<apex:outputPanel>
 <apex:pageBlock title="Notes" mode="maindetail">
  <apex:pageBlockTable value="{!MainOppNotes}" var="n">
   <apex:column headerValue="Action" width="5%">
    <apex:commandLink action="{!viewNote}" id="viewNote"
value="View" styleClass="actionLink">
     <apex:param name="noteId" assignTo="{!noteId}" value="{!n.Id}" />
    </apex:commandLink>
   </apex:column>
   <apex:column headerValue="Name" width="65%">{!n.Title}</apex:column>
   <apex:column headerValue="Last Modified" width="30%">
    <apex:outputtext value="{!n.LastModifiedByName}" />,&nbsp;
    <apex:outputtext value="{!n.LastModifiedDateStr}" />
   </apex:column>
  </apex:pageBlockTable>
 </apex:pageBlock>
</apex:outputPanel>

The getMainOppNotes method from the extension class:

// Get notes from the main opportunity.
public List<RetObject> getMainOppNotes() {
 List<RetObject> rtObjs = new List<RetObject>();
 RetObject rt = new RetObject();
 List<Note> mainOppNotes = 
[select Id, Title, LastModifiedDate, LastModifiedById 
from Note where ParentId = :mainOppId];

 for (Note n : mainOppNotes) {
  rt = new RetObject();
  rt.Id = n.Id;
  rt.Title = n.Title;
  rt.LastModifiedDate = n.LastModifiedDate;
  rt.LastModifiedByName = userMap.get(n.LastModifiedById);
  rtObjs.add(rt);
 }
 rt = null;

 return rtObjs;
}

In the method above there is first a SOQL-query to get all notes for a specific opportunity. I want to return Id, Title, LastModifiedDate and LastModifiedBy name for the Note. Since Note has a field LastModifiedById we can only get the id which is not very readable.

A solution to this is to create an wrapper class to the extension class which can be used as an internal object that can hold all the values I wan’t to return to the VF-page. In this case it is called RetObject.

For each Note an instance of RetObject is created and put in a list of the RetObject type. The method is set to return a list of this type and that is what is done when the looping has finished.

The RetObject type contains 4 fields. One is the LastModifiedByName. All fields except this field get their values from a Note. The LastModifiedByName field gets its value from the user map. The usermap consists of a map of type String and Id and contains all active users in the org. This way we can do a quick lookup using the LastModifiedById from the Note.

The user map is created in the constructor and is defined as a private class attribute:

// Create user map
List<User> users = [select id,name from User where isActive = true];
userMap = new Map<ID, String>();

for (User u : users) {
 userMap.put(u.id, u.name);
}

The wrapper class looks like this:

// Wrapper class. Used to return a set of information to 
// display to the user on the VF-page
public class RetObject {
 public RetObject() {}

 public ID Id {get; set;}
 public String Name {get; set;}
 public String Title{get; set; }
 public Datetime LastModifieddate {get; set; }
 public String LastModifiedDateStr {get {
  return LastModifieddate.format('yyyy-MM-dd hh:mm');
 } set; }
 public String LastModifiedByName {get; set;}
}

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

In the former post I briefly described the Cloud-Based Flow Designer. In this article I will show how to create a simple flow using the Cloud-Based Flow Designer in Salesforce with Apex plug-ins. The plug-in will take a name from the user and display it back backwards.

Create the flow
  • Start by creating a flow (App Setup -> Create -> Workflow & Approvals -> Flows).
  • You can see all your flows in this list. Press ‘New Flow’. If you already have one, press ‘Open’.
Design the flow
The start screen
  • Drag a screen element from the Palette to the canvas.
  • Name it (Get Name).
  • Add a field (Name, textbox).
  • Set it as the start element in the flow. Click the arrow in the upper right corner so that it becomes green.
The end screen
  • Drag a screen element from the Palette to the canvas.
  • Name it (Result).
  • Click on ‘Display Text’ on the ‘Add a Field’ tab. This is the output text field of the screen. Note the name of this field. Mine is named ‘NameBackwardsResult’.
  • As the output, select ‘SCREEN INPUT FIELDS’ as resource and choose the already defined ‘Name’ field.
  • It should now say ‘{!Name} backwards is’.
  • We will come back to this one a bit later.
  • Save flow.
Create an Apex Plug-in

We need to use some Apex now to do the actual work of creating the name input string backwards. For this you need to use Eclipse and the Force.com IDE. I assume you have knowledge of this.

Create a new Apex class. The class should implement the the two methods invoke and describe of the Process.Plugin interface.
The describe method names the plugin and defines input and output parameters.
The invoke method does the actual work. It gets the input parameters, does some calculations and return something.
Once this class has been created this way it will be visible in the Palette tab of the Flow Designer . The tag defined in the describe method is the name of the section and the name defined is the name of the actual plugin. See ‘Name Backwards’ and ‘nameBackwardsPlugin’ in the screenshot below.

 

My Apex class called FlowTest is implemented like this.
global class FlowTest implements Process.Plugin {

// Invoke method
global Process.PluginResult invoke(Process.PluginRequest request) {
String name = (String) request.inputParameters.get('name');
String nameBackwards = '';

try {
String[] nameList = new String[name.length()];
Integer counter = 0;

for (Integer i=name.length()-1; i>=0; i--) {
nameList[counter] = name.substring(i, i+1);
counter++;
}

for (Integer j=0; j<=nameList.size(); j++) {
nameBackwards+=nameList[j];
}

} catch (Exception e) {
System.debug(e);
}

Map<String,Object> result = new Map<String,Object>();

// Add value to output parameter and return.
result.put('result', nameBackwards);
return new Process.PluginResult(result);
}

// Describe method. Returns the describe information for the interface
global Process.PluginDescribeResult describe() {
Process.PluginDescribeResult result = new Process.PluginDescribeResult();
result.Name = 'nameBackwardsPlugin';
result.Tag = 'Name Backwards';

result.inputParameters = new
List<Process.PluginDescribeResult.InputParameter> {
new Process.PluginDescribeResult.InputParameter('name',
Process.PluginDescribeResult.ParameterType.STRING, true)
};

result.outputParameters =
new List<Process.PluginDescribeResult.OutputParameter> {
new Process.PluginDescribeResult.OutputParameter('result',
Process.PluginDescribeResult.ParameterType.STRING)
};

return result;
}
}

You can have several input- and output arguments. Just define any extra input- and output parameters to the inputParameter and outputParameter lists in the describe method. Create new strings to get extra input parameters in the invoke method and add more key-values in the map for the output parameters in the invoke method.

Add plug-in to flow
Open up the flow again. If it was already open you probably need to refresh it or close and open it again.

Now click the Resources tab and double-click on variables. You are going to create a variable that will be assigned the output value of the plugin. My is named ‘NameBackwards’ and is of type text.

In the Palette you should now see the plugin as described in the screenshot above. Drag the nameBackwardsPlugin to the canvas. Mine is named ‘Calculate Name Backwards’. You will have to define inputs and outputs to the plugin corresponding to what you have just coded in the Apex plugin. So in this case the input would be the screen input field ‘Name’ and the output would be the variable ‘NameBackwards’ you just created.

Back to the end screen
Open up the end screen by double clicking it and go to the ‘Field Settings’ tab. Select resources and variables and ‘NameBackwards’ and add to the end of the output string. It should now read ‘{!Name} backwards is {!NameBackwards}!’.

See the Resources tab to see what we have created so far.

Finish the flow
Just add connectors between the elements and you’re done. Click a diamond for an element, hold the mouse button down and release it when you are in a receiving  elements diamond.

The flow is done. Save and Run to test it. You can also put the flow in a Visualforce page. This would be the code for doing that. ‘Play_with_Name’ below is the name of the flow.

<apex:page>
<h1>Your name backwards!</h1>
<flow:interview name="Play_with_Name"></flow:interview>
</apex:page>

Running the flow
Opening the new Visualforce page and running the flow will result in these screens.

Good Luck!

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

The Cloud-Based Flow Designer

by Niklas Waller on December 29, 2011

in Salesforce

With the Winter ’12 release of Salesforce comes the new Cloud-Based Flow Designer (as a beta). It works with production quality but has some limitations. You can find it under Workflow & Approval in the Administration Setup.

A visual workflow allows administrators to set up collections of screens, or flows, that step users through the process of collecting and updating data. Simple flows can easily be setup. Elements are dragged onto a canvas from a left pane. For example screens to the user to get input, logic like decision handling and ways to get, create and update underlying Salesforce data.

Sometimes there might be a need for more complex calculations and in these cases Apex functionality can be used. Special Apex classes are then created and can be used as plugins to the flows with in- and out parameters. The next blog post will cover an example of this.

Furthermore created flows can be added to Visualforce pages. Hence, this is a very good tool for administrators, sometimes in cooperation with developers, to create useful and complex flows to provide a very good solution to their customers and users.

Limits of a flow:
Maximum number of versions per flow 10
Maximum number of steps per flow 2000
Maximum number of active flows per organization 500
Maximum number of flows per organization 1000
Maximum size of uploaded flow file 3MB

 
The differences from the already existing Desktop-Based Flow Designer version can be found here. Download the latest version of the desktop version here.

See a demo of the Cloud-Based Flow Designer here.

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

About dates in Salesforce for API usage

by Niklas Waller on December 22, 2011

in Salesforce

In SOQL-queries or in data to be imported into Salesforce via for example Apex Data Loader dates have to be on a specific form.

Simple date:
YYYY-MM-DD

Date, time and time zone:
YYYY-MM-DDThh:mm:ss+hh:mm
(put datetime in eastern time zone in hours and minutes)

YYYY-MM-DDThh:mm:ss-hh:mm
(put datetime in western time zone in hours and minutes)

YYYY-MM-DDThh:mm:ssZ
(datetime in GMT)

As the Salesforce help says dateTime field values are stored as Coordinated Universal Time (UTC) in Salesforce. When one of these values is returned in Salesforce, it is automatically adjusted for the time zone specified in your organization preferences. SOQL queries return dateTime field values as UTC values.

If you have an application querying a record in Salesforce with for example LastModifiedDate as a condition in the SOQL-query you need to specify the timezone or else you will get the results in GMT.

Look at this example. I have a SOQL query in a Java class which queries Salesforce via web services. The SOQL-query looks like this:

String soqlQuery = "SELECT Id, Name FROM " + obj + " where LastModifiedDate > 2011-12-18T11:00:00+01:00 and LastModifiedDate < 2011-12-18T11:30:00+01:00";

A record in my org was created at 11:15 GMT+01. Using this query will give me a result. Using the GMT variant or no timezone will give me nothing.

If you are trying to insert a record in the same org and you want to set a date field using Apex Data Loader you must however use the GMT variant or else the date will be one day earlier than specified. This is because DataLoader is running on Java which uses the installed machines default time zone. See this blog post for more on this.

Have you got more info on this? Tell us!

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

2 comments

My new toys

by Niklas Waller on December 15, 2011

in Technology

A few days ago I got some new toys – an iPad 2 and a Buffalo Terastation NAS Server.

The iPad 2 is something that I am very happy with of course. Right now I am only using it for fun but the idea is also to be able to see how apps and websites that I develop will look on it. It looks nice and is highly functional. Better that my expectations and I have really waited a long time. I am very curious about iPad 3 though but I couldn’t wait for it.

ipad2

The Buffalo TeraStation ES NAS 8TB is something that I am quite satisfied with as well. It contains a raid system with 4 disks á 2 TB. It was quite easy to setup and provides lots of functionality. The Print Server was up and running in no time. Itunes the same way once you understand how it all works. All videos of different kinds have been added to the NAS server and these are now streamed to the iMac in the livingroom using XMBC with no performance loss at all. I can also set up webaccess or FTP for access from anywhere. There is also backup functionality that can be scheduled. Even the iPad can access the NAS-server although this is a special case that I will tell you more about next.

A NAS-server makes life more structured and easy for the company but also for private matters – especially if you’re a tech geek.

Buffalo-TeraStation-ES

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

Wohill on Twitter – got to do better

by Niklas Waller on December 8, 2011

in Twitter,Web

I was looking at how we are doing on Twitter and its bad to be honest. We have not prioritized this at all. I used TweetsMap to see how we are spread over the world as you can see below. This has got to change! More followers this way please! We will start try to be more active.

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment

When a report is scheduled to run you can decide to email the results to yourself or to others.

First you choose to schedule the report. Assuming you are in edit mode of a report, press the button ‘Schedule Future Runs…’.

Now specify next to ‘Email Report’ if the report should be emailed to yourself or to more people. Choosing ‘To me and/or others…’ provides you with more options.

Now you can choose from public groups, roles, roles and subordinates and users. What is possible to choose from here is actually already determined by the access to the report folder where the report is located.

Edit the report folder and choose from the following settings.

The first option makes it possible to choose any email recipient in the report. The last option lets you limit the access a bit.

You are only able to select individual users as recipients of report emails if the folder that contains the report is accessible to All Users. If the Report folder is shared to a group you can only email to the group, not the individual users within the group.

Share and Enjoy:

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • Google Buzz
  • RSS
  • Slashdot
  • Technorati
  • Add to favorites
  • DZone
  • LinkedIn
  • MySpace
  • Tumblr

Be the first to comment