Apex を使用したレコードの共有
プログラムから共有にアクセスするには、共有する標準オブジェクトまたはカスタムオブジェクトに関連付けられている共有オブジェクトを使用する必要があります。たとえば、AccountShare は Account オブジェクトの共有オブジェクト、ContactShare は Contact オブジェクトの共有オブジェクトです。他のオブジェクトについても同様です。さらに、すべてのカスタムオブジェクトの共有オブジェクトには次のように名前が付けられています。MyCustomObject はカスタムオブジェクトの名前です。
MyCustomObject__Share
主従関係の従側にあるオブジェクトには、関連付けられた共有オブジェクトはありません。従レコードへのアクセスは、主の共有オブジェクトと関係の共有設定により定義されます。詳細は、Salesforce オンラインヘルプの「カスタムオブジェクトのセキュリティの概要」を参照してください。
共有オブジェクトには、Force.com 共有管理、ユーザ共有管理、Apex 共有管理の 3 種類の共有すべてをサポートするレコードが含まれています。組織の共有設定、ロールの階層、および特定オブジェクトの「すべての参照」や「すべての編集」などの権限、「すべてのデータの参照」および「すべてのデータの編集」を使用してユーザに暗黙的に付与された共有は、このオブジェクトでは追跡されません。
各共有オブジェクトには、次のプロパティがあります。
| プロパティ名 | 説明 |
|---|---|
| objectNameAccessLevel | 共有 sObject に対し、指定されたユーザまたはグループが権限を与えられたアクセスレベル。プロパティ名は、オブジェクト名に AccessLevel が追加したものです。たとえば、LeadShare オブジェクトのプロパティ名は LeadShareAccessLevel です。有効な値は、次のとおりです。
|
| ParentID | オブジェクトの ID。この項目は更新できません。 |
| RowCause | ユーザまたはグループにアクセス権が付与される理由。この理由によって、共有のタイプが決まります。共有のタイプは、共有レコードの変更権限を制御します。この項目は更新できません。 |
| UserOrGroupId | アクセス権を付与するユーザまたはグループの ID。グループには、ロールまたはテリトリーに関連付けられた公開グループまたは共有グループを指定できます。この項目は更新できません。 |
ユーザまたはグループでは標準オブジェクトまたはカスタムオブジェクトは共有できません。カスタマーコミュニティユーザは、Apex 共有を使用できません。オブジェクトを共有できるユーザおよびグループの種別についての詳細は、『Salesforce および Force.com のオブジェクトリファレンス』の「User」と「Group」を参照してください。
Apex を使用したユーザ共有管理の作成
Apex または SOAP API を使用して、1 つのユーザまたはグループに対してレコードの共有を直接設定できます。レコードの所有者が変更されると、共有は自動的に削除されます。次の例のクラスには、参照アクセスのある特定のユーザまたはグループ ID を伴うジョブ ID によって指定されたジョブを共有するメソッドが含まれます。また、このメソッドを検証するテストメソッドも含まれます。このクラス例を保存する前に、Job というカスタムオブジェクトを作成します。
1swfobject.registerObject("clippy.codeblock-0", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class JobSharing {
18
19 public static boolean manualShareRead(Id recordId, Id userOrGroupId){
20 // Create new sharing object for the custom object Job.
21 Job__Share jobShr = new Job__Share();
22
23 // Set the ID of record being shared.
24 jobShr.ParentId = recordId;
25
26 // Set the ID of user or group being granted access.
27 jobShr.UserOrGroupId = userOrGroupId;
28
29 // Set the access level.
30 jobShr.AccessLevel = 'Read';
31
32 // Set rowCause to 'manual' for manual sharing.
33 // This line can be omitted as 'manual' is the default value for sharing objects.
34 jobShr.RowCause = Schema.Job__Share.RowCause.Manual;
35
36 // Insert the sharing record and capture the save result.
37 // The false parameter allows for partial processing if multiple records passed
38 // into the operation.
39 Database.SaveResult sr = Database.insert(jobShr,false);
40
41 // Process the save results.
42 if(sr.isSuccess()){
43 // Indicates success
44 return true;
45 }
46 else {
47 // Get first save result error.
48 Database.Error err = sr.getErrors()[0];
49
50 // Check if the error is related to trival access level.
51 // Access levels equal or more permissive than the object's default
52 // access level are not allowed.
53 // These sharing records are not required and thus an insert exception is acceptable.
54 if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION &&
55 err.getMessage().contains('AccessLevel')){
56 // Indicates success.
57 return true;
58 }
59 else{
60 // Indicates failure.
61 return false;
62 }
63 }
64 }
65
66}
671swfobject.registerObject("clippy.codeblock-1", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@isTest
18private class JobSharingTest {
19 // Test for the manualShareRead method
20 static testMethod void testManualShareRead(){
21 // Select users for the test.
22 List<User> users = [SELECT Id FROM User WHERE IsActive = true LIMIT 2];
23 Id User1Id = users[0].Id;
24 Id User2Id = users[1].Id;
25
26 // Create new job.
27 Job__c j = new Job__c();
28 j.Name = 'Test Job';
29 j.OwnerId = user1Id;
30 insert j;
31
32 // Insert manual share for user who is not record owner.
33 System.assertEquals(JobSharing.manualShareRead(j.Id, user2Id), true);
34
35 // Query job sharing records.
36 List<Job__Share> jShrs = [SELECT Id, UserOrGroupId, AccessLevel,
37 RowCause FROM job__share WHERE ParentId = :j.Id AND UserOrGroupId= :user2Id];
38
39 // Test for only one manual share on job.
40 System.assertEquals(jShrs.size(), 1, 'Set the object\'s sharing model to Private.');
41
42 // Test attributes of manual share.
43 System.assertEquals(jShrs[0].AccessLevel, 'Read');
44 System.assertEquals(jShrs[0].RowCause, 'Manual');
45 System.assertEquals(jShrs[0].UserOrGroupId, user2Id);
46
47 // Test invalid job Id.
48 delete j;
49
50 // Insert manual share for deleted job id.
51 System.assertEquals(JobSharing.manualShareRead(j.Id, user2Id), false);
52 }
53}
54Apex による共有管理の作成
開発者は Apex による共有管理を使用すると、Apex または SOAP API を通じて、アプリケーションの動作をサポートする共有をプログラムで操作できるようになります。この種類の共有は、Force.com による共有管理に類似しています。「すべてのデータの編集」権限を持つユーザのみが、レコードへの Apex による共有管理を追加または変更できます。Apex による共有管理は、レコードの所有者を変更しても維持されます。
Apex による共有管理には、Apex 共有の理由を使用する必要があります。Apex 共有の理由は、ユーザやユーザグループでレコードを共有した理由を開発者が追跡するための 1 つの方法です。複数の Apex 共有理由を使用することで、共有レコードの更新や削除に必要なコーディングを簡略化することができます。また、開発者は、同じユーザやグループに対して異なる共有理由を設定して複数の共有を設定できます。
- ユーザインターフェースでレコードの共有を参照すると、[理由] 列に表示ラベルが表示されます。これにより、ユーザとシステム管理者が共有の目的を理解できます。表示ラベルは、トランスレーションワークベンチを使用する翻訳についても有効化されます。
- この名前は、API および Apex で理由を参照するときに使用します。
Apex 共有の理由の名前の形式は次のとおりです。
1MyReasonName__c1Schema.CustomObject__Share.rowCause.SharingReason__c1Schema.Job__Share.rowCause.Recruiter__c詳細は、「Schema クラス」を参照してください。
- [設定] で、 をクリックします。
- カスタムオブジェクトを選択します。
- [Apex 共有の理由] 関連リストで [新規] をクリックします。
- Apex 共有の理由の表示ラベルを入力します。ユーザインターフェースでレコードの共有を参照すると、[理由] 列に表示ラベルが表示されます。表示ラベルは、トランスレーションワークベンチを使用する翻訳についても有効化されます。
- Apex 共有の理由の名前を入力します。この名前は、API および Apex で理由を参照するときに使用します。この名前は、アンダースコアと英数字のみを含み、組織内で一意の名前にする必要があります。最初は文字であること、スペースは使用しない、最後にアンダースコアを使用しない、2 つ続けてアンダースコアを使用しないという制約があります。
- [保存] をクリックします。
Apex による共有管理の例
この例では、人事採用アプリケーションの構築中で、Job というオブジェクトが存在すると仮定しています。ジョブにリストされた採用担当者および採用担当マネージャにレコードへのアクセス権が付与されていることを確認したいと考えています。次のトリガは、ジョブレコード作成時に採用担当者および採用担当マネージャにアクセス権を付与します。この例では、User レコードと関連付けられた、Hiring_Manager および Recruiter という 2 つの参照項目を持つ Job というカスタムオブジェクトが必要です。また、Job カスタムオブジェクトには、Hiring_Manager と Recruiter という 2 つの共有の理由を追加する必要があります。
1swfobject.registerObject("clippy.codeblock-5", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17trigger JobApexSharing on Job__c (after insert) {
18
19 if(trigger.isInsert){
20 // Create a new list of sharing objects for Job
21 List<Job__Share> jobShrs = new List<Job__Share>();
22
23 // Declare variables for recruiting and hiring manager sharing
24 Job__Share recruiterShr;
25 Job__Share hmShr;
26
27 for(Job__c job : trigger.new){
28 // Instantiate the sharing objects
29 recruiterShr = new Job__Share();
30 hmShr = new Job__Share();
31
32 // Set the ID of record being shared
33 recruiterShr.ParentId = job.Id;
34 hmShr.ParentId = job.Id;
35
36 // Set the ID of user or group being granted access
37 recruiterShr.UserOrGroupId = job.Recruiter__c;
38 hmShr.UserOrGroupId = job.Hiring_Manager__c;
39
40 // Set the access level
41 recruiterShr.AccessLevel = 'edit';
42 hmShr.AccessLevel = 'read';
43
44 // Set the Apex sharing reason for hiring manager and recruiter
45 recruiterShr.RowCause = Schema.Job__Share.RowCause.Recruiter__c;
46 hmShr.RowCause = Schema.Job__Share.RowCause.Hiring_Manager__c;
47
48 // Add objects to list for insert
49 jobShrs.add(recruiterShr);
50 jobShrs.add(hmShr);
51 }
52
53 // Insert sharing records and capture save result
54 // The false parameter allows for partial processing if multiple records are passed
55 // into the operation
56 Database.SaveResult[] lsr = Database.insert(jobShrs,false);
57
58 // Create counter
59 Integer i=0;
60
61 // Process the save results
62 for(Database.SaveResult sr : lsr){
63 if(!sr.isSuccess()){
64 // Get the first save result error
65 Database.Error err = sr.getErrors()[0];
66
67 // Check if the error is related to a trivial access level
68 // Access levels equal or more permissive than the object's default
69 // access level are not allowed.
70 // These sharing records are not required and thus an insert exception is
71 // acceptable.
72 if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
73 && err.getMessage().contains('AccessLevel'))){
74 // Throw an error when the error is not related to trivial access level.
75 trigger.newMap.get(jobShrs[i].ParentId).
76 addError(
77 'Unable to grant sharing access due to following exception: '
78 + err.getMessage());
79 }
80 }
81 i++;
82 }
83 }
84
85}- 共有の直接設定アクセスレベルが「参照」に設定されている場合、「更新」に設定された新しい共有行を挿入すると、元の共有行はより高いアクセスレベルを示す「更新」に更新されます。
- ユーザが子レコード (取引先責任者、ケース、商談など) にアクセスできるため取引先にアクセスでき、取引先共有ルールが作成されている場合、親の暗黙的共有の共有理由は、共有ルールの共有理由で置き換えられ、高い方のアクセスレベルを示します。