Audit9 Blog

The Audit9 Blog provides content for Architects, Developers and ISVs with a technical interest in the Salesforce cloud platform and ecosystem.

Blog authored by Mark Cane, Salesforce Certified Technical Architect. All views expressed are mine and mine alone. All content provided on this blog is for informational purposes only.

External Sharing Model – Pilot

An interesting addition to the Summer ’12 release is the pilot of separate OWD settings on custom objects for external users. For example, a custom object may have public read-write sharing for internal users and private sharing for external users. In many cases this will simplify the sharing model significantly as the rules below have historically applied to all users including portal users and guest users.

Is there a user who can’t see all records == private
Is there a user who can’t edit all records == public read-only
else == public read-write

Applying the above OWD rules across internal and external users typically results in an overly private model for internal users and a resultant proliferation of performance-expensive sharing rules. The new separation of concerns here is a great improvement for custom objects, but not standard objects (hopefully this is on the roadmap).

Seemingly simple, yet powerful functional enhancements such as this that reduce the amount of custom configuration applied to an org are to be applauded.

Customer Portal Enterprise Admin Record Access

This post is the second in a series exploring record access considerations with the different portal user license types. This post covers the Customer Portal Enterprise Admin type.

By way of reminder, the decision tree below should be used when making the high-level decision on the appropriate license type for the different user populations within your portal.

Customer Portal Enterprise Admin
The Customer Portal Enterprise Admin (CPEA) user license type relates to the Customer Portal Manager Custom license type (as displayed within Salesforce). Note, the Customer Portal Manager Standard license type is obsolete in the sense that it is not available to new customers. The CPEA user license provides coverage for use cases such as B2B empowerment to manage portal users (Delegated Portal User Admin), extended standard object permissions and non-trivial record access requirements (beyond those applicable to HVPU).

CRUD permissions :
Create, Read and Update on Account, Asset, Case, Contact
All on Custom Objects
Create and Read on Idea
Read on Article, Price Book, Product, Solution, Answers (Question, Reply)

Default record access :
CPEA users are placed in the role hierarchy as below, as descendant roles of the Account Owner’s role.

So for each account where a CPEA user is activated (Acme in the case above), a set of 3 roles is created under whichever role the account owner has allocated. Executive users can view manager owned records and so on. The number of roles created can be set between 1 to 3, giving control over user sharing granularity versus proliferation of user roles (and consequential impact on performance).

Sharing options :
Role-based and criteria-based (CBS) sharing rules, manual sharing, Apex Sharing, Apex Managed Sharing.
Case teams.
Can have the “Super User” permission – this provides Read and Update on cases submitted by all users related to the same account (includes case comments and attachments).

Other considerations :
The CPEA user license is significantly more expensive than the HVPU license types – for good reason, and should be used only when the additional functionality is necessary – perhaps in a complementary model. It always makes sense to perform a full upfront analysis of the access model requirements for a portal solution – retrospectively addressing functional issues incurred through the wrong license type selection can be costly and entail inelegant design compromise.

Note. This page on the Salesforce help site provides an excellent reference for further information.

New Advanced Apex Programming Book

Interesting new book covering advanced Apex topics such as execution context, asynchronous programming, robust design patterns and so on.. Excellent addition to the Force.com Developer’s arsenal!

Advanced Apex Programming – Author: Dan Appleman
Table of Contents

Update (2012-09-10)
I now have my hands on this book and have to say I’m really impressed. So often I work on projects where the principles of Thinking in Apex have been overlooked and basic mistakes made – now there’s an alternative to having to infer the right mindset to bring to Force.com development from the technical documentation. Well documented design patterns that address real world concerns can only help raise the quality bar.

High Volume Portal User Record Access

When designing portal solutions on the Force.com platform it is imperative to understand the sharing model implications of the user licenses and related base user profiles in play. In this post I’ll outline the key considerations in respect to record access for high-volume portal user license types; Authenticated Sites (aka Platform Portal) and Service Cloud Portal. In both cases users do not have roles and can’t be added to teams, groups or sharing rules. So how do you provide record access?

Note, this topic (and complex sharing models in general) is fundamental to understand if you’re considering the Salesforce Certified Technical Architect certification.

So to start with, the decision tree below should be used when making the high-level decision on the appropriate license type for the different user populations within your portal.

The following items examine each of the HVPU license types in turn with a view to clarifying how record access is achieved and the constraints to consider.

Applicable to all user license types are the following record access rules, which should be considered carefully if a public sharing model is in place and a portal is added:
Read on all records where the object org-wide default is public read-only
Read-write on all records where the object org-wide default is public read-write

HVPU License Types:

Authenticated Sites
The Authenticated Sites, or Platform Portal User, license is intended for high-volume scenarios (up to millions of users) where access to Standard Objects is not necessary and record access requirements are simplistic.

