Jul 21, 2014

Generic Package Extension Architecture - Accessing External Classes

Salesforce partners often have to dodge complexities that relate to the multi-tenancy nature of the Salesforce platform. A partner package can be installed into different edition orgs with unknown sharing models and may need to interact with unique custom objects on these orgs. Many of these complexities can be handled by dynamic handling of SOQL and object, but sometimes more drastic measures need to be taken.

For example, a partner may have a customer that requires completely different business logic for a certain piece of the partner code. Adding customized code to an otherwise generic package is not the best idea, but is possible. But what if the custom code need to query objects that only exist on the target org?

In this post, we’ll review how to use an extension architecture that uses an interface to generically call other packages or even methods in the target org that are unknown during package build.


First, let’s review a few benefits to using this approach:
  • The base package can execute logic related to custom objects that do not exist in every org.
  • The base package can dynamically call classes residing in the hosting org or in a separate “extension” package.
  • Each implementation of the interface is independent of the others, can have different business logic, and is not affected by other implementations.

Step 1: Create Interface in the Base Package
In this step, we’ll create an interface class and a method that is so generic, that the base class can use it with any number of parameters and can get a response that includes any number of variables.
  1. Create a new global class inside the main package.
  2. Create a new global interface inside the new global class.
  3. Create new generic method inside the new global interface (allows different implementation to use in completely different way). Define the return type as a generic list of object and one parameter as list of object.

Step 2: Create External Class
In this step, we’ll create the external class that implements the base package interface. Parameters that are sent into this class go by the normal apex rules, so maps and lists that are passed can be manipulated in the external class (passed by reference). Since the interface blueprint is flexible, the implementation can be static in case a different API need to be accessed (needs to be static in some cases because a trigger starts the execution).
  1. Create a new class in a separate package or in the target org itself. Class should implement the interface from Step 1.
  2. Define a the generic class that is present in the interface. In the class, replace the input param list with variables that have concrete types, and using the new variables, call a class that does all the work. End the method by returning something.


Step 3: Call External Class From Within The Base Package
Now that the internal interface and the external implementation are ready, this new architecture can be used. If the base package may include many implementations of the interface, Use an unprotected custom setting to store and retrieve the name of the external class and the namespace (if in an extension package).
  1. Check if an external class needs to be executed by checking that it exists in the custom setting.
  2. Get the class name that needs to be executed. If the class is in a separate package, the package namespace is needs too.
  3. Use forName method to create a type variable from the string names of the class and namespace.
  4. Use the type to create a new instance of the class. Cast into the interface since it is a concrete type that is available within the base package.
  5. Use the class instance to call the generic class in #3.
  6. Cast the returned object to the appropriate type.


Apr 29, 2014

Salesforce1 Mobile Device Testing

Being in the middle of the Salesforce1 Developer Week, lots of Salesforce professionals will be tackling the Salesforce1 Mobile Workbook and begin customizing their own Salesforce.com orgs soon after.

If you've worked with Salesforce1 before, you're probably already familiar with this URL:
<yourInstance>.salesforce.com/one/one.app

If you're not, try it out in your browser window.  Replace <yourInstance> with your instance (IE: na4.salesforce.com/one/one.app).  You'll see the equivalent of the Salesforce1 mobile app within your window.  This makes testing slightly easier as you begin the early stages of a Salesforce1 related project.



As you begin to fine-tune though, you'll need to begin doing some mobile testing as users will use different sized mobile phone and tablet screens.  As seen above, using the one/one.app link, you get a nice preview, but the preview is sized to your screen's resolution.  You're screen's resolution may not always be the best representation of what your users are seeing.

Wouldn't it be nice if you could simulate the actual devices' resolutions?

Using Google Chrome's Developer Tools, you can!



Once the Developer Tools are open, within the bottom panel, click on the "Emulation" tab.  Choose a device to test with from the "Device" drop-down and then click on the "Emulate" button.



