Pages

Wednesday, October 8, 2014

Salesforce: Related List in Page Layout

Background: we found out that Page Layouts have the same Last Modified Date by the same person, although we just update one of the Page Layout. The date shown is the latest date perform to update one of the Page Layout for that object.



Analysis
When you edit a page layout, select a Related List with Properties (tool icon) and click the icon. Many of us not aware that Apply column information to other page layouts: at below if you not scroll down.


When you scroll down, you will see all other page layouts available and they are selected by default.


Conclusion: make sure none of this checkbox is selected, otherwise it will effect:
  • Available fields, column order and Sort field asc/desc in other Page Layout selected will be overwritten
  • Last Modified Date and By in other Page Layout selected will be updated.



Friday, October 3, 2014

Salesforce: Custom Permission for Validation Rule

Custom Permission has been introduce on Summer '14 release as Developer Preview (only in Developer Edition organizations). Custom permissions let developer define access checks that can be assigned to users via permission sets or profiles, similar to how you assign user permissions and other access settings. For example, you can define access checks in Apex that make a button on a Visualforce page available only if a user has the appropriate custom permission.

You can query custom permissions to determine which users have access to a specific custom permission, use Salesforce Object Query Language (SOQL) with the SetupEntityAccess and CustomPermission sObjects.

On Winter '15 release, Custom Permission become Generally Available, and even better it is accessible via Validation Rule and Formula Field, admin should take most advantage of this enhancement.

Use Case:
Only a few people allowed to change Account Name when Type = Customer. This few people have different Profile or Role. Account OWD sharing setting is Public Read Only.

Solution: Create Validation Rule

1. Hardcode UserId or UserName
 AND(   
 $Profile.Name <> 'System Administrator',   
 $User.Id <> '00550000000rlrX',   
 $User.Username <> 'myname@mydomain.com',   
 OR(   
 ISPICKVAL(Type, "Customer"),   
 ISPICKVAL(PRIORVALUE(Type), "Customer")   
 ),   
 ISCHANGED(Name)   
 )  
** UserId = 15 characters, not 18 characters

Cons: Difficult to maintain, admin need to update each validation rules (if many) affected for maintenance.


2. Custom Setting
Create a new Text or Text Area custom field to store UserId separated by comma
 AND(  
 $Profile.Name <> 'System Administrator',  
 NOT CONTAINS($Setup.Special_User__c.UserId__c,$User.Id),  
 OR(  
 ISPICKVAL(Type, "Customer"),  
 ISPICKVAL(PRIORVALUE(Type), "Customer")  
 ),  
 ISCHANGED(Name)  
 )  
** UserId = 15 or 18 characters is fine, because we are using CONTAINS()


Setup | Develop | Custom Settings

Click Manage link | Edit button

Pros:
  • One place to store all UserIds
  • Can be used in multiple validation rules
  • Custom setting can be implement by User or Profile
Cons:
  • Admin need to keep updating User IDs in Custom Setting
  • Max length of Text or Text Area in Custom Setting is only 255 characters, so this can cover up to 16
  • IDs only.

3. Public Group
Ability to use Public Groups in Validation Rule would be ideal, but this is not exist yet. Here an idea in IdeaExchange Allow use of Public Groups from Validation Rules with 980 points right now.


4. Custom Permission
Compare to option 1 and 2, using Custom Permission would be better option. Here step-by-step to create Custom Permission and to implement it for Validation Rule.

i. Create Custom Permission
  • Setup | Develop | Custom Permissions
  • Click New button
  • Enter Label, Name and Description

ii. Create Permission Set
  • Setup | Manage Users | Permission Sets
  • Click New button
  • Enter Label, API Name, and Description
  • Click Save button
  • Click Custom Permissions link
  • Add Custom Permissions created from Step 1
  • Click Save button
  • Click Manage Assignments button
  • Click Add Assignments button, to add users
  • Select users as required
  • Click Assign button and Done

iii. Create Validation Rule
 AND(  
 $Profile.Name <> 'System Administrator',  
 NOT($Permission.Special_User),  
 OR(  
 ISPICKVAL(Type, "Customer"),  
 ISPICKVAL(PRIORVALUE(Type), "Customer")  
 ),  
 ISCHANGED(Name)  
 )  
