Simon Fell > Its just code

Saturday, April 18, 2009

App Updates

I released an updated SoqlX a couple of weeks back, it has a number of tweaks, exposing relationship names to make building SOQL-R queries easier, and the filtering object/field list I discussed earlier. And I just posted an updated version of SFFS, the file extension information is now bundled into the filenames for those files that don't have it in the name directly (depending on how your documents got into salesforce to start with, they might have the extension separate to the name). Follow me on twitter to find out these things as they happen.

< 11:54 AM PDT # > tags : OSX Salesforce.com [playing "Phoenix" by The Prodigy (from Always Outnumbered, Never Outgunned [Bonus Track])]

Thursday, March 12, 2009

SoqlX Filters

Following on from the filtered object/field list discussion, seems like showing all the fields and highlighting the matching ones seems like the best bet (but filtering the list of objects based on the object name, or the object having a field matching the filter), expect to find this in the next release of SoqlX.

< 10:22 PM PDT # > tags : OSX Salesforce.com [playing "Butterfly (Tilt's Mechanism Mix)" by Tilt (from Resident: Two Years Of Oakenfold At Cream (Disc 1))]

Tuesday, March 10, 2009

SoqlX Filters

I've been thinking about adding a filter widget to the object/field list in SoqlX, right now i have so that the object list is filtered based on whether the object name or any of its fields match the filter criteria, but the actual list of fields for the object remains intact. (as shown on the left). Is that what you would find most useful? should it also filter the field list itself ? something else? (I also thought about leaving it with the full list of fields, but having it highlight the fields that match the filter). Ping me on twitter and let me know what you think.

< 10:24 PM PDT # > tags : OSX Salesforce.com [playing "Song For Life (Steppin' Razor Mix)" by Leftfield (from Renaissance: The Mix Collection [Disc 1])]

Thursday, March 05, 2009

SoqlX tips'n'tricks

Everything you wanted to know about SoqlX but were afraid to ask.

Login


Remember that SoqlX uses the API to connect to Salesforce, and so depending on how your admin configured it, you may need to use your api security token in addition to your password to login (just add the token to your password, so if your password is beerisnice and your security token is abv123, enter beerisniceabv123 as the password). getting a LOGIN_MUST_USE_SECURITY_TOKEN error means you most likely forgot to include your security token (search for security token in the salesforce.com online help for more info)


Let SoqlX store your login credentials on the keychain, you'll be much happier, next time you run soqlX it'll remember your username & password (and if you have multiple salesforce.com logins, tryout trapdoor)

Queries

To execute a SOQL query, type the SOQL query into the box at the top, click Query (or Query All), the results will appear in the table, or for count queries, in the bottom left hand corner.



SOQL-R is supported, so you can do both queries that access foreign key relationships (like accessing account from a contact), and also aggregate child queries (like select accounts, and the contacts for each account). Double click the magnifying glass to expand the 2nd query table at the bottom, showing the child rows for that particular parent row.


If you queried the Id column, then you can edit data inline, just double click the cell and type in your new value, when the cell looses focus, the reecord in salesforce will be updated. You can also check the deleted checkbox and hit the delete button to delete row(s). click the Delete column header to quickly toggle the checkbox on all rows.

Using the Window menu, you can open launch the salesforce.com web application, for the user you're currently logged in as. You can also open the recent queries window, which'll show you the last 10 queries you've executed, double click a query to copy it to the SOQL query textbox. Watch the recent queries list animate the changes as you run new queries!.


Use File -> Save Query Results to save the results of your current query as a CSV file.


Schema
The list of objects & fields appear in the list view on the left hand side, double click an object to automatically create a query to select all the fields. Use the details button at the bottom left to turn on the details window which shows all the properties for the object or field selected in the list.

Hit the schema button at the bottom right to switch to a graphical view of your schema, showing how objects are related.

Use the +/- widget inside each object to control the set of fields that appear (id + name field, all standard fields, all fields). Double click an object (or select from the list view) to make that the primary object of the view.
Right click on any object in the listview and pick 'generate schema report' to generate the schema report for that object, this contains the graphical schema view, along with a table detailing all the fields for the object, you can print or export to PDF the schema report.

Got questions, or suggestions for new features, catch me on twitter.

< 8:37 PM PST # > tags : OSX Salesforce.com [playing "Run Wild" by New Order (from Get Ready)]

Wednesday, February 04, 2009

all the way to 20

bah, forget 11, this baby goes all the way to 20!

But seriously, memory is dirt cheap right now, why short change yourself

< 7:51 PM PST # > tags : OSX Technology

Saturday, January 03, 2009

Ritual Coffee now at Whole Foods

You 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.

< 1:38 PM PST # > tags : Coffee

Sunday, December 07, 2008

Maildrop 2.0

I 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.

< 2:43 PM PST # > tags : OSX Salesforce.com [playing "Lush 3-4 (Warrior Drift Psychick Warriors Ov Gaia)" by Orbital (from Diversions)]

Wednesday, December 03, 2008

Maildrop 2 beta 4

I 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.

< 9:04 PM PST # > tags : OSX Salesforce.com [playing "Try All You Want" by Electronic (from Electronic)]

Saturday, November 22, 2008

Maildrop 2.0 beta

I'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,

Download the current Maildrop 2.0 beta and give it a whirl.

< 11:47 AM PST # > tags : OSX Salesforce.com [playing "Don't Leave" by Faithless (from Reverence/Irreverence [UK] Disc 1)]

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.

< 7:05 PM PST # > tags : Salesforce.com [playing "Battle Song" by Deltron 3030 (from The Instrumentals)]
Twitter API from ApexCode

I'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

< 3:03 PM PST # > tags : Salesforce.com Web Services [playing "Mastermind" by Deltron 3030 (from The Instrumentals)]

Friday, October 31, 2008

Maildrop and Quicksilver videos

Michael 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!.

< 10:53 AM PDT # > tags : OSX Salesforce.com [playing "Sultans Of Swing" by Dire Straits (from Sultans Of Swing [Disc 1] [Special Edition])]

Monday, October 27, 2008

PocketSOAP moving to Google code

Since 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.

< 3:53 PM PDT # > tags : PocketHTTP PocketSOAP [playing "Single Handed Sailor" by Dire Straits (from Communique)]

Sunday, October 26, 2008

moving SVN

I'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.

Using this info i was able to quickly move my repository from SVN 1.4.x on Windows to a new 1.5.x SVN setup on OSX. (anyone who codes should be using a version control system)

< 7:46 PM PDT # > tags : OSX [playing "Brothers In Arms" by Dire Straits (from On The Night (LIVE))]
Metadata API and Reports

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)

  1. Calls the partner api login to get a sessionId and the URL to the metadata API for your username.
  2. Creates and configures an instance of the metadata client.
  3. Calls ListMetadata to find out all the ReportFolders.
  4. Using the list of ReportFolders, calls ListMetadata again to find out all the Reports in those ReportFolders.
  5. 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);
            }
        }
    }
}
< 4:47 PM PDT # > tags : .NET Salesforce.com