Tuesday, December 30, 2014

Salesforce logo sizes

Here's an aggregation of standard logo sizes to consider for use with various Salesforce components.

Salesforce standard logo sizes
Placement Width x Length (px) Max File Size (kB)
custom app logo 300 x 55 20
My Domain login page header 250 x 125 100
community login page header 250 x 125 100
Salesforce1 app branding 150 x 50 none
community Chatter email logo 460 x 560 none

Sunday, December 21, 2014

Where's the Visualforce viewstate coming from?

I felt inspired by a Q&A on Salesforce Stack Exchange to explore the growth of viewstates in Visualforce under different conditions. Without even reaching all of the test cases I originally set out to investigate, I was extremely surprised by my initial findings:

  • A viewstate only gets created once an apex:form is added to the page, regardless of what controller, if any, is used; but more curiously...
  • A viewstate grows when non-form elements outside the apex:form are added to the page! How? And why?


So while I wait for a response from the case that I'm going to log with Salesforce Support, I welcome anyone else's explanation for this phenomenon. Below are the Visualforce pages that support my findings above.

Visualforce viewstate exhibits
Number Title Observations
Exhibit 1 No controller, no form No viewstate
Exhibit 2 Standard Lead controller, no form No viewstate
Exhibit 3 Custom controller with persistent property, no form No viewstate
Exhibit 4 No controller, one form There's a viewstate! Which seems odd, since there's no controller to process the viewstate.
Exhibit 5 No controller, two forms There's a single viewstate that's slightly larger than what's in Exhibit 4.
Exhibit 6 No controller, two forms There's a single viewstate that's slightly larger than what's in Exhibit 5, even though literally nothing has changed except for the addition of a new p element outside of the apex:form!

I've posted the source code for the Visualforce pages to GitHub as a gist.

Friday, December 12, 2014

Workflow rule to record prior Owner Name

Sometimes you want to record a prior (a.k.a. "old") value for a particular field when it changes. If you need this simply for auditing purposes, tracking field history or implementing feed tracking should be all you need. But an alternative is needed if you want to do something else with that prior value, such as presenting it on a report or using it in a formula field.

The easiest way to record the prior value for something like Owner ID (could be Lead, Account, etc.) is to create a workflow rule and field update that uses the PRIORVALUE() function.

To see this in action, try the following to store the prior Lead Owner ID:

  1. Create a Text(80) field on the Lead object named PriorLeadOwnerId
  2. Create a workflow rule named, "Set Prior Lead Owner ID". For evaluation criteria, select "Evaluate the rule when a record is created, and every time it’s edited". For rule criteria, enter this simple formula: ISCHANGED( OwnerId )
  3. Add a new field update action to the workflow rule named PriorLeadOwnerName, which updates the Prior Lead Owner Field by setting it to the formula: PRIORVALUE( OwnerId )
  4. Activate the workflow rule

Tip: If you want store prior related values, such as Owner Name instead of Owner ID, you can actually use a formula field to work around a PRIORVALUE() limitation.

For example, you can create a formula field labeled Lead Owner Name with formula, BLANKVALUE( Owner:Queue.Name , Owner:User.FirstName + ' ' + Owner:User.LastName ), and then use this formula field in the above workflow rule instead of OwnerId.


At this time, workflow rules still cannot set a Lookup field to a dynamic or formulaic value. So while a workflow rule certainly is convenient, it's important to remember that in Winter '15 there are a couple of other more powerful alternatives. 
  • Lightning Process Builder: Beta feature that seems poised to replace workflow rules once it becomes GA.
  • Apex triggers: Nothing quite beats the power and precision of an Apex trigger, But you will need to learn some new syntax and guidelines.

Tuesday, December 9, 2014

Checkbox field for Live Agent pre-chat form

A [simple question](http://salesforce.stackexchange.com/questions/58529/live-agent-pre-chat-api-custom-checkbox-field/58533#58533) on Salesforce Stack Exchange exposed some unexpected behavior with the Pre-Chat API: Checkbox fields cannot be included as part of the pre-chat form.

The behavior appears to be that if a checkbox field is added to the specification for liveagent.prechat.findorcreate.map and liveagent.prechat.findorcreate.map.doCreate, the creation process fails and the desired record is not created. As soon as the checkbox field is removed from the form definition, the record creation process executes as expected.

While the reason for this behavior is unclear, at least there are two possible workarounds. The key to both workarounds is knowing that the String value of a marked checkbox is "on", and the String value of an unmarked checkbox is blank or null.


Workaround 1: Workflow rule


Assuming you've already created your Checkbox field, you can create a complementary Text field to hold the checkbox value submitted through the pre-chat form. For example, let's say your Checkbox field is named IsEmployed__c. You can create a Text field named IsEmployedText__c, and bind the pre-chat form field to IsEmployedText__c.

To close the loop, you'll create a workflow rule that basically sets IsEmployed__c to true when IsEmployedText__c equals "on", and another workflow rule to set IsEmployed__c to false otherwise.

Workaround 2: Formula field


Start with a Text field, and then simply create a Formula (Checkbox) field that uses NOT( ISBLANK( __ ) ) to produce the equivalent Boolean value.

For example, create a Text field named IsEmployedText__c, and bind the pre-chat form to this field. Within Salesforce, create a Formula (Checkbox) field named IsEmployed__c, and put int he formula NOT( ISBLANK( IsEmployedText__c ) ).

Wednesday, December 3, 2014

Where are my Featured Search images in Kokua?

If you're trying out the new Community Designer feature (in beta still, as of Winter '15), you may run into a bit of trouble getting the images to appear correctly for Featured Search on Kokua's homepage. At a high level, there are the three key requirements to getting an images to appear on the homepage, explained in more detail below.

  • Name the image as DataCategoryUniqueName-s.jpg
  • Upload the image as an asset using Site.com Studio
  • Enter a value using merge fields into the Category Image URL: {!Global.PathPrefix}/{!DataCategory.Name}.jpg