If you see something like the image on the left, you'll want to manually refresh your page.  When you do, you'll see a resized version of the Salesforce1 app/page as your users on that device would see it on their device.



 It's worth noting that if you don't see your user's device in the list, you can set your own custom resolution by clicking on the "Emulation" tab at the top, then "Screen" link on the left.  This is also where if you need to test portrait vs landscape screen modes, you can inverse the X and X resolutions (even if SF1 doesn't support it).



Apr 18, 2014

CTI 2.0 or Higher Settings Missing?

Usually while configuring Salesforce, you'll be logged in with a User that has been assigned the "System Administrator" Standard User Profile.  When you're using this type of account, you typically think you have access to and will see every configurable part of Salesforce.com that your Org has.

This isn't always the case - take for example the "CTI 2.0 or Higher Settings" section under Setup --> Customize --> Call Center --> SoftPhone Layouts --> Edit --> Inbound Calls.

This is what I was seeing:



This is what I was missing:



According to the documentation, "This section only displays if your CTI adapter was built using the CTI Developer's Toolkit 2.0 or higher."  While working in a client's Org, a 2.0+ CTI adapter was being utilized in both their sandbox and production Orgs.  Within their sandbox, I was able to see this section, whereas in production, the setting was missing.

Naturally, I wondered if it had to do with release differences, but the sandbox was Spring '14 and production Winter '14.

Setup-wise, they were identical with one small difference - my user account was no longer listed as a User under the Call Center's settings (Setup --> Customize --> Call Center --> Call Centers --> Click on the Name --> Click on the "Manage Call Center Users" button).  



Once it was added back, voilĂ !  The "CTI 2.0 or Higher Settings" section was back.


Mar 31, 2014

Calling Out to a Google API with an Access Token

One of the best things about Google is that they have APIs for all their services. These APIs come handy when you are trying to integrate with Google from within Salesforce. I recently had to do just that, and thought I'd share some of what I've learned...

First of all, I quickly realized that I couldn't use the Force.com Google Data API Toolkit because Google Data APIs are part of an older generation of Google APIs that have authentication methods that are deprecated. Also the Google Data APIs don’t include some of the more interesting enterprise applications that Google has to offer. The newer Google APIs are SOAP based and you need to go through an OAuth 2.0 handshake to authenticate. I used the OAuth 2.0 for Web Server Applications flow, and didn't have too much issues with it (look for a future post on that).

Since Apex does not have a client library for the newer Google APIs, I had to download the WSDL for the resource I wanted to access. I had a couple of issues with wsdl2apex, but was able to get around them. However, after some poking around, I realized that the wsdl conversion didn't work too well after all and had to abandon that path (I think it is related to how wsdl2apex translates attributes and gets around reserved words).

My next move was to make the SOAP call with a normal http callout, which meant that I had to create the XML body to add to the callout. Most of the transaction was a no brainer, but I had a hell of a time figuring out how to include the access token in the SOAP XML. I found the documentation to be either confusing, old, or plain wrong (I did complain about it in one of the boards, and they made some changes since). After quite a bit of trials and errors, I finally got it right though.

So to spare you the extra time it took me to put this together, the following is a very quick sample method that shows you how to set the SOAP headers, including the access token. Please note that this code sample is very simplified - I jammed everything into one method and did not include coverage. Also, in my implementation I am using the XML stream writer to create the body, but in this case included the XML as a sting so it is easier to read.

Mar 21, 2014

Inspecting SOAP Callouts Generated From WSDL (WebServiceCallout)

When working with SOAP callouts from within Salesforce, you can’t view the transaction headers and body xml before you invoke the callout. So if the callout fails or an exception occurs, you have very little information to help you solve the problem.

Here's what you need to do to see the contents of your outbound API requests:
  1. Start a free Runscope account (runscope.com) - it's a very useful tool if you do any type of integration.
  2. In Runscope, click on the Captures link on the left menu. At the top of the Captures page, you’ll see a capture URL for the bucket you’re in. Copy that URL.
  3. Use the URL from #2 to create a new Remote Site setting in your org.
  4. Find the interface port class that was created when you converted from a WSDL using wsdl2apex, or the location of the WebServiceCallout.invoke() method in your code.Change the callout endpoint to the URL from #2, and save the class.
  5. Execute your code (anonymous or through UI).
  6. Go to the Runscope Captures page again. A new capture will be shown. Expand the new capture and click on the Request link - that will show you exactly how your transaction is formatted including the header and the SOAP body.

