この文章は Salesforce 機械翻訳システムを使用して翻訳されました。詳細はこちらをご参照ください。
英語に切り替える

Apex による共有管理の再適用

Salesforce では、組織のデフォルトアクセスレベルが変更されると、オブジェクトの全レコードの共有が自動的に再適用されます。再適用により、適切な場合は共有管理が追加されます。また、付与されたアクセス権が冗長である場合は、すべてのタイプの共有が削除されます。たとえば、オブジェクトの共有モデルが「非公開」から「公開/参照のみ」に変更されると、ユーザに「参照のみ」アクセス権を付与する共有の直接設定が削除されます。

Apex 共有管理を再適用するには、Salesforce が提供する再適用を行うインターフェースを実装する、Apex クラスを記述する必要があります。その後、[Apex 共有の再適用] 関連リストのカスタムオブジェクトの詳細ページで、クラスとカスタムオブジェクトを関連付ける必要があります。

Apex 共有の理由と Apex による共有管理の再適用は、カスタムオブジェクトでのみ使用できます。

メモ

Apex 共有の理由を指定するカスタムオブジェクトの詳細ページからこのクラスを実行します。ロックの問題により、アプリケーションのロジックに定義されたユーザへのアクセス権限の付与が Apex コードで実行されない場合、管理者はオブジェクトの Apex 共有管理を再適用する必要があることがあります。Database.executeBatch メソッドを使用して、Apex 共有管理の再適用をプログラムで呼び出すこともできます。

カスタムオブジェクトの組織のデフォルト共有アクセスレベルが更新される度に、関連付けられたカスタムオブジェクトに定義された Apex 再適用クラスも実行されます。

メモ

Apex 再適用の実行を監視または停止するには、[設定] から、[クイック検索] ボックスに「Apex ジョブ」と入力し、[Apex ジョブ] を選択します。

共有の再適用のための Apex クラスの作成

Apex 共有管理を再適用するには、再適用を行う Apex クラスを記述する必要があります。このクラスは、Salesforce が提供する Database.Batchable インターフェースを実装している必要があります。

Database.Batchable インターフェースは、Apex 共有管理の再適用など、すべての Apex の一括処理プロセスに使用されます。このインターフェースは、組織で複数回実装できます。実装する必要があるメソッドの詳細は、「Apex の一括処理の使用」を参照してください。

Apex 共有管理の再適用を作成する前に、ベストプラクティスについても検討してください。

組織のデフォルトのアクセスレベルは、最も権限の大きいアクセスレベルに設定することはできません。カスタムオブジェクトの場合、このレベルは「公開/参照・更新可能」です。詳細は、「共有の理解」を参照してください。

重要

Apex による共有管理の再適用の例

この例では、人事採用アプリケーションの構築中で、Job というオブジェクトが存在すると仮定しています。ジョブにリストされた採用担当者および採用担当マネージャにレコードへのアクセス権が付与されていることを確認したいと考えています。次の Apex クラスでこの検証を実行できます。この例では、User レコードと関連付けられた、Hiring_Manager および Recruiter という 2 つの参照項目を持つ Job というカスタムオブジェクトが必要です。また、Job カスタムオブジェクトには、Hiring_Manager と Recruiter という 2 つの共有の理由を追加する必要があります。このサンプルを実行する前に、メールアドレスを、エラー通知とジョブ完了通知を送信する有効なメールアドレスに置き換えます。

