VF Page :
<apex:page controller="opportunityList2Con">
<apex:form >
<apex:pageBlock >
<apex:pageBlockTable value="{!opportunities}" var="o">
<apex:column value="{!o.name}"/>
<apex:column value="{!o.closedate}"/>
</apex:pageBlockTable>
<apex:commandButton rendered="{!setCon.hasPrevious}" value="first" action="{!setCon.first}"/>
<apex:commandButton rendered="{!setCon.hasPrevious}" value="Previous" action="{!setCon.previous}"/>
<apex:outputText rendered="{!(setCon.pageNumber * setCon.pageSize) < setCon.ResultSize}" value="{!setCon.pageNumber * setCon.pageSize} Of {!setCon.ResultSize}"></apex:outputText>
<apex:outputText rendered="{!(setCon.pageNumber * setCon.pageSize) >= setCon.ResultSize}" value="{!setCon.ResultSize} Of {!setCon.ResultSize}"></apex:outputText>
<apex:commandButton rendered="{!setCon.hasNext}" value="next" action="{!setCon.next}"/>
<apex:commandButton rendered="{!setCon.hasNext}" value="last" action="{!setCon.last}"/>
</apex:pageBlock>
</apex:form>
</apex:page>
Apex Controller :
public class opportunityList2Con {
// ApexPages.StandardSetController must be instantiated
// for standard list controllers
public ApexPages.StandardSetController setCon {
get {
if(setCon == null) {
setCon = new ApexPages.StandardSetController(Database.getQueryLocator(
[select name,closedate from Opportunity]));
}
return setCon;
}
set;
}
// Initialize setCon and return a list of records
public List<Opportunity> getOpportunities() {
setCon.setpagesize(5);
return (List<Opportunity>) setCon.getRecords();
}
}
Sunday, December 18, 2016
Monday, December 12, 2016
Locking record editing
Way to lock a record from being edited even by the record owner after some criteria was met. For a while, there were lots of ideas thrown about such as validation rules and changing the record type so it uses a different page layout. Both of these solutions weren’t ideal. Validation rules happen after the user has entered in a bunch of data and then, wham! they can’t save. So frustrating! Also, you’d have to check every field to see if it had changed. Changing the record type is another option, but users can still edit the record through the API or list view edit functionality.
We then came around to Approval Processes and how they can lock the record. Now wouldn’t it be nice if we could lock the record too? Unfortunately, there isn’t a way in workflow rules or apex to just set an isLocked field to true.
Now that we’ve whined about this lack of functionality, let’s try to work around it. I found that you can submit a record for approval from Apex. I immediately started thinking of triggers, so when I got home, I whipped up a solution pretty quickly. (Yeah, I lead an exciting life.)
First, let’s set up an approval process. I want my Account records to lock after the rating is changed to Cold, so I’ll set that as my entry criteria. You need at least one step in an approval, but I didn’t want to deal with it, so I set the criteria on the set to False. This means that as soon as the record is submitted for approval, it is approved and left locked!
Now all I need is a trigger to catch the event and submit the approval:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | trigger AccountRecordLock on Account (after update, after insert) { for (Account a : Trigger.New) { if (a.Rating == 'Cold') { // Create an approval request for the account Approval.ProcessSubmitRequest req1 = new Approval.ProcessSubmitRequest(); req1.setComments('Automatic record lock.'); req1.setObjectId(a.id); // Submit the approval request for the account Approval.ProcessResult result = Approval.process(req1); } } } |
Now as soon as I save a record with a Rating of Cold, the approval process kicks off and locks the record. Only users with Modify All permission on the object can now edit it!
You can add the Approval Process related list to the view to see that the approval was kicked off.
Tuesday, December 6, 2016
Avoid Hard Coded URLS
Hopefully you know that hard coding links in Salesforce is a no-no. You might one day be migrated from one instance to another (NA1 to NA9999, or whatever they are up to these days). You also want to make sure that your links work in all your sandboxes. There is nothing worse than clicking on a link in a sandbox and all of a sudden you are in production!
First of all, I recommend that you implement My Domain. This means that you won’t have to worry about moving from one instance to another. Plus you need it on to use Lightning Components. You want those, right?
For the most part within Salesforce, you can just avoid the beginning part of the URL and use relative URLs. So, a URL button that takes you to a Visualforce page can just have ‘/apex/MyPage’.
The first “gotcha” is with managed packages. If you want to link to pages in a managed package, then you can still use a relative URL. Just use the format of ‘/apex/NAMESPACE__ManagedPage’.
Another good trick is to use the URLFOR function within Visualforce. This future proofs your pages so that if Salesforce ever changes the URL scheme (hello, Lightning), everything will still work. For example, to get the link to edit an account, I can use the following syntax.
{!URLFOR($Action.Account.Edit, account.id)}
Email templates are another tricky part of links. Since the email is being sent out, you can’t use relative URLs. You need to link into your instance. For the object that is triggering the email, you can use the merge field called Detail Link. This looks like {!Opportunity.Link}.
But what if you want to provide a link in your email to a related record? For example, an email gets sent from an opportunity, but you want a link to the account? First, let’s create a formula field on the object that will trigger the email and call it Base URL and make it a text formula field with the following formula:
LEFT($Api.Partner_Server_URL_360, FIND( '/services', $Api.Partner_Server_URL_360))
You can now use this formula field in your email templates to link to other records or Visualforce pages. So, from an opportunity email, the merge field would be: {!Opportunity.Base_URL__c}{!Opportunity.AccountId}.
By implementing these changes, you can be assured your links will always go to the correct instance.
Thursday, December 1, 2016
Expression Operators in Lightning Components
The expression language supports operators to enable you to create more complex expressions.
Arithmetic Operators
Expressions based on arithmetic operators result in numerical values.
Operator | Usage | Description |
---|---|---|
+ | 1 + 1 | Add two numbers. |
- | 2 - 1 | Subtract one number from the other. |
* | 2 * 2 | Multiply two numbers. |
/ | 4 / 2 | Divide one number by the other. |
% | 5 % 2 | Return the integer remainder of dividing the first number by the second. |
- | -v.exp | Unary operator. Reverses the sign of the succeeding number. For example if the value of expenses is 100, then -expenses is -100. |
Numeric Literals
Literal | Usage | Description |
---|---|---|
Integer | 2 | Integers are numbers without a decimal point or exponent. |
Float | 3.14
-1.1e10
| Numbers with a decimal point, or numbers with an exponent. |
Null | null | A literal null number. Matches the explicit null value and numbers with an undefined value. |
String Operators
Expressions based on string operators result in string values.
Operator | Usage | Description |
---|---|---|
+ | 'Title: ' + v.note.title | Concatenates two strings together. |
String Literals
String literals must be enclosed in single quotation marks 'like this'.
Literal | Usage | Description |
---|---|---|
string | 'hello world' | Literal strings must be enclosed in single quotation marks. Double quotation marks are reserved for enclosing attribute values, and must be escaped in strings. |
\<escape> | '\n' | Whitespace characters:
Escaped characters:
|
Unicode | '\u####' | A Unicode code point. The # symbols are hexadecimal digits. A Unicode literal requires four digits. |
null | null | A literal null string. Matches the explicit null value and strings with an undefined value. |
Comparison Operators
Expressions based on comparison operators result in a true or false value. For comparison purposes, numbers are treated as the same type. In all other cases, comparisons check both value and type.
Operator | Alternative | Usage | Description |
---|---|---|---|
== | eq | 1 == 1
1 == 1.0
1 eq 1
![]()
undefined==nullevaluates to true.
| Returns true if the operands are equal. This comparison is valid for all data types.![]()
Don’t use the == operator for objects, as opposed to basic types, such as Integer or String. For example, object1==object2 evaluates inconsistently on the client versus the server and isn’t reliable.
|
!= | ne | 1 != 2
1 != true
1 != '1'
null != false
1 ne 2
| Returns true if the operands are not equal. This comparison is valid for all data types. |
< | lt | 1 < 2
1 lt 2
| Returns true if the first operand is numerically less than the second. You must escape the < operator to < to use it in component markup. Alternatively, you can use the lt operator. |
> | gt | 42 > 2
42 gt 2
| Returns true if the first operand is numerically greater than the second. |
<= | le | 2 <= 42
2 le 42
| Returns true if the first operand is numerically less than or equal to the second. You must escape the <= operator to <= to use it in component markup. Alternatively, you can use the le operator. |
>= | ge | 42 >= 42
42 ge 42
| Returns true if the first operand is numerically greater than or equal to the second. |
Logical Operators
Expressions based on logical operators result in a true or false value.
Operator | Usage | Description |
---|---|---|
&& | isEnabled && hasPermission | Returns true if both operands are individually true. You must escape the && operator to && to use it in component markup. Alternatively, you can use the and() function and pass it two arguments. For example, and(isEnabled, hasPermission). |
|| | hasPermission || isRequired | Returns true if either operand is individually true. |
! | !isRequired | Unary operator. Returns true if the operand is false. This operator should not be confused with the ! delimiter used to start an expression in {!. You can combine the expression delimiter with this negation operator to return the logical negation of a value, for example,{!!true} returns false. |
Logical Literals
Logical values are never equivalent to non-logical values. That is, only true == true, and only false == false; 1 != true, and 0 != false, and null != false.
Literal | Usage | Description |
---|---|---|
true | true | A boolean true value. |
false | false | A boolean false value. |
Conditional Operator
There is only one conditional operator, the traditional ternary operator.
Operator | Usage | Description |
---|---|---|
? : | (1 != 2) ? "Obviously" : "Black is White" | The operand before the ? operator is evaluated as a boolean. If true, the second operand is returned. If false, the third operand is returned. |
Wednesday, November 30, 2016
Using SOQL to Determine Your Force.com User’s Permissions
"Now that a user can have multiple
permission sets in addition to their profile, how can I tell what permissions
they have?”
Summer ’12 introduces the ability to answer
this question using the permission set API. We’ve created a new field on the
PermissionSet SObject called IsOwnedByProfile. This field determines whether a permission
set is a custom one or if it is parented by a profile. This is made possible
because for every profile, there is one underlying permission set. That way,
permissions layer equally across permission sets without having to treat a
profile differently. In the setup user interface, you only see the profile but
in the API, you can see both the profile and the underlying permission set.
As a result, running the following query in
SOQL will return both permission sets you’ve created and permission sets
parented by a profile:
SELECT Id,IsOwnedByProfile,Label
FROM PermissionSet
FROM PermissionSet
By adding IsOwnedByProfile to the WHERE
clause, you will quickly differentiate between permission sets you’ve created
versus those parented by a profile:
SELECT Id,IsOwnedByProfile,Label
FROM PermissionSet
WHERE IsOwnedByProfile = TRUE
FROM PermissionSet
WHERE IsOwnedByProfile = TRUE
Once you have the hang of this, you can start
to answer all sorts of questions about your users such as, “which users have
Read on Accounts and why”:
SELECT Assignee.Name, PermissionSet.Id,
PermissionSet.isOwnedByProfile, PermissionSet.Profile.Name, PermissionSet.Label
FROM PermissionSetAssignment
WHERE PermissionSetId
IN (SELECT ParentId
FROM ObjectPermissions
WHERE SObjectType = 'Account' AND
PermissionsRead = true)
FROM PermissionSetAssignment
WHERE PermissionSetId
IN (SELECT ParentId
FROM ObjectPermissions
WHERE SObjectType = 'Account' AND
PermissionsRead = true)
You might need to answer questions about a specific
user such as, “what are all of the Account fields where John Doe has at least
Read access and why”
SELECT Id, SObjectType, PermissionsRead,
Parent.label, Parent.IsOwnedByProfile
FROM ObjectPermissions
WHERE (ParentId
IN (SELECT PermissionSetId
FROM PermissionSetAssignment
WHERE Assignee.Name = 'John Doe'))
AND
(PermissionsRead = true)
AND
(SobjectType = 'Account')
FROM ObjectPermissions
WHERE (ParentId
IN (SELECT PermissionSetId
FROM PermissionSetAssignment
WHERE Assignee.Name = 'John Doe'))
AND
(PermissionsRead = true)
AND
(SobjectType = 'Account')
Using permission sets in this way, you can
find out why a user has access to an apex page, class or a particular user,
object, or field permission, regardless of whether it’s through their profile
or permission set.
These SOQL queries are great if you have one
off questions about your user’s permissions. If you have a more regular need to
query user’s permissions, think about creating a Visualforce page with an Apex
controller that uses these queries to find out what your users can do and why.
Tuesday, November 29, 2016
Retrieving RecordTypeId with Describe Call in Salesforce
//Generate a map of tokens for the sObjects in your organization
Map gd = Schema.getGlobalDescribe();
//Retrieve the describe result for the desired object
DescribeSObjectResult result = gd.get('Account').getDescribe();
//Generate a map of tokens for all the Record Types for the desired object
Map recordTypeInfo = result.getRecordTypeInfosByName();
system.debug('RECORD TYPES:' + recordTypeInfo);
//Retrieve the record type id by name
String recordTypeId = recordTypeInfo.get('RECORDTYPE_NAME').getRecordTypeId();
OR
//Retrieve the record type id by name
String recordTypeId = Schema.getGlobalDescribe().get('Account').getDescribe().getRecordTypeInfosByName().get('RECORDTYPE_NAME').getRecordTypeId();
System.debug('@@@-----'+recordTypeId);
Wednesday, November 23, 2016
Display collapsible data in VF page
Use Case
We may need to display collapsible data in VF page. This can be
easily achieved using the VF component <Apex:PageBlockSection> . However
this component is not always useful and may distort the UI when used with some
specific components – like <Apex:repeat>.
Solution
We can customize our code to enclose the rows to be collapsed
using HTML tag <TBODY> and set its id dynamically. Now, using
JavaScript function we can get collapsible and expandable data rows.
Reusable Code
VF Page
<apex:page
controller="CollapsibleClass" sidebar="false">
<script>
function switchMenu(obj,obj1,obj2)
{
var el =
document.getElementById(obj);
if
( el.style.display != 'none' ) {
el.style.display = 'none';
}
else {
el.style.display = '';
}
var e2 =
document.getElementById(obj1);
if
( e2.style.display != 'none' ) {
e2.style.display = 'none';
}
else {
e2.style.display = '';
}
var e3 =
document.getElementById(obj2);
if
( e2.style.display != 'none' ) {
e3.style.display = 'none';
}
else {
e3.style.display = '';
}
}
</script>
<table border="0" width="50%" id="myTable">
<tr>
<td width="1%" bgcolor="grey"></td>
<td width="19%" bgcolor="grey"><b> Account
Name</b> </td>
<td
width="19%" bgcolor="grey"><b>Account
Type</b> </td>
</tr>
<apex:variable
value="{!0}" var="rowNum1"/>
<apex:repeat
var="account" value="{!accountDataList}"
id="rFirst">
<tr>
<td width="1%" bgcolor="Lightblue">
<apex:outputpanel id="plusimage"
style="{!IF(account.hideOrShow == '', "display:none;",
"")}" >
<apex:image url="{!URLFOR($Resource.Images, 'Plus_Image.gif')}"
onclick="switchMenu('inlinetablesec{!rowNum1}','{!$Component.minusimage}','{!$Component.plusimage}')"
title="Show TSF details"/>
</apex:outputpanel>
<apex:outputpanel id="minusimage"
style="{!account.hideOrShow}">
<apex:image url="{!URLFOR($Resource.Images, 'Minus_Image.gif')}"
onclick="switchMenu('inlinetablesec{!rowNum1}','{!$Component.plusimage}','{!$Component.minusimage}')"
title="Hide TSF details"/>
</apex:outputpanel>
</td>
<td
width="20%" bgcolor="Lightblue">
<apex:outputLink value="/{!account.id}" id="accountLink"
target="_blank">{!account.name}</apex:outputLink>
</td>
<td
width="19%" bgcolor="Lightblue">
{!account.type}
</td>
</tr>
<tbody
id="inlinetablesec{!rowNum1}"
style="{!account.hideOrShow}">
<tr>
<td
width="1%"></td>
<td
bgcolor="grey" width="20%"><b>Contact
Name</b></td>
<td bgcolor="grey" width="20%"><b>Contact
Phone</b></td>
</tr>
<apex:variable
value="{!0}" var="rowNum"/>
<apex:repeat
var="cont" value="{!account.contacts}"
id="rSecond">
<tr>
<td
width="1%"></td>
<td
bgcolor="LightGrey" width="20%">{!cont.name}</td>
<td bgcolor="LightGrey"
width="20%">{!cont.phone}</td>
</tr>
<apex:variable
var="rowNum" value="{!rowNum + 1}"/>
</apex:repeat>
</tbody>
<apex:variable var="rowNum1" value="{!rowNum1 + 1}"/>
</apex:repeat>
</table>
</apex:page>
Controller Class
public
class CollapsibleClass {
public
list<AccountData> accountDataList {get;set;}
Public CollapsibleClass(){
accountDataList = new list<AccountData>();
prepareData();
}
//Wrapper class used to hold date for alternate table 4
public class AccountData
{
public
String id {get; set;}
public
String name {get; set;}
public
String type {get; set;}
public String hideOrShow {get; set;}
public List<Contact> contacts {get;set;}
public
AccountData()
{
}
}
private
void prepareData() {
for (Account acc : [SELECT Account.Name,Account.type,(SELECT
Contact.Name,Contact.Phone FROM contacts) FROM Account WHERE Account.Id IN
(SELECT Contact.accountId FROM Contact) limit 5]) {
AccountData accData = new AccountData();
accData.id= acc.id;
accData.name = acc.name;
accData.type = acc.type;
accData.contacts = acc.contacts;
accData.hideOrShow = 'display:none;';
accountDataList.add(accData);
}
}
}
Screen Shot
Subscribe to:
Posts (Atom)