Mar 3, 2014

Known Issue: Publisher Update Actions Invoked by Inline Edits

One of our Orgs required multiple field updates to be performed through a Publisher Action.  The Action was working great from Salesforce1 as well as from the Chatter feed when it was realized that the field updates defined in one of the Publisher Actions was triggered from normal updates to the record.

I was able to replicate this in a Developer Org - below are the details:

Here's the Action:



The Action is simple - when used, the Opportunity's Stage is updated to "Closed Won."  When looking at the Action's Layout, it works with or without (displayed below) fields present:



From Chatter, the Action looks like this:



From Salesforce1, it looks like this:



With the Chatter feed displayed at the top of the page, when you make an inline-edit, the Publisher Action will run:



  1. Before:  The Stage is already set to "Prospecting" and the Approval picklist is blank
  2. Update:  Using inline editing, the "Approval" field is updated to "Approved" and then the "Save" button is clicked.
  3. After:  Once the page refreshes from the "Save," the Stage has been flipped to "Closed Won" as defined in the Publisher Action.
Again, this is in a brand new Developer Org where the only things created were the "Approval" picklist field and the Publisher Action.  The above is not occurring due to any Apex Code behind or Workflow Rule Field Updates.

After pinging Salesforce, we've learned that this is a known issue with a patch due out soon.  A possible workaround was to add another Action, a Create action for the same entity, if possible, and locate it to the right of the Update Action.

Other short-term solutions include hiding the Chatter feed when using inline editing mode or using the "Edit" mode and avoid using inline editing mode.

Dec 18, 2013

Creating A Generic Picklist Selection Dropdown in Apex

When creating a custom search feature with Visualforce and Apex, I normally create a few filters for relevant picklist fields. It's a nice way to give the user quick and easy way to search for records. The best thing about these custom picklist fields is that you can add the "All records" or "no records" options When creating the picklist options, in addition to the the picklist values that you can get from the schema.

The first thing you want to do is to write a generic method that builds the list of select options. With a generic method you can create several filters with the same code.
Next you want to create a getter that Visualforce can call to get the picklst values:
And lastly, you want to write a property that the Visualforce page can read and write to when ou use makes their selection. You can use that value in your search SOQL

Oct 10, 2013

Anatomy of the Group By Cube SOQL Query

Ahead of my Dreamforce session Custom Analytics Using SOQL Cubed Results (register here), I decided to write a preview post about the Group By Cube syntax you want to use.

Let's start with a simple SOQL statement that retrieves three fields from the Case object, and filter it to only bring back records that are closed. I always add aliases to the fields in Group By queries because it makes working with the aggregated results in Apex a little easier, and eliminates the need to include the namespace in Apex if you're packaging the code for the. AppExchange.

Next, add the GROUP BY CUBE Statement with all 3 fields that are being retrieved. The order of the fields here does not make a difference as groupings for all field combinations are returned in the query.

Next add some aggregate functions. Getting summarized information is why you use Group By Cube, so its you chance to go nuts with AVE(), MIN(), SUM(), COUNT(), etc. Don't forget to ad an alias for the function here as well.

Because cubed results return 8 types of groupings, we need a way to identify which fields are used for the subtotals of each row. For that we can use the SOQL Grouping() function, which is basically like asking the platform if the field values are included in the specific row.

The last thing that we want to add is a way to assure that the results are sorted in a predictable way. This will make processing in Apex more convenient and economical. For that we are going to add an Order By clause that will assure that each subtotal grouping will be presented together. We also want to sort the values within each grouping, so you should add the 3 fields from the select to the end of the Order By.

That's it! Here's how the query looks like:

This is what the query returns with some sample results:

Oct 2, 2013

Winter '14: Cron Job Name and Type in SOQL

A handy enhancement will be arriving in Winter '14 that makes working with Scheduled Jobs much cleaner. The update will allow you to query for the Name and Type of CronTrigger records.

