Thursday, November 18, 2021

Restriction Rules Explained

Currently Salesforce allows you to create rules that restrict the visibility of records. For example, even if we have the OWD of an object to Public Read or beyond, we can still hide its records from certain users by adding a rule called "Restriction Rule".

Restriction Rules help us to restrict access to selected records from a group of users. It is made Generally Available (GA) since Winter '22 of Salesforce.  Remember that the restriction rule can only be created from the lightning experience and not from the classic experience.  We will take an example to illustrate each of its functionality, pros, and cons.

Scenario : I have a custom object named Engagement holding a checkbox field "Active".  The OWD for this object is "Public Read Only," but there are a specific set of Users belonging to profile "Sales Executive" who should be denied access to Engagement records where Active is TRUE.  Due to the OWD being "Public Read Only," the user cannot be restricted from accessing it.  

Solution : A Restriction Rule becomes relevant at this point. So let's create one to address the problem.   Restriction Rules are object-specific and so which we need to navigate to the object to create one.

Navigation : Goto Setup > Object Manager > Choose the object for which the Restriction Rule has to be created and on the left pane an option "Restriction Rule" will be available.(Refer to the below image for Reference).  Then click on "New Rule" 



There will be 3 sections here:

1. 
Rule Detail :
This will hold the details of the rule.  Populate values for all the fields under the section "Rule Detail".  Ensure to check the checkbox "Is Active". 

2. 
User Criteria :This is the place where we have to specify the Users for which
 the restriction Rule should apply. 
Currently, only two options are available :
   i. User Criteria
       If we want to identify users by a field on the User object 
       (only on the User object, multi-levels are not supported)
   ii. Permission Criteria 
      If we want to identify Users by using a "Custom Permission"

For our scenario, we wish to apply rule to all users associated with profile "Sales Executive".  But we need to understand the fact that the field Profile on User is a lookup and so only ID of the profile will be available on the User record.  As Restriction Rules can access data on the User object but not on the associated object, we have copy ID of the profile and use it in our criteria.

3. Record Criteria

It is here where criteria of records to be shown are specified. Any record that doesn't meet the criteria will be not shown to the User.  Only those fields on objects (on which restrictions are written) will be able to be selected.
For our scenario we wish to apply the Restriction for the Engagements where Active is unchecked. (Refer to below image for reference)

Save the record and you are all set.  Despite OWD being "Public Read Only", Engagement records with Active checkbox checked will no longer be visible to Users belonging to profile "Sales Executive".

It's definitely a useful feature, but it's equally important to know what the limitations are. Below are some of them.

  • Restriction rules are not supported for all objects. Only custom objects, contracts, events, tasks, time sheets, and time sheet entries can be restricted.
  • You can only have one Filter Entry under "User Criteria" and "Record Criteria". You cannot configure any Filter Logic and have multiple Filter Entries.
  • Only "Equals" is available as "Operator" for sections "User Criteria" and "Record Criteria".
  • Checkbox, Text, Number, Date, DateTime, Time, and Id(Lookup) are the only data types supported.  Only fields belonging toe mentioned data-types will be coming up in the User/Record Criteria

It feels like there are many limitations and I hope limitations will be lessened and scope will be extended in the upcoming releases.

Source : https://developer.salesforce.com/docs/atlas.en-us.restriction_rules.meta/restriction_rules/restriction_rules_about.htm
  

Thanks for being such an awesome reader and I appreciate your feedback.

..Bazingaa..

Monday, November 1, 2021

Move Reports from one folder to another using Apex

In many cases, moving reports from one folder to another was a problem that we faced at least once and resorted to manual modifications as the approaches available to do this are very limited and have high limitations.  Same applies for deleting a report too.

It is possible to move reports from one folder to another by using some migration tools. However, we will not cover those approaches in this post, since using migration tool requires lot of manual intervention to perform retrieval and deployment.  

Before we start with our process, we need to have the below mentioned two classes created in your org 

Let's understand by taking an example.  In my org, there are two reports
  • Report "Numeric Methods" in Folder "Economics" and 
  • Report "Quant" in Folder "Commerce".  

Now let's try to move both of the reports into folder "Aptitude".   Point to remember here is we need to make use of the the Full API Name of the report (i.e., Folder Developer Name + '/'+ Report Developer Name) to make use of it in apex.

Folder Developer Name can be fetched with the below SOQL
SELECT Id, DeveloperName FROM Folder

Report DeveleloperName can be fetched with the below SOQL
SELECT Id, Name, DeveloperName FROM Report

For the above mentioned reports, this is how the full name looks like
  • Commerce/Quant
  • Economics/Numeric_Methods
Execute the below logic in anonymous block of Dev Console and this will move the two reports mentioned into folder "Aptitude".

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//Below variable to hold the list of Reports to be moved
List<String> lstStrRepNames = new List<String>();
lstStrRepNames.add('Commerce/Quant');
lstStrRepNames.add('Economics/Numeric_Methods');

//Below variable to hold the Developer Name of target Folder
String strTargetFolder = 'Aptitude';

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

List<MetadataService.Report> reportsToUpdate = (List<MetadataService.Report>) service.readMetadata('Report', lstStrRepNames).getRecords();
for(MetadataService.Report objRep : reportsToUpdate){
    //In full name replace source folder with targetFolder by using '/' as seperator
    objRep.fullName = objRep.fullName.replace(objRep.fullName.substringBeforeLast('/'), strTargetFolder);
}
List<MetadataService.SaveResult> results = service.updateMetadata(reportsToUpdate);

Now coming to the process of deleting reports, let's try to delete the reports which we just moved to the folder "Aptitude".  The developer names are like below
  • Aptitude/Quant
  • Aptitude/Numeric_Methods
Execute the below logic in Anonymous Block and that will do the job of deleting them.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//Below variable to hold the list of Reports to be deleted
List<String> lstStrRepNames = new List<String>();
lstStrRepNames.add('Aptitude/Quant');
lstStrRepNames.add('Aptitude/Numeric_Methods');

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();

List<MetadataService.DeleteResult> results = service.deleteMetadata('Report', lstStrRepNames);


For demonstration purpose, I wrote the core-logic.  To keep it generic enough, you may have to write the logic written in an apex method with parameters and enhance it as per your requirement.

PS : Keep in your thoughts that, above logic cannot process more than 10 records in a single transaction.  So if the count is greater than 10, batch apex has to be leveraged to achieve the same

Hope this helps.  

..Bazingaa..