1global class JobSharingRecalc implements Database.Batchable<sObject> {
2    
3    // String to hold email address that emails will be sent to. 
4    // Replace its value with a valid email address.
5    static String emailAddress = 'admin@yourcompany.com';
6    
7    // The start method is called at the beginning of a sharing recalculation.
8    // This method returns a SOQL query locator containing the records 
9    // to be recalculated. 
10    global Database.QueryLocator start(Database.BatchableContext BC){
11        return Database.getQueryLocator([SELECT Id, Hiring_Manager__c, Recruiter__c 
12                                         FROM Job__c]);  
13    }
14    
15    // The executeBatch method is called for each chunk of records returned from start.  
16    global void execute(Database.BatchableContext BC, List<sObject> scope){
17       // Create a map for the chunk of records passed into method.
18        Map<ID, Job__c> jobMap = new Map<ID, Job__c>((List<Job__c>)scope);  
19        
20        // Create a list of Job__Share objects to be inserted.
21        List<Job__Share> newJobShrs = new List<Job__Share>();
22               
23        // Locate all existing sharing records for the Job records in the batch.
24        // Only records using an Apex sharing reason for this app should be returned. 
25        List<Job__Share> oldJobShrs = [SELECT Id FROM Job__Share WHERE ParentId IN 
26             :jobMap.keySet() AND 
27            (RowCause = :Schema.Job__Share.rowCause.Recruiter__c OR
28            RowCause = :Schema.Job__Share.rowCause.Hiring_Manager__c)]; 
29        
30        // Construct new sharing records for the hiring manager and recruiter 
31        // on each Job record.
32        for(Job__c job : jobMap.values()){
33            Job__Share jobHMShr = new Job__Share();
34            Job__Share jobRecShr = new Job__Share();
35            
36            // Set the ID of user (hiring manager) on the Job record being granted access.
37            jobHMShr.UserOrGroupId = job.Hiring_Manager__c;
38            
39            // The hiring manager on the job should always have 'Read Only' access.
40            jobHMShr.AccessLevel = 'Read';
41            
42            // The ID of the record being shared
43            jobHMShr.ParentId = job.Id;
44            
45            // Set the rowCause to the Apex sharing reason for hiring manager.
46            // This establishes the sharing record as Apex managed sharing.
47            jobHMShr.RowCause = Schema.Job__Share.RowCause.Hiring_Manager__c;
48            
49            // Add sharing record to list for insertion.
50            newJobShrs.add(jobHMShr);
51            
52            // Set the ID of user (recruiter) on the Job record being granted access.
53            jobRecShr.UserOrGroupId = job.Recruiter__c;
54            
55            // The recruiter on the job should always have 'Read/Write' access.
56            jobRecShr.AccessLevel = 'Edit';
57            
58            // The ID of the record being shared
59            jobRecShr.ParentId = job.Id;
60            
61            // Set the rowCause to the Apex sharing reason for recruiter.
62            // This establishes the sharing record as Apex managed sharing.
63            jobRecShr.RowCause = Schema.Job__Share.RowCause.Recruiter__c;
64            
65         // Add the sharing record to the list for insertion.            
66            newJobShrs.add(jobRecShr);
67        }
68        
69        try {
70           // Delete the existing sharing records.
71           // This allows new sharing records to be written from scratch.
72            Delete oldJobShrs;
73            
74           // Insert the new sharing records and capture the save result. 
75           // The false parameter allows for partial processing if multiple records are 
76           // passed into operation. 
77           Database.SaveResult[] lsr = Database.insert(newJobShrs,false);
78           
79           // Process the save results for insert.
80           for(Database.SaveResult sr : lsr){
81               if(!sr.isSuccess()){
82                   // Get the first save result error.
83                   Database.Error err = sr.getErrors()[0];
84                   
85                   // Check if the error is related to trivial access level.
86                   // Access levels equal or more permissive than the object's default 
87                   // access level are not allowed. 
88                   // These sharing records are not required and thus an insert exception 
89                   // is acceptable. 
90                   if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  
91                                     &&  err.getMessage().contains('AccessLevel'))){
92                       // Error is not related to trivial access level.
93                       // Send an email to the Apex job's submitter.
94                     Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
95                     String[] toAddresses = new String[] {emailAddress}; 
96                     mail.setToAddresses(toAddresses); 
97                     mail.setSubject('Apex Sharing Recalculation Exception');
98                     mail.setPlainTextBody(
99                       'The Apex sharing recalculation threw the following exception: ' + 
100                             err.getMessage());
101                     Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
102                   }
103               }
104           }   
105        } catch(DmlException e) {
106           // Send an email to the Apex job's submitter on failure.
107            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
108            String[] toAddresses = new String[] {emailAddress}; 
109            mail.setToAddresses(toAddresses); 
110            mail.setSubject('Apex Sharing Recalculation Exception');
111            mail.setPlainTextBody(
112              'The Apex sharing recalculation threw the following exception: ' + 
113                        e.getMessage());
114            Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
115        }
116    }
117    
118    // The finish method is called at the end of a sharing recalculation.
119    global void finish(Database.BatchableContext BC){  
120        // Send an email to the Apex job's submitter notifying of job completion.
121        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
122        String[] toAddresses = new String[] {emailAddress}; 
123        mail.setToAddresses(toAddresses); 
124        mail.setSubject('Apex Sharing Recalculation Completed.');
125        mail.setPlainTextBody
126                      ('The Apex sharing recalculation finished processing');
127        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
128    }
129    
130}