CRUD permissions :
Read on Document, Price Book, Product, Account, Asset
Read and Create on Idea
Full access to Custom Objects

Default record access :
Custom object records owned by the user
Read on Account related to the portal user
Read and Update on Contact related to the portal user
Master records where the user has access to the detail record and vice-versa

Sharing options :
Sharing Sets – provides sharing to records (account, case or custom object) related to the portal user’s contact or parent account (via lookup field). Access level can be read-only or read-write but can’t be more restrictive than the OWD for the object. Sharing Sets are at the user profile level, not per-portal and each user profile can be associated with only one sharing set. For example – you can provide write access to the portal users related account via a sharing set, or provide full access to all custom object records in a private OWD object which are related to the same account as the portal user.

Other considerations :
A Share Group defined at the portal level provides access to records owned by HVPU to other users, via Public Group, Role, Role and Subordinates and Users.

Use case :
A reasonable exemplar use case for this license type would be event attendees registering for an event within an event management portal. In such a case the attendee may need to login after registration to provide further details, make payment, book a session etc. The attendee needs access to their own records held in custom objects and update permission on their Contact record. The attendee may also require visibility as to who else from their company is attending. Basically this license type is for external user Force.com solutions underpinned by custom objects.

Service Cloud Portal
Relates to the High Volume Customer Portal User License and is intended for high-volume scenarios (up to millions of users) where access to Standard Objects (cases etc.) is a definite requirement but record access requirements are simplistic.

CRUD permissions :
Read and Update on Account
Create and Read and Update on Asset, Contact, Case
Read on Document, Price Book, Product
Create and Read on Idea
Full access to Custom Objects

Default record access :
Records owned by the user
Read and Update on Account and Contact related to the portal user
Master records where the user has access to the detail record and vice versa.

Sharing options :
Sharing Sets – see above.

Other considerations :
Share Groups – see above.

Use case :
The typical use case for Service Cloud Portal licensing is mass B2C self-service support scenarios – offering case logging, idea creation etc.

In the next post I’ll extend coverage to the Customer Portal Enterprise Admin and Partner Portal license types.

Note. This page on the Salesforce help site provides an excellent reference for further information.

Mixed Authentication Schemes

In certain cases it may be desirable to have some users authenticate in the standard Salesforce manner, whilst another user population authenticates via single sign-on (SSO). This mixed authentication scheme scenario may result from rollouts to new departments etc. What are the considerations?

The first concern is whether Delegated or Federated Authentication will be used for the SSO population.

With Delegated Authentication (DA), a mixed scheme is straightforward as the SSO configuration is applied through a User Profile setting, enabling a subset of users to have their authentication request routed to the DA on-premise authentication web service. The remaining users will simply login in the standard Salesforce manner.

With Federated Authentication (FA) via SAML 2.0, a mixed scheme is not so trivial. There is no User Profile setting or indeed any user-level setting controlling whether a user is SSO-enabled or not. Instead SSO is inferred from the manner by which the authentication is approached. If the user accesses https://login.salesforce.com and attempts to sign-in they will be authenticated as a Salesforce user, no SSO. With the identity provider initiated SAML flow, the user is handed-across from the IdP in a pseudo-authenticated state, if the service provider (i.e. Salesforce) can validate the SAML assertion and map the user to an active Salesforce user (via username or Federation Id) then authentication takes place. So the IdP initiated flow poses no problems in a mixed scheme, standard Salesforce users authenticate as normal, SSO users authenticate via the IdP (intranet link etc.).

So far so good. But what if the client also needs to support the service provider initiated SAML flow in a mixed scheme? Perhaps Chatter Desktop or Mobile is being rolled out.

The service provider initiated flow requires a My Domain to be configured in the Salesforce org e.g. https://force365.my.salesforce.com, this enables anonymous authentication attempts using the My Domain to be mapped to the correct SSO configuration and routed to the IdP. Which works perfectly for SAML enabled clients and deep-links. For the standard users in the org we now have a problem – all unauthenticated references to resources based on the My Domain will take the user to the IdP to authenticate. So sharing of links via email etc. will require the standard user to have pre-existing session, otherwise they will be presented with an unfamiliar login challenge (Active Directory Forms Authentication perhaps) or worse a login failure in the integrated case. In theory this could be resolved by ensuring the standard users are correctly configured for authentication both at the IdP and Salesforce, enabling SSO authentication to occur where required.

Salesforce Public or Resource Calendar?

Not entirely a technical concern, however here I’ll quickly share some notes on public and resource calendars.

1) Resource Calendar – events can’t be added directly, instead events are added through adding the resource to events occurring on User or Public Calendars (select Resources in the search within list). A typical use case for resource calendars would be room bookings, where the room is the resource. Other use cases could be shared equipment, overhead projector (10 years ago maybe) etc..

