I've developed a simple system to control the write access against certain fields, on certain records.
The primary class - WriteAccessControl. This class is instantiated with a set of restricted fields that can be checked against in your objects validator class/method.
public class WriteAccessControl{
//The fields that have Write restrictions
public set<string> Fields {
get {
if(Fields == null) {
Fields = new set<string>();
}
return Fields;
} private set;
}
//the storage of permissions against fields against ids
private map<id,map<string,boolean>> WriteMap {
get {
if(WriteMap == null) {
WriteMap = new map<id,map<string,boolean>>();
}
return WriteMap;
}
set;
}
//Set the access of an id, and field
public void SetAccess(id pId, string pField, boolean pAccess) {
if(Fields.Contains(pField.ToUpperCase())) {
if(WriteMap.get(pId) == null) {
WriteMap.put(pId, new map<string, boolean>());
}
WriteMap.get(pId).put(pField.ToUpperCase(),pAccess);
} else {
throw new JNHException(JNHSystem.MessageCodes.Get('WRITE_CONTROL100'),pField.ToUpperCase());
}
}
//Get the write access of the field:
// pass in the Id of the record in question, and the name of field
// Get back a boolean of TRUE if the field can be written to, or FALSE if not
public boolean GetAccess(id pId, string pField) {
system.debug('UDBG:::WriteAccessControl.GetAccess: ENTERED with id: ' + pId + ', and field: ' + pField);
boolean b = false;
system.debug('UDBG:::WriteAccessControl.GetAccess: fields are currently: ' + fields + '. Does it contain ' + pField.ToUpperCase() + '? ' + (Fields.Contains(pField.ToUpperCase())));
if(Fields.Contains(pField.ToUpperCase())) {
//Check that we are in the write map
if(WriteMap.get(pId) != null) {
//And check the field is in the list
if(WriteMap.get(pId).get(pField.ToUpperCase()) != null) {
//And finally the boolean
b = WriteMap.get(pId).get(pField.ToUpperCase());
}
}
} else {
throw new JNHException(JNHSystem.MessageCodes.Get('WRITE_CONTROL100'),pField.ToUpperCase());
}
return b;
}
//Construct with your set of fields
public WriteAccessControl(set<string> pFields) {
for(string s : pFields) {
Fields.add(s.ToUpperCase());
}
}
}
To instantiate this in your validator class:
public static JNHSystem.WriteAccessControl WriteAccessControl{
get {
if(WriteAccessControl == null) {
WriteAccessControl = new JNHSystem.WriteAccessControl(new set<string>{
'MyCustomField__c',
'MyOtherCustomField__c'
});
}
return WriteAccessControl;
}
private set;
}
And finally, when you need to access this via a validating method at the end of your business automation:
//The following field(s) are not writable at this time
public void PO116(){
for(integer i : Context.NewRecords.KeySet()) {
for(string field : PurchaseOrder_val.WriteAccessControl.Fields) {
if( ((Context.IsNew(i) && Context.GetRecord(i).Get(field) != null) ||
(Context.Ischanged(i,field))) && !PurchaseOrder_val.WriteAccessControl.GetAccess(Context.GetRecord(i).Id, field)) {
Context.GetRecord(i).AddError(
new JNHException(
GetMessageCode('PO116'),
field
),false);
}
}
}
}
Don't be confused by the above method - I have a class that serializes triggers and gives me helpers like "IsNew" or "IsChanged". the principle is that you want to check all records in context, and what circumstances you deem necessary.
I hope others find this useful.