1 Comments

There are times when we write some process that takes a long time to execute. The process might be launched by the end user (let’s assume this is a requirement). If this is the case we will need to provide some feedback to the user regarding the status of the process. In common UI design this is achieved by a progress bar control. It might not be obvious but the user perception changes when feedback is provided. If end users start the long operation, and no feedback is provided, they will wonder whether the process hang or is still running, and they will probably think that the application is dead slow. If feedback is provided then they know that the operation is still in progress and they will be more comprehensive about the application performance (of course, there will be a point when they will change their mind and go back thinking the application is slow).

Lets assume that we created the long running process as a server task method, and we create a windows client task to let users fire this process. Unfortunately in Pivotal there is no way, at least out of the box, to provide this kind of feedback. To make things worst, once the server task is fired, the users looses control of the application (it looks like the application is hang) until the server task finishes and returns to the client. So, what can we do to provide users with some feedback?

One approach that I have taken is to execute the whole process in an asynchronous way, returning control to the user immediately while the process is queued on the server task. I will talk about this approach in an upcoming article, but for now lets suppose that all we want to do is to provide feedback using a well known progress bar.

We mentioned before that once the server task is fired the windows client application freezes until the server task finishes. This is due to the fact that, out of the box, the server task is launched in the same UI thread. We all know that this is bad for the UI experience. So in this article I will explain how to launch a server task in a background thread, leaving the UI free to do UI work, such as showing a progress bar.

Lets start with want we want to accomplish: we want to fire the server task from the task pad, show a modal form displaying a progress bar and informing the user that a long process is being executed, and once the process finishes we want to close the modal form. The following image shows what we want to accomplish:

modalProgressForm

Lets assume this is our server task method:

[TaskExecute]
public virtual void LongRunningOperation(Id recordId)
{
    Trace.WriteLine(String.Format("Starting LongRunningOperation with recordId '{0}'", recordId));
    System.Threading.Thread.Sleep(20000);
    Trace.WriteLine(String.Format("Finishing LongRunningOperation with recordId '{0}'", recordId));
}

Lets assume that we will fire the server task from a task in the task pad. We would normally write something like this:

[ClientTaskCommand]
public virtual void LongRunningTask()
{
    try
    {
        Globals.Execute(this.DataTemplate, "LongRunningOperation", new object[] { this.RecordId });
        PivotalMessageBox.Show("Done", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
    }
    catch (Exception e)
    {
        Globals.HandleException(e.InnerException ?? e, true);
    }
}

But, as explained before this will freeze the whole UI, so we need to do the server task call in another thread. Lets start by creating a client side library, and write the following method:

/// <summary>
/// Displays a modal form showing a progress bar. 
/// The form will execute the <paramref name="methodName"/> on <paramref name="serverTaskName"/> in a background thread.
/// If the <paramref name="closeOnFinish"/> is <c>true</c> then the modal form will auto close when the server task is finished executing
/// </summary>
/// <param name="title">The title of the modal form</param>
/// <param name="message">A message to display beneath the progress bar informing the user of what's going on</param>
/// <param name="closeOnFinish">If <c>true</c> the the form will auto close when the server task execution ends. If <c>false</c> then the user will be able to dismiss the modal form using a button when the server task execution ends.</param>
/// <param name="serverTaskName">The name of the server task to execute</param>
/// <param name="methodName">The method on the server task to execute</param>
/// <param name="parameters">The parameters required by the server task method</param>
public static void ExecuteTaskWithProgress(string title, string message, bool closeOnFinish, string serverTaskName, string methodName, params object[] parameters)
{
    TransitionPointParameter transitionPointParameters = new TransitionPointParameter();

    transitionPointParameters.SetExtendedParameter("title", title);
    transitionPointParameters.SetExtendedParameter("message", message);
    
    transitionPointParameters.SetExtendedParameter("serverTaskName", serverTaskName);
    transitionPointParameters.SetExtendedParameter("methodName", methodName);
    transitionPointParameters.SetExtendedParameter("closeOnFinish", closeOnFinish);

    for(int i = 0; i < parameters.Length; i++)
    {
        transitionPointParameters.SetUserDefinedParameter(i + 1,parameters[i]);
    }

    Id systemId = Id.Create(Globals.GetSystemSetting("System_Id"));
    Globals.ShowModalForm("Progress Form", systemId, transitionPointParameters.ParameterList);
}

This method is just an utility method that takes some parameters and creates a transition point parameter structure that will be passed to a modal form. The parameters are explained as part of the XML documentation. The only thing that might be weird is the way the parameters are packed into the transition point parameters structure, it is made this way because otherwise values would be lost if sending the object array is it is. We will how to unpack these parameters later. For now, we need to create the “Progress Form” form. You have probably guessed that this form is attached to the System table. This is how we design the form:

progressFormIs pretty straightforward: it has two controls, one is a PivotalCustomControlContainer which we named progressContainer, this is where, at runtime, we will put the progress bar. The other control is a PivotalLabel which we named messageText, and is where we put the message that we want to display to the user (the parameter message in the ExecuteTaskWithProgress method defined above). This form is associated to the System table and is called “Progress Form”. Make sure you give the appropriate permissions to the security groups to see the form. Now all we need is to create a form client task that takes care of the work.

We use the Pivotal CRM Templates for Visual Studio to quickly create a form client task (the templates take care of all the plumbing for us). All we need to do is override the OnFormInitialized method:

public partial class ProgressModalForm : FormClientTask
{
    public ProgressModalForm()
    {
    }

    public override void OnFormInitialized()
    {
        try
        {
            base.OnFormInitialized();
            SetupProgress();
            ExecuteTask();
        }
        catch (Exception e)
        {
            Globals.HandleException(e, true);
        }
    }
    
    private void SetupProgress()
    {
        //...
    }
    
    private void ExecuteTask()
    {
        //...
    }
}

All the work is done in those two methods: SetupProgress and ExecuteTask. First we need to define some variables at the class level, we will explain these variables later:

private Exception exception = null;
private Dispatcher uiDispatcher = null;
private ProgressBar progress = null;

The SetupProgress method takes care of the UI part: it creates the progress bar, removes the form buttons and sets some parameters such as the message and the form title. Here is the code for this method. I’m obviating the part that setups the buttons since this is found in Pivotal documentation, and I don’t want to extend the article with boilerplate code.

private void SetupProgress()
{
    // code to setup form buttons, and the logic to display the OK button based on the closeOnFinish parameter
    
    
    string title = TypeConvert.ToString(this.TransitionPointParameter.GetExtendedParameter("title"));
    this.FormView.Title = title;

    string message = TypeConvert.ToString(this.TransitionPointParameter.GetExtendedParameter("message"));
    PivotalLabel messageLabel = this.FormControl.GetControlByName("messageText") as PivotalLabel;
    if (messageLabel != null) messageLabel.Text = message;

    PivotalCustomControlContainer progressContainer = this.FormControl.GetControlByName("progressContainer") as PivotalCustomControlContainer;
    if (progressContainer != null)
    {
        Application.EnableVisualStyles();

        Panel panel = new Panel();
        panel.Size = progressContainer.Size;

        progress = new ProgressBar()
        {
            Style = ProgressBarStyle.Marquee,
            Dock = DockStyle.Fill
        };
        panel.Controls.Add(progress);

        progressContainer.CustomControl = panel;
    }
}

Notice how the progress bar is created using a Marquee stile, meaning that this will show a progress bar that repeats until the task is finished. It is also possible to create a progress bar to show actual progress, but I will leave that for some other article (or you can use the Pivotal CRM Data Visualization Module by Grupo Lanka). It is important to notice that you need to enable visual styles (Application.EnableVisualStyles()) for the progress bar to actually work. Also, notice how we keep the progress bar control globally available in the class, using one of the variables we defined earlier. This is because we need to stop the progress bar once the task execution ends.

The ExecuteTask method is where all the magic happens:

private void ExecuteTask()
{
    uiDispatcher = Dispatcher.CurrentDispatcher;

    string serverTaskName = TypeConvert.ToString(this.TransitionPointParameter.GetExtendedParameter("serverTaskName"));
    string methodName = TypeConvert.ToString(this.TransitionPointParameter.GetExtendedParameter("methodName"));

    List<object> parameters = new List<object>();
    List<Type> parameterTypes = new List<Type>();

    for (int i = 1; i <= this.TransitionPointParameter.UserDefinedParametersNumber; i++)
    {
        object parameter = this.TransitionPointParameter.GetUserDefinedParameter(i);
        Type type = parameter.GetType();
        if (type.Name == "Byte[]")
        {
            parameters.Add(Id.Create(parameter));
            parameterTypes.Add(typeof(Id));
        }
        else
        {
            parameters.Add(parameter);
            parameterTypes.Add(parameter.GetType());
        }
    }

    Action action = new Action(() =>
    {
        try
        {
            this.SystemClient.ExecuteServerTask(serverTaskName, methodName, parameterTypes.ToArray(), parameters.ToArray());
        }
        catch (Exception e)
        {
            exception = e;
        }
    });
    action.BeginInvoke(new AsyncCallback(OnExecuteFinished), action);
}

The first thing is to set the current dispatcher for the UI thread. We will need this when changing UI components from inside the thread that executes the server task (remember that you can only change UI components from the UI thread). The next step is to unpack the parameters from the transition point parameters. We then need to configure an Action object and invoke that action in a new thread. The action will call the server task using the Pivotal API, as showed in line 31. We can use the BeginInvoke method of the Action object to start execution of the server task in a non-UI thread. This is done in line 38. We need to provide an AsyncCallback method that will be executed once the action execution ends:

private void OnExecuteFinished(IAsyncResult ar)
{
    Action action = ar.AsyncState as Action;
    action.EndInvoke(ar);

    if (uiDispatcher != null)
    {
        uiDispatcher.BeginInvoke(new Action(() =>
        {
            this.FormView.Dirty = false;
    
            if (exception != null)
                Globals.HandleException(exception, true);

            if (TypeConvert.ToBoolean(this.TransitionPointParameter.GetExtendedParameter("closeOnFinish")))
            {
                try { this.FormView.CloseForm(true); } catch {}
            }
            else
            {
                if (progress != null) progress.Style = ProgressBarStyle.Blocks;
            }
        }));
    }
}

Once the server task ends execution the OnExecuteFinished method is called and the UI is updated. Notice how any modifications to the UI controls are done using the dispatcher we captured before, making sure that the UI is modified in the UI thread. We then take care of closing the form, or just stopping the progress bar (depending on the cloaseOnFinish parameter).

There are some things that need to be taken care of in order to have a complete solution:

  • Make sure you use LD Strings all around to make the code globalized
  • Right now, if the user closes the modal form the the server task will still be executing on the background but the user will not get any feedback. A good implementation will have to check whether the user can close the modal form or not.
  • Provide a good error handling behavior (in the examples above, this is done using the variable exception) to improve the user experience.

This is all there is to it. Make sure you display a nice progress bar showing some feedback to end users next time you need to execute a heavy task.

I encourage you to look at the Pivotal Development Tools provided by Grupo Lanka, since they have very nice tools to easy up Pivotal development, including this one feature to show progress bars.

0 Comments

If you’ve worked with Pivotal CRM you’re probably familiar with the RSys_Last_Id table. This table is located in the ED and stores the last assigned id (the primary key for a record) for every table in the system. When the Pivotal Business Server (PBS) needs to create a new record it will use this table to know what is the next id to assign to that record.

rsysLastIdWhen you create a new table (using the Toolkit) and then do an Apply Customization Changes (ACC) using the Pivotal Administration Console (PAC) then a new record will be inserted into RSys_Last_Id, with two values: one for the table name and the other value for the id, which is initialized to 0x0000000000000000.

When the first record for the table is created the PBS will assign its id the value 0x0000000000000001, however if we look at the RSys_Last_Id table, the Last_Id_Value for the table is not 0x0000000000000001, but instead is 0x0000000000000010.

The reason why this occurs is due to the way the PBS works with these ids. The PBS has a cache of ids per table. When the PBS starts, this cache is empty, but immediately after creating the first record for a table the PBS creates a cache of 16 ids for such table. For the case of a new table, when the first record is created the PBS requests 16 Ids from the RSys_Last_Id table, and thus the Last_Id_Value is set to 0x0000000000000010 (these values are hex numbers: 10 hex = 16 decimal). The PBS assigns ids from the cache until the cache is empty again and no more ids are available for the table. When the 16th record is created the PBS will use the last id from the cache and the cache will be empty. When the 17th record is created the PBS realizes the cache is empty, so it requests another 16 ids from the RSys_Last_Id table, so the value of the Last_Id_Value for such table is set to 0x0000000000000020 (20 hex = 32 decimal).

Notice that if the PBS is shutdown the ids that are still in cache are lost. This explains why the record ids for a given table might have missing ids. As an example: suppose that 5 records have been created for a table, the PBS has used 5 ids out of the cache. At this point the PBS is shutdown, and the ids in the cache (16 – 5 = 11 ids) will be lost. When the PBS is restarted (it starts with an empty cache) and a new record for the table is requested (the 6th record, for this example) the PBS will ask again the RSys_Last_Id table for another 16 ids, but the RSys_Last_Id table had the Last_Id_Value set to 0x0000000000000010 (before the PBS was restarted), so this time it will provide 16 ids, ranging from 0x0000000000000010 to 0x0000000000000020. The 6th record will be created with the id 0x0000000000000010. At this point the table will have 6 records, with ids: 0x0000000000000001, 0x0000000000000002, 0x0000000000000003, 0x0000000000000004, 0x0000000000000005, 0x0000000000000010. Ids from 0x0000000000000006 to 0x000000000000000A were lost.

0 Comments

I recently had to resolve a performance problem for one of our Pivotal systems and I thought I should share the findings. Pivotal has what are called server and client tasks which, as their name suggest, constitute logic that runs on the client and server side, respectively. The platform allows to pass objects between the server and client layers of the applications, and also to pass data between different layers/tasks on the server side. Internally this is accomplished using serialization. The platform uses this to pass data (using ADO.Net DataSet, DataTable and DataRow) from the server to the client, and from server to server. It turns out that passing a DataRow from server to client could affect systems performance under certain conditions.

The short version is the following: when passing a DataRow from a server task to another server task the platform is actually serializing the whole DataTable to which the data row belongs. The bigger the DataTable the longer it takes to serialize and reach the other task. Following is a more detailed explanation of this particular case.

Scenario

Let’s assume that we have a task that needs to work with a several data records. The task is designed so the work is split in two subtasks: one subtask reads all the required records from the database, and another subtask takes care of processing a specific record. There are many reasons why this is a good design practice: modularity, separation of concerns, transaction handling, etc. As an example, lets assume that we need to design an archiving process that is executed monthly. Every month there could be thousands of records to archive. If we do the archiving in a single task then we could have transaction issues (usually the transaction timeout is set to 60 seconds, and if the archiving takes longer than this then the transaction will be rolled back). To avoid transaction issues we create one task that selects all records that require archiving in a non-transactional context (all we are doing is a read) and then, for each record (or a batch of records if we want more performance) we call another task, executing under a transactional context, that takes care of the archiving.

Processing each record individually might not be the best approach here. Processing records in batch will be better. But for the sake of simplicity, and to illustrate the actual problem, I will explain the scenario using individual record processing. The concepts also apply to the case where batch record processing is used instead.

The Implementation

We’ve decided to implement our process using the design mentioned above, two separate tasks, one for selecting records and another to process such records:

  • The first task creates a DataTable (using the platform API to retrieve data from the database) containing all the records that need to be processed. It then loops for each of the data rows in the table, passing each row (a DataRow object) to the other task that takes care of processing the row.
  • The second task receives a DataRow as input, creates the transaction, and processes the record.

Back to our example, the archiving process does a query on the database retrieving all the records for the previous month (it could be thousands) and puts them in a DataTable, and then for each DataRow calls another task that actually does the archiving.

The Performance

We could think that our process is well designed: we are doing just one read on the database (the first task) and then we are processing each record individually (the second task). We could assume that the time to process an individual record is a constant, and what changes is the number of records to process. In other words, if each record is processed in a time t, then processing n records will take t*n time.

It turns out that, under this scenario, the time to process each record will be proportional to the number of records to process. The time to process each individual record is no longer a constant t, but a function on the number of records to process. We can express the total time as f(n)*n.

Obviously, f(n)*n is much bigger than t*n as n increases (our case). So why is this happening?

The Reality

When objects are passed between layers/tasks (in our case, passing a DataRow between the first task and the second task) these are serialized. In other words, each task will be executing on its own thread and in the case of this platform, if we want to pass parameters between these threads we need to serialize them (either using XML or binary serialization).

The problem is that in order to pass a DataRow as a parameter we have to serialize the DataRow. This might not be obvious but the serialization of the DataRow not only serializes the individual record, but it also serializes the whole DataTable to which the row belongs. So for example, if the first task creates a DataTable of 1000 records, when we serialize each record and pass the DataRow to the second task, we are actually serializing the whole DataTable and passing all the 1000 records to the second task (and we would this 1000 times).

This explains the formula stated before. If I have to process 1000 records it’s actually serializing the whole table 1000 times. The more records has the table the longer it will take to serialize it, and the longer will take to process each individual record.

The Correct Implementation

So, what can we do to solve this problem and have a scenario where the processing time is t*n? The right implementation would have the first task that creates the DataTable to pass the record id (primary key) instead of the DataRow to the second task that processes the record. The first task still creates the DataTable, but this table will contain only one field: the record id. This id will be passed to the second task, which will have to make a database read to get all the information for the particular record, and then process it.

But with this scenario we are doing n+1 database reads. So, why is this more efficient than the previous scenario where we only made 1 read to the database? The answer lies in the fact that, for this platform in particular, the time to do a database read is much lower than the time to serialize a big DataTable. So, if each database read takes d time, our processing time will be d+(t+d)*n, which will be a lot lower than d+f(n)*n, for big n.

0 Comments

When creating a custom Visualforce page there are times when we would like to have a section showing system fields. In a standard visualforce page (the ones automatically generated by the platform) we see the following fields:

  • Created By: showing the name of the user who created the record and the creation date and time
  • Last Modified By: showing the name of the last user who modified the record and the date and time of this modification
  • Owner ID: The name of the user who owns the record, its picture, and a link that allows to change the owner.

Most of this information can be showed by using apex:outputField commands. For example:

<apex:pageBlock mode="mainDetail" >
  <apex:pageBlockSection title="{!$Label.SystemInformation}" columns="2">
    <apex:outputField value="{!Contact.CreatedById}" />
    <apex:outputField value="{!Contact.CreatedDate}" />
    <apex:outputField value="{!Contact.LastModifiedById}"/>
    <apex:outputField value="{!Contact.LastModifiedDate}"/>
    <apex:outputField value="{!Contact.OwnerId}"/>
  </apex:pageBlockSection>
</apex:pageBlock>

This will produce the following output:

standardSystemInformation

Notice that we get all the necessary information, but we lose some cool features:

  • There is no “Change” link to change the owner
  • There is no photo of the owner
  • The format is not like the one in the standard page.

We could get the format we desire by tweaking the visualforce a little bit (e.g. use apex:outputPanel to group fields together in the same line) but having the “change” and the photo is not trivial thing. Besides, by using apex:outputPanel and apex:outputText we lose the cool popup that shows summary information when we hover the mouse over the links.

The following example contains a Visualforce component that can be added to any custom page to display the system fields in a format that mimics the format produced by a standard page.

<apex:component controller="SystemInformationComponentController"> 
    <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.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="LookupSHoverDetail.getHover('{!$Component.createdBy}', '/{!createdById}/m?retURL={!URLENCODE($CurrentPage.Url)}&isAjaxRequest=1').show();"                       
                 value="{!URLFOR('/' + createdById)}">{!createdByName}</apex:outputLink>&nbsp;
                <apex:outputText value="{!convertedCreatedDate}" />                                                                         
            </apex:outputPanel>
        </apex:pageBlockSectionItem> 
        <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>&nbsp;
                <apex:outputText value="{!convertedLastModifiedDate}" />                                                                         
            </apex:outputPanel>
        </apex:pageBlockSectionItem>
        <apex:pageBlockSectionItem >
            <apex:outputLabel for="owner" value="{!$Label.Owner}" />
            <apex:outputPanel >
                <apex:image value="{!ownerPhoto}" width="16" height="16"/>&nbsp;
                <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>&nbsp;
                <apex:outputLink value="{!URLFOR('/' + recordId + '/a?retURL=' + URLENCODE($CurrentPage.Url))}">[Change]</apex:outputLink>
            </apex:outputPanel>
        </apex:pageBlockSectionItem>        
    </apex:pageBlockSection>
</apex:component>

This component requires the following controller:

public class SystemInformationComponentController{
    public sObject recordValue { get; set;}
    
    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 Id ownerId {
        get {
            return (Id)recordValue.get('OwnerId');
        }
    }  
    
    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 {
            Id ownerId = (Id)recordValue.get('ownerId');
            User owner = [select smallphotourl from user where id = :ownerId limit 1];
            return owner == null ? null : owner.SmallPhotoUrl;

        }
    }           
}

To use the component, simply call it from your custom page passing the record for the page:

<apex:page standardController="Contact">
  <apex:pageBlock mode="mainDetail" >
    <apex:outputField value="{!Contact.CreatedById}" rendered="false" />
    <apex:outputField value="{!Contact.CreatedDate}" rendered="false" />
    <apex:outputField value="{!Contact.LastModifiedById}" rendered="false" />
    <apex:outputField value="{!Contact.LastModifiedDate}" rendered="false" />
    <apex:outputField value="{!Contact.OwnerId}" rendered="false" />
    <c:SystemInformationComponent record="{!record}" />
  </apex:pageBlock>
</apex:page>

This will produce the following:

customizedSystemInformation

Notice how we get all the features we need and they look as a standard page output.

There are some things to consider when using this component:

  • Custom labels: for the component to be used in multiple languages, notice that the field labels use the following custom labels:
    • $Label.SystemInformation
    • $Label.CreatedBy
    • $Label.LastModifiedBy
    • $Label.Owner
  • The controller is based in the generic sObject type, allowing the component to be used in any standard or custom object
  • Datetimes are formatted using the format() method to show the converted datetime in the user’s profile timezone
  • In order to display the owner photo, the system must have Chatter enabled.
  • When using the component, the calling page must include the following fields (using an <apex:outputField value=”…” rendered=”false” />):
    • CreatedById
    • CreatedDate
    • LastModifiedById
    • LastModifiedDate
    • OwnerId

This is to make the fields available to the standard controller (and thus, the underlying record property) so it can be used on the component’s controller (see the above example to see how)

For completeness, here is the test for the controller:

@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];
            
        SystemInformationComponentController 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);
    }
}

I have written a better approach to this, please have a look at Showing System Fields in a Custom Visualforce Page (an improved approach) for more information

1 Comments

Pivotal can send emails from either client or server side:

  • Client side: Pivotal will always use MAPI (Outlook or LotusNotes) to send emails. You just need to have your email client appropriately configured.
  • Server side: here we have two options:
    • Use the email client (Outlook or Lotus Notes)
    • Use SMTP

In all the projects where I’ve participated the SMTP option for server side emails has always been used, mostly because SMTP is the preferred choice for IT and because having Outlook configured on a server is not the preferred choice. This is why development projects (either developed on premise or on the Client’s infrastructure) always need to be using SMTP. Of course, if SMTP is the option we’ll be using, unit and functional tests will need to be made using SMTP.

Following is a brief overview of the configuration for having server side emails working on Pivotal.

Server side emails using Outlook

PBS configuration using a specific accountEmails are sent by the PBS (Pivotal Business Server) component. PBS executes under a security context identified by credentials. By default, when PBS is first installed, these credentials will be "Interactive User”. This means that the PBS will be executing with the credentials of the currently logged-in user. This also means that when an email is sent using Outlook the PBS will be using the Outlook profile for the logged-in user. The right thing to do is to configure the PBS to use a specific account. In this case the PBS will be using the Outlook profile configured for such account (in the example the “macv607\pbs” user). You will need to login on the computer with that account and configure Outlook to send/receive emails. Do some tests to make sure that the Outlook client works fine.

 

Server side emails using SMTP

Pivotal SMTP configurationEmails are sent by the PBS, but these will be sent using the SMTP protocol. The SMTP configuration is made using the Pivotal Configuration Utility. If the “Use SMTP for Sending E-mail” is checked then the PBS will use SMTP as its first option for sending email. This means that it will not use Outlook. This is the right choice for most projects.

 

Considerations when using SMTP

  • Using Office 365: Office 365 uses strict security protocols (secure SMTP and requires authentication). The SMTP configuration screen for Pivotal doesn’t allow to use secure SMTP. This means that you will not be able to use SMTP for Office 365. If this is the case then you must use Outlook to send server side emails.
  • For development, the common thing to do is to configure a local SMTP server. You could use any SMTP server, but I recommend hMailServer, a powerful free email server that supports SMTP, POP, IMAP, etc.
  • You will use Outlook to see the emails sent by the PBS. You will have to configure Outlook to use the SMTP server you set up for the development/tests. Here is an example of how to configured Outlook to access the hMailServer:

Outlook configuration for SMTP