2) Public Calendar – group calendar unrelated to individual user calendars – events can be added directly. A typical use case for public calendars would be team vacation calendar, or basically any scenario where a group of users share an interest.

3) User Calendar – Standard users and partner portal users have their own calendar. Access to a user’s calendar can be role hierarchy based and via Calendar sharing rules.

4) Limits. There are no explicit limits on the number of public or resource calendars you can have in an org.

5) API. Events can’t be added or modified via the API. Instead a proxy object could be used with Apex triggers to broker changes via Apex code.

UI Tips and Tricks – Country List

This tip introduces a simple pattern used by many AppExchange solutions to manipulate standard layouts. In short, a custom sidebar component is added which contains JavaScript code which manipulates the page at the DOM level. The following simple example shows how the country fields on the account layout can be changed to lists – a common customer request. A Country__c custom object with the Name field populated with country names is required. The Dojo library is also used. Please note, the code below is old and hasn’t been tested recently, I provide this for illustration of the pattern, it certainly won’t be winning any prizes.

So, in the example, Dojo runs the replaceCountryInputs() function when the DOM is ready, this finds the country fields by their ID (Firebug is a good way to browse the page markup), we then remove the original input element and replace with a select element with the same Id. The select element is then populated in JavaScript with the result of a soql query – using the Ajax API. Finally we need to add the inline editing handlers to the new select element – and we’re done.

On the configuration side, the sidebar component must be assigned to the home page layout for relevant users and the setting to enforce display of custom sidebar components must be enabled.

As I’ve said, the use case here isn’t significant it’s the possibility that this technique enables in terms of standard layout manipulation. Be aware that the field ids may be subject to change.

[sourcecode language=”javascript”]

<script src="/js/dojo/0.4.1/dojo.js"></script>
<script src="/soap/ajax/19.0/connection.js" type="text/javascript"></script>
<script type="text/javascript">
var arCountries = getCountries();
var vProcess = replaceCountryInputs();

function replaceCountryInputs()
{
if (document.getElementById(‘acc17country’)!=null) {
var defaultVal = document.getElementById(‘acc17country’).value;
var cInput = swapFieldType(‘acc17country’);
setCountryListWithDefault(cInput, defaultVal);
}
if (document.getElementById(‘acc18country’)!=null) {
var defaultVal = document.getElementById(‘acc18country’).value;
var cInput = swapFieldType(‘acc18country’);
setCountryListWithDefault(cInput, defaultVal);
}
if (document.getElementById(‘acc17_ilecell’)!=null) {
SetupInline(‘acc17’);
}
if (document.getElementById(‘acc18_ilecell’)!=null) {
SetupInline(‘acc18’);
}
}

function swapFieldType(i)
{
var cInput = document.getElementById(i);
var cInputParent = cInput.parentNode;
cInputParent.removeChild(cInput);
cInput = document.createElement(‘select’);
cInput.size = 1;
cInput.id = i;
cInput.name = i;
cInputParent.appendChild(cInput);
return cInput;
}

function setCountryListWithDefault(i, d)
{
if(i!=null) {
if(arCountries.length>0) {
for(x=0;x<arCountries.length;x++) {
if(arCountries[x]==d) {
i.options[x] = new Option(arCountries[x], arCountries[x], false, true);
} else {
i.options[x] = new Option(arCountries[x], arCountries[x], false, false);
}
}
} else {
i.options[x] = new Option(‘No countries found’, ‘No countries found’, false, true);
}
}
}

function SetupInline(prefix) {
var _element = document.getElementById(prefix + ‘_ilecell’);
if (_element) {
_element.ondblclick = function() {
var _loaded = false;
if (!sfdcPage.editMode)
sfdcPage.activateInlineEditMode();

if (!sfdcPage.inlineEditData.isCurrentField(sfdcPage.getFieldById(_element.id)))
sfdcPage.inlineEditData.openField(sfdcPage.getFieldById(_element.id));

var idInput = prefix+’country’;

if (document.getElementById(idInput)!=null) {
var defaultVal = document.getElementById(idInput).value;
var cInput = swapFieldType(idInput);
setCountryListWithDefault(cInput, defaultVal);
}
}
}
}

function getCountries()
{
sforce.sessionId = getCookie(‘sid’);
sforce.connection.sessionId=sforce.sessionId;
var out = [];
try {
var queryCountries = sforce.connection.query("Select Id, Name FROM Country__c ORDER BY Name");
var countries = queryCountries.getArray(‘records’);
for(x=0;x<countries.length;x++) {
out[out.length] = countries[x].Name;
}
} catch(error) {
alert(error);
}
return out;
}
dojo.addOnLoad(replaceCountryInputs);
</script>
[/sourcecode]