Note: The ".jpg" extension is not required, but it must match what you entered for the Category Image URL.

Name the image


To figure out what to name an image for a data category, you need to know the data category's Category Unique Name is. To figure this out, first go to Setup > Customize > Data Categories > Data Category Setup. Next, click the Actions button next to an existing data category, then click Edit Category. Now the Category Unique Name field and value will be exposed.


For the Featured Search box on the homepage, you need to specify 's' (stands for "square") for the <size> portion of the filename. Here are some examples.

Examples of image filenames for Featured Search
Data Category Unique NameImage TypeImage Filename
Technology_EnablementJPEGTechnology_Enablement-s.jpg
Customer_EngagementJPEGCustomer_Engagement-s.jpg
StrategyPNGStrategy.png

Note: The Winter '15 documentation for Featured Search says that you should use the convention <datacategoryname>-<size>.<filetype>. However, a more accurate convention is <datacategoryuniquename>-<size>.<filetype>.

Upload the image


This part is relatively simple. All you need to do is open Site.com Studio for the community's site, and then upload the image as an asset.

  1. Go to Setup > Customize > Communities > All Communities
  2. Click Community Designer next to your community
  3. Click the top header's picklist next the the word "Communities", and click Go to Site.com Studio
  4. In the left-hand navigation tree, click Assets under All Site Content
  5. Click the Import... button, and upload your image


Note: If you upload a .zip file where the images are stored in folders, you will need to tweak your Category Image URL in the last step.

Enter a value for Category Image URL


Finally, while you're in Site.com Studio, you need to edit the main page and enter a value for the Category Image URL property in Featured Search.
  1. On the Overview tab, click Site Pages under All Site Content
  2. Double-click main under Site Map to open the page
  3. In the Views section, double-click Kokua Home to edit the view
  4. Under Page Structure, click Featured Data Categories
  5. In the Property Editor in the right sidebar, enter this exact value for Category Image URL: {!Global.PathPrefix}/{!DataCategory.Name}.jpg


Note: In the previous step if you uploaded a .zip file where the images are stored in folders, you need to include the folder names in the Category Image URL value, such as: {!Global.PathPrefix}/folder/subfolder/{!DataCategory.Name}.jpg

Friday, November 21, 2014

Login with Facebook for a community with Visualforce

Adding an auth provider (e.g., Login with Facebook) to a Salesforce Communities instance is pretty easy.


The Login with ... button is pretty magical: It knows what community you're logging into, and it knows what URL you're trying to access upon authentication. Based on that information when you click the button, you're taken to the correct authentication URL based on the Auth Provider configuration in Salesforce.

But... for orgs that have customized their login pages with Visualforce, how do you add this button? Luckily, adding the same magical button to a custom Visualforce login page doesn't require you to manually reconstruct the OAuth authentication endpoint. Instead, you can just use the AuthConfiguration.getAuthProviderSsoUrl() method.

Simply put, you need just three things:
  • Your community's base URL
  • The relative path after the base URL to which the user should be taken upon successful authentication. This is also known as the startUrl.
  • The URL Suffix (a.k.a. DeveloperName) of your auth provider (e.g., Facebook) as configured in Salesforce

Once you have this information, all you need to do is create an action in your Apex controller that returns a PageReference object, constructed from the URL returned by AuthConfgiuration.getAuthProviderSsoUrl().


This same approach can be applied to other auth providers as well, such as Twitter or Google.

AuthConfiguration.getAuthProviderSsoUrl(String, String, String) explained

Curious about AuthConfiguration.getAuthProviderSsoUrl(String, String, String)? Here are some notes about the parameters that may not be apparent the first time you read the documentation.

String cUrl


You'll want this to match exactly what you see when you're looking at your community's Administration Settings. Don't add any extra trailing slashes!


String startUrl


This should be the URL relative to the value you put for cUrl, including the leading forward-slash ('/'). For example, if you want to land the user at the Home tab, startUrl should be set to "/home/home.jsp" (again, note the inclusion of the leading forward-slash).

Think about it this way: If you concatenate cUrl and startUrl, exactly as written, you should end up with a valid URL to the desired page or resource within your community.

String developerName


This is the DeveloperName field from the AuthProvider object. Or, you can find the value by looking at the URL Suffix field on the Auth Provider detail page.


You can find this value dynamically by querying the AuthProvider object.

Sunday, November 9, 2014

Add, edit and remove parent and child records in Lightning component

After figuring out that I could leverage indexVar and custom data-* attributes to efficiently remove items from a list in Lightning, I decided to see whether the same component pattern could be applied to enable dynamic management of lists at a parent object level and at a child object level.


In the screenshot above (please excuse the ugly styles), the behavior is such that when a user clicks Edit for an account, the list of contacts changes to show only contacts for the selected account.

The basic building blocks of this app are the ns:manageAccounts and ns:manageContacts components, added to the Lightning application as follows:


And within both components, the following pattern is used:


The above concepts are simplified in their illustration, as the full setup also uses several custom events and event handlers. Please take a look at the oneAccount.app repo on GitHub for the full source code behind the sample app.

Lightning really appears to makes dynamic UI updates really simple for the developer, similar to other frameworks like Knockout. The real benefit of Lightning is that it's built directly on top of Salesforce, and as a result makes it easy to leverage the power of Apex in your apps and components.

Friday, October 31, 2014

Simple Lightning-to-Lead tutorial for the Salesforce1 Platform

If you're brand new to Lightning on the Salesforce1 Platform, and you're having a bit of trouble following the cool but relatively complex Expenses sample app from the Lightning Components Developer's Guide, maybe this Lightning-to-Lead tutorial will work better for you.

The goal is simple: Create a one-page app that basically does what Web-to-Lead does, which is to create a new Lead record. Nothing glorious, but it's just complicated enough to go over some core concepts. When you're done, you'll end up with a simple page with a few fields and a lone button.


Sound simple? Give it a try and let me know what you think!

Friday, October 24, 2014

