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

Newer Version Available

This content describes an older version of this product. View Latest

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

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

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 つの共有の理由を追加する必要があります。このサンプルを実行する前に、メールアドレスを、エラー通知とジョブ完了通知を送信する有効なメールアドレスに置き換えます。

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

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

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

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

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

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

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