Open Finder Window in Terminal

One of the few (maybe only) things I miss from my Windows days, was the abilty to right-click a window in Explorer and select 'Open Command Window Here'.

01openpromptheres

Mac's Finder doesn't has a similar function, which is a bit of a pain when I want to run ANT (aka Force.com Migration Tool ) to quickly get code in/out of a Salesforce org.

In the past, I used Automator and a Applescript to do this. But, recently I found a nice toolbar shortcut, called CdTo, that does this with one click.

  1. Download the zip file from here.
  2. Unzip the file and copy the CdTo application for your OSX version to your Applications directory. (I put my copy in Utilities)

    Cdto

  3. Drag and drop the CdTo app to the Finder toolbar.

    Cdto2

  4. Now you see a new icon on the Finder window. From now on you can click that to open the current Finder folder in Terminal.

    Cdto3

 

Advertisements

Firefox 4 Impressions

I installed Firefox 4 a few days ago and I have to say I like some of the improved features a lot. I've been using Firefox as my primary browser for several years now and aside from some past(?) issues with memory bloating, I've continued to prefer it even as I transitioned from Windows (IE – no contest) to Mac (Safari – close, but no cigar).

Now, browsers are a pretty commoditized product and I wasn't expecting any major changes in the overall experience with this new release. But, I have to say that two of the new features really impressed me and are going to have definite impact on the way I use my browser.

App Tabshttps://support.mozilla.com/en-US/kb/what-are-app-tabs
This is a great idea. Rather than using bookmarks for the web applications you're always in (Gmail, Facebook, Typepad, Mint, Salesforce), just have a permanent tab for each.

Tab Groupshttp://support.mozilla.com/en-US/kb/what-are-tab-groups
I love Expose on the Mac. I swipe four fingers on my trackpad and get a clean view of the kajillion windows I have open at any given time. Tab Groups is a similar feature for Firefox. Hit Command + Shift + E and you get a similar view of all the open tabs. When I'm doing Salesforce development, I may have a dozen or so open tabs all with the same tab title. Being able to see them all and quickly identify the one I want on sight is a great time-saver.

I was also really pleased to see that Firebug had released it's FF4 compatible update the day prior. That's an add-on I can't do without, so it was wonderful to see that Mozilla had done a good job of keeping partners in the loop on the impending upgrade. Well done, gentlemen.

Upload and Parse CSV via VisualForce

Update August 22nd, 2015 – Please note that this is an experiment in reading a CSV and pulling the data into Visualforce. In no way should this be part of a Production-ready solution as it DOES NOT SCALE. You will hit limits as the number of rows in the CSV increases. As a solution, this would NOT be considered a best practice.

I recently had to develop a Visualforce page that allowed a Salesforce user to upload a CSV file and then generate some records from the parsed values. Yes, I know that is what the Data Loader is for, but the intended user here was not technically adept. And I thought it was an interesting experiment.

Csv upload

The Salesforce Developer Wiki already had a sample class for parsing the file – http://wiki.developerforce.com/index.php/Code_Samples#Parse_a_CSV_with_APEX . So, I had to just create the VF page and my controller. Though, I did modify the parsing method from the Wiki to use the carriage return in the string split. CSV’s produced on my Mac didn’t parse correctly using the newline ‘\n’. I found a good post here explaining how Windows and Mac/Unix handle carriage returns differently. Here’s the results.

Visualforce page: uploadCSV

<apex:page controller=”uploadCSVcontroller” >
  <apex:form >
  <apex:inputFile value=”{!contentFile}” filename=”{!nameFile}” /><br/>
  <apex:commandButton value=”Upload” id=”theButton”/>
  </apex:form>
  <apex:outputPanel id=”results”>
  <p>nameFile: {!nameFile}</p>
  <p>rowCount: {!rowCount}</p>
  <p>colCount: {!colCount}</p>
    <table title=”CSV Output” border=”1″ width=”100%”>
       <apex:repeat value=”{!results}” var=”row”>
           <tr>
               <apex:repeat value=”{!row}” var=”cell”>
                   <td> {!cell} </td>
               </apex:repeat>
           </tr>
       </apex:repeat>
     </table>
  </apex:outputPanel>