In v28.0, you were limited to CreatedById, CreatedDate, CronExpression, EndTime, Id, LastModifiedById, NextFireTime, OwnerId, PreviousFireTime, StartTime, State, TimesTriggered, andTimeZoneSidKey.  Unfortunately, no way to query for the job name or type.


Winter '14's v29.0 brings with it a new object called "CronJobDetail" and a few fields (Id, JobType, and Name).  CronTrigger relates to a CronJobDetail through its CronJobDetailId field.

Sound semi-familiar?  Take a look at our earlier post, Syncing Salesforce Changes to an External System with Future/Schedule Architecture.  In this post, we schedule an Apex job to handle our future/schedule processing and create a small side object called CronJob__c.  We stored the ID of the scheduled CronTrigger so that we could remove the job afterwards.  This object is no longer necessary now that we can query for our CronTriggers by name.



The types that are provided back are numeric - here is what each value means:
  • 0 - Data Export
  • 3 - Dashboard Refresh
  • 4 - Analytic Snapshot
  • 7 - Scheduled Apex
  • 8 - Report Run
  • 9 - Batch Job



Sep 18, 2013

DeleteRestrictedByFkException not found in section Exception


While working on a project recently, our newest addition to the team, Will, stumbled upon an undocumented bug when handling exceptions.

Error: __MISSING LABEL__ PropertyFile - val DeleteRestrictedByFkException not found in section Exception

Scenario:  When deleting a record that is a parent on a restrictedDelete Lookup field, deletion will fail throwing an exception describing the child-records that exist, preventing the deletion.  Normally, passing this exception to an ApexPages.addMessages() for use on a Visualforce page within an <apex:pageMessages />, a VF_PAGE_MESSAGE is encountered and pushed to the page instead of the expected exception (as seen in the screenshot above).