Pros:
  • User management is control in Permission Set
  • No limitation on number of users


Reference: Custom Permissions Overview



Thursday, October 2, 2014

Chatter External license and feature

Each Salesforce org will get 5000 free Chatter Free license and 500 free Chatter External license, but I heard is possible to request for more if you purchase lot of Salesforce paid licenses.

Chatter External
This license is designed to invite customers to Chatter groups. Customers are users outside of a company’s email domain. Customers can access information and interact with users only in the groups they’re invited to. They have no access to Salesforce objects or data.

Chatter Free
The Chatter Free license is designed for users who don’t have Salesforce licenses but need access to Chatter. These users can access standard Chatter items such as people, profiles, groups, and files, but they can’t access any Salesforce objects or data. Chatter Free users can also be Chatter moderators. Chatter Free users don’t see tabs like other Salesforce users. Chatter Free users access Chatter feeds, people, groups, and files using the links in the sidebar of the Chatter page.

Chatter Only aka Chatter Plus
The Chatter Only license is also known as the Chatter Plus license. It is designed for users that don’t have Salesforce licenses but need access to some Salesforce objects in addition to Chatter. These users can access standard Chatter people, profiles, groups, and files, plus they can:
  •  View Salesforce accounts and contacts
  •  Use Salesforce CRM Content, Ideas, and Answers
  •  Access dashboards and reports
  •  Use and approve workflows
  •  Use the calendar to create and track activities
  •  Modify up to ten custom objects

Note: You can upgrade a Chatter Free license to a standard Salesforce or Chatter Only license at any time. You can’t convert a standard Salesforce or Chatter Only license to a Chatter Free license.

So, what is the difference between Chatter Free with Chatter External license in features? Here are few limitations of Chatter External license compare to Chatter Free license:

Feed
- No option for All Company feed
- Not able to Follow people

Groups
- Not able to create new Group
- Not able to see all Active Group and request to join

Topic
- Not able to search by Topic

See the difference in screenshot below:

Chatter External

Chatter Free

If you use Salesforce or Chatter Free and your business partner invite you to join their Chatter Group, you will receive email, by default, it will add guest in the username before domain name, example: yujohan@guest.gmail.com





Salesforce: Event Log Files

As of Winter '15 release, Event Log Files has been Generally Available. It contains useful information for assessing organizational usage trends and user behaviour.

However, there is no access through Salesforce user interface, you only can access the data through the Force.com REST API, this mean you can write visual force page with apex code for the UI, but fear not, I'll share few free app able to access the data.

Log files are generated asynchronous, one day after an event take place, but this should be good enough to understand your user usage. Log data is read-only, you can’t insert, update, or delete log data. It does NOT count towards org file storage.

For Production org (Enterprise, Unlimited, and Performance Edition), you need to purchase User Event Monitoring to get this feature fully activated, otherwise you will have free access to the login and logout log files with one-day data retention only. Once purchase, you can access all log file types with 30-day data retention.

Developer Edition (DE) organizations have free access to all log types with one-day data retention.
So, it is good idea to integrate this data to your back-end database for further analysis. Once it enabled, you will see object called EventLogFile from API.

Log Files have 29 event type:
• API
• Apex Callout
• Apex Execution
• Apex SOAP
• Apex Trigger
• Asynchronous Report Run
• Bulk API
• Change Set Operation
• Content Distribution
• Content Document Link
• Content Transfer
• Dashboard
• Document Attachment Downloads
• Login
• Login As
• Logout
• Metadata API Operation
• Multiblock Report
• Package Install
• Queued Execution
• Report
• Report Export
• REST API
• Sandbox
• Sites
• Time-Based Workflow
• UI Tracking
• URI
• Visualforce Request

Sample SOQL query:
Select Id, EventType, LogDate, LogFile, LogFileContentType from EventLogFile


As you can see in query result, it will create a record for each LogDate by EventType. The content is stored in LogFile and you need to read it with code, or you can use REST tool for testing. 