</apex:page>

Apex Class:

public class uploadCSVcontroller {

    public Blob contentFile { get; set; }
    public String nameFile { get; set; }
    public Integer rowCount { get; set; }
    public Integer colCount { get; set; }

    public List<List<String>> getResults() {
        List<List<String>> parsedCSV = new List<List<String>>();
        rowCount = 0;
        colCount = 0;
        if (contentFile != null){
            String fileString = contentFile.toString();
            parsedCSV = parseCSV(fileString, false);
            rowCount = parsedCSV.size();
            for (List<String> row : parsedCSV){
                if (row.size() > colCount){
                    colCount = row.size();
                }
            }
        }
        return parsedCSV;
    }
    /*
    Credit to
    http://wiki.developerforce.com/index.php/Code_Samples#Parse_a_CSV_with_APEX
    */
    public static List<List<String>> parseCSV(String contents,Boolean skipHeaders) {
        List<List<String>> allFields = new List<List<String>>();

        // replace instances where a double quote begins a field containing a comma
        // in this case you get a double quote followed by a doubled double quote
        // do this for beginning and end of a field
        contents = contents.replaceAll(‘,”””‘,’,”DBLQT’).replaceall(‘”””,’,’DBLQT”,’);
        // now replace all remaining double quotes – we do this so that we can reconstruct
        // fields with commas inside assuming they begin and end with a double quote
        contents = contents.replaceAll(‘””‘,’DBLQT’);
        // we are not attempting to handle fields with a newline inside of them
        // so, split on newline to get the spreadsheet rows
        List<String> lines = new List<String>();
        try {
            //lines = contents.split(‘\n’); //correction: this only accomodates windows files
            lines = contents.split(‘\r’); // using carriage return accomodates windows, unix, and mac files
            //http://www.maxi-pedia.com/Line+termination+line+feed+versus+carriage+return+0d0a
        } catch (System.ListException e) {
            System.debug(‘Limits exceeded?’ + e.getMessage());
        }
        Integer num = 0;
        for(String line: lines) {
            // check for blank CSV lines (only commas)
            if (line.replaceAll(‘,’,”).trim().length() == 0) break;

            List<String> fields = line.split(‘,’); 
            List<String> cleanFields = new List<String>();
            String compositeField;
            Boolean makeCompositeField = false;
            for(String field: fields) {
                if (field.startsWith(‘”‘) && field.endsWith(‘”‘)) {
                    cleanFields.add(field.replaceAll(‘DBLQT’,'”‘));
                } else if (field.startsWith(‘”‘)) {
                    makeCompositeField = true;
                    compositeField = field;
                } else if (field.endsWith(‘”‘)) {
                    compositeField += ‘,’ + field;
                    cleanFields.add(compositeField.replaceAll(‘DBLQT’,'”‘));
                    makeCompositeField = false;
                } else if (makeCompositeField) {
                    compositeField +=  ‘,’ + field;
                } else {
                    cleanFields.add(field.replaceAll(‘DBLQT’,'”‘));
                }
            }

            allFields.add(cleanFields);
        }
        if (skipHeaders) allFields.remove(0);
        return allFields;      
    }

}

 

 

Mac Daddy – Salesforce for Mac OSx

My recent infatuation with all things Mac has been taken to new levels since I was able to convince my IT department to replace my crappy Dell with a shiny new Macbook Pro. Hot!

Now 80% of my work on any given day is 'in the cloud' developing for Salesforce.com using a browser, so I wasn't overly concerned with the transition. That being said there is still some software that I depend on.So I decided to post a list of the Mac equivalents for other Salesforce Developers using OSX.

So what didn't make the cut?

  • Force.com Excel Connector – In my opinion, a superior tool to the Data Loader for the purpose of quickly querying and updating Salesforce records. I used it extensively for cleaning up data during migrations. Alas, the geniuses at Redmond decided not to support VB Macros in Excel 2008 for Mac. Seriously? Why would you release a product on one platform that is missing a major piece of functionality readily available on the other. Oh, maybe you're trying to build a monopoly and undercut use of the other platform. Ok, then. Nevermind. This may have been a non starter anyway since the Connector relies on the Office Toolkit which is only for Windows. Tried the Mac version of Mass Update Anything, but the UI is all screwed up. Looks like the version is from 2007, so it might not be compatible with Leopard.
  • Notepad++ – Using TextWrangler but not loving it. Might try TextMate
  • SnagIt and Camtasia – I make a lot of screen captures and videos for training. Using Snap Pro X because that was recommended by my IT dept, but it feels like a downgrade. It doesn't have an editor for adding callouts, screen zooms, etc. Might try the Mac beta of SnagIt and see if I can make a case for Camtasia for Mac.
  • Internet Explorer – Don't laugh. I need to see how VisualForce pages I develop look in IE, especially since I use Firefox exclusively. Also, the web-based app I use to log my billable hours … wait for it … only runs in IE! My advise; if you're a company with a web-based product that only runs on IE, start laying people off. IE's market share is around 60% and declining . As they lose market share so will you. In geek speak that means; IF(eggs > 1 && baskets == 1) THEN FAIL

Contacts/Cases rollup for Accounts in Salesforce

** Update Sept 3rd 2015 – This code is not recommended as it does not scale for Contacts greater than 1,000. I will be posting an update version of this code soon. **

If you’re familiar with using roll-up/summary fields in Salesforce, you’ve probably come up against the fact that you can’t create those fields types to count the number of cases or contacts related to a given account record. I don;t know why that is, but I recently created a workaround using Apex triggers to perform that function in the same automated fashion.

You’ll need to add two fields to your account object. The labels can be changed but the API names should be left as is, if you don’t want to change the code.

  • Number of Cases – Number_of_Cases__c
  • Number Of Contacts – Number_of_Contacts__c

Then add the following triggers to your org. First, the Case trigger;

/* Provide summary of Number of Cases on Account record */

trigger CaseTrigger on Case (after delete, after insert, after undelete,
after update) {

    Case[] cases;
    if (Trigger.isDelete)
        cases = Trigger.old;
    else
        cases = Trigger.new;

    // get list of accounts
    Set<ID> acctIds = new Set<ID>();
    for (Case cse : cases) {
            acctIds.add(cse.AccountId);
    }
   
    Map<ID, Case> casesForAccounts = new Map<ID, Case>([select Id
                                                            ,AccountId
                                                            from Case
                                                            where AccountId in :acctIds]);

    Map<ID, Account> acctsToUpdate = new Map<ID, Account>([select Id
                                                                 ,Number_of_Cases__c
                                                                  from Account
                                                                  where Id in :acctIds]);
                                                                
    for (Account acct : acctsToUpdate.values()) {
        Set<ID> caseIds = new Set<ID>();
        for (Case cse : casesForAccounts.values()) {
            if (cse.AccountId == acct.Id)
                caseIds.add(cse.Id);
        }
        if (acct.Number_of_Cases__c != caseIds.size())
            acct.Number_of_Cases__c = caseIds.size();
    }

    update acctsToUpdate.values();
}

Next the Contact trigger;

/* Provide summary of Number of Contacts on Account record */

trigger ContactSumTrigger on Contact (after delete, after insert, after undelete,
after update) {

    Contact[] cons;
    if (Trigger.isDelete)
        cons = Trigger.old;
    else
        cons = Trigger.new;

    // get list of accounts
    Set<ID> acctIds = new Set<ID>();
    for (Contact con : cons) {
            acctIds.add(con.AccountId);
    }
   
    Map<ID, Contact> contactsForAccounts = new Map<ID, Contact>([select Id
                                                            ,AccountId
                                                            from Contact
                                                            where AccountId in :acctIds]);

    Map<ID, Account> acctsToUpdate = new Map<ID, Account>([select Id
                                                                 ,Number_of_Contacts__c
                                                                  from Account
                                                                  where Id in :acctIds]);
                                                                
    for (Account acct : acctsToUpdate.values()) {
        Set<ID> conIds = new Set<ID>();
        for (Contact con : contactsForAccounts.values()) {
            if (con.AccountId == acct.Id)
                conIds.add(con.Id);
        }
        if (acct.Number_of_Contacts__c != conIds.size())
            acct.Number_of_Contacts__c = conIds.size();
    }

    update acctsToUpdate.values();

}

Now those two fields will maintain a real-time count of the contacts/cases related to a parent account even if those records are deleted and undeleted (restored from the Recycle bin).

You can Download (7k) all the triggers and test classes necessary for deployment via Eclipse or Ant. Enjoy.

Netbook undergoes a OSX change – Part 2

Pc-gun So after being able to successfully load Mac OSX on "my" MSI Wind U120-024US, I had to take inventory and see what did and did not make the transition.

First order of business was to upgrade the Bios from .10C to .10F. MSI does a great job of getting you lost on its website. (I suspect Engrish may not be the native language of the web team. ) In any case, I finally tracked down the latest bios and , even better, the user manual .

This post gave very clear instructions on updating the 120 Bios. Creating the bootable USB drive for DOS was simple enough having had practice creating several by this point. I copied over the three files from the download and rebooted the Wind. On my first attempt I got "ERROR: Bios has no flash information available". According to MSI's FAQs that error is asking you to plug in the power cord. Because an message reading, "Please plug in the power cord" would be confusing. I did that and it worked on the second pass.

Next, I wanted to upgrade the OS to 10.5.7. I knew I might be pressing my luck but I pushed on anyway. The update itself went smoothly. After rebooting, the mouse and keyboard didn't work, but I expected that from some of the posts I had read. I hooked up my USB mouse and keyboard to install the necessary drivers. (BTW if you write your system password to a text file on the desktop prior to the update you can save yourself the need for the keyboard by just using the mouse to copy and paste when prompted for it.) Of course I made a huge mistake and used the 10.5.6 'Everything You Need 'package from the main wiki page. DON"T DO THAT. Installing the mouse/keyboard drivers and Realtek wireless worked. But, when I got to the part about updating the video ktext, I rebooted to a grey screen with funky black lines all over the place. More Zebra than Leopard. (That's a Mac pun, kids.) 

Well, I solved that mishap by hitting the "any" key (A 'lil tech support humor here. This is as funny as it gets, folks.) during the darwin boot screen, entering -x at the boot options prompt. This got me into safe mode and I was able to delete the ktexts I had installed and then restore the originals which had been given a .orig suffix. That's two hours of my life I won't get back.

Take two; I was able to find a package with all the relevant drivers for 10.5.7 on this post. Again I pressed my luck and decided to select the options for Realtek Ethernet and the Trackpad despite some allusions to issues by both the author and several posts. Living on the edge here. After getting caught in an endless reboot cycle, I realized that might not have been a good idea. Safe mode didn't work this time. So I booted into single user mode using the -s option at the Darwin prompt. Then I used the instructions here to delete the RealtekR1000.kext. If you get the read-only error like I did use the mount -rw -a command suggested in the first comment of that thread.

Take two point five; Ok booted successfully again. Now time to review the update items and see what does/does not work.

  • Video – Booted to 800 x 600 but a quick check of the display pref pane showed options for 1024 x 800. Awesome.
  • Network – Wireless still worked from my previous install. The Realtek RTL8187SE wireless drivers are also included in the MSi WindOSX86 Upgrade Pack v1.5. The wireless app pops up on every boot which is annoying. I used the Auto-Close for Realtek app suggested on the OS X post install tweaks and fixes post and that seems to have resolved that. As mentioned previously, Ethernet driver was a total bust. It turns out that the first driver package installed the RealtekR1000.kext driver to the location /System/Library/Extensions/IONetworkingFamily.kext/Contents/PlugIns/ which is for a retail install. The correct location is /System/Library/Extensions/. So essentially I had the same driver in two seperate locations. Booting into single user mode and deleting the first one fixed the errors and got the ethernet working perfectly. More details in this post.
  • Battery – The guage appears on the toolbar as expected. Calculates percentage and time albeit slowly. Fully charged with a 6-cell battery estimate was 3:50 battery life. I tried the VoodooPower app suggested on the aforementioned post and didn't see any significant change right away. However after comming out a sleep mode the battery guage showed 5:30. That's almost too good to be true. I left it on for a while and noticed issues with hiberate and random reboot requests. Went into single user mode and deleted the kext.
  • Sleep mode – Closing the lid puts the notebook into sleep instantly and hitting the power button brings it back just as quickly which is where my Windows laptop falls woefully short.
  • Audio – Integrated speaker works fine. Headphone did not. I had success with Chud/Audioee4Wind from the post install thread but I hated having to use script and swtiches to slect between the external speakers and headphones. VoodooHDA works much better and switches between the two automatically. You'll need to uninstall Audiee4Wind and delete the AppleAzilla.kext prior to installing it. Getting the external mic jack to work took a bit of fiddling with the settings in the Voodoo pref pane. I used the following; (Setting the recording level seems to be the critical piece. Other settings refine the quality.)
    • Speaker: PCM 100%, Speaker 100%, Mic 100%, IMix 25%, Recording Level 80%
    • Mic: Speaker 100%, Mic 100%, Input mix 25%
  • Camera – Need to hit Fn + F6 to turn it on. Had no luck with CamTwist. ManyCam worked like a charm with Skype. You have to set ManyCam to start on boot and set the camera in Skype preferences to ManyCam after it loads.
  • Trackpad -Worked fine after running the 1.5 upgrade pack. I decided to try installing Two-Finger scrolling but the mouse was nearly unusable afterwards. It did scroll, but only down and not up and even that took pressing rather hard. The cursor twitched and jumped all over the place. I made some attempts to use the FFscroll pref pane to tweak the settings (hard to do with a disfunctional mouse) but it never seemed to save the changes and half the time would not open at all. I'll leave that to braver souls than I.
  • Key Mapping – I wanted to use the windows key as the command key. System Preferences > Keyboard & Mouse > Modifier Keys. Changed Option Key to map to Command and Command Key to map to Option. Now I can Windows + C to copy and Windows + V to paste. Maybe I'll find a little sticker to label the key better.
  • Bluetooth – I don't know if this even comes with my model nor do I have anything to test it with.

Final Impressions: Getting this Mac clone set up took quite a bit more effort than I originally anticipated. Ironically, all this command line bios-changing driver error fixing blue screening reminded me of setting up a Windows machine. Which was not necessarily a bad thing. I learned quite a bit more about the Mac OS. I think with today's computers being much more like sealed devices, people learn less about what is under the hood and make these things work. I remember when my dad bought one of the first PC's. We turned it on and the screen blinked a cursor at you. It took my brother and I days to get just DOS running. These days computers are like cars; everyone knows how to drive, but they pay some guy big bucks just cause he knows how to change the wiper fluid. Not that I mind. I'm complaining all the way to the bank.

Netbook undergoes a OSX change

Mac-pc My girlfriend got a shiny white MacBook several months ago and I’ve been seriously sweating it. (I’m borrowing it right now) For one, it turns on in the time it take my crappy laptop to inform me that its from Dell. Don’t get me started on trying to get XP to hibernate/sleep. I just leave it on 24/7 to save myself 30 minutes of “Loading…” hell each morning.

So when she decided to get a Netbook for her monthlong stay in South Africa, I jumped at the chance to make a mini mac clone. So I ‘persuaded’ her to buy a MSI Wind 120 after doing some Google research which cited that as one of the models well-suited for ‘hack-a-mac’.

Working in the SAaS sphere for a few years now, I haven’t done the whole fdisk, bios, etc thing in a while. Luckily there were a lot of posts on the MSI Forums to help.

I started by purchasing a 4GB USB drive, downloaded MSIWindOSX86.iso from the Pirate Bay, and started following the instructions at http://wiki.msiwind.net/index.php/Bootable_Flash_Drive_for_OSX_(using_a_Mac)

  1. Using the MacBook, popped in the USB key
  2. Open Disk Utility app
  3. Click on destination USB
  4. Click on Partition
  5. In Volume Scheme pull down menu select 1 Partition
  6. Click on Options
  7. Radio Button Master Boot Record, click ok
  8. Format Mac OS Extended Journaled
  9. Apply
  10. When the formatting is complete, click Restore
  11. Mount the WindMSIOSX86.iso image (that means double click on it for us less Mac-literate) it will appear on the left sidebar of the Disk Utility now
  12. Drag the mounted WindMSIOSX86.iso to the ‘ SOURCE ‘ section
  13. Drag the usb drive partition to the ‘ Destination ‘ section
  14. Check ‘ erase destination ‘ click ok. Restore to USB will take about 10 minutes or so…
  15. Download the latest Chameleon EFI

OK the last step took me quite a while. Most forums mentioned the Chameleon DMG and direct you to the Developers site. But all I could find was the source code for the latest stable version 1.0.11 not the compiled package. I tried the 2.0 RC1 package but it kept stalling at the message “Writing Package Receipt. It took some searching to finally find a copy at FilesTube; Chameleon-1.0.11-installer.dmg.

  1. Double click the chameleon dmg to mount it, run through its
    prompts and install it to your flash / usb key – this makes it
    bootable.Make sure to change the target because it will default to your Mac’s harddrive.

Now you need to prep the MSI Wind. I didn’t want to delete the recovery or windows partitions in case things went south. Luckily the Wind came with three partitions, recovery, OS_Install, and a third empty partition. I ended up creating a Ubuntu live bootable from a spare 1GB drive following the suggestion on this post; http://forums.msiwind.net/viewtopic.php?f=32&t=4701 and then deleting the third partition. I tried going through the next step leaving the space unpartitioned, but I couldn’t get past the select a destination step in the Mac OS installer. So I went back and created and unformatted primary partition.

  1. Plug Flash / USB key into msi wind, boot from it (either change in bios or hit f11 for boot menu)
  2. Run through the installer till you get to the select a partition screen. You’ll see the recovery partition and windows partition with red exclamation marks on them meaning you can’t use those. Open the Utilities menu and open Disk Utility. From here you can erase the unformatted partition and format it as  Mac OS Extended Journaled. (You may in fact have been able to delete and format the partition in one step saving yourself the previous boot from Ubuntu.)
  3. Close the disk utility. You should now have a new partition without the red mark and that you can select.
  4. Click next till you get to the install button. Before clicking that click the Customize button. Expand the options until you see Kernel. Uncheck that option. (I don’t now why but a bunch of post mentioned this. I think it has to do with making your os able to handle updates)
  5. Run the install. It should take a few minutes and reboot your Wind automatically.

Now when my machine rebooted, I got a HFS+ error. Now the last post I cited suggested using Ubuntu to fix the boot flags. That didn’t work for me. I ended up using the suggestion here

  1. Boot from bootable flash drive back into macosx installer
  2. Open Terminal from the Utilities menu
  3. At the command prompt type
fdsk -e /dev/rdisk0
flag 3
update
write
quit
reboot
  1. All 3 partitions will show in darwin bootloader, with osx the default.

Success!! My wind booted to Mac OSX with the gloriously satisfying intro movie. The next steps are to make sure everything works. Already I’m seeing some issues with the wi-fi. I’ll post more as I figure it all out.