Apex による共有管理の再適用のテスト

この例では、5 つ��� Job レコードを挿入し、前の例で使用した一括処理クラスに実装される一括処理ジョブを呼び出します。この例では、User レコードと関連付けられた、Hiring_Manager および Recruiter という 2 つの参照項目を持つ Job というカスタムオブジェクトが必要です。また、Job カスタムオブジェクトには、Hiring_Manager と Recruiter という 2 つの共有の理由を追加する必要があります。このテストを実行する前に、組織全体の Job のデフォルト共有設定を [非公開] に設定します。テストからはメールメッセージは送信されないため、また、一括処理クラスはテストメソッドによって呼び出されるため、この場合、メール通知は送信されません。

1@isTest
2private class JobSharingTester {
3   
4    // Test for the JobSharingRecalc class    
5    static testMethod void testApexSharing(){
6       // Instantiate the class implementing the Database.Batchable interface.     
7        JobSharingRecalc recalc = new JobSharingRecalc();
8        
9        // Select users for the test.
10        List<User> users = [SELECT Id FROM User WHERE IsActive = true LIMIT 2];
11        ID User1Id = users[0].Id;
12        ID User2Id = users[1].Id;
13        
14        // Insert some test job records.                 
15        List<Job__c> testJobs = new List<Job__c>();
16        for (Integer i=0;i<5;i++) {
17        Job__c j = new Job__c();
18            j.Name = 'Test Job ' + i;
19            j.Recruiter__c = User1Id;
20            j.Hiring_Manager__c = User2Id;
21            testJobs.add(j);
22        }
23        insert testJobs;
24        
25        Test.startTest();
26        
27        // Invoke the Batch class.
28        String jobId = Database.executeBatch(recalc);
29        
30        Test.stopTest();
31        
32        // Get the Apex job and verify there are no errors.
33        AsyncApexJob aaj = [Select JobType, TotalJobItems, JobItemsProcessed, Status, 
34                            CompletedDate, CreatedDate, NumberOfErrors 
35                            from AsyncApexJob where Id = :jobId];
36        System.assertEquals(0, aaj.NumberOfErrors);
37      
38        // This query returns jobs and related sharing records that were inserted       
39        // by the batch job's execute method.     
40        List<Job__c> jobs = [SELECT Id, Hiring_Manager__c, Recruiter__c, 
41            (SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause FROM Shares 
42            WHERE (RowCause = :Schema.Job__Share.rowCause.Recruiter__c OR 
43            RowCause = :Schema.Job__Share.rowCause.Hiring_Manager__c))
44            FROM Job__c];       
45        
46        // Validate that Apex managed sharing exists on jobs.     
47        for(Job__c job : jobs){
48            // Two Apex managed sharing records should exist for each job
49            // when using the Private org-wide default. 
50            System.assert(job.Shares.size() == 2);
51            
52            for(Job__Share jobShr : job.Shares){
53               // Test the sharing record for hiring manager on job.             
54                if(jobShr.RowCause == Schema.Job__Share.RowCause.Hiring_Manager__c){
55                    System.assertEquals(jobShr.UserOrGroupId,job.Hiring_Manager__c);
56                    System.assertEquals(jobShr.AccessLevel,'Read');
57                }
58                // Test the sharing record for recruiter on job.
59                else if(jobShr.RowCause == Schema.Job__Share.RowCause.Recruiter__c){
60                    System.assertEquals(jobShr.UserOrGroupId,job.Recruiter__c);
61                    System.assertEquals(jobShr.AccessLevel,'Edit');
62                }
63            }
64        }
65    }
66}

再適用に使用される Apex クラスの関連付け

再適用に使用される Apex クラスはカスタムオブジェクトと関連付けられている必要があります。

Apex による共有管理の再適用クラスをカスタムオブジェクトと関連付ける手順は、次のとおりです。
  1. カスタムオブジェクトの管理設定から [Apex 共有再適用] に移動します。
  2. このオブジェクトの Apex 共有を再適用する Apex クラスを選択します。選択するクラスは、Database.Batchable インターフェースを実装している必要があります。同じ Apex クラスを、同じカスタムオブジェクトと複数関連付けることはできません。
  3. [保存] をクリックします。