Share Chatter files across communities... or not

"One version of the truth" is the holy grail in content management. Chatter Files is salesforce.com's solution for allowing users to manage document revisions in Salesforce, while making sure that links to the file always give people the latest version. But with Communities, "one version of the truth" becomes more difficult to achieve.

Take the following scenario:
  1. John Doe collaborates with internal users to create a pitch deck for a new product.
  2. John uploads the file to the Competitive Advantage Chatter group in his org's internal community.
  3. Several iterations later, the deck is ready to be shared in his company's partner community, called AllianceNet.
  4. John uses the Global Header to switch from his org's internal community to the AllianceNet community.
  5. John goes to the All AllianceNet group to share the file, already stored in Chatter, with the group, but... where's the file?



As it stands in Winter '15, files created or uploaded to one community, internal or external, can only be shared within that same community in Chatter. So, to share a file across communities, users effectively have to recreate a duplicate instance of the file by uploading it again as a new file to the new community.

The only way that files can be shared or seen across communities is to create a Salesforce CRM Content library that's shared with users who have access to multiple communities. But even then, users will not be able to attach the file to a Chatter post. They'll be able to find the file in Global Search, and share URL links to the file, but unfortunately "native" Chatter file attachments will not be possible.

If this limitation with file sharing across communities affects or bothers you, please promote the idea, "Sharing Files from Chatter to Communities".

Friday, October 10, 2014

Invalid apex:attribute name values

The apex:attribute component in Visualforce allows developers to pass custom Apex class instances as attributes from the parent component or page to the child component. In this manner, it's possible to pass a page controller itself as an attribute to a component on the page, using a simple self property like the following.

public CustomController getSelf() {
    return this;
}

What the documentation doesn't say is that apparently (at least in Winter '15, API 32.0) there are keywords to avoid when naming your attribute, even though the Visualforce parser will not tell you there's a problem.

Trying to name your attribute "controller" will cause something like the following error to appear when saving a page: Wrong type for attribute &lt;c:buggersitepaypalcheckout controller=&quot;{!self}&quot;&gt;. Expected BuggerSiteSimpleSelfRegController, found String



Trying to name your attribute "parent" will cause a completely blank error message to appear when saving a page. But it's an error that will prevent you from saving your page, nonetheless.



Do you know any other hidden no-no's with naming apex:attribute elements? Please share so that we can all avoid these frustrating quirks int he future.

Monday, September 22, 2014

Your request cannot be processed at this time. The site administrator has been alerted.

In setting up and testing self-registration for a new community built on Salesforce Communities, you may encounter this confounding error: "Your request cannot be processed at this time. The site administrator has been alerted."

"Alerted" in the above statement refers to an email alert that went out to the site administrator. But who is the "site administrator"? You can find the email address used for this notification by locating the Site Guest User for your community's Force.com site.
  1. Open Setup > Manage Communities
  2. Click Force.com for the community in question
  3. Click Edit
  4. Change Site Contact to the user who should receive notifications when errors arise with self-registration. Make sure the user's email address is valid.


For sandboxes, make sure your Access to Send Email is set to "All email", under Setup > Email Administration > Deliverability. (Thanks Lynnet!)


Below are a few common reasons why you may be encountering the problem:
  • The community is not published. In case you're working with a community that is offline or in Preview status, you need to publish the community before self-registration will work.
  • The Account Owner does not have a User Role assigned. If you're creating a new Account record on the fly, especially in B2C situations, you need to make sure you assign a default account owner that also has a User Role value. Any role will do, and you can use either a workflow rule or Apex to perform the assignment.
  • The site guest user does not have Create and Read permissions on the Account and Contact objects. Edit Public Access Settings for the community's Force.com site to grant these permissions, along with field permissions for any fields that are included on the self-registration form. Note that by default the site's profile will not have these permissions.
  • Self-registration is not enabled for the community. Go to Manage Communities, click Edit and make sure the Login settings show that self-registration is enabled.

For more details on setting up a community for self-registration, please refer to the Getting Started with Communities guide.

Wednesday, August 20, 2014

Close recurring tasks using Apex

At least one developer had reported encountering an error when trying to automate updates to Task records using Apex: System.UnexpectedException: Apex Task trigger cannot handle batch operations on recurring tasks. This error occurs even in simple scenarios, such as simply closing any open tasks on a close opportunity.

While I agree with what I imagine to be general consensus that this error seems crazy, luckily there appears to be a workaround: Update a custom flag using Apex, and then use workflow rules to handle the dirty work of updating restricted standard field(s).

While I've only tested this approach on the scenario below, I think it will be applicable to solving other similar problems with updating Task records using Apex.

Sample solution to close recurring tasks using Apex


The anonymous Apex code below illustrates the solution.


And there's nothing special about the checkbox field, the workflow rule or the field update. The idea is that when the checkbox is checked, the workflow rule fires and performs the field update.


Friday, August 8, 2014

Created By and Last Modified By not set before insert or update

After some frustrating troubleshooting of what I thought would be simple code today, I learned something unexpected: Created By and Last Modified By are not populated or set in the before context of DML operations. This means that before insert, CreatedById, CreatedDate, LastModifiedById and LastModifiedDate are all empty. And before update, LastModifiedById and LastModifiedDate will reflect the previous person to modify the record, not the current user (which would be yourself if you are the one editing the record).

While this was counter-intuitive at first, I came to realize that there's no need to rely on those fields in the context of Apex triggers. All I really need is UserInfo.getUserId() and DateTime.now() to get the time.

Just like that... I learned something new today.

Tuesday, August 5, 2014

Person Accounts ignore Contact workflow rules and triggers

Every once in a while I find myself discussing the pros and cons Person Accounts, and inevitably a question comes up about how Account and Contact workflow rules and triggers are impacted. The vague statement that precipitates the question usually goes something like this, "Turning on Person Accounts creates problems for existing workflow rules and triggers." Today I finally decided to explore the validity of this statement myself, and the results were... well, disappointing.