How to read the log file?

1. REST Explorer
Available for free from AppExchange. Once installed, you need to configure following: 
1) Configure Base URL as Custom Setting
 * Go to Setup | Develop | Custom Settings | Rest Explorer Configuration | Manage | New.
 * Set the name to 'baseUrl' and the value to your Salesforce instance, for example 'https://na1.salesforce.com'.

2) Configure a Remote Site Setting.
 * Go to Setup | Security Controls | Remote Site Settings | New Remote Site.
 * Set Remote Site Name to the name of your choice, and set Remote Site URL to the same value of the baseUrl above, for example 'https://na1.salesforce.com'.

Then you can access it through a new tab created REST Explorer. Enter LogFile value from query above to the textbox provided.


This is the result for API EventType
 Raw Response  
 Sforce-Limit-Info: api-usage=36/15000  
 Date: Wed, 01 Oct 2014 23:38:12 GMT  
 Transfer-Encoding: chunked  
 Expires: Thu, 01 Jan 1970 00:00:00 GMT  
 Set-Cookie: BrowserId=Ouz260FsTl6kC3LUW7VTQg;Path=/;Domain=.salesforce.com;  
 Expires=Sun, 30-Nov-2014 23:38:12 GMT  
 Content-Type: text/csv  
 "EVENT_TYPE","TIMESTAMP","REQUEST_ID","ORGANIZATION_ID","USER_ID","RUN_TIME","CLIENT_IP","URI","REQUEST_STATUS","API_TYPE","API_VERSION","CLIENT_NAME","METHOD_NAME","ENTITY_NAME","ROWS_PROCESSED","REQUEST_SIZE","RESPONSE_SIZE","QUERY"  
 "API","20140929223903.454","3tX96QXMYovdpU0CcVVk6-","00DB00000000Vgy","005B0000000ybnD","18","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","UserPreference","0","","",""  
 "API","20140929223903.516","3tX96QRNJyk4QU0CcVgkAk","00DB00000000Vgy","005B0000000ybnD","105","a.b.c.d","/services/data/v32.0/tooling/sobjects/User/005B0000000ybnD","S","","32.0","devconsole","retrieve","User","1","","",""  
 "API","20140929223904.043","3tX96Se_dRhYFSHyqUDgI-","00DB00000000Vgy","005B0000000ybnD","34","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","","query","ApexExecutionOverlayAction","0","","",""  
 "API","20140929223904.444","3tX96U8AoglYZSHyqU0xdk","00DB00000000Vgy","005B0000000ybnD","38","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","","query","ApexLog","0","","",""  
 "API","20140929223905.164","3tX96WmjfEhYFhHyqUDgI-","00DB00000000Vgy","005B0000000ybnD","47","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","IDEWorkspace","1","","",""  
 "API","20140929223905.441","3tX96Xv75g988xHyqUCmFF","00DB00000000Vgy","005B0000000ybnD","21","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","TraceFlag","1","","",""  
 "API","20140929223905.671","3tX96YlO2KdYFhHyqUDgI-","00DB00000000Vgy","005B0000000ybnD","23","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","MetadataContainer","1","","",""  
 "API","20140929223906.008","3tX96ZnGj1T88xHyqUCmFF","00DB00000000Vgy","005B0000000ybnD","84","a.b.c.d","/services/data/v32.0/tooling/sobjects/TraceFlag/7tfB00000000J0zIAE","S","","32.0","devconsole","update","TraceFlag","1","","",""  
 "API","20140929223906.032","3tX96Ywqmkt7BU0CcVFeSk","00DB00000000Vgy","005B0000000ybnD","337","a.b.c.d","/services/data/v32.0/tooling/sobjects/User/005B0000000ybnDIAQ","S","","32.0","devconsole","update","User","1","","",""  
 "API","20140929223906.475","3tX96a4QSch7BU0CcVFeSk","00DB00000000Vgy","005B0000000ybnD","206","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","MetadataContainerMember","0","","",""  
 "API","20140929224424.784","3tX9P7Yw7Er2QxHyqUDeZk","00DB00000000Vgy","005B0000000ybnD","23","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","ApexCodeCoverage","0","","",""  
 "API","20140929224425.792","3tX9P8CnQYT8ISHyqUCmFF","00DB00000000Vgy","005B0000000ybnD","856","a.b.c.d","/services/data/v32.0/tooling/sobjects/ApexClass/01pB0000000AJ6QIAW","S","","32.0","devconsole","retrieve","ApexClass","1","","",""  
 "API","20140929224426.238","3tX9PCh0VJnduE0CcVVk6-","00DB00000000Vgy","005B0000000ybnD","98","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","ApexClass","10","","",""  
 "API","20140929224426.430","3tX9PCnNTY34WE0CcVgkAk","00DB00000000Vgy","005B0000000ybnD","262","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","devconsole","query","ApexClass","1","","",""  
 "API","20140929224512.669","3tX9RuvnfjKePE0CcVVMjk","00DB00000000Vgy","005B0000000ybnD","26","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","","query","ApexLog","1","","",""  
 "API","20140929224527.721","3tX9Sn1k6qKYfhHyqU0xdk","00DB00000000Vgy","005B0000000ybnD","17","a.b.c.d","/services/data/v32.0/tooling/query/","S","","32.0","","query","ApexLog","2","","",""  
 "API","20140929225015.272","3tX9iX7wbcq8TSHyqUCmFF","00DB00000000Vgy","005B0000000ybnD","47","a.b.c.d","/services/data/v32.0/tooling/sobjects/ApexPage/066B00000001YGXIA2","S","","32.0","devconsole","retrieve","ApexPage","1","","",""  
 "API","20140929225905.190","3tXACN8ylBSehU0CcVVMjk","00DB00000000Vgy","005B0000000ybnD","69","a.b.c.d","/services/data/v32.0/tooling/sobjects/TraceFlag/7tfB00000000J0zIAE","S","","32.0","devconsole","update","TraceFlag","1","","",""  
 "API","20140929230507.422","3tXAXSXhhkZ50U0CcVgkAk","00DB00000000Vgy","005B0000000ybnD","79","a.b.c.d","/services/data/v32.0/tooling/sobjects/IDEWorkspace/1deB00000000JIgIAM","S","","32.0","devconsole","update","IDEWorkspace","1","","",""  
