Showing System Fields in a Custom Visualforce Page (an improved approach)
- Posted in:
- visualforce
- salesforce
In a previous post (Showing System Fields in a Custom Visualforce Page) I talked about how to show system information (Created By, Last Modified By, and Owner) in a custom visualforce page. The approach I presented works but it has some shortcomings:
- Doesn’t work as well when the object is a detail object in a Master-Detail relationship (there is no owner here for the detail object, as the owner is the one from the master object)
- It requires to put some fields (as hidden) in the visualforce page.
I present here a better approach that takes into consideration the above points.
Let’s start by the component controller:
public class ComponentControllerSystemInformation { public sObject recordValue { get { return recordValue; } set { recordValue = value; Schema.DescribeSObjectResult d = recordValue.getSObjectType().getDescribe(); String soql = 'select CreatedById,CreatedDate,LastModifiedById,LastModifiedDate' + (isMaster?',OwnerId':'') + ' from ' + d.Name + ' where Id = \'' + recordValue.Id + '\''; recordValue = Database.query(soql); } } public Id recordId { get { return recordValue.Id; } } public Id createdById { get { return (Id)recordValue.get('CreatedById'); } } public String createdByName { get { User createdByUser = [select name from user where id = :createdById limit 1]; return createdByUser == null ? null : createdByUser.Name; } } public String convertedCreatedDate { get { DateTime createdDate = (DateTime)recordValue.get('CreatedDate'); return createdDate.format(); } } public Id lastModifiedById { get { return (Id)recordValue.get('LastModifiedById'); } } public String lastModifiedByName { get { User lastModifiedByUser = [select name from user where id = :lastModifiedById limit 1]; return lastModifiedByUser == null ? null : lastModifiedByUser.Name; } } public String convertedLastModifiedDate { get { DateTime lastModifiedDate = (DateTime)recordValue.get('LastModifiedDate'); return lastModifiedDate.format(); } } public String ownerName { get { User ownerUser = [select name from user where id = :ownerId limit 1]; return ownerUser == null ? null : ownerUser.Name; } } public String ownerPhoto { get { try { Id ownerId = (Id)recordValue.get('ownerId'); User owner = [select smallphotourl from user where id = :ownerId limit 1]; return owner == null ? null : owner.SmallPhotoUrl; } catch(System.RequiredFeatureMissingException e) { System.debug('Chatter not enabled in organization:' + e.getMessage()); return null; } } } public Boolean isMaster { get { Schema.DescribeSObjectResult d = recordValue.getSObjectType().getDescribe(); Map<String, Schema.SObjectField> fields = d.fields.getMap(); Schema.SObjectField field = fields.get('OwnerId'); return field != null; } } public Id ownerId { get { if(!isMaster) return null; return (Id)recordValue.get('OwnerId'); } } }
Notice lines 9-11 where I query the fields that are required by this component to work, removing the requirement I had in the previous solution to include those fields in the visualforce markup. Notice also in line 81 a new property isMaster which we will use to show or hide the owner information in the visualforce page.
The component can now be written as follows:
<apex:component controller="ComponentControllerSystemInformation"> <apex:attribute name="record" assignTo="{!recordValue}" type="sObject" description="The object for which to display system information" required="true"/> <apex:pageBlockSection title="{!$Label.SystemInformation}" columns="2"> <apex:pageBlockSectionItem > <apex:outputLabel value="{!$Label.LastModifiedBy}" /> <apex:outputPanel > <apex:outputLink id="lastModifiedBy" onblur="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}').hide();" onfocus="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}', '/{!lastModifiedById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" onmouseout="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}').hide();" onmouseover="LookupHoverDetail.getHover('{!$Component.lastModifiedBy}', '/{!lastModifiedById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" value="{!URLFOR('/' + lastModifiedById)}">{!lastModifiedByName}</apex:outputLink> <apex:outputText value="{!convertedLastModifiedDate}" /> </apex:outputPanel> </apex:pageBlockSectionItem> <apex:pageBlockSectionItem rendered="{!NOT(isMaster)}" /> <apex:pageBlockSectionItem rendered="{!isMaster}"> <apex:outputLabel for="owner" value="{!$Label.Owner}" /> <apex:outputPanel > <apex:image value="{!ownerPhoto}" width="16" height="16"/> <apex:outputLink id="owner" onblur="LookupHoverDetail.getHover('{!$Component.owner}').hide();" onfocus="LookupHoverDetail.getHover('{!$Component.owner}', '/{!ownerId}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" onmouseout="LookupHoverDetail.getHover('{!$Component.owner}').hide();" onmouseover="LookupHoverDetail.getHover('{!$Component.owner}', '/{!ownerId}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" value="{!URLFOR('/' + ownerId)}">{!ownerName}</apex:outputLink> <apex:outputLink value="{!URLFOR('/' + recordId + '/a?retURL=' + URLENCODE($CurrentPage.Url))}">[Change]</apex:outputLink> </apex:outputPanel> </apex:pageBlockSectionItem> <apex:pageBlockSectionItem > <apex:outputLabel value="{!$Label.CreatedBy}" /> <apex:outputPanel > <apex:outputLink id="createdBy" onblur="LookupHoverDetail.getHover('{!$Component.createdBy}').hide();" onfocus="LookupHoverDetail.getHover('{!$Component.createdBy}', '/{!createdById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" onmouseout="LookupHoverDetail.getHover('{!$Component.createdBy}').hide();" onmouseover="LookupHoverDetail.getHover('{!$Component.createdBy}', '/{!createdById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();" value="{!URLFOR('/' + createdById)}">{!createdByName}</apex:outputLink> <apex:outputText value="{!convertedCreatedDate}" /> </apex:outputPanel> </apex:pageBlockSectionItem> </apex:pageBlockSection> </apex:component>
Notice lines 18 and 19, where we use the new property isMaster to show or hide the owner information based on whether this is a detail object in a Master-Detail relationship or not.
With this, the visualforce page is simplified as follows:
<apex:page standardController="Contact"> <apex:pageBlock mode="mainDetail" > <c:ComponentControllerSystemInformation record="{!record}" /> </apex:pageBlock> </apex:page>
We include the component in line 3 inside a pageBlock tag. Our page will show the system information just as in a standard page:
And finally, for completeness, here is the test for the component:
@isTest public class SystemInformationComponentTest{ @isTest public static void TestComponent() { Account record = new Account(Name = 'Test'); insert record; record = [select ownerId,createdById,lastModifiedById,createdDate,lastModifiedDate from account where id = :record.id]; User owner = [select name,smallPhotoUrl from user where id = :record.ownerId]; ComponentControllerSystemInformation controller = new SystemInformationComponentController(); controller.recordValue = record; System.assertEquals(record.Id, controller.recordId); System.assertEquals(record.CreatedById, controller.createdById); System.assertEquals(record.LastModifiedById, controller.lastModifiedById); System.assertEquals(record.OwnerId, controller.ownerId); System.assertEquals(record.CreatedDate.format(), controller.convertedCreatedDate); System.assertEquals(record.LastModifiedDate.format(), controller.convertedLastMOdifiedDate); System.assertEquals(owner.Name, controller.ownerName); System.assertEquals(owner.SmallPhotoUrl, controller.ownerPhoto); System.assertEquals(owner.Name, controller.lastModifiedByName); System.assertEquals(owner.Name, controller.createdByName); System.assertEquals(true, controller.isMaster); } }
Now, our custom pages have the same functionality as a standard one.