Simon Fell > Its just code
Saturday, January 03, 2009
Ritual Coffee now at Whole FoodsYou can now get fresh (with roast dates) Ritual Coffee Roasters coffee at Whole Foods, (at least at the Whole Foods on California St in SF), this is a big deal most supermarket coffee is both gross and stale, having no roast date marked, and no idea how long its been sat on the shelf. I just picked up some LifeSaver espresso that was roasted Dec 30. Nice work by the Ritual crew. I also believe you can get the excellent Barefoot Coffee at Whole Foods in the south bay.
Sunday, December 07, 2008
Maildrop 2.0I just posted the final Maildrop 2.0 build. You can download it from the project download page, or if you're already running Maildrop, just restart it, or pick check for updates to have it autoupdate.
Major new features include support for email attachments, the new buttonbar UI to replace the scripts menu, and the configurable search column for the 'What' search.
Maildrop 2.0 runs on Tiger and Leopard, I have some ideas for the next version, which may mean its Leopard only, why are you still on Tiger anyway?
Thanks to everyone who tried out the beta builds and provided feedback.
Wednesday, December 03, 2008
Maildrop 2 beta 4I did further battle with the AppleScript ninja's and won, the new beta version of Maildrop includes support for attachments, for create case, all the attachments on the email are automatically added to the case (there's a pref to turn this off). For add Email, the attachments are listed with size and name, and you can choose which to upload, and which record they should be associated with. Download beta 4 and give it whirl. Feedback welcome.
Saturday, November 22, 2008
Maildrop 2.0 betaI've been working on a 2.0 version of Maildrop, one of the pain points of the current version, especially for users new to Mac is the use of the scripts menu to trigger the maildrop actions. rather than interacting with the Maildrop application directly, so I've been working on an alternative approach. I came up with this, its a small button bar window that will hover above Mail or Entourage.
In order for it to not get in the way, when neither Mail or Entourage are the active app it will go to the back of the window list (like a panel window, except its in a different application). In addition the button bar shows which set mail client is active for maildrop, and will automatically switch between Mail and Entourage based on the active app (for people unluckly enough to have to use both).
There's also some additions to the Add Email dialog, now there's an additional column in the "related to what" results, this by default will show the case subject field for case records, you can edit this mapping, and add a additional field for each object type by right clicking on the search results table and selecting configure search columns
The config sheet for this will appear, for each object type, you can select a field from the drop down list.
There are a number of other fixes and tweaks (closing the add email dialog with the window chrome doesn't leave the app in the modal state anymore), login errors are shown better, etc. One feature I'd like to get in before the final 2.0 release is to support attachments.
Finally, a few tips for Maildrop, these apply to the current 1.5 release as well,
- on either of the search boxes pressing enter will perform the search, no need to switch to the mouse to click the search button.
- the "related to who" search is done automatically when the add email dialog opens, that's why the "related to what" search field has the first focus.
- If you create a new lead or contact in the related to who section, it should be automatically selected for you.
- If the "Type" field on Task is available to the user, Maildrop will automatically populate it with the value Email (check the fields FLS settings for this)
Download the current Maildrop 2.0 beta and give it a whirl.
Sunday, November 02, 2008
Dreamforce 08#Dreamforce08 Is already in full swing, I'll be there most of the time, in the dev lounge, or in some of the API sessions, and of course, I'll be at both the monday night Hackathon and the meet the developers session on wednesday morning. Feel free to stop me and ask your burning API, Apex, OSX, iPhone or other force.com questions, If you can't find me in the lounge, you can track me down on twitter (@superfell). And finally, the great thing about being at Moscone, the BlueBottle cafe is only a couple of blocks away for some of the best coffee on the west coast.
Twitter API from ApexCodeI've been trying out the Twitter API from Apex code, turns out to be reasonably straight forward, i was able to put together a little client class that can get your friends timeline, and to post new updates (the rest of the api is basically a matter of typing at this point). Using this means you can post twitter updates with just 2 lines of apex code
TwitterForce t = new TwitterForce('someUsername', 'somePassword');
t.postUpdate('@arrowpointe working on it (this from apexcode)', '985929063');
The last parameter to postUpdate is the statusId of an existing status you're replying to, or null for something that's not a reply, postUpdate returns you a TwitterForce.Status object that contains all the information about the new post. You can also fetch your current timeline, with
TwitterForce t = new TwitterForce('someUsername', 'somePassword');
List<TwiterForce.Status> statuses = t.friendsTimeline();
Using this i was able to throw together a custom visualforce controller that lets me set my twitter login info, then see my timeline, and post update.


Rebuilding the twitter UI in VF is not that interesting, but being able to programatically interact with Twitter from apex code is. Here's the code, it includes the TwitterForce client class, some tests, the demo VF controller, and demo VF page. remember you'll need to add http://twitter.com/ to your org's remote sites for the API calls to work. Enjoy, and see you at #dreamforce. apex Twitter API.zip
Friday, October 31, 2008
Maildrop and Quicksilver videosMichael Wilde over on the Splunk blog put together a couple of great short videos on getting upto speed with Maildrop and the Quicksilver plugin. Thanks Michael!.
Monday, October 27, 2008
PocketSOAP moving to Google codeSince pretty much day 1 (so about 2900 days ago!), I ran the PocketSOAP projects off my personal CVS server, including anon access and a web viewer, I planned on & off to move to SourceForge, but their future always looked shaky back then and this was at the bottom of the todo list. I'm finally bitting the bullet and moving them to Google Code.
Sunday, October 26, 2008
moving SVNI've been moving my SVN repository from a Windows box into my Mac, so far has proved to be straightforward enough, certainly much much easier than when i looked about 18 months ago.
- OSX binaries for SVN now on the Tigris.org site.
- instructions for running svnserve using launchd
- How to move a subversion repository
I've seen a few questions on this recently (this is new for v14.0), so i thought I put together a sample of calling retrieve in the metadata file api to fetch a package of all your reports. This is in VS.NET 2005, but should be easy to translate to other environments. The code does the following steps (this is more complex that other cases because Reports don't support Wildcards)
- Calls the partner api login to get a sessionId and the URL to the metadata API for your username.
- Creates and configures an instance of the metadata client.
- Calls ListMetadata to find out all the ReportFolders.
- Using the list of ReportFolders, calls ListMetadata again to find out all the Reports in those ReportFolders.
- Uses Retrieve / CheckStatus / CheckRetrieveStatus to fetch a package of the reports, and writes the package out to c:\reports.zip
using System;
using System.Collections.Generic;
using System.Text;
namespace mdRetrieve
{
class Program
{
static void Main(string[] args)
{
// regular Enterprise/Partner Login call
// I added a WebReference of the partner wsdl as sf, and the Metadata WSDL as md
sf.SforceService svc = new sf.SforceService();
sf.LoginResult lr = svc.login(args[0], args[1]);
// set up a MetdataService client
md.MetadataService ms = new md.MetadataService();
ms.SessionHeaderValue = new md.SessionHeader();
ms.SessionHeaderValue.sessionId = lr.sessionId;
ms.Url = lr.metadataServerUrl;
Console.WriteLine("Logged in as {0}", lr.userInfo.userName);
String [] reportFiles = ListReports(ms);
RetrieveReports(ms, reportFiles);
}
static String [] ListReports(md.MetadataService ms)
{
// can't use wildcards with reports, so need to fetch the list
// of ReportFolders first, then fetch all the reports in
// each folder.
md.ListMetadataQuery q = new md.ListMetadataQuery();
q.type = "ReportFolder";
md.FileProperties[] fp = ms.listMetadata(new md.ListMetadataQuery[] { q });
if (fp == null)
{
Console.WriteLine("No report folders returned");
return new String[0];
}
List reportFiles = new List();
q.type = "Report";
foreach (md.FileProperties p in fp)
{
q.folder = p.fullName;
// listMetadata can take more than one item at a time
// left as an exercise for the reader to batch up these calls.
md.FileProperties[] rps = ms.listMetadata(new md.ListMetadataQuery[] { q });
if (fp == null) continue;
foreach (md.FileProperties rp in rps)
{
Console.WriteLine("{0}", rp.fileName);
reportFiles.Add(rp.fullName);
}
}
return reportFiles.ToArray();
}
static void RetrieveReports(md.MetadataService ms, String [] reportFiles) {
// build up an unpackaged retrieve request for the list of reports.
md.RetrieveRequest r = new md.RetrieveRequest();
r.apiVersion = 14.0;
r.unpackaged = new md.Package();
md.PackageTypeMembers m = new md.PackageTypeMembers();
m.name = "Report";
m.members = reportFiles;
r.unpackaged.types = new md.PackageTypeMembers[] { m };
// start the retrieve request
md.AsyncResult ar = ms.retrieve(r);
// wait for it to complete, sleeping as necassary.
while (!ar.done)
{
System.Threading.Thread.Sleep(ar.secondsToWait * 1000);
ar = ms.checkStatus(new String[] { ar.id })[0];
}
// did it work ?
if (ar.state == md.AsyncRequestState.Error)
Console.WriteLine("{0} {1}", ar.statusCode, ar.message);
else
{
// now actually go get the results
md.RetrieveResult rr = ms.checkRetrieveStatus(ar.id);
if (rr.messages != null)
foreach (md.RetrieveMessage rm in rr.messages)
Console.WriteLine("{0} : {1}", rm.fileName, rm.problem);
// write the zipFile out to a disk file.
using (System.IO.FileStream fs = new System.IO.FileStream("c:\\reports.zip", System.IO.FileMode.Create))
fs.Write(rr.zipFile, 0, rr.zipFile.Length);
}
}
}
}
Monday, September 08, 2008
Apex and FKsJust like i described yesterday for the API, you can do exactly the same foreign key resolution using external Ids in Apex code, e.g.
Case c = new Case(subject='Apex FKs');
Account a = new Account(extId__c='00001');
c.account = a;
insert c;
Sunday, September 07, 2008
FK Resolution with external IdsOne of the tedious things about integrating data across systems is mapping keys, e.g. your account master system is pushing data into Salesforce.com, both systems have their own beliefs on what the primary key for one of those accounts is. One of the things Salesforce.com has supported for a while is the concept of external Ids, these are custom fields in Salesforce that you've said are a primary key in some other system. By setting this up, you can delegate all of the key match drudgery to the Salesforce.com infrastructure. Most people know about the upsert function, which allows the caller to create or update a row based on an external Id value, rather than the Saleforce.com primary key. But wait, order in the next 5 minutes and we'll throw in the ability to resolve foreign keys as well, this much less understood feature allows you while calling create/update/upsert, to use external Ids to resolve foreign keys as well. Keeping with our account master example, you may have a system that needs to create Cases in Salesforce, and it only know's the account Master accounts Id, and not the salesforce account Id. To use this, rather than passing the FK itself, you populate the relationship element, and populate its externalId field. Is harder to explain than to show an example, e.g. this C# code will create a new case, related to an account, where we only know one of the accounts external identifiers.
static void createCase(sf.SforceService s, String accExtId, String caseSubject) {
sf.Case c = new sf.Case();
c.Subject = caseSubject;
sf.Account a = new sf.Account();
a.extId__c = accExtId;
c.Account = a;
sf.SaveResult sr = s.create(new sf.sObject[] { c })[0];
if (sr.success)
Console.WriteLine("New case created with Id {0}", sr.id);
else
Console.WriteLine("Error creating case : {0} {1}", sr.errors[0].statusCode, sr.errors[0].message);
}
Note that rather than populating the AccountId field on case with the Salesforce.com Account's Id, we populate an Account with its extId__c value instead. Because you can have multiple external ids on a particular object, this nested object is used to tell Salesforce.com which particular external Id field you're using. If you're not a C# fan, then the raw soap request looks like this
<soap:Body>
<create xmlns="urn:enterprise.soap.sforce.com">
<sObjects xmlns:q1="urn:sobject.enterprise.soap.sforce.com" xsi:type="q1:Case">
<q1:Account>
<q1:extId__c>00001</q1:extId__c>
</q1:Account>
<q1:Subject>test case</q1:Subject>
</sObjects>
</create>
</soap:Body>
Thursday, August 28, 2008
binary data + meta dataI see Joe is busy standardizing a way to bundle binary data with atom metadata, looks a lot like SwA, just switch out the soap envelope for an atom envelope. Amazon's S3 uses DIME for their SOAP API (good for them, I always liked DIME much better than SwA or MTOM), and their REST api uses HTTP extension headers to carry any metadata. Is there anything else? beside bas64, which is fine for smallish stuff fails in the face of larger binary data (the more things change the more they stay the same), should i be brushing up my mime parsing skills. ? is WebDav interop any better than it used to be?
Wednesday, August 13, 2008
bad week to be in the email businessContinued problems with MobileMe, the recent Gmail outage, and now NetIdentity are still trying to pick up the pieces from a multiday outage.
Sunday, August 10, 2008
FinanceThere's going to be no MS Money 2009 release, and my Money 2006 "subscription" is about to expire, to add insult to injury, the link the app to "upgrade" doesn't do anything. I looked at Mac alternatives a while back and was disappointed. There's new versions of MoneyDance and iBank out, so I guess i'll have to take another look at those. Of course, the curse of being a programmer is I'm tempted to just go build my own, all I really need is an easy way to import data, and to get some reports out, nothing too fancy (even in Money half of the useful feature's I'd like to use like asset allocation tracking don't work anyway).
