こんにちは、管理人の@Salesforce.Zです。
Apexによる共有管理はやったことがないでしょう
こんな方なら、ぜひ当記事を読んでほしい。
本来の共有管理は
・プロファイル
・ロール階層
・所有者
・共有ルール
・共有モデル
で制御すれば、大体いける。
しかし、シンプルのものしか対応できない。複雑な共有管理になる、コードを書く必要がある。
その場合、Apexを使用したレコードの共有が登場
★ 共有の初歩理解 ★ Apexによる共有のサンプル(標準OBJ) ★ Apexによる共有のサンプル(カスタムOBJ)
目次
共有の初歩理解
Salesforceでは、データごとに所有者がある。
その所有者がデータ OR レコードと呼ぶに対する 作成/編集/更新/削除の行為ができる。
所有者ではない場合、レコードすら見えないのがほとんどだ
通常のSalesforce使い方
1.ユーザでSalesforceにログイン
2.Salesforceの中に入ったら、ログインユーザでデータを作成したら
たとえば、Account取引先という標準オブジェクトのレコードを1件作成した。
ログインユーザはそのレコードに対して、作成/編集/更新/削除の行為ができる
実際には、裏で、Salesforceの標準機能で、自動的に Accountの共有オブジェクトであるAccountShareを1件作成している。
ここからが大事、Salesforce以外の案件では、通用する権限管理の考え方

データに対して、権限を付与する。権限:作成権限、更新権限、編集権限、削除権限
所有者には、この4つ権限をもつ、Salesforce上では、所有者に自動的にこの4つの権限をもつ共有レコードが作成され
所有者ユーザに共有している。
上図のように、右側に付随する取引先の共有レコードが1件作成されて、付与権限はALL、全ての権限を与えた、 この権限を受けるのがUserOrGroupIdに設定された UserId または GroupIdである。
Apexによる共有の例
Apexによる共有のサンプル(標準OBJ)
trigger AccountTriggerHandler on Account (after insert) {
//標準ならオブジェクト名+Shareで共有レコードを作成すればいい
AccountShare acc = new AccountShare()
acc.AccountId = どの取引先を共有するか、その取引先のID;
acc. AccountAccessLevel = 'Read';//その他はEdit, Allがある
acc. UserOrGroupId = どのユーザ また どの公開グループに共有するか、そのID;
// 必須項目設定
acc. RowCause = 'Manual';
acc. OpportunityAccessLevel = 'None';
acc. CaseAccessLevel = 'None';
//この共有レコードを作成したら、AccountIdに設定した取引先を、UserOrGroupIdに設定したユーザ or 公開グループに閲覧権限を付与することになる
insert acc;
}
Apexによる共有のサンプル(カスタムOBJ)
この例では、人事採用アプリケーションの構築中で、Job というオブジェクトが存在すると仮定しています。ジョブにリストされた採用担当者および採用担当マネージャにレコードへのアクセス権が付与されていることを確認したいと考えています。次のトリガは、ジョブレコード作成時に採用担当者および採用担当マネージャにアクセス権を付与します。この例では、User レコードと関連付けられた、Hiring_Manager および Recruiter という 2 つの参照項目を持つ Job というカスタムオブジェクトが必要です。また、Job カスタムオブジェクトには、Hiring_Manager と Recruiter という 2 つの共有の理由を追加する必要があります。
trigger JobApexSharing on Job__c (after insert) {
if(trigger.isInsert){
// Create a new list of sharing objects for Job
List<Job__Share> jobShrs = new List<Job__Share>();
// Declare variables for recruiting and hiring manager sharing
Job__Share recruiterShr;
Job__Share hmShr;
for(Job__c job : trigger.new){
// Instantiate the sharing objects
recruiterShr = new Job__Share();
hmShr = new Job__Share();
// Set the ID of record being shared
recruiterShr.ParentId = job.Id;
hmShr.ParentId = job.Id;
// Set the ID of user or group being granted access
recruiterShr.UserOrGroupId = job.Recruiter__c;
hmShr.UserOrGroupId = job.Hiring_Manager__c;
// Set the access level
recruiterShr.AccessLevel = 'edit';
hmShr.AccessLevel = 'read';
// Set the Apex sharing reason for hiring manager and recruiter
recruiterShr.RowCause = Schema.Job__Share.RowCause.Recruiter__c;
hmShr.RowCause = Schema.Job__Share.RowCause.Hiring_Manager__c;
// Add objects to list for insert
jobShrs.add(recruiterShr);
jobShrs.add(hmShr);
}
// Insert sharing records and capture save result
// The false parameter allows for partial processing if multiple records are passed
// into the operation
Database.SaveResult[] lsr = Database.insert(jobShrs,false);
// Create counter
Integer i=0;
// Process the save results
for(Database.SaveResult sr : lsr){
if(!sr.isSuccess()){
// Get the first save result error
Database.Error err = sr.getErrors()[0];
// Check if the error is related to a trivial access level
// Access levels equal or more permissive than the object's default
// access level are not allowed.
// These sharing records are not required and thus an insert exception is
// acceptable.
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
&& err.getMessage().contains('AccessLevel'))){
// Throw an error when the error is not related to trivial access level.
trigger.newMap.get(jobShrs[i].ParentId).
addError(
'Unable to grant sharing access due to following exception: '
+ err.getMessage());
}
}
i++;
}
}
}
共有オブジェクトがない、コードエラーの場合
対象オブジェクトの共有モデルが非公開にならないと、共有オブジェクトがSalesforceに自動的に作ってくれない
手動で作成もできない。なので、ない場合、共有モデルを確認し、非公開でなければ、Apexによる共有できない。
終わりに
共有管理は、普段、共有ルールで、対応できるが柔軟性がそんなにないため、
Apexによる共有をなれたら、もっと複雑な対応ができ、抵抗感もなくなる。
提案時にもいろいろイメージしやすいだろう。
この間、ニュースで、楽天のセキュリティ事件もあり、権限周りがちゃんとマスタしたほうがよいでしょうか