CLIENT_IP has been masked.

This is the result for Report Event Type
 Raw Response  
 Sforce-Limit-Info: api-usage=37/15000  
 Date: Wed, 01 Oct 2014 23:46:38 GMT  
 Transfer-Encoding: chunked  
 Expires: Thu, 01 Jan 1970 00:00:00 GMT  
 Set-Cookie: BrowserId=Xcteq7xKS0uNZylaeBBafg;Path=/;Domain=.salesforce.com;  
 Expires=Sun, 30-Nov-2014 23:46:38 GMT  
 Content-Type: text/csv  
 "EVENT_TYPE","TIMESTAMP","REQUEST_ID","ORGANIZATION_ID","USER_ID","RUN_TIME","CLIENT_IP","URI","REQUEST_STATUS","ENTITY_NAME","DISPLAY_TYPE","RENDERING_TYPE","REPORT_ID","NUMBER_EXCEPTION_FILTERS","NUMBER_COLUMNS","SORT","NUMBER_BUCKETS"  
 "Report","20140929224335.668","3tX9MEjMd8xeOE0CcVVMjk","00DB00000000Vgy","005B0000000ybnD","520","222.165.97.180","/00OB0000000YEn2","S","","S","W","00OB0000000YEn2","0","10","","0"  
 "Report","20140929224340.773","3tX9MZQw71teOE0CcVVMjk","00DB00000000Vgy","005B0000000ybnD","68","222.165.97.180","/00OB0000000YEn2","S","","S","X","00OB0000000YEn2","0","10","","0"  
 "Report","20140929224344.199","3tX9MkwB0pk4Vz0CcVgkAk","00DB00000000Vgy","005B0000000ybnD","139","222.165.97.180","/00OB0000000YEn2","S","","S","W","00OB0000000YEn2","0","10","","0"  