The good news is that turning on Person Accounts will not break any of your existing functionality regarding Business Accounts and Contacts, as long as your Apex code is not automatically creating Contact records for accounts to emulate a one-to-one Account-Contact model.

The bad news is that none of your Contact workflow rules and triggers will fire for Person Account records. To me, this is a problem and poor design of the Person Accounts feature. Admins should at least be given the option to configure, perhaps via a checkbox, whether Contact workflow rules and triggers will fire for Person Account records. But as it currently stands, all benefits from existing Contact workflows and triggers are lost for person accounts.

If you agree with why the above bad news is bad, please promote this idea, "Person Accounts to work with Contact workflows and triggers." And read on if you want to know how I arrived at the above conclusions, which were tested in an Enterprise Edition org with Person Accounts enabled.

Test Setup (Summer '14)


The tests makes use of the following Text fields, for which history tracking is turned on:

  • Account Trigger Message (Account field)
  • Account Workflow Message (Account field)
  • Contact Trigger Message (Contact field)
  • Contact Workflow Message (Contact field)
To interact with those fields, we'll create two triggers and two workflow rules that simply set values for the implied fields on the Account and Contact object.
  • SetAccountTriggerMessage (Account trigger)
  • Set Account Workflow Message (Account workflow rule)
  • SetContactTriggerMessage (Contact trigger)
  • Set Contact Workflow Message (Contact workflow rule)
Now, with the stage set, let's move on to the tests.

Test 1: Create a person account


What happens when we create a person account?  Account workflow rules and triggers fire, but Contact workflow rules and triggers do not.

Test 2: Update an Account-only field on the person account


What happens when we update the Website field? Account workflow rules and triggers fire, but Contact ones do not.

Test 3: Update a Contact-only field on the person account


What happens when we update the Birthdate field? Oddly enough, Account workflow rules and triggers fire, but Contact ones do not.

Test 4: Update a special Account-Contact field on the person account


Knowing that the Account Name is linked to First Name and Last Name for Person Account records, what workflows and triggers will fire when you edit First Name or Last Name? Once again, Account workflow rules and triggers fire, but Contact ones do not.

Workaround for Contact workflows and triggers


The only supportable way to get Contact workflows and triggers to fire for Person Account records seems to be to recreate those workflows and triggers on the Account object. Whether this workaround is a pro or a con depends on your point of view. Obviously this creates more work and maintenance, but it allows for more granular and logical control over what happens with Contact fields for a person account vs. a regular contact.

Wednesday, July 9, 2014

XML Parsing Error: XML or text declaration not at start of entity

I'm not sure whether it's an XML specification requirement, but it appears that when rendering XML with Salesforce there cannot be any whitespace between the apex:page start tag and the first character.

The following code will work:


But the following code with visually pleasing indention will not:


If there are any unexpected spaces present after the apex:page start tag, you will likely encounter an error: XML Parsing Error: XML or text declaration not at start of entity


Monday, June 23, 2014

Faraday::SSLError certificate verify failed


Faraday::SSLError
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

In recent memory, this is easily the most frustrating error I've had to troubleshoot. I needed SSL to work so that I could establish secure communication between Salesforce and my Rails app on Heroku. Unfortunately, neither Heroku's sample Ruby (off Rails) app nor general OmniAuth documentation seemed to give any insight into the error or how to resolve it.

Piecing together bits of knowledge gleaned from Google searches, I eventually arrived at the following solution for Rails development on Windows 8:

  • Download root certificates in PEM format
  • Set an environment variable called CA_FILE, pointing to the downloaded PEM file
  • Create an omniauth.rb initializer file, as suggested by OmniAuth
  • Specify the :client_options hash value for the :salesforce provider, as shown below

I know I'm going to run into this error again in the future, and hopefully this blog post will help me and hopefully others resolve the problem in the most secure and supportable means available.

コンポーネントからページコントローラメソッドを呼ぶこと

初めて日本語でブログ投稿ですから、いろいろな文法的と文化的のミスしてしまって申し訳ありません。下記のコンテンツは「Controller Component Communication」の翻案です。

ある時にapex:Componentからページコントローラメソッドを呼ぶ必要が出て来ます。この場合で、ページコントローラをapex:attributeでコンポーネントコントローラに渡せば、以上の呼ぶことが出来てなります。

標準コントローラお使ってサンプルコードが提供されてお参照してください。サンプルコードはLeadCustomSaveページの作成方法です。


LeadCustomSave.page



ControllerHandleExtension.cls



CustomSave.component



CustomSaveController.cls


Wednesday, June 18, 2014

Rails directories for Heroku Connect

Heroku Connect is a new add-on offering that simplifies data synchronization between Heroku apps and Salesforce. At its core, Heroku Connect periodically syncs selected objects and fields between Salesforce and Heroku. While the demo Rails app provided by Heroku gives a solid recommendation for extending ActiveRecord::Base to create an analog for models in the Heroku Connect database, some gaps still existed in terms of how to best set up your Rails app and define your classes.

After some experimentation, I decided on the following setup for my app:

  • Heroku Connect model paths: app/models/heroku_connect/schema_name/table_name.rb
  • HerokuConnectRecord::Base
  • Autoload lib modules and classes

I'll explain the setup in more detail below, but quick question: How would you set up your Rails app directories for Heroku Connect?

Heroku Connect model paths


All models in your Rails app by default go in the app/models/ directory. Rails knows to autoload models stored in the models/ directory, and as a result it seemed to reason that Heroku Connect models should go in the same directory. But a namespace is needed to prevent conflicts between regular Rails models and Heroku Connect models. And moreover, what if you have multiple Heroku Connect databases in play?

My implementation addresses this by first moving Heroku Connect models into its own namespace a la the heroku_connect/ directory. Next, different Heroku Connect databases can be split by the schema name or some other unique identifier for each database.

Examples of Heroku Connect model class paths, assuming the schema name "salesforce"
sObject NameClass NameModel Path
Car__cHerokuConnect::Salesforce::Car__capp/models/heroku_connect/salesforce/car__c.rb
OpportunityLineItemHerokuConnect::Salesforce::OpportunityLineItemapp/models/heroku_connect/salesforce/opportunity_line_item.rb
AssessmentPeriod__cHerokuConnect::Salesforce::AssessmentPeriod__capp/models/heroku_connect/salesforce/assessment_period__c.rb

Note: For custom object models, the double-underscores can be written as-is in the name of the .rb file. To be more technical, use the String.underscore method in irb to derive the expected name of the model's .rb file. For example, JointVenture__c would become "joint_venture__c.rb", and Lazy_Boy__c would become "lazy_boy__c.rb".

HerokuConnectRecord::Base


Heroku recommends extending ActiveRecord::Base to create a base for all Heroku Connect models. I thought that instead of calling my base class SalesforceBase, I would call it HerokuConnectRecord::Base to be mimic the naming convention for ActiveRecord::Base. While there's no direct need for this to get going, I figured I would adopt this convention in case I need to extend other Active__ modules or classes down the road for Heroku Connect.

All that's needed to set up this class is the following code, placed within lib/heroku_connect_record/base.rb:

module HerokuConnectRecord
  class Base < ActiveRecord::Base
    self.abstract_class = true
    establish_connection ENV['DATABASE_URL']
  end
end

All Heroku Connect module classes would then extend this class, as in:

module HerokuConnect
  module Salesforce
    class Car__c < HerokuConnectRecord::Base
      self.table_name = "salesforce.car__c"
    end
  end
end

Autoload lib modules and classes


Finally, to autoload HerokuConnectRecord::Base (and potentially future custom classes for Heroku Connect), I adopted ifyouseewendy's recommendation to add the following line to config/application.rb:

config.autoload_paths += %W(#{config.root}/lib)

Tuesday, June 10, 2014

Auto External ID for Multi-Partner Integrations

Some companies need to integrate with multiple partner systems, each having its own set of unique identifiers. When you only have one or two of these external identifiers to manage, you can get away with setting up system-specific External ID fields. But happens when you trying to integrate with more than two such systems?

To make things worse, sometimes you have legacy ERP systems (e.g., in the higher education industry) that don’t have unique identifiers stored in a single field. Instead they use inferred keys derived from combinations of other fields.

In lieu of implementing a full MDM solution, one option to easily set up and manage these external identifiers can be to create just three components in Salesforce for each object: two fields and a workflow rule.

Auto External ID


The Auto External ID field is a Formula (Text) field that produces the string that represents the expected, globally unique external identifier. For example, the field could be a concatenation of two other values that produces something like “001154-900100203”.



Having this field also enables administrators to easily troubleshoot problems with the External ID value by comparing the expected identifier with the actual identifier.

{!ObjectLabel} External ID


The {!ObjectLabel} External ID field is a simple Text field configured to be a unique external identifier. The intent is that the Auto External ID value will always be stored in this field, so that DML operations via API’s can match on this field.



The {!ObjectLabel} part of this is simply a recommended labeling convention that helps your field look more like an out-of-the-box Salesforce field.

Set {!ObjectLabel} External ID


Finally, the Set {!ObjectLabel} External ID workflow rule should be configured to fire only when the Auto External ID and the actual External ID values do not match. This way, you avoid having a workflow rule that continuously edits a field blindly even when there is no need to update the field.



The workflow rule makes use of a simple field update that just sets the External ID field to the Auto External ID value.



And that’s all there is to this pattern! Now you can quickly create external identifier fields that are consistently derived and simple to troubleshoot.

Monday, May 26, 2014

Last Event or Task Date vs. Last Activity Date

Salesforce gives users the ability to run reports on the Last Activity Date for all objects, out of the box. But what if users wanted to distinguish between Last Event Date vs. Last Task Date? Or break up the data by other criteria?

Luckily, the Declarative Rollups for Lookups package (thanks to Andy Fawcett) can solve this problem, with only a few minor tweaks to work around a Salesforce limitation: field ActivityDate does not support aggregate operator MAX.

The general idea is to create two custom fields on the object of your choice: Last Event Date and Last Task Date. Follow these up with two Lookup Rollup Summaries, and you can now easily split your activity data by Tasks vs. Events. The end result? Reports like the following.


Follow this tutorial and give these new fields a try! Please add a comment below to let me know whether this tip works for you.

Friday, May 23, 2014

Report Folder: Embedded Analytics

I thought it'd be worthwhile to jot a few notes about consistency in managing Salesforce reports designed for embedded analytics. Specifically, help admins quickly identify and find these reports, so that accidental edits can be avoided in organizations with hundreds or thousands of reports.

The idea is pretty simple:

  • Put all reports used for embedded analytics in a folder called Embedded Analytics
  • Begin all report names with the object label and a colon (':'), so that the target object is immediately understood. For example: "Contact: Activities Last 30 Days"
  • Begin all report descriptions with, "Embedded chart showing ..."

Wednesday, April 30, 2014

Custom Button connection.js: INVALID_SESSION_ID

After deploying a custom button from a sandbox to Production, I noticed that the button stopped working. Puzzled, I opened the Web Console in Firefox to find the following error message: INVALID_SESSION_ID.

I was not doing anything special with sforce.connection.sessionId in my code. All the button does is {!REQUIRESCRIPT('/soap/ajax/30.0/connection.js')} and move on to query and update the database. The same code was working fine in my sandbox still, so I figured the next best thing would be to actually look at sessionId to see what's wrong.

At first glance, sessionId looked like a perfectly fine mess of characters that are reasonable for a Session ID value.

00DG0000000krVL!ARQAQP3vNkwdt8OU3WVIjlAwQvWtGcFcIjWq.0jb7A5wYNljj6Oog50.wDma4uUiB.PeDMqx1mLwkMyGj78gyMGLRLGTxic1

Then, I compared sessionId to the sid cookie, and I noticed that the two values were very different.

00DG0000000krVL!ARQAQLWR697R.eqoAy4178kYPxh2kJGv6BOpv_wsHc6KBjY6UPY01.Rfncn6WAMS2klSm.gLvDOWicNKujfE4smTPBLJvyS5

Refreshing the page in my browser did nothing for the problem. The same two values consistently appeared, and the INVALID_SESSION_ID error consistently broke my code.

Finally, the fix was implemented in the form of additional JavaScript in a static resource. This resource, added using REQUIRESCRIPT() after connection.js, simply maps the cookies in the current document and then stores the sid value in sforce.connection.sessionId. The addition of this step resolved the INVALID_SESSION_ID error as far as I could tell.

Has anyone else ever encountered this issue, in Spring '14 or earlier? I feel like I've stumbled upon a bug, because as far as I know connection.js used in a custom button should not need this kind of "initialization".

Saturday, April 26, 2014

Auto-naming Records in Salesforce

Usually on Sales Cloud implementations, business users will want to know, "What should we name our opportunities in Salesforce?" Great question, and a better follow-up, "How can we get our users to follow this convention?"

The Best Practices for Sales Managers still recommends, "Opportunity name reflects what the deal entails. ... it will help to simply adopt a consistent opportunity naming convention." While there are many ways to automatically name a record in Salesforce, each having different pros and cons, the method described below should offer the greatest flexibility and ease of maintenance for administrator.

When implemented for Opportunities or for any object, business users will be able to:
  • Create the record with a custom name; or
  • Create the record with an automatically generated name matching the naming convention
Furthermore, business users will be able to:
  • Change the record name later, regardless of how the name was generated
  • Easily revert to the automatically generated name

One Rule. One Field. One Update.


All that's needed are three components: a formula field, a workflow rule and one field update.

For simplicity and consistency across objects, the formula field can be called Auto Name (AutoName__c), and its text output should be exactly what you would expect to see based on your naming convention. What's nice about using this formula field is that you can visually see what the result would be if you were to apply auto-naming to existing records.


Next, create a workflow rule called Set Opportunity Name, with a simple formula determining the entry criteria: LEN( Name ) <= 1. The idea is that if the user types in a single character in for the record name, which is a required field, Salesforce assumes that the user wants to apply the auto name.



Finally, add a quick field update called Opportunity Name to the workflow rule. All this field update needs to do is to set the Opportunity Name field to be exactly the same as Auto Name, using a formula.


With these three components, you can help your users make the most of auto-naming in Salesforce while still giving them the flexibility of later customization.

Monday, April 14, 2014

SAML 2.0 Single Sign-On (SSO) Tutorial for Salesforce

Conceptually understanding single sign-on (SSO) is great, but actually implementing SSO can be a tricky business. Especially if you don't do it very often. Or if you have no experience with SSO at all. While there are many modes of SSO available for consumer apps, such as OAuth, SAML is still very prevalent in the enterprise applications space.

Salesforce is amazing in that with no hassle and for free (read $0.00), you can set up a SAML SSO environment with two DE orgs, one as a service provider and one as an identity provider. This should give you a fine test bed for exploring the protocol and also for testing integrations with other systems. Unfortunately the best tutorial previously available is now outdated, presenting a challenge to new SSO admins trying to wrap their heads around SAML 2.0 and Salesforce.

To bridge the gap, I consolidated a day's worth of tinkering and re-learning into a single SAML 2.0 SSO Tutorial for Salesforce. I hope that other developers and admins can benefit from this guided exploration of both sides of SAML 2.0, using Salesforce both as an identity provider and as a service provider.

UPDATED April 15, 2014: Thank you, Chuck Mortimore for sharing a link to a more in-depth walkthrough on Developer Force for implementing SAML 2.0 SSO using Salesforce.

Case-Insensitive 18-Character Org ID in 15 Seconds

In some situations you want to get your org's case-insensitive 18-character ID, instead of the case-sensitive 15-character ID shown in the Salesforce.com Organization ID field on the Company Info page.

You can actually copy the 18-character ID to your clipboard in 15 seconds, using only your web browser. If this video demo goes a bit too fast, below are the written steps:

  1. Open the Developer Console
  2. In the bottom panel, open the Query Editor tab
  3. For the query, write: SELECT Id FROM Organization
  4. Click the Execute button. The keyboard shortcut is to press Tab, Tab, Enter.
  5. Double-click the single ID returned to select the 18-character value
  6. Copy and paste as needed

Tuesday, April 1, 2014

Contact Age Formula Field

Salesforce users may want to see the current age of a contact, without resorting to mental date arithmetic based on the standard Birthdate field. Since this example is not listed (at the time of writing) in the Sample Contact Management Formulas, the formula below should do the trick for anyone looking to add this info to Salesforce.


Sunday, March 16, 2014

3 Ways to Tell What Version of Salesforce You're Using

Your out-of-the-box Salesforce experience is typically driven by two attributes: your Salesforce edition and the version (i.e., which release) of your org.

The edition (e.g., Enterprise Edition, Developer Edition) is readily identified by examining your browser's title bar or running a query: SELECT Id, Name, OrganizationType FROM Organization

But finding the version is a bit trickier, with no "official" method to reference. You can try one of the three methods below:
  • Switch to one of the standard, out-of-the-box apps (e.g., Sales), and look at the app logo

  • Create or edit a formula field, and look for the largest API version in the $Api object

  • Generate an Enterprise WSDL (Setup > Build > Develop > API) and look for the SOAP service endpoint location

You can convert the API version number into a release using the assumptions that Salesforce pushes three releases a year (Winter, Spring, Summer) and that each release corresponds to a new major version number. For example, we know that Winter '14 is API version 29.0. This means that 28.0 is Summer '13, and 30.0 will be Spring '14.

Thursday, March 13, 2014

Allowed User License Changes: Upgrades, Downgrades, Switches?

An infrequent but significant question that companies ask about Salesforce user licenses is: Can I switch from this license to that license? At the time of Winter '14, I could not find an official matrix of allowed user license changes. However, the question lingers, and I balked at the thought of manually trying to change user licenses to see what happens every time I needed an answer.

Using what I hope is a valid approach, I've compiled a matrix of allowed changes, using what licenses existed in my Winter '14 test org. I'm sharing the high-level summary of my approach in case anyone case to reproduce the same results.
  1. Create test profiles for every user license
  2. Run an Apex test to compile the results of every possible change among profiles
Below are the user licenses that were analyzed and included in these results:
  • Chatter External
  • Chatter Free
  • Chatter Only
  • Content Only
  • Customer Community
  • Customer Community Login
  • Customer Portal Manager Custom
  • Customer Portal Manager Standard
  • Force.com - One App
  • Gold Partner
  • High Volume Customer Portal
  • Knowledge Only
  • Partner Community
  • Partner Community Login
  • Salesforce
  • Salesforce Platform
  • Siteforce Only
  • Work.com Only
My wish is that anyone asking the same question can now refer back to this matrix for a quick answer. All feedback on the accuracy of the results will be greatly appreciated.

Friday, February 21, 2014

"Illegally" Reparenting Children in Master-Detail Relationship with Apex

If you have a Master-Detail field in Salesforce that's not reparentable, what do you think will happen if you use Apex to change the field value on a record? I had expected to see an exception thrown. But after a few hours of head-scratching, I discovered I was wrong.

If you try to change the value of a Master-Detail field that is not configured so that "child records can be reparented," you will not see an error. Salesforce will simply leave the existing value in place as if you never attempted to make the change in the first place.

The interesting implication here is that developers should write Apex tests to validate expected an Master-Detail configuration. Admins and developers can work together more reliably, if you explicitly write an Apex test that confirms whether a Master-Detail field can be reparented. This way, when an admin changes the field configuration and unintentionally breaks something, a red flag will be raised by the test method, pointing you clearly to the unexpected configuration change.

To see what happens if you reparent a child record when that's not "allowed", try this:
  1. Create a custom object labeled "Alias" (Alias__c), with the record name configured as a text field
  2. Create a Master-Detail field on Alias labeled "Account" (Account__c)
  3. Configure the Account field on Alias so that child records cannot be reparented to other parent records after they are created
  4. Create an AliasUtilTest class, as seen in this paste, with a test method to assert that changing the Account field on an alias is a futile effort
  5. Run the test method in AliasUtilTest

Monday, February 17, 2014

AJAX Toolkit 29.0 Home Page Component

For orgs trying to push the limits of the standard Home tab in Salesforce, using JavaScript is essential. However, trying to pull in the AJAX Toolkit is not as easy as adding a merge field to a new home page component, because merge fields don't work here. Fortunately, making an assumption about where the toolkit resides allows us to create a home page component that's equivalent to {!REQUIRESCRIPT("/soap/ajax/29.0/connection.js")}.

This 1-minute video shows you how to create the component and add it to your home page layout, using the sample code from "Home Page Component: AJAX Toolkit 29.0". The only real "trick" that's needed to make the component work is to extract the session ID cookie and pass it to the sforce object.

Now all of your home page components can be Ajax-enabled.

Monday, February 10, 2014

Recommended Usernames for Salesforce Communities

With the shift from Portals to Communities, salesforce.com delivered an attractive package of changes and improvements for a critical feature. However, one subtle change in Communities produced significant implications for Portals-to-Communities migrations and new Communities implementations: the username namespace.

What do you do with usernames that were once acceptable but can no longer be used? And how do you prevent future collisions with other companies and their communities, so that we all play nicely as multi-tenants in the Salesforce world?

In short, there are a few options you can adopt, depending on your use case and appetite for supporting customizations:
  • The domain-qualified username; or
  • The community-qualified username; or
  • A (domain-community-qualified) hybrid username using both of the above schemes, and probably the best option of the three

All naming conventions are described below, and in all options the optimal implementation requires that you create a custom login page.

Background


Every username has an interesting property: The username must be unique within a given namespace or context. For example, you and I can both use the username "superuser", if you register "superuser" with Twitter and I register "superuser" with Instagram. In this case, Twitter and Instagram represent two unconnected namespaces. But as soon as you try to register the same username with Instagram, you'll get an error telling you that the username is already taken (by me).

Salesforce used to have separate namespaces for every single portal. This means that I could log into my company's org as martyc@slalom.com and also log into a partner portal as martyc@slalom.com, even though the usernames were identical. The reason is that in the Portals age, Salesforce considered my company's org and the partner portal as distinct and separate where usernames were concerned.

But with Salesforce Communities, usernames are all of a sudden pooled together into redesigned namespaces. Partner Community usernames now exist in a namespace that is shared among all internal organizations and partner communities, even including salesforce.com's own Partner Portal. On the other side, the namespace for Customer Community usernames is unique for each org, but the namespace includes Partner Community and internal usernames within the same org.

Companies that want to create a consistent customer or partner experience should choose a naming convention that minimizes the likelihood of username collisions.

Edit: Customer Community usernames share a namespace with other Partner Community and internal usernames within an org, but not with other orgs. The post originally stated that Customer Community usernames were pooled together with Partner Community usernames and internal usernames across all orgs. Props to Andy Ognenoff for noting this inaccuracy!

Domain-Qualified Usernames


The domain-qualfiied username is a naming convention where a community user's username also contains that user's email domain. For example, John Smith is a Salesforce user at Acme Corporation, which has a partnership with the Zenith Group. Knowing that John's regular username is jsmith@acme.com (also his email), Zenith's admin sets John's partner community username to be jsmith.acme.com@zenith.com.

Effectively, the domain-qualified username contains the following elements, which can be joined and transformed as you wish. The example above shows a period between the partner's prefix and domain, with the parent company's domain coming last.
  • Customer/partner email prefix (e.g., jsmith)
  • Customer/partner email domain (e.g., acme.com)
  • Parent company's domain (e.g., zenith.com)

For best results, you should implement a custom login page that accepts the user's email as the username, transforming that email to the community username behind the scenes.



Sample Community Login URL What the User Enters Username Stored in Salesforce Sample Email
https://zenith.force.com/partner/login jsmith@acme.com jsmith.acme.com@zenith.com jsmith@acme.com
https://zenith.force.com/partner/login martyc@slalom.com martyc.slalom.com@zenith.com martyc@slalom.com

Community-Qualified Usernames


The community-qualified username is a naming convention where a community user's username contains an additional "subdomain" after the '@' symbol. For consistency and easy decision-making, you can adopt a community's URL path as the subdomain. For example, Jane Doe is a customer of two divisions within Gamma Corporation, and Jane is also a customer of the Delta Group. Jane logs in as "jdoe" to all three communities, even though Jane's true usernames for the three communities are:

  • jdoe@products.gamma.com
  • jdoe@services.gamma.com
  • jdoe@my.delta.com.

As you can see, the usernames only differ in what comes after the '@' symbol. In this case, they key components in the username are:

  • Unique prefix (e.g., jdoe), self-selected by the user or auto-assigned by the parent company
  • Unique community subdomain (e.g., services.gamma.com), which includes the parent company's primary domain

Again, for best results, you should implement a custom login page. This time the custom page should accept the unique prefix (that comes before the '@' symbol) as the username, and then add on the community subdomain behind the scenes.



Sample Community Login URL What the User Enters Username Stored in Salesforce Sample Email
https://gamma.force.com/products/login jdoe jdoe@products.gamma.com jane.doe@gmail.com
https://gamma.force.com/services/login jdoe jdoe@services.gamma.com jane.doe@gmail.com
https://delta.force.com/my/login jdoe jdoe@my.zenith.com jane.doe@gmail.com

Hybrid Usernames


The (domain-community-qualified) hybrid username is a naming convention that combines the elements of both the domain-qualified username and the community-qualified username. As a result, the hybrid username can provide the following benefits:

  • Intuitive username for the customer or partner. Customers and partners can log in using their emails.
  • Minimized risk of duplicate usernames, across all Salesforce orgs and communities

Inside an org, every community username contains:
* Customer/partner email prefix (e.g., jsmith)
* Customer/partner email domain (e.g., acme.com)
* Unique community subdomain (e.g., services.gamma.com), which includes the parent company's primary domain

Due to the complexity of the stored username, you must implement a custom login page to succeed with this option. The login page should accept the user's email as the username, again transforming that email to the community username behind the scenes.



Sample Community Login URL What the User Enters Username Stored in Salesforce Sample Email
https://zenith.force.com/partner/login jsmith@acme.com jsmith.acme.com@partner.zenith.com jsmith@acme.com
https://zenith.force.com/customer/login martyc@slalom.com martyc.slalom.com@customer.zenith.com martyc@slalom.com
https://gamma.force.com/products/login jane.doe@gmail.com jane.doe.gmail.com@products.gamma.com jane.doe@gmail.com

Other schemes?


If you have other user naming conventions or username schemes for Salesforce Communities, please share them in the comments below!

Friday, January 31, 2014

JS for Salesforce Home Page Component

While it's not possible to add a Visualforce page or component to the standard Salesforce home page, it is possible to inject straight HTML and JavaScript into the home page via an HTML Area component. Furthermore, you can use Salesforce's AJAX Toolkit to connect to the API, which opens up a world of dynamic possibilities for your Home tab.

There's only one trick to connecting to the API: You need the current user's session ID. Luckily, this information is stored in the sid cookie and can be retrieved and easily stored in the sforce.connection.sessionId property.


From here, you can use all of the tools in the AJAX Toolkit to bring your data to life on the Home tab.

Monday, January 27, 2014

Extending Chatter Profile with Custom Subtabs

Out of the box, Salesforce only gives users limited access to user information through the Chatter Profile page. There are two tabs: Feed, showing the Chatter feed, and Overview, giving a nice overview of additional Chatter relationships and the ability to edit ~1 dozen User fields. However, organizations that want to extend the Chatter Profile page will not find any button-click admin options in Setup.

Thankfully, custom Profile subtabs give those orgs an option to extend the Chatter Profile without having to completely re-engineer the entire page (which is not really feasible considering the Chatter feed and related components). What's required is some Visualforce magic to expose related information directly on the Chatter Profile page as subsequent tabs coming after "Feed" and "Overview".


To provide access to personal data without exposing that data to other users, Salesforce provides a thoughtful separation of different subtab use cases. Admins have the option of showing custom subtabs in one or more of four different contexts.
  • Profile (Others): When a user looks at someone else's profile in the internal Salesforce "community"
  • Profile (Self): When a user looks at his/her own profile in the internal Salesforce "community"
  • Profile in Communities (Others): When a user looks at someone else's profile in a customer or partner community
  • Profile in Communities (Self): When a user looks at his/her own profile in a customer or partner community

Without restrictions on what to extend, subtabs really give developers the ability to deliver any functionality desired within the constraints of Visualforce. And knowing that the profile user's Salesforce ID can be retrieved from the sfdc.userId URL parameter, developers can provide a fully personalized experience for the user.

Thursday, January 23, 2014

Secret Search Layout Feature in Salesforce1

If you open an object tab in Salesforce1, you'll probably see something very similar to the screenshot below. A search box. Available list views. Recent items with... a teaser field on each item?


S1 has an interesting feature that enables users to see one field on each recent item on an object tab's home page. This feature appears to be undocumented in either the first version of the Salesforce1 App Developer Guide, or the "Customizing Search Layouts" Help page.

To configure which field appears here, modify the Columns Displayed for the Search Results Search Layout on the object. This is counter-intuitive, because in the browser app the columns displayed for recent items is controlled through the {!ObjectLabel} Tab Search Layout.


Have you found any other hidden configuration options for S1? Please share them in the comments!

P.S. Thank you to Brian Casey for tipping me off to this interesting discovery!