UI Tips and Tricks – Picture Upload

In the very early 90s I was employed as a professional Visual Basic programmer (and no that isn’t a contradiction in terms) enjoying greatly the development of client-server accounting systems. Good times. In those days tips and tricks sites were a big part of how the community shared knowledge. Following some recent reminiscing, through this series of posts, I’ll share some UI tips and tricks that aren’t necessarily an architects concern however I hope will prove helpful to some.

Ok, so down to business. Today’s tip concerns the upload and display of record images in the context of standard functionality, for example a Product image or Contact photo. The latter I’ll use as an example.

1. Add a field to Contact named [Photo Document Id], of the text type, 18 char length.

2. Add a field to Contact named [Photo], of the text formula type, formula set as below.
[sourcecode language=”xml”]
<formula>IMAGE(‘/servlet/servlet.FileDownload?file=’Photo_Document_Id__c”)</formula>
[/sourcecode]

3. Add a Visualforce page named ContactPictureUploader, with no markup between the page tags.
4. Add an Apex class ContactPictureUploaderController, code as below, set as the VF page controller and ensure the page action is set to the initialise method.
[sourcecode language=”java”]
public with sharing class ContactPictureUploaderController {
private Id contactId;

public ContactPictureUploaderController(){
contactId = ApexPages.currentPage().getParameters().get(‘cid’);
}

public PageReference initialise(){
List<Attachment> listA = [select a.Id from Attachment a
where a.createdById =:UserInfo.getUserId()
and a.parentId=:contactId order by a.createdDate desc limit 1];
if (listA.size()>0){
Id attachmentId = listA[0].Id;

Contact c = [select Id from Contact where Id=:contactId];
c.Photo_Document_Id__c = attachmentId;
update c;
}
return new PageReference(‘/’+contactId);
}
}
[/sourcecode]
VF Page markup.
[sourcecode language=”html”]
<apex:page controller="ContactPictureUploaderController" action="{!initialise}">
<!– Controller context page – no markup –>
</apex:page>
[/sourcecode]

5. Add a custom button to the Contact object of the JavaScript type, named Upload Photo, script as below.
[sourcecode language=”javascript”]
parent.window.location.href=’p/attach/NoteAttach?pid={!Contact.Id}&retURL=/apex/ContactPictureUploader?cid={!Contact.Id}’
[/sourcecode]

In short, the solution works by invoking the standard attachment page, which on submit redirects to the VF page which copies the uploaded document id to the Contact Photo Document Id field, then redirects back to the contact record. The image field on the Contact object then loads the image using the IMAGE() formula field, easy. The image field can then be used on the contact layout, related lists etc..

Summer ’12 Lookup Relationships

The Summer ’12 release introduces some fundamental changes to the functionality of lookup relationships. In summary:

1. Optionality. Lookup relationships can now be set as mandatory (Required Attribute). This is great news in that the usual validation rule enforcement can now be forgotten.

2. Referential integrity. Prior to Summer ’12, the parent record could be deleted without regard to related child records, which would have their lookup fields nulled. This remains the default behaviour. You can however specify one of the following behaviours in the optional case, for mandatory lookups only 2 and 3 are possible. I’m using the terms parent and child here in the loosest sense for convenience, the nature of the relationship is associative.

2.1 Clear the child field value – default
2.2 Prevent the parent being deleted (Don’t allow deletion of the lookup record that’s part of a lookup relationship)
2.3 Cascade delete (Delete this record also) – This one requires activation via salesforce support, and ignores the sharing model, meaning if a user has record access to the parent, they can delete it and related children without requiring permissions at the child level. This option is restricted to cases where the child is a custom object.

The above enhancements to the lookup relationship type start to blur the lines between lookup and master-detail, however the key differentiation remains in terms of ownership versus association.

SOQL Record Locking

There may be cases where a pessimistic locking strategy makes sense within your custom Apex script, for example the available inventory statistic on a Stock record may need to be locked while your code works out the correct value to decrement.

The interesting, but rarely used SOQL keyword “FOR UPDATE” can be applied to SOQL query statements to lock the returned records such that only the current call context can modify the records via DML operations.

[sourcecode language=”java”]
Account a = [select Id, Name from Account where City=’Edinburgh’ LIMIT 1 FOR UPDATE];
[/sourcecode]

Results are automatically ordered by ID, a preferred ORDER BY clause is not supported. When applying custom locking, remember to order locks from parent to child and always, always follow the same sequence in all cases, otherwise you open up the possibility of introducing deadlocks.

Remember each Apex call context is an implicit transaction with auto-commit, i.e. committed or rolled-back atomically. For finer, conditional transaction control within a call context use savepoints (Database.setSavePoint()).