The following example uses a custom object packaged with a page layout using a Visualforce page to demonstrate how different subscribing users view the same page.
public with sharing class BookExtension { private ApexPages.StandardController stdController; public BookExtension (ApexPages.StandardController ct) { this.stdController = ct; if( ! Test.isRunningTest()) { // You can't call addFields() in a test context, it's a bug stdController.addFields(accessibleFields); } } public List<String> accessibleFields { get { if (accessibleFields == null) { // Get a list (map) of all fields on the object Map<String, Schema.SobjectField> fields = Schema.SobjectType.Book__c.fields.getMap(); // Save only the fields accessible by the current user Set<String> availableFieldsSet = new Set<String>(); for (String s : fields.keySet()) { if (fields.get(s).getDescribe().isAccessible() // Comment out next line to show standard/system fields && fields.get(s).getDescribe().isCustom() ){ availableFieldsSet.add(s.toLowerCase()); if(Test.isRunningTest()) System.debug('Field: ' + s); } } // Convert set to list, save to property accessibleFields = new List<String>(availableFieldsSet); } return accessibleFields; } private set; } }
<apex:page standardController="Book__c" extensions="BookExtension" > <apex:pageBlock title="{!Book__c.Name}"> <apex:pageBlockSection > <apex:repeat value="{!accessibleFields}" var="f"> <apex:pageBlockSectionItem > <apex:outputLabel value="{!$ObjectType['Book__c'].Fields[f].Label}"/> <apex:outputText value="{!Book__c[f]}"/> </apex:pageBlockSectionItem> </apex:repeat> </apex:pageBlockSection> </apex:pageBlock> </apex:page>
@isTest public class BookExtensionTest { public static testMethod void testBookExtension() { // Create a book to test with Book__c book = new Book__c(); book.Author__c = 'Harry Lime'; insert book; Test.startTest(); // Add the page to the test context PageReference testPage = Page.booksView; testPage.getParameters().put('id', String.valueOf(book.Id)); Test.setCurrentPage(testPage); // Create a controller for the book ApexPages.StandardController sc = new ApexPages.StandardController(book); // Real start of testing BookExtension // BookExtension has only two methods; to get 100% code coverage, we need // to call the constructor and get the accessibleFields property // Create an extension with the controller BookExtension bookExt = new BookExtension(sc); // Get the list of accessible fields from the extension Set<String> fields = new Set<String>(bookExt.accessibleFields); // Test that accessibleFields is not empty System.assert( ! fields.isEmpty()); // Test that accessibleFields includes Author__c // This is a bad test; you can't know that subscriber won't disable System.assert(fields.contains('Author__c'.toLowerCase()), 'Expected accessibleFields to include Author__c'); Test.stopTest(); } }
When the page is viewed from the subscribing organization, it should include all the packaged Book fields, plus the newly created Rating field. Different users and organizations can continue to add whatever fields they want, and the dynamic Visualforce page will adapt and show as appropriate.