やりたいこと
親レコードの共有の情報を子レコードにも反映させたい!
前提条件
・カスタムで「機密情報」と「機密情報詳細」オブジェクトがあるとします
・機密情報詳細は機密情報への参照項目を持っています
・「機密情報」と「機密情報詳細」オブジェクトは基本的に非公開で、詳細画面の共有ボタンから共有されているユーザまたはグループにしか権限がないとします
機密情報の詳細画面
上記の共有設定を子レコードにもボタンひとつで反映させたい
実装内容
機密情報オブジェクト(親)に子コードも含めて、共有設定の詳細が確認できるコンポーネントをLWCで実装しました
「この共有設定を以下のレコードにも反映」ボタンを押下すると、確認画面が開きます
「はい」を押下し、ページをリフレッシュすると、子レードにも親と同じ共有設定になっていることがわかります
コーディング詳細
reflectShareInfoという名前でLWCを作成しました。JavascriptとHTMLは以下の通りです
import { api, LightningElement, wire, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import secretRelatedInfo from '@salesforce/apex/GetShareInfoController.secretRelatedInfo';
import startReflectShareInfo from '@salesforce/apex/ReflectShareInfoController.startReflectShareInfo';
import MyModal from 'c/reflectShareinfoModal';
const columns = [
// { label: '機密情報名', fieldName: 'recordName', type: 'text' },
{ label: 'ユーザまたはグループ名', fieldName: 'UserOrGroupName', type: 'text' },
{ label: 'アクセス種別', fieldName: 'RowCause', type: 'text' },
{ label: 'アクセスレベル', fieldName: 'AccessLevel', type: 'text' },
];
export default class ReflectShareInfo extends LightningElement {
@api recordId;
@track thisRecordtData;
@track tableData;
@track columns = columns;
@wire(secretRelatedInfo, {siId: '$recordId'})
gettableData({ error, data }) {
if (data) {
// secretInfoDetailAndShareInfoは編集できないため、編集できる形に再定義
var targetData = JSON.parse(JSON.stringify(data.secretInfoDetailAndShareInfo));
let tmpTableData = []
// 関連レコードの共有設定のテーブル情報
Object.keys(targetData).forEach(function(element){
for(var i = 0; i<targetData[element].length; i++) {
// テーブルデータは参照情報を扱ってくれないため、参照情報を取り出してテーブルデータとして定義
var targetDataUserOrGroupName = targetData[element][i]['UserOrGroup']['Name'];
targetData[element][i]['UserOrGroupName'] = targetDataUserOrGroupName;
var sidRecordInfo = data.siDetailMap[targetData[element][i]['ParentId']];
targetData[element][i]['targetRecordName'] = sidRecordInfo['Name'];
var recordName = targetData[element][i]['Parent']['Name'];
targetData[element][i]['recordName'] = recordName;
}
tmpTableData.push({key:element, value:targetData[element]});
});
this.tableData = tmpTableData;
// 表示されているレコードの共有情報整備
var displayedShareData = JSON.parse(JSON.stringify(data.sishlst));
displayedShareData.forEach(element => {
// console.log(element);
element['UserOrGroupName'] = element['UserOrGroup']['Name'];
});
this.thisRecordtData = displayedShareData;
}else if(error){
console.log('Something happened: ' + JSON.parse(JSON.stringify(error)));
}
}
// 共有設定を反映するボタンを押下した際に起動
async handleClick() {
console.log('Click pushed');
var result = await MyModal.open({
size: 'small',
label: 'sample label',
description: 'Confirm the share info'
});
console.log(result);
if (result) {
await startReflectShareInfo({siId: this.recordId}).then(result =>{
console.log('success');
this.showSuccessToast('成功', '共有設定の反映が成功しました。画面をリフレッシュして共有情報をご確認ください')
}).catch(error =>{
console.log('Something happened: ' + JSON.parse(JSON.stringify(error)));
this.showErrorToast('失敗', '共有設定の反映が失敗しました')
})
}
}
showErrorToast(title, message) {
const evt = new ShowToastEvent({
title: title,
message: message,
variant: 'error',
mode: 'dismissable'
});
this.dispatchEvent(evt);
}
showSuccessToast(title, message) {
const evt = new ShowToastEvent({
title: title,
message: message,
variant: 'success',
mode: 'sticky'
});
this.dispatchEvent(evt);
}
}
<template>
<lightning-card title='このレコードの共有情報' icon-name="custom:custom11" if:true={thisRecordtData}>
<lightning-datatable
key-field="id"
data={thisRecordtData}
columns={columns}
hide-checkbox-column
show-row-number-column>
</lightning-datatable>
<div slot="footer" class="slds-clearfix">
<lightning-button
label="この共有設定を以下のレコードにも反映"
title="Non-primary action"
class="slds-var-m-left_x-small slds-float_left"
onclick={handleClick}>
</lightning-button>
</div>
</lightning-card>
<template for:each={tableData} for:item="shareData">
<lightning-card title={shareData.key} icon-name="custom:custom11" if:true={tableData} key={shareData.key}>
<lightning-datatable
key-field="id"
data={shareData.value}
columns={columns}
hide-checkbox-column
show-row-number-column>
</lightning-datatable>
</lightning-card>
</template>
</template>
モーダル画面はreflectShareinfoModalという名前で作成しています
import LightningModal from 'lightning/modal';
export default class ReflectShareinfoModal extends LightningModal {
handleYes() {
this.close(1);
}
handleNo() {
this.close(0);
}
}
<template>
<lightning-modal-header label="以下の内容でよろしいですか?"></lightning-modal-header>
<lightning-modal-body>
<p>既存の共有設定を上書きしますがよろしいですか?</p>
</lightning-modal-body>
<lightning-modal-footer>
<lightning-button label="はい" onclick={handleYes} class="slds-m-right_x-small"></lightning-button>
<lightning-button label="いいえ" onclick={handleNo}></lightning-button>
</lightning-modal-footer>
</template>
現状の共有設定の取得処理
※共有の情報を管理しているオブジェクトは[オブジェクト名]__Shareというオブジェクトです(標準オブジェクトの場合は[オブジェクト名]Share)
詳細は以下を参照ください
public with sharing class GetShareInfoController {
public class SecretRelatedInfoReturn {
@AuraEnabled public List<SecretInfo__Share> sishlst;
@AuraEnabled public Map<Id, List<SecretInfo__Share>> secretInfoAndShareInfo;
@AuraEnabled public List<SecretInfoDetail__Share> srdlst;
@AuraEnabled public Map<Id, SecretInfoDetail__c> siDetailMap;
@AuraEnabled public Map<String, List<SecretInfoDetail__Share>> secretInfoDetailAndShareInfo;
}
@AuraEnabled(cacheable=true)
public static SecretRelatedInfoReturn secretRelatedInfo(Id siId) {
System.debug('########start getSecretInfo method');
System.debug(siId);
SecretRelatedInfoReturn sri = new SecretRelatedInfoReturn();
Map<Id, SecretInfo__Share> secretInfoShareMap = new Map<Id, SecretInfo__Share>(
[
SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause, UserOrGroup.Name
FROM SecretInfo__Share
WHERE ParentId = :siId
]
);
sri.sishlst = secretInfoShareMap.values();
Map<Id, List<SecretInfo__Share>> parentIdSecretInfoShare = new Map<Id, List<SecretInfo__Share>>();
for (SecretInfo__Share variable : secretInfoShareMap.values()) {
System.debug(variable);
if (!parentIdSecretInfoShare.keySet().contains(variable.ParentId)) {
parentIdSecretInfoShare.put(variable.ParentId, new List<SecretInfo__Share>());
}
parentIdSecretInfoShare.get(variable.ParentId).add(variable);
}
sri.secretInfoAndShareInfo = parentIdSecretInfoShare;
Map<Id, SecretInfoDetail__c> secretInfoDetailMap = new Map<Id, SecretInfoDetail__c>(
[
SELECT Id, Name, SecretInfo__c
FROM SecretInfoDetail__c
WHERE SecretInfo__c = :siId
WITH SECURITY_ENFORCED
]
);
sri.siDetailMap = secretInfoDetailMap;
Map<Id, SecretInfoDetail__Share> secretInfoDetailShareMap = new Map<Id, SecretInfoDetail__Share>(
[
SELECT Id, Parent.Name, ParentId, UserOrGroupId, AccessLevel, RowCause, UserOrGroup.Name
FROM SecretInfoDetail__Share
WHERE ParentId IN :secretInfoDetailMap.keySet()
]
);
Map<String, List<SecretInfoDetail__Share>> parentIdSecretInfoDetailShare = new Map<String, List<SecretInfoDetail__Share>>();
String keyElement;
for (SecretInfoDetail__Share variable : secretInfoDetailShareMap.values()) {
System.debug(variable);
keyElement = variable.Parent.Name + '-' + variable.ParentId;
if (!parentIdSecretInfoDetailShare.keySet().contains(keyElement)) {
parentIdSecretInfoDetailShare.put(keyElement, new List<SecretInfoDetail__Share>());
}
parentIdSecretInfoDetailShare.get(keyElement).add(variable);
}
sri.secretInfoDetailAndShareInfo = parentIdSecretInfoDetailShare;
System.debug('sri: ' + sri);
sri.srdlst = secretInfoDetailShareMap.values();
return sri;
}
}
共有設定の反映を担当しているApex。
子レコードに既存の共有設定がある場合は、削除してから親レコードの共有設定の内容を上書きしています。
public without sharing class ReflectShareInfoController {
@AuraEnabled
public static Boolean startReflectShareInfo(Id siId) {
// 表示している機密情報レコードの共有情報を取得
List<SecretInfo__Share> siShareList = [
SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause
FROM SecretInfo__Share
WHERE ParentId = :siId
AND RowCause = 'Manual'
];
System.debug('siShareList取得後');
System.debug(siShareList);
// 表示している機密情報レコードに紐づく機密情報詳細レコードを取得
List<SecretInfoDetail__c> sIDList = [
SELECT Id, Name, SecretInfo__c, SecretInfo__r.Name
FROM SecretInfoDetail__c
WHERE SecretInfo__c = :siId
];
System.debug('sIDList取得後');
System.debug(sIDList);
// 機密情報詳細レコードの共有情報で手動設定されたものを取得
List<SecretInfoDetail__Share> sidShareList = [
SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause
FROM SecretInfoDetail__Share
WHERE ParentId IN :sIDList
AND RowCause = 'Manual'
];
System.debug('sidShareList取得後');
System.debug(sidShareList);
if (Schema.SObjectType.SecretInfoDetail__Share.isDeletable()) {
if (sidShareList.size() > 0) {
System.debug('delete前');
delete sidShareList;
}
}else {
System.debug('共有設定の削除権限がありません');
return False;
}
// 機密情報詳細レコードの共有情報を取得
List<SecretInfoDetail__Share> afterDeleteShareList = [
SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause
FROM SecretInfoDetail__Share
WHERE ParentId IN :sIDList
];
// Mapを作成
Map<Id, SecretInfoDetail__Share> secretDetailShare = new Map<Id, SecretInfoDetail__Share>();
// UserOrGroupIdをキー、SecretInfoDetail__Shareをバリューにするマップを作成
for (SecretInfoDetail__Share scretShareInfo : afterDeleteShareList) {
secretDetailShare.put(scretShareInfo.UserOrGroupId, scretShareInfo);
}
System.debug('afterDeleteShareList取得後');
System.debug(secretDetailShare);
// 表示している機密情報レコードの共有情報を関連する機密情報詳細レコードへコピー
List<SecretInfoDetail__Share> newSecretInfoDetailShareList = new List<SecretInfoDetail__Share>();
for (SecretInfoDetail__c targetSecretInfoDetail : sIDList) {
for (SecretInfo__Share siShareInfo : siShareList) {
// 機密情報詳細レコードの所有者や設定の共有でデフォルトで共有されているユーザやグループを考慮
if (secretDetailShare.containsKey(siShareInfo.UserOrGroupId)) {
System.debug('共有済みのためスキップ');
continue;
}
// 新規の共有設定を作成
SecretInfoDetail__Share sidShare = new SecretInfoDetail__Share();
sidShare.ParentId = targetSecretInfoDetail.Id;
sidShare.UserOrGroupId = siShareInfo.UserOrGroupId;
sidShare.AccessLevel = siShareInfo.AccessLevel;
newSecretInfoDetailShareList.add(sidShare);
}
}
if (newSecretInfoDetailShareList.size() > 0) {
System.debug(newSecretInfoDetailShareList);
insert newSecretInfoDetailShareList;
}
return True;
}
}
終わりに
ある特定の部署などによっては、レコードの情報をオープンにしたくないという要望があるかと思います。
そんな時のための参考になれば幸いです