Credit and thanks to Adam Torman for sharing sample code, how to access Event Log File via Visualforce and Apex code.


2. Developer Workbench
From https://workbench.developerforce.com login with API version 32.0 or beyond. Go to utilities | Rest Explorer, query with 
/services/data/v32.0/query?q=SELECT+Id+,+EventType+,+LogFile+,+LogDate+,+LogFileLength+FROM+EventLogFile
Click Execute button, it will show total record the same as you query from Developer Console.

From here, click Expand All link to see all record in detail, then click link at LogFile


It will display Raw Result the same as using Rest Explorer installed package above. You can copy and paste the result from header "EVENT_TYPE","TIMESTAMP" ... into Notepad and save as CSV file.

Sample CSV file for Event Type = ReportExport.


3. Salesforce Event Log File Browser
Go to URL https://salesforce-elf.herokuapp.com
This application is pretty simple, open the URL above, select login to Production or Sandbox and allow Salesforce Event Log File Browser to access your Salesforce data.
Once you are in, you can download each event log into CSV file.



Reference



Tuesday, September 30, 2014

Salesforce: How to sort Multi-Select Picklist in View ?

Multi-Select Picklist is a great feature for user to select multiple values controlled. You can use the multi-picklist field in View, Report, etc.

Recently we have issue when used it in View, user not able to sort b value using this multi-select picklist field. A quick idea is to create a text formula field, but looking at multi-select picklist fields can only be used in these functions: INCLUDES(), ISBLANK(), ISNULL() for formula field, TEXT() is out of picture here.

ISCHANGED() and PRIORVALUE() only in assignment rules, validation rules, workflow field updates, and workflow rules in which the evaluation criteria is set to Evaluate the rule when a record is: created, and every time it’s edited.

Solution:
Create text formula field with INCLUDES()
Example:
 IF( INCLUDES( Country__c, 'Singapore'), 'Singapore',  
     IF( INCLUDES( Country__c, 'Australia'), 'Australia',  
         IF( INCLUDES( Country__c, 'Malaysia'), 'Malaysia',  
             IF( INCLUDES( Country__c, 'Thailand'), 'Thailand',  
                 IF( INCLUDES( Country__c, 'Hong Kong'), 'Hong Kong', 'Other')  
             )  
         )  
     )   
 )   

Cons: when you have multiple values in the field, it will return based on the first value match.


ReferenceTips for Working with Picklist and Multi-Select Picklist Formula Field



Sunday, September 28, 2014

Salesforce: Long Text Area formula field

As of Winter '15 release, when you use function LEN() with Long Text Area field, you will get error message Error: You referenced an unsupported field type called "Long Text Area" using the following field:. The same error when you use it with Rich Text Area field, it also happen when you use those fields with other formula such as: MID(), FIND(), BEGINS(), CASE() and etc.

This limitation has been voted in AppExchange with 3470 points currently, please vote this idea.

Workaround: create Workflow Rule with Field Update action

Example: we would like to count length of Description field with Case

1. Create Custom Number Field
  • Setup | Customize | Cases | Fields 
  • Click New button in Case Custom Fields & Relationships
  • Select Number and Next
  • Enter Label, Length and Description
  • Click Next to continue and follow the wizard to save

2. Create Field Update for Workflow Rule
  • Setup | Create | Workflow & Approvals | Field Updates
  • Click New Field Update button
  • Enter Field Update Name, Description
  • Select Case object, and above field created
  • Choose Use a formula to set the new value 
  • Enter the formula into Formula Editor: LEN(Description)

3. Create Workflow Rule
  • Setup | Create | Workflow & Approvals | Workflow Rule
  • Click New Rule button
  • Select Case object
  • Enter Workflow Name, Evaluate the rule when a record is = created, and every time it’s edited
  • Rule Criteria = True

Click Next to continue then choose Select Existing Action in Immediate Workflow Actions, select field update action created in step 2 above. Save and remember to Activate to Workflow.

Cons: as workflow will only fired when there is update occur to the object, otherwise the value for that custom number field will be blank. To make it all populated, you need to mass update the object for all records.



Page-level ad