Using a standard dev org, I was able to quickly replicate this, using the sample record data provided. One of the pre-made Accounts ("United Oil & Gas Corp.) was associated with a few different cases.



If you attempt to delete the Accout in the Salesforce.com UI, you'll be directed to a new page that informs youthat it can't be removed due to the Account being related to various Case records (Case.AccountId).



Programatically, you can can do it with the following sample code.


Visualforce Page:


Apex Class:


When you access the new Visualforce page with the Account's ID (https://yourInstance.visual.force.com/apex/errorTest?id=001i000000JEOgk), you'll see:



The "Delete" button is designed to delete the Account record matching the ID in the URL.  We know we enountered an error in the UI, so we know to expect a page message along the lines of "System.DmlException: Delete failed. First exception on row 0 with id 001i000000JEOgkAAH; first error: DELETE_FAILED, Your attempt to delete United Oil & Gas Corp. could not be completed because it is associated with the following cases.: 00001001, 00001002, 00001021, 00001022, ..., ..., ..."

However, we see:



If we look at the debug logs, we can see the exception we wanted occuring ([7] below), but then we see the DeleteRestrictedByFkException VF_PAGE_MESSAGE occuring when the original exception is being passed to apexPages.addMessages(e) [9]:



I opened a Case with Salesforce support to see if there was another issue or if it was indeed a bug:
 "...this is a known issue with salesforce and we have noticed the same in the past which is currently with our R&D department, Unfortunately, we have not recieved any ETA for the fix..."
"We have noticed this error in different conditions and the R&D is working on the same to quickly fix this as soon as possible." 

If you're looking for a solution, I simply replaced:

With:

Aug 29, 2013

Geolocation Fields

I've been working on a new Arduino project that uses a low power GPS module to log geographic coordinates as I'm driving around.  Every minute after the module has a fix, I log the date/time along with unit's current latitude and longitude to a CSV on a 4GB micro-sd.

The raw data looks like this
When I want to plot these coordinates onto a map, I head over to the Google Maps Engine, upload my data, and I end up with something that looks like this:

To and from the gym tonight
So what does this have to do with Salesforce?  Geolocation fields!  Perhaps you'll be attaching your very own GPS logger to an advertisement adorned weather balloon that will float over San Francisco during Dreamforce '13.  Swap the SD card out for 3G/GPRS and potentially transmit your aeronautic advertisement's coordinates back to your Salesforce org.  Or perhaps start with something simpler...



Geolocation fields are currently beta and available for all editions of Salesforce.  Check out the notes within the Salesforce documentation as this beta field type is laced with various limitations like:
Other limitations of this geolocation beta release include:
  • Geolocation fields are not supported in custom settings.
  • Geolocation fields are not available in dashboards, Visual Workflow, or workflow and approvals.
  • Geolocation fields cannot be searched.
  • Geolocation fields are not available in Schema Builder.
  • DISTANCE and GEOLOCATION formula functions are available only when creating formula fields and in Visual Workflow.
  • Geolocation is supported in Apex only through SOQL queries, and only at the component level.

Also, note that Geolocation field counts as three separate fields

  1. Latitude
  2. Longitude
  3. A mysterious internally designated field
When creating a new field, "Geolocation" is one of the recently added field types.  You may have noticed it, but if you didn't, you'll find it in the options between the "Email" and "Number" types:




On Step 2 of the new field creation wizard, you'll specify the coordinate display notation, being in either "Degrees, Minutes, and Seconds," or "Decimal" notation.  You'll also need to specify the precision by way of decimal places.  Looking at my test data above, I've recorded 5 decimal places and will specify this below.




Once you have your new field added to our object and assigned to a page layout, this is what your new Geolocation field(s) will look like.  Remember the note about each Geolocation field counting as three?  Here you will see the two that are visible to the user.


What about the 3rd field that was designated for internal use?  Here's what it looks like within the Force.com IDE Schema:


If you attempt to use it in a standard select SOQL query, you'll find that you're out of luck... but wait there's a catch!


Per Salesforce, Winter '13 brought along two formula functions that can be used within SOQL queries; Distance to determine the distance between two locations and Geolocation which is a pairing of a latitude and longitude.  An example usage of these would be a query that brings back records based on proximity to a set of coordinates.  

  • The use of "Geolocation" will allow us to specify a latitude and longitude
  • The use of  "Distance" will allow us to indicate a Geolocation field (you guessed it, that mysterious 3rd field), a new Geolocation to compare against, and whether we want to compare in miles ("mi") or kilometers ("km").




I hope you've enjoyed this post and as always, feel free to leave comments.  We'd love to hear how you're using Geolocation fields in your business environment.


Aug 26, 2013

CRM Science at Dreamforce '13

Three of the CRM Science geeks and one of CRM Science's clients will be led sessions at this year's Dreamforce (Salesforce.com's annual user and developer conference), held at San Francisco's Moscone Center, November 18th through the 21st.

Abstracts of our sessions are below with links to the Dreamforce org where you can still join the session Chatter and the YouTube recordings.

Create Powerful Reports Using Force.com and SOQL Cubed Results - Ami Assayag

"Group by cube" is a powerful feature of SOQL that every developer should have in their toolkit. It's a very handy tool when you need to create complex Visualforce reports because you can get aggregate results for different groupings of values in a single SOQL call. Join us as we go over an example of using one "cubed" SOQL call to generate pivot data of multiple Case fields, and use the data to build Visualforce charts that are not available through standard reportingSession Link




Chrome Extensions for Salesforce Professionals - Kirk Steffke

Learn about two new Google Chrome browser extensions on the Chrome Web Store and AppExchange that provide Salesforce.com administrators and developers with time-saving tools and quick settings shortcuts to facilitate the day to day responsibilities they are faced with. These tools from CRM Science will save you time! Session Link

Answers Live Session 2 & Nonprofit HUB Answers Live Session 1 - Thomas Taylor

Making the Salesforce Success Community Answers a LIVE experience! Join us and get your burning non-developer-related Salesforce questions answered in a Q & A style forum. Get to meet and ask questions of some of the outstanding community contributors who have become the de facto Answers army. Nonprofit users can attend sessions targeted to their needs and ask questions of a panel with over 25 collective years of nonprofit-focused Salesforce experience.

Goin' Pro Bono: Leveraging Salesforce Professionals Within the NPO Space - Thomas Taylor

Preparing for successful skills-based volunteering projects for pro-bono ninjas.  Understanding the common risks and learning helpful tips for a pro-bono engagement.  We'll discuss setting reasonable expectations of the project and volunteer, barricading them from free reign, being a good consumer of this available resource, and signs the Pros are stumbling.  How to be a thoughful and effective non-profit volunteer. Session Link


Business Agility using Workflow & Approvals - PointRoll (CRM Science Client)

Not all logic needs to be coded programmatically. Force.com's workflow and approvals engine lets you easily invoke tasks, trigger email alerts, database updates, or messaging based on business events. Join us to learn the platform's workflow capabilities, and hear how other customers have gained valuable business process improvements using workflows. Session Link

Jul 15, 2013

Schedule Batch Apex in Summer '13

You could always schedule a Batch Apex before Summer '13.  Though, a new system method in this release did make it significantly easier to do.

Previously, I would create my batch class, with its batchable interface declared and its start, execute, and finish methods within.

Here's a sample batch class that runs through all of an org's Contacts and checks a box called myCheckbox__c, if it isn't already checked.

Then, to keep things tidy, I would create a 2nd class with the schedulable interface and a simple method.

Here's that schedulable class:

Then you could queue up the batch class by executing something like this:

Your job will be scheduled, on the hour, every hour.


You could have also implemented both the batchable and schedulable interfaces in the same class and provided a method to do your scheduling.

The new system.scheduleBatch() method that was included in Summer '13

You don't need to:
  1. Implement the schedulable interface (in the same or separate class)
  2. Create a Cron Schedule String (the '0 0 * * * ? *' string above) 
  3. Clean up finished jobs (scheduled jobs hang around until they are manually deleted or system.abortjob() takes care of them.  ScheduledApex clean up after themselves and are removed after running.
The above can be condensed into this:


And scheduled by calling:




Within the system.scheduleBatch() method 3 (and an optional 4th) parameters are provided:
  1. Instance of the batchable class
  2. The Job's name
  3. # of minutes to start the job in
  4. Scope size - 200 is the default for a batchable class' execute method.  This allows you to specify up to 2000 records per batch - just beware of hitting additional limits if you do.

Jul 7, 2013

New SOQL Clauses and System Fields

Summer' 13 and v28.0 brought a few new SOQL features for developers.  Included in those features are the FOR VIEW and FOR REFERENCE SOQL Clauses and their related LastViewedDate and LastReferencedDate date/time fields.  The new fields, unlike the other system date/time fields are dependent on the current user, meaning your values will be different from mine (illustrated below).

Let's define what a view and a reference are, per the release notes.  Records are considered:
Viewed - "when the user sees the details associated with it, but not when the user sees it in a list with other records."  
Referenced = "when a related record is viewed."
Here's a sample scenario to help understand these fields and how the clauses can be used.

User A creates a "Kirk Steffke" Contact record that is related to a CRM Science Account.  Upon creation, the LastViewedDate and LastReferencedDate for both will the same as the CreatedDate.

Contact:  Kirk Steffke

Account:  CRM Science

Notice above the LastViewedDate and LastReferencedDate fields.  These values are for User A, the user that created both records.  Observe closely and you'll notice that the Account's LastReferencedDate is slightly later than the LastViewedDate.  This is due to creating the Account record through the "New" button in the Contact's Account Lookup window.  The Account is first referenced upon the Account record's creation and a second time when User A is taken to the newly created Contact record.  The reference comes from the Account Lookup field on the Contact.

Since these two fields are user dependent, let's see how the data differs for another user that has never accessed either record.

Account:  CRM Science as viewed by User B

Contact:  Kirk Steffke as viewed by User B

The LastViewedDate has an impact certain areas of the user interface such as the Recent Items list in the toolbar or the items that quickly appear when using the search feature.  Below is a side by side comparison for the two different users.  The new Contact and Account records do not display in the quick search pop-down or in the Recent Items.


As seen here, just like the other sytem date/time fields, they aren't directly updatable.



However, the new SOQL clauses will do just that:

Don't expect to see an updated value when you run the above query; the query will update the LastReferencedDate for any rows returned as they are returned.  When you use the FOR REFERENCE or FOR VIEW clauses, the values returned will never be the actual values, they'll be the last values.  To see the actual values without updating them, use the original query where the clauses are omitted.


Jun 27, 2013

Securely Storing Credentials (and other 'stuff' too)

Building on Ami's last post, Syncing Salesforce Changes to an External System with Future/Schedule Architecture, I want to expand on a solution for storing your external systems' credentials securely.

Many times, when developing a custom Salesforce application, there are settings unique to the application that need to be stored somewhere.  A common solution is a dedicated Salesforce object with the fields necessary to support these options; there will probably be Checkboxes and Picklists that control and shape how the app works.  If an external system is involved, you'll probably have additional fields for things like a Username, Password, Token/Cookie, and an Expiration Date of sorts.

While this may work for some orgs (in a rush, don't know any better, or just don't care), there are some improvements that can be made.  Minimally, this involves making sure that your Profile permissions are in check (you always do, right?).  A step up would be the use of an Encrypted Text Field to store your Password.  However, if you're developing an Application for the AppExchange, you need a solution that will pass the AppExchange Security review.

Before we start building, check out Secure Coding Storing Secrets, particularly the Apex and Visualforce Applications section.

It says that when included in a Managed Package, a "Protected" Custom Setting is "only accessible programmatically via Apex code that exists within your package."

For this blog post, we'll use a Custom Object to store our Application specific settings as referred to above.  To maintain usability, we'll keep the Password field, but create a trigger to take the user inputted value, store it in a Custom Setting (as recommended by the Salesforce page), and then mask the Password field on the Custom Object.

We'll start out by building a Custom Object to hold our application specific settings, including the expected fields for calling out to our external system.

"I'd don't always use the  Schema Builder, but when I do..."
"... it looks like this."

Next, let's define our App's Custom Setting, where in this post, we'll store our Password securely.  You can expand on this and store more or all of your credential details if you desire.

Browse to Setup --> App Setup --> Develop --> Custom Settings and click on the "New" button.



Provide the basic details for your Custom Settings (like a name that applies to your package and a suitable description).  Make sure that you select "List" for the Setting Type and "Protected" for the Visibility settings, then click on the "Save" button.



After saving, you should be sitting on the Custom Setting Definition details page for your new Custom Setting.  The next thing we need to do is to create our Password field for this Custom Setting.  Click on the "New" button within the Custom Fields block.



On the next few screens, go ahead and create a new text field called Password.

If you've never used Custom Settings before, you should be getting the feeling that they are very similar to Custom Objects.  So much like them, that you can even retrieve their records via SOQL queries.


We're done with schema changes, so let's take a look again at the process we're building.

  1. A User will create/update an Application Setting (the Custom Object we built above) record with a new Password
  2. A trigger will look for a row within our Package Custom Settings that matches the Username
    1. If found, the Custom Setting's Password will be updated
    2. If not found, a new Custom Setting will be created
  3. The trigger will then replace the Application Setting's Password field with 8 asterisks (********).
Trigger Code:



Let's see what happens when you create a new Application Setting:



After clicking on "Save" the first thing I should notice is my password will be replaced with "********."


So far so good!  Now let's check on our Custom Settings.  We should have one record, with the password I provided.


Let's update the Password and make sure that it changes the Custom Setting and continues to replace the Password in the Custom Object.


The Custom Setting:

Great!  Now from any Apex code, I can query my Custom Setting for the applicable username and end up with my password.  Within a Managed Package, the Custom Settings created won't be visible to an Org that installs it.  Like they Salesforce page said, the Custom Settings will only be retrievable by the Apex code included in my package.  You can use a SOQL similar to this to get the applicable settings:

Notice the LIMIT?  That's to avoid another red flag that may occur on your AppExchange Security Review findings report.  All queries will need either a WHERE or a LIMIT.

That's all there is to it!  Adapt, clean, and condense this code to fit your needs and enjoy!