A Mention in the Developers Challenge

October 2, 2009 · Filed Under Development, X-Squared On Demand, salesforce.com · Comment 

The salesforce.com Developer Force Challenge has concluded, and the team of Force Squared and The Enforcer won a mention!

Our Daily Shinro site was listed “for sheer exuberance!”

I’m really proud of the site, though the lion’s share of the kudos go to John for the concept and site design. I just coded whatever he told me to code; he’s the creative one!

So if anyone is looking for a custom Force.com Site or website integration to Salesforce, contact us and let’s discuss your needs!

Show Current Year Using Only Visualforce

August 17, 2009 · Filed Under Tips and Tricks, salesforce.com · 1 Comment 

While perusing some websites, looking at html code, I saw something interesting:

A company had used Javascript to update the copyright date of its website using Javascript. This is the code.

Copyright &copy; 2005-<script type="text/javascript">document.write(new Date().getFullYear());</script> This Company. All rights reserved.

Of course, as a Force.com developer, I prefer to use Visualforce and Apex over Javascript (as it makes the client thinner - thick clients are not my preferred modus operandi).

This is the Visualforce version of the above code; it requires no Apex controller nor extension, and will always display the current year.

Copyright &copy; 2005-{!year(today())} This Company. All rights reserved.

Feel free to use this snippet in your Visualforce pages (including Sites) to ensure that on January 1, you won't look foolish for failing to (shudder) manually update the footer in your site template.

Happy coding!

Overload Apex Class to be Controller AND Extension

July 24, 2009 · Filed Under Apex, Development, Visualforce, X-Squared On Demand · 5 Comments 

Wow - today brought an interesting discovery. Here's the situation:

Coding the new premium version of Mass Update Contacts (details to come), I replaced the two parts of the page with Apex Components. This will allow the app to support custom address fields and international address formats.

I didn't want to write one ControllerExtension for the main page, a CustomController for the view section component, and another CustomController for the pageblocktable component. So here is the overloaded class constructor. Note that this works because an extension passes the StandardController to the constructor, and a CustomController passes nothing:

public with sharing class VersatileClass {

private Account account;

public VersatileClass(){
	system.debug('OPERATING AS CONTROLLER');
		if(System.currentPageReference().getParameters().get('id')==null){
			//Include error checking here
		} else{
			string AId = System.currentPageReference().getParameters().get('id');
			account = [select id, name from Account where id = :AId];
			//And whatever else you want to do
		}
}

public VersatileClass(ApexPages.StandardController controller) {
	system.debug('OPERATING AS EXTENSION');
		if(System.currentPageReference().getParameters().get('id')==null){
			//Include error checking here
		} else{
			this.account = (Account)controller.getRecord();
			//And whatever else you want to do
		}
	}
}

Enjoy! This should save people a lot of time.

Email Inbox Version 2

July 15, 2009 · Filed Under Apex, Development, Visualforce, salesforce.com · 4 Comments 

Well, it took about ten minutes from the release of Email Inbox Version 1 for people to request additional features, screenshots, etc. Version 2 includes some neat icons, and this post has the multiple-times-requested screenshots we promised.

First, a teaser screenshot. (There's a better one below the code.)

EmailInbox screenshot

Here's the updated Visualforce code:

<apex:page controller="EmailMessageController" action="{!ViewData}">
    <apex:sectionHeader title="Email Messages" subtitle=""></apex:sectionHeader>
    <apex:pageblock id="emailblock">
        <apex:facet name="header">
            <apex:form >
                <apex:panelGrid styleClass="list"
                    columnClasses="pbTitle,pbButton,pbHelp" columns="3" border="0"
                    cellpadding="0" cellspacing="0">
                    <apex:outputLabel ><h3>Messages</h3></apex:outputLabel>
                    <apex:commandButton value=" Refresh " styleClass="btn"
                        action="{!ViewData}" rerender="emailblock"></apex:commandButton>
                    <apex:SelectList value="{!wheretext}" size="1"  id="controllerselectlist">
                        <apex:actionSupport event="onchange" action="{!ViewData}"
                            reRender="emailblock"></apex:actionSupport>
                        <apex:selectOptions value="{!views}" />
                    </apex:SelectList>
                </apex:panelGrid>
            </apex:form>
        </apex:facet>
        <apex:form >
            <apex:pageblocktable value="{!Messages}" var="e" id="emailtable"
                bgcolor="#F3F3EC" styleClass="list" rowClasses="dataRow"
                onRowMouseOver="hiOn(this);" onRowMouseOut="hiOff(this);">
                <apex:column >
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.EmailMessage.fields.Subject.label}{!IF(sortExpression=='Subject', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="Subject" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                    <apex:outputLink value="/{!e.Id}" target="_blank">{!e.Subject}</apex:outputLink>
                </apex:column>
                <apex:column >
                    <apex:facet name="header">
                    {!$ObjectType.Contact.fields.Name.label}
                    </apex:facet>
                    <apex:outputLink value="/{!e.Parent.ContactId}" target="_blank"
                        rendered="{!IF(e.Parent.ContactId != '',true,false)}">{!e.FromName}</apex:outputLink>
                    <apex:outputtext value="{!e.FromName}"
                        rendered="{!IF(e.Parent.ContactId != '',false,true)}" />
                </apex:column>
                <apex:column >
                    <apex:facet name="header">
                    {!$ObjectType.Account.fields.Name.label}
                    </apex:facet>
                    <apex:outputLink value="/{!e.Parent.AccountId}" target="_blank">{!e.Parent.Account.Name}</apex:outputLink>
                </apex:column>
                <apex:column value="{!e.FromAddress}">
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.EmailMessage.fields.FromAddress.label}{!IF(sortExpression=='FromAddress', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="FromAddress" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
                <apex:column value="{!e.Status}">
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.EmailMessage.fields.Status.label}{!IF(sortExpression=='Status', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="Status" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
                <apex:column value="{!e.MessageDate}">
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.EmailMessage.fields.MessageDate.label}{!IF(sortExpression=='MessageDate', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="MessageDate" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
                <apex:column >
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="Inbound/Outbound{!IF(sortExpression=='Incoming', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="Incoming" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                    <apex:image url="/img/emailInbound.gif" rendered="{!e.Incoming}" />
                    <apex:image url="/img/emailOutbound.gif" rendered="{!NOT(e.Incoming)}" />
                    <!--<apex:outputfield value="{!e.Incoming}" rendered="{!NOT(e.Incoming)}" />-->
                </apex:column>
                <apex:column >
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="Attachment{!IF(sortExpression=='HasAttachment', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="HasAttachment" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                    <apex:image url="/img/emailHasAttach.gif" rendered="{!e.HasAttachment}"/>
                    <apex:outputfield value="{!e.HasAttachment}" rendered="{!NOT(e.HasAttachment)}" />
                </apex:column>
                <apex:column >
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.Case.fields.CaseNumber.label}{!IF(sortExpression=='ParentId', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="ParentId" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                    <apex:outputLink value="/{!e.ParentId}">{!e.Parent.CaseNumber}</apex:outputLink>

                </apex:column>
                <apex:column value="{!e.ToAddress}">
                    <apex:facet name="header">
                        <apex:commandLink action="{!ViewData}"
                            value="{!$ObjectType.EmailMessage.fields.ToAddress.label}{!IF(sortExpression=='ToAddress', 
                            IF(sortDirection='ASC','▼','▲'),'')}">
                            <apex:param value="ToAddress" name="column"
                                assignTo="{!sortExpression}"></apex:param>
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>
            </apex:pageblocktable>
        </apex:form>
    </apex:pageblock>
</apex:page>

The Apex controller code has not changed from Version 1.

Here's the screenshot, which includes some emails from around the Salesforce ecosystem:

Incoming (and auto-response) Emails

Incoming (and auto-response) Emails

Things to notice and things we've learned:
  1. Salesforce will search ANY Email field to match an incoming email to a Contact. (nice job, Salesforce!) - We know because Simon Fell's Contact.Email is sfell at salesforce.com, and his Contact.Secondary_Email__c is simon at fell.com in the sandbox. Ditto (with her own email addresses) for Irene Brodt.
  2. Auto-response emails will be included. We turned off auto-response after a few emails came in.
  3. Though the Email Address field is coded just to show the field value, Salesforce includes the Gmail link. (We assume that's because we activated the integration, but we may be wrong. Has anyone not activated that?)
  4. We did not (yet) implement the super Email/Web2Case trigger code that Marco Casalaina posted in the Salesforce Support Blog, but if we had, then every email in the list would be associated with a Contact. (Example: Michael Smith of Force2b.net, who will be a Contact from now on!)
Enjoy!

Visualforce Email Inbox

Sonny Cloward, SysAd at Rainforest Alliance, approached us about writing a Visualforce page to display all incoming emails for a given Case Queue. This led a few interesting discoveries. Here's how we handled the (donated time) project:

First, the page was built upon the template Sam Arjimandi built at Salesforce. Instead of the Account object, we substituted the EmailMessage object. But something didn't work.

Fields have certain attributes:
Property Description
Autonumber The API creates an autonumber.
Create Value for the field can be specified during create using the API.
Defaulted on create When created, a default value is supplied if no other value is specified.
Delete Value for the field can be deleted using the API.
Filter Can be used as filter criteria in a SOQL query FROM or WHERE clause.
idLookup Can be used to specify a record in an upsert() call. The Id field of each object has this property and some Name fields. There are exceptions, so check for the property in any object you wish to upsert().
Nillable The field can contain a null value.
Query The field can be queried with SOQL using the API.
Replicate The value of the field can be replicated using the API.
Restricted picklist A picklist that depends on the value of another picklist for the values it displays.
Retrieve Value of the field can be retrieved using the API.
Search Can be searched with SOSL using the API.
Update Can be updated using the API.

The important one here is "Filter" because (as the documentation states) this allows the field to be used in a WHERE clause. Also, however, (as the documentation does not state) it allows the field to be used in an ORDER BY clause. So all fields on the EmailMessage object that do not allow filtering/ordering had to be presented plainly, without Sam's cool PageBlockTable sorting features. Once this was done, Sonny had some great ideas:

  1. Show the email subject, but make that a hyperlink to the email message itself
  2. Link the Case Number (EmailMessage.ParentId) to the Case (EmailMessage.Parent)
  3. Show the Case Contact (EmailMessage.Parent.Contact.Name), linking to the Contact (EmailMessage.Parent.ContactId)
  4. Show the Case Account (EmailMessage.Parent.Account.Name), linking to the Account (EmailMessage.Parent.AccountId)
  5. Provide filters - Incoming only, Unread only, etc.
You'll see in the Apex Code where we added parent object fields to the SOQL query, and where we used a List to populate the query's filter.
public with sharing class EmailMessageController {
   public String EmailMessage { get; set; }
   private List<EmailMessage> messages;
   private String sortDirection = 'ASC';
   private String sortExp = 'MessageDate';
   public String wheretext;

   public EmailMessageController(){
   	wheretext = '';
   }

   public String sortExpression { get {
        return sortExp;
     }
     set {
       //if the column is clicked on then switch between Ascending and Descending modes
       if (value == sortExp)
         sortDirection = (sortDirection == 'ASC')? 'DESC' : 'ASC';
       else
         sortDirection = 'ASC';
       sortExp = value;
     }
   }
   
public void setWhereText(String value) {  
   whereText = value;
 }
public string getWhereText(){
	return wheretext;
}
   
public List<SelectOption> getViews(){
	List<SelectOption> options = new List<SelectOption>();
	options.add(new SelectOption('WHERE e.id != null','All'));
	options.add(new SelectOption('WHERE e.Incoming = true AND e.Status = \'0\' ','Incoming Unread'));
	options.add(new SelectOption('WHERE e.Incoming = true AND e.Status = \'1\' ','Incoming Read'));
        options.add(new SelectOption('WHERE e.Incoming = true','All Incoming'));
        options.add(new SelectOption('WHERE e.Incoming = false','All Outgoing'));
        options.add(new SelectOption('WHERE e.ToAddress = \'support@x2od.com\'','Support Queue'));
        options.add(new SelectOption('WHERE e.ToAddress = \'support2@x2od.com\'','Support Queue2')); //etc.
        return options;
}

 public String getSortDirection() {
    //if not column is selected 
    if (sortExpression == null || sortExpression == '')
      return 'ASC';
    else
     return sortDirection;
 }

 public void setSortDirection(String value) {  
   sortDirection = value;
 }
  
   public List<EmailMessage> getMessages() {
       return messages;
   }
   
   public PageReference ViewData() {
       //build the full sort expression
       string sortFullExp = sortExpression  + ' ' + sortDirection;
      
       //query the database based on the sort expression
       messages = Database.query('Select e.FromAddress, e.Parent.ContactId, e.Parent.Contact.Name, e.Parent.Account.Name, e.ToAddress, e.Parent.CaseNumber, e.Parent.AccountId, e.TextBody, e.SystemModstamp, e.Subject, e.Status, e.ParentId, e.MessageDate, e.LastModifiedDate, e.LastModifiedById, e.IsDeleted, e.Incoming, e.Id, e.HtmlBody, e.Headers, e.HasAttachment, e.FromName, e.CreatedDate, e.CreatedById, e.CcAddress, e.BccAddress, e.ActivityId From EmailMessage e ' + wheretext + ' order by ' + sortFullExp + ' limit 1000');
       return null;
   }
}

And here's the Visualforce Page:

<apex:page controller="EmailMessageController" action="{!ViewData}">
	<apex:sectionHeader title="Email Messages" subtitle=""></apex:sectionHeader>
	<apex:pageblock id="emailblock">
		<apex:facet name="header">
			<apex:form>
				<apex:panelGrid styleClass="list"
					columnClasses="pbTitle,pbButton,pbHelp" columns="3" border="0"
					cellpadding="0" cellspacing="0">
					<apex:outputLabel><h3>Messages</h3></apex:outputLabel>
					<apex:commandButton value=" Refresh " styleClass="btn"
						action="{!ViewData}" rerender="emailblock"></apex:commandButton>
					<apex:SelectList value="{!wheretext}" size="1"	id="controllerselectlist">
						<apex:actionSupport event="onchange" action="{!ViewData}"
							reRender="emailblock"></apex:actionSupport>
						<apex:selectOptions value="{!views}" />
					</apex:SelectList>
				</apex:panelGrid>
			</apex:form>
		</apex:facet>
		<apex:form>
			<apex:pageblocktable value="{!Messages}" var="e" id="emailtable"
				bgcolor="#F3F3EC" styleClass="list" rowClasses="dataRow"
				onRowMouseOver="hiOn(this);" onRowMouseOut="hiOff(this);">
				<apex:column>
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.Subject.label}{!IF(sortExpression=='Subject', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="Subject" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
					<apex:outputLink value="/{!e.Id}" target="_blank">{!e.Subject}</apex:outputLink>
				</apex:column>
				<apex:column>
					<apex:facet name="header">
					{!$ObjectType.Contact.fields.Name.label}
					</apex:facet>
					<apex:outputLink value="/{!e.Parent.ContactId}" target="_blank"
						rendered="{!IF(e.Parent.ContactId != '',true,false)}">{!e.FromName}</apex:outputLink>
					<apex:outputtext value="{!e.FromName}"
						rendered="{!IF(e.Parent.ContactId != '',false,true)}" />
				</apex:column>
				<apex:column>
					<apex:facet name="header">
					{!$ObjectType.Account.fields.Name.label}
					</apex:facet>
					<apex:outputLink value="/{!e.Parent.AccountId}" target="_blank">{!e.Parent.Account.Name}</apex:outputLink>
				</apex:column>
				<apex:column value="{!e.FromAddress}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.FromAddress.label}{!IF(sortExpression=='FromAddress', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="FromAddress" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
				<apex:column value="{!e.Status}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.Status.label}{!IF(sortExpression=='Status', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="Status" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
				<apex:column value="{!e.MessageDate}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.MessageDate.label}{!IF(sortExpression=='MessageDate', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="MessageDate" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
				<apex:column value="{!e.Incoming}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.Incoming.label}{!IF(sortExpression=='Incoming', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="Incoming" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
				<apex:column value="{!e.HasAttachment}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.HasAttachment.label}{!IF(sortExpression=='HasAttachment', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="HasAttachment" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
				<apex:column>
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.Case.fields.CaseNumber.label}{!IF(sortExpression=='ParentId', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="ParentId" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
					<apex:outputLink value="/{!e.ParentId}">{!e.Parent.CaseNumber}</apex:outputLink>

				</apex:column>
				<apex:column value="{!e.ToAddress}">
					<apex:facet name="header">
						<apex:commandLink action="{!ViewData}"
							value="{!$ObjectType.EmailMessage.fields.ToAddress.label}{!IF(sortExpression=='ToAddress', 
							IF(sortDirection='ASC','▼','▲'),'')}">
							<apex:param value="ToAddress" name="column"
								assignTo="{!sortExpression}"></apex:param>
						</apex:commandLink>
					</apex:facet>
				</apex:column>
			</apex:pageblocktable>
		</apex:form>
	</apex:pageblock>
</apex:page>

There are some other cool bits:

If there is no Case.Contact, the table will display the FromName, pulled from the email message.

An interesting point: You may notice that EmailStatus is presented in numerical form. For instance, Incoming Unread is 0, Incoming Read is 1, etc. The documentation, however, says, "Read only. The status of the email. For example, “New,” “Unread,” “Replied,” “Sent.”" So we're not sure of the exact mapping. 3 seems to be Sent, so 2 is probably Replied... but we're not sure.

Don't forget: EmailMessage has two lookups (foreign key): Case, and Activity. This Activity is the task created when Salesforce receives the email, and is - according to the documentation - assigned to the Case Owner. We're not sure what happens when the Case is owned by a Queue. Feel free to comment and share your experiences.

That's it! Enjoy.

Next Page »