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!

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.

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.

X-Squared featured in Inc.com article on drop.io

March 19, 2009 · Filed Under X-Squared On Demand · Comment 

Those who follow us on Twitter know that drop.io is one of our favorite tools. We use it to collaborate with clients, to send files without taking up valuable inbox space (companies have been known to enforce miniscule Exchange inbox sizes), and even to share music playlists.
Howard Greenstein of Inc.com published a story today(“Stop, Collaborate, and Listen“) on drop.io and asked David to comment on how drop.io can help SMBs in service delivery.
It’s a good story that outlines the more basic features that drop.io offers. There is so much more available, both through the front-end and via drop.io’s RESTful API. In fact, David has been working on a Salesforce-drop.io integration that will allow inexpensive file storage and selective file-sharing with clients.
Please read Howard’s story and give drop.io a shot – you’ll enjoy it.

X-Squared Featured in ITA Newsletter

December 8, 2008 · Filed Under X-Squared On Demand · Comment 

This week, X-Squared On Demand was featured in the Illinois Technology Association‘s Industry Weekly newsletter ITA Member Q&A.

Thank you to the ITA for featuring us!

Next Page »