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.
