0

I implemented user preferences for a Visualforce tool using custom metadata type record to save the preferences. Although the VF controller is a "with sharing" class, it calls the (metadata api based) record update method in a separate "without sharing" class. I used custom metadata type instead of custom settings due to Salesforce's "strong recommendation" to do so.

Although it works for system administrator, for a standard user I am getting this error in deployment status: "insufficient access rights on cross-reference id". There error disappears if I assign that profile the "Modify Metadata Through Metadata API Functions" permission (which requires "View Setup and Configuration").

I thought that abstracting out to a class without sharing (which only allows specific custom metadata types) would let me do this, but unfortunately it is not working. I may have to try to reimplement using custom settings instead of custom metadata types, but still don't know if the same issue will arise. I guess I could use a custom object.

Is there any way to get this to work for standard user? If not with custom metadata types, then should it work with custom settings? Or should I just use a custom object?

p.s. This page shows someone using a proxy class to resolve a similar issue for using custom settings from Visualforce it seems. But the answer is from 2014 and it seems silly that it would work so I am not going to depend on it as a guide to using custom settings. At this point guessing a simple custom object is the only solution..

global with sharing class MyVFCustomController {
 public void savesettings() {
   ...
  public UserSettingsMetadata meta = new publicUserSettingsMetadata();
  meta.upsertMetadataRecord(objname,recname,prefsmap);
 }
}

public without sharing class publicUserSettingsMetadata {
    /*
    Allow a user to set his own user preferences using metadata api
    even if the user does not have metadata api / modify all permissions,
    but limit the custom metadata types accessible in this way.
    Used by Visualforce User Settings pages.
    */
    private static Set<String> allowedPublicSettingTypes = new Set<String>{
        'Global_Meeting_Report_User_Setting__mdt'
    };// list of custom metadata types allowed public access
    public publicUserSettingsMetadata() {
    }
    
    public void upsertMetadataRecord(String objname, String label, Map<String, Object> fieldWithValuesMap) {
        // Insert or overwrite user settings record based on unique metadata fullname
        // Usage: upsertMetadataRecord(fullname,label,prefsmap);//fullname = label = 'GlobalMtgReportUser_' + userid
        // fullname example: 
        // Global_Meeting_Report_User_Setting__mdt.GlobalMtgReportUser_0056F0000072VjyQAE
        if ((String.isBlank(objname)) || String.isBlank(label)) {return;}
        if (allowedPublicSettingTypes.contains(objname) == false) {return;}
        Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata();
        customMetadata.fullName = objname + '.' + label;
        customMetadata.label = label;
        for (String key: fieldWithValuesMap.keySet()) {
            Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue();
            customField.field = key;
            customField.value = fieldWithValuesMap.get(key);
            customMetadata.values.add(customField);
        }
        Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
        mdContainer.addMetadata(customMetadata);
        PublicSettingsCallback callback = new PublicSettingsCallback();
        if (Test.isRunningTest() == false) {
            Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, callback);
            System.debug('Metadata enqueued jobId: '+jobId);
        }
    }
}

public class PublicSettingsCallback implements Metadata.DeployCallback {
    public PublicSettingsCallback() {}
    
    public void handleResult(Metadata.DeployResult result,
    Metadata.DeployCallbackContext context) {
        Id jobId = context.getCallbackJobId();
        System.debug(result.status);
}
1
  • 1
    Due to time constraint I decided to just use a custom object. But if anyone knows would like to hear.
    – skycafe
    Jun 2, 2022 at 6:49

1 Answer 1

0

Okay, no answers yet. As far as I can tell it isn't directly possible, at least with VisualForce. The only way I can see is to create a decoupled mechanism such as a REST service and separate client. I solved the issue by reimplementing using a custom Settings object.

1
  • Given the requirement (allowing non admin user to update metadata) there is no other way but either to assign user permission of View Setup and Configuration permission or go the custom object route. You choose well ;-)
    – mubs
    Oct 18, 2023 at 3:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .