Page headers and footers in renderAs=”PDF” Visualforce pages

This page Creating Professional PDF Documents with CSS and Visualforce describes how @page and @bottom-right CSS can be used to include headers and footers in paginated PDF output. But the default styling for the footer text is a largish serifed font. So this:

@page {
    @bottom-right {
        content: "{!documentName} {!claimantInsuredNumbers}/{!claim.Name} - Page " counter(page) " of " counter(pages);
    }
}

results in this in the output bottom right of each PDF page:

which if CSS is being used to style the page content can leave the footer presented in a mis-matching style.

Googling for @bottom-right results in a hit on this page Prince Page Headers and Footers that includes examples of setting styles. Borrowing from that on the assumption that the technique is generic, this change:

@page {
    @bottom-right {
        content: "{!documentName} {!claimantInsuredNumbers}/{!claim.Name} - Page " counter(page) " of " counter(pages);
        font-family: sans-serif;
        font-size: 80%;
    }
}

results in the desired styling:

PS

The :first page CSS pseudo-class is supported allowing e.g. the information to be skipped on the first page:

@page :first {
    @bottom-right {
        /* Nothing */
    }
}
Advertisements

Filtering picklists by RecordType in Visualforce

When you add a record type to an object, you can sub-set the picklist entries per record type. The default UI respects this configuration but Visualforce will not until this idea visualforce please respect profile/record type for picklists is delivered; it is presently marked “Coming in the Next Release”; it is now marked “Delivered in Winter ’11” so the work-around below should no longer be needed.

Here is an example of the sort of work-around that you have to resort to at present as a stop-gap.

First, in the Visualforce itself, instead of:

<apex:inputField value="{!adjustment.Type__c}" required="true"/>

you need this (made messier by the panels that achieve the “required” styling):

<apex:pageBlockSectionItem >
    <apex:outputLabel value="{!$ObjectType.Adjustment__c.fields.Type__c.label}"/>
    <apex:outputPanel layout="block" styleClass="requiredInput">
        <apex:outputPanel layout="block" styleClass="requiredBlock"/>
        <apex:selectList value="{!adjustment.Type__c}" size="1">
            <apex:selectOptions value="{!typeOptions}"/>
        </apex:selectList>
    </apex:outputPanel>
</apex:pageBlockSectionItem>

Then controller code needs to be added to build the option values. The real killer here is that there is presently no API to obtain the picklist entries for each record type. That means that the mapping must be duplicated in code (the accept method in the code below). Perhaps this is tolerable for a picklist or two with simple mapping, but crazy where many picklists, picklist entries, record types and different mappings are involved.

So for a simple mapping case, instead of no code in the Apex controller, code and data like this has to be added:

public List<SelectOption> typeOptions {
    get {
        if (typeOptions == null) {
            typeOptions = new List<SelectOption>();
            typeOptions.add(new SelectOption('', '--None--'));
            DescribeFieldResult d = Adjustment__c.Type__c.getDescribe();
            for(PicklistEntry e : d.getPicklistValues()) {
                if (e.isActive() && accept(e)) {
                    typeOptions.add(new SelectOption(e.getValue(), e.getLabel()));
                }
            }
        }
        return typeOptions;
    }
    set;
}
private Boolean accept(PicklistEntry e) {
    if (adjustment.RecordTypeId != AdjustmentRecordType.getTermLife()) {
        // Only show the entries not in the hide set
        Set<String> hide = new Set<String>{
            'State Income Tax',
            'Life Interest (Pre-Tax)',
            'Loan (Pre-Tax)',
            'Dividends (Pre-Tax)'
            };
        return !hide.contains(e.getValue());
    } else {
        // Show all entries
        return true;
    }
}
public PageReference save() {
    // Work-around for ugly id being displayed to user if required="true" is used on the apex:selectList.
    if (adjustment.Type__c == null) {
        adjustment.Type__c.addError('You must enter a value');
        return null;
    }
    return standardController.save();
}

See this post for some background on doing the required check in code in the save method.

If you find that you are having to do a lot of this sort of coding, you might be able to build on Sam Arjmandi’s A Picklist Component For Your Visualforce Pages by finding a way to hook in the record type to picklist entry mapping. Also remember that there is presently a governor limit of 10 on field describes.

Hiding the “Delete” and “Clone” buttons in an apex:detail

In theory components like apex:detail and apex:relatedList allow you to create custom pages that combine the default UI with your own markup. As the default UI is pretty snazzy, this seems like a good way to go. But in the few cases I’ve tried this, the lack of fine control over what these components render has made me back away from the approach.

The one case where I am using an apex:detail component I ended up combining it with this styling:

<style type="text/css">
input.btn[name="del"] {
    display: none;
}
input.btn[name="clone"] {
    display: none;
}
</style>

which leaves the “Edit” button showing but ensures that the “Delete” and “Clone” buttons are not rendered. (This will obviously break if the names of the buttons change in future versions of Visualforce.)

The CSS magic is described in e.g. W3C Selectors.

Note that you could also remove the “Delete” and “Clone” buttons from the default UI layout. But in my case I wanted those buttons displayed in the default layout page but not displayed in my custom page. And you can’t force a particular layout in apex:detail.