Friday, November 30, 2012

Amazon.com knew I was pregnant before my mother

All it took was a purchase of one nursing bra and Amazon began flooding me with advertisements for pregnancy and baby-related products.  Some people find this kind of targeted advertising convenient.  I find it invasive and annoying.  I don't even mind it if I'm outright searching for something and they show me related products, but now whatever I'm doing in Amazon, I'm bombarded with suggestions on what I might want for my breasts, nether region or yet-to-be-born baby.

With the "convenience" that comes with having your every digital move catalogued and analyzed, we lose a significant amount of privacy.  Today it's my own view of Amazon, Google, Facebook, etc. that's affected by my purchases, searches and comments; what of the day when Amazon starts recommending to my friends and family viewing my Wish List that I might appreciate some nursing pads or stretch mark cream?  And if you think they won't, you're taking the concept of "corporations are people too, my friend" too seriously.  I'm sure there are plenty of Amazon employees and even executives who would oppose such tactics, but ultimately it's the bottom-line that will drive such decisions; after all it's their legal obligation to their shareholders to scrabble for every dime they can.

I quit Facebook the day I commented that I was going to Boston and started getting advertisements for Boston restaurants and events in my sidebar.  I knew that day was coming though and had always been conscientious of what I posted there.  I don't think people, particularly young people, are always that aware.  Once it's online, assume it's there forever.  Go ahead and delete or close your account thinking that's wiped it out, it's still on some server somewhere.

Think Facebook won't turn over a log of your full history (deleted or not) to a government intelligence agency if asked, even without a warrant?  Did you think the same thing about phone companies before the Bush administration made it clear that saying the word "terrorist" absolves both the government and telecommunications companies of having to adhere to any laws, right down to the very Constitution they supposedly hold so dear?  And it's not just the government you have to worry about, Facebook has already "accidentally" revealed what was supposed to be private information of users to third-party application developers when those users installed their apps.

Whatever privacy scandals we hear about related to social media and search engines (try googling "Google Germany Privacy" for some insight on how they push the boundaries) is only what they've been caught doing and assuredly just the tip of the iceburg, and advances in technology and the evolution of artificial intelligence will bring about privacy breaches we can't even yet fathom.  It's a strange, new world we live in.

There is a bit of "turnaround is fair play" going on in the world though.  The same companies that push the boundaries of the trust we put in them with our personal data have put their own trust in the government and companies of China as they use factories there for manufacturing and setup branch offices.  China doesn't have the same ideas about intellectual property as we do.  Companies have found themselves victim to corporate espionage, stealing and recreation of their designs and even disturbingly finding monitoring devices built into electronics that were not in the design.  The Chinese government offers lip services to such breaches, but rarely takes any action on the matter.  Be patriotic and come back to the USA guys; we may expect living wages and safe, non-toxic work environments, but at least you can have us arrested and sue us if we steal from or sabotage you.

Friday, October 5, 2012

Zoo Life- The End

My stint as a docent at the Prospect Park Zoo came to an end a few months back and I thought I'd take a moment to talk about why.  Partly just for the irony of it; you'll see what I mean.

Here's how the story began, but not why I ultimately quit.  I was part of the animal handling program there.  This had been going on for a number of years; docents who had gone through training and been certified were able to present animals to the public during tours and scheduled presentations on  weekends.  Not all of the zoo animals mind you, only ones the animal keepers had designated as tractable based on species and the individual, like chinchillas, chickens and some snake and lizard species.  It was a rewarding program for the docents and a nice chance for the public to get up-close-and-personal with some of the animals, including a chance to touch them.

Rather suddenly, the program was ended.  To my knowledge, there was no unfortunate incident that brought this on, and that's the kind of rumor that spreads pretty quickly.  This seemed more like a policy decision from the managerial level, probably from someone new to the zoo who was worried about liability.  That's a guess though, we were just told that the program was no more.  I was disappointed, but also recognized that I was just a volunteer there and as such didn't have any say in policy.  Some of the other docents took it a lot harder, there were meetings (including tearful pleas) and emails and petitions on the web to appeal the end of the program.  Frankly, it was all a little melodramatic for my tastes.

At this point though, I'm still a loyal volunteer and staying out of the fray.  I even received a call from the NY Post (blech) on the matter and had nothing but good things to say about the zoo.  Until the day I was informed that a new condition of being a volunteer was that I had to sign an agreement saying that I would never make any public (via media, Facebook, email or however) negative statements about the zoo.  Excuse me?  I had no problem with restrictions about what I could say while I was at the zoo acting as a representative, but trying to restrict my free speech as an individual outside of the zoo?  That was unacceptable to me.  If I was an employee, it would be illegal to require that of me; we have whistleblower protection laws in this country for that.  If they did ask their employees to sign the same agreement, I hope the illegality of it comes back to bite them in the ass later.

I don't like it when people try to inappropriately assert authority over me, or anyone.  People who do so are generally bad human beings even beyond their pathetic little power grabs.  Being the CEO of my own company has made me rather sensitive to it.  So told them I wouldn't sign and why and left the zoo on my final day of volunteering sniffling as I did so.  I really did enjoy my time there until some asshole  who didn't even have the courage to identify themselves came along and ruined it for me.  God I hate having principles sometimes.

Thursday, October 4, 2012

The Joy of Salesforce Governor Limits

Working within the governor limits in Salesforce can be a real balancing act.  I understand why they're there; it's a shared environment and you can't have developers writing code that bogs down the servers for everyone else. Especially because, let's face it, there are lot of really shitty developers out there. But that doesn't mean I don't curse their name when I see those dreaded LimitException errors.

I started writing Apex as soon as it came out; back when the governor limits were a lot more stringent and nifty tricks for avoiding them like future methods didn't exist yet.  Nowadays, I have a number of clients processing large amounts of data and/or implementing very complex business processes on their data, so I've come up with quite a few tips and tricks on dealing with the limits that I thought I'd share.

Bulk Proofing Code and Future Methods

I'm lumping these together because you don't really need me to explain them to you.  Unlike many other bloggers, I'm not interested in proving how clever I am by regurgitating information already published much more eloquently by Salesforce themselves.  So instead I'll just refer you to them-
If you want more info on those, Google is your friend.  If you can't figure out how to bulk-proof your code, you probably have no business writing code; this is shared environment coding 101 (and is not so bad for developers working within their own environments to know either).  

Two drawbacks of Future methods that are worth noting if you're considering implementing them-
  • Your future method may be generating records or updating fields that your users are expecting to see instantly, and it can be confusing for them if they don't. Generally it happens fast enough that all they have to do is refresh the page to see it, but asynchronous logic is a little abstract to explain to a non-developer (and even to a lot of developers).
  • If your future method hits an error, either your user will never know it or you'll have to write in logic that emails them if an error is hit- but again, kind of confusing to an end-user.  Be prepared to have to fix errors being hit in future methods that you receive by email and remember to go back and clean-up any data affected by the fact that your method didn't fire correctly.

Chunking Future Methods

I've had scenarios where I have DML operations on multiple records, either set off by Apex or data imports, that fire a future method and then hit governor limits within that method.  Each time you run code, you can set off up to 10 Future methods so if governor limits are an issue in your future method, consider breaking up whatever you're sending into the method into manageable chunks.  An example of some trigger logic that does exactly that here-

//Breaking this into chunks of 30 to avoid governor limits
Map<Integer, Set<Id>> mapSetStats = new Map<Integer, Set<Id>>();
for (Attendance__c a : Trigger.new){ 
    if ((a.Status__c == 'Attended' &&Trigger.isInsert)
     || (a.Status__c != Trigger.oldMap.get(a.Id).Status__c 
         &&Trigger.oldMap.get(a.Id).Status__c == 'Attended')
    ){
    //for first attended or when last Set in the Map has 30 ids, add a new Set to the map
    if (mapSetStats.size() == 0 || mapSetStats.get(mapSetStats.size()).size() == 30){
    mapSetStats.put(mapSetStats.size() + 1, new Set<Id>());
    }
    mapSetStats.get(mapSetStats.size()).add(a.Id);
    }

}
//Sync Monthly/Annual Client Statistics:
if (mapSetStats.size() > 0){
for (Integer i : mapSetStats.keySet()) 
            clsAttendance.syncClientStatistics(mapSetStats.get(i), new Set<Id>());
}

In this example, "mapSetStats" is populated by individual Sets of Ids, each of which is no larger than 30 records each.  I happen to know that users will never update more than 300 records at a time (10 Future calls * 30 record batches) so I can get away with this.  If chunking isn't feasible for any reason, then you'll need another solution, such as....

Business Logic in Scheduled Code

I've used this not only for getting around governor limits but also for limits on the other side of external web service callouts. Here's the basic idea-
  • Instead of sending records to a Future method for processing, find a way to gather records that require processing in a query.  In some scenarios, I've created a checkbox field specifically for this purpose and updated that field in a trigger to true, but sometimes the very nature of what you need to accomplish (e.g. a blank field that needs to be filled in) means that you don't have to explicitly update it to identify those records in a query.
  • Create a Batchable class which queries for the records that have been marked in the step above.  For details on this, see Salesforce's article on Using Batch Apex.  
  • Create a Schedulable class which performs your logic on records from your Batchable class.  Again, you don't need me to figure this out, see Apex Scheduler.
  • When you use Database.executeBatch to get records from the Batchable class, you can pass in an optional second parameter to set the limit on the number of records returned- you just need to figure out what's the largest number of records you can process at once without hitting the limits.
  • If you did need to explicitly mark records for processing, like my checkbox example above, make sure you un-mark them when the processing is complete.  Otherwise, the same records will just keep being update in an endless loop.
As with Future methods, users will need to understand that there will be a delay in the updates that happen as a result of scheduled code.  I've been lucky enough that in the cases where I have had to use scheduled code, users haven't had an issue with that.  If your users do have a problem with it, remind them that the fancy, magical updates they're looking for were probably absolutely impossible for them to accomplish just a few years ago so they'll have to shut up and deal.  Depending on your relationship with them, you might need to re-phrase that.

Sunday, September 2, 2012

Using Third-Party Generated Certificates in HTTPRequest Calls

I've been doing various integrations with external web services lately and have learned some things along the way. The latest integration I did required two-way authentication.  The Salesforce documentation makes some assumptions about these integrations-
  1. That the service you're integrating with provides a parseable WSDL.
  2. That the service you're integrating with allows you to upload a signed certificate that was generated from Salesforce for the two-way integration.
And if both of those hold true, the Salesforce documentation (and various related blog posts) will be sufficient for you.  However-
  1. Not all services provide a WSDL, and even those that do might include XML elements that are not supported by Salesforce.  In this case, you're on your own for writing an Apex class to interact with the web service.
  2. Some services provide a WSDL that Salesforce can parse, but you'll find that you need to edit the resulting Apex class to include properties that were excluded.  Though I didn't dig in to see if this was the fault of the WSDL file itself or the parser, I suspect the latter.  How to go about editing that generated Apex class is worth it's own post.
  3. Some services provide you with a client certificate file that you need to include in your request- you don't have the option of generating one in Salesforce and getting it signed.
None of these are insurmountable, but they'll take a lot more leg-work on your part and in the case of having to provide a third-party generated client certificate in an HTTP request, I found myself having to piece together how to do so from various forum posts.  So I'll focus on that today.  

So an external service (in my case, the credit card processor First Data) provides you with a client certificate file and a password for that file.  Now what?  In Salesforce's documentation for the HTTPRequest class you'll notice a method called "setClientCertificate" with a note that this method is deprecated and you should really use "setClientCertificateName".  But if you've been provided with the client certificate, "setClientCertificate" is what you'll need.  Here's how to get it to work-
  • Upload the client certificate as a Static Resource
  • In Apex, query for that file and base64 encode the body of the file into a string variable.
  • In your HTTPRequest variable, use the setClientCertificate method with the base64 encoded certificate as the first argument, and the certificate password as the second argument.
Oh sure, it's easy when you know how to do it.  And a note that alternatively, you can base64 encode the certificate yourself and use the resulting string in setClientCertificate, but the above seemed a little more elegant.  The actual code to accomplish this-


String cert;

for (StaticResource sr : [Select Id, Body from StaticResource where Name =: <certfilename>]){
cert = EncodingUtil.base64Encode(sr.Body);
}
HttpRequest hReq = new HttpRequest();
hReq.setEndpoint(<url>);
hReq.setMethod('POST');
hReq.setClientCertificate(cert, <certpassword>);

If you were lucky enough to have a WSDL that Salesforce was able to parse, you'll be setting the "clientCert_x" variable of the stub to the base64 encoded string and the "clientCertPasswd_x" variable to the password.

Once again, hope this saves someone some time!

Saturday, September 1, 2012

Encrypted Fields in Apex- one gotcha

I've been doing some work integrating Salesforce with a credit card processor and hit upon a issue with encrypted fields in Apex that I haven't found documented anywhere.  It may actually be entirely intentional, but without documentation it's confusing and I did see a couple other people hitting the same problem.  According to Salesforce documentation, when you work with an encrypted field in Apex, the code will always see the unmasked value of the field.  But there's an exception, if you pass an Sobject into a method, you'll find that you'll be retrieving the masked value of the field.   I've only tested with static methods so far, and it might be specific to those.

More concrete examples.  You have an Opportunity that has already been saved with a value in the Credit Card Number field, which is encrypted.  You pass that Opportunity record into the following method.


public static void EncryptedExample(Opportunity opp){
String strCC = opp.Credit_Card_Number__c;
System.debug('Can the code see the masked number 9 in this field? '     
                     +strCC.contains('9'));
//No, it can't!
}

This is only an issue if the SObject you're passing in has been saved already.  If you pass in an SObject that either hasn't been inserted or if the value of the encrypted field has been updated but not committed to the database, then that field hasn't been encrypted and masked yet and the code therefore sees the actual value.

The solution?  You'll have to query for the record within your method and then all works as expected-


public static void EncryptedExample(Id idOpp){
Opportunity opp = [Select Amount, Payment_Type__c, Deposit__c, Credit_Card_Number__c, Credit_Card_Exp__c from Opportunity where Id =: idOpp];
String strCC = opp.Credit_Card_Number__c;
System.debug('Can the code see the masked number 9 in this field? ' 
                     +strCC.contains('9'));
//Yes it can!
}
Hope this helps someone, I know I spent a couple hours baffled (at first I thought it was a mistake in my webservice callout).

Sunday, August 19, 2012

Happy Terrace Garden Time

In looking back on my last two posts, they're a bit preachy and serious; time for something lighter.  This year's garden.  Last year was our first attempt at a garden on our terrace.  This year we had learned our lessons on what plants can take our windy environment and how to best care for the plants we have.  I won't blather on- have I mentioned that when I write an instant messaging widget I'm going to call it Blather? The pictures are more interesting but a quick overview of our crops-

  • We upscaled last years upside-down tomatoes in a coconut fiber basket to a bigger basket (16"), and eventually a coconut fiber cover that helps keep the moisture from being blown away.  The roots from the tomatoes grow up into the fiber, it's interesting to peak under.  
  • The garlic chives from last year (in the smaller basket where the tomatoes used to be) survived inside for the winter and share the basket with chocolate peppermint, which can be a nice touch in my coffee some mornings.
  • There's a golden raspberry bush, which I had never heard of before, but they grow stouter than their red raspberry cousins from my childhood that would be a bit of a monster on our terrace full grown.  
  • I bought a kumquat tree last fall and we had it inside for the winter.  It had kumquats when I purchased it, which we consumed over the winter, so this is the first time I get to see them growing from flowers.
  • Carnival blend carrots, which will grow as purple, red, white, yellow and orange.
  • Hot cherry peppers, which have turned out to be very tolerant to the wind.
  • Strawberries in an upside-down commercial bag, hung inside of a reclaimed old-school metal aquarium platform.
  • In the earlier pictures is a venus flytrap, which provided garden justice to a couple of plant-eating pests, but sadly now needs to be replaced.  

Late May, the Early Days

Overview of almost everything

You can see one kumquat here, it held on since last fall when I bought the tree

Top-down view into the raspberry water bottle / greenhouse. Wasn't it cute?

Mid-June, the flowering begins (as do excited visits from bees)

And if you look just above the basket in the center, the Empire State Building.  Much better in person
Once the raspberry outgrew it, the hot cherry pepper plant spent some time in the greenhouse

The first of the tomato flowers

Rokku enjoys the garden as well




 Over time, we gave all of the plants watering spikes with their own supply of water, it got them through the hot, windy days of July while still yielding us plenty of fruits and veggies to eat.

Miscellaneous


For awhile, the venus flytrap was magical at night.  The hope was to attract bugs.

In the day, our sentry bee stood guard

 The fruiting


Isn't it beautiful?
Rokku always gets first dibs

Except on the hot peppers

Yay!

The kumquat begins to flower

The mint expands outwards

August

We've had a few tomato, garlic chive and hot cherry pepper meals from our garden at this point.
The golden raspberry has a decent number of flowers now

Little baby kumquats!

And Rokku heads off into the sunset



Thursday, August 9, 2012

If you say "social media" just one more time!

As a tangentially related aside to the following rant- I'm really sick of the high percentage of "experts" I see on TV brought on to talk to me about technology, though they have zero background actually working in technology themselves.  And I'm talking about working in a capacity that creates technology, not just that they've used technology to blog or tweet before, or that they had an idea that they then hired actual technical people to implement for them.

It's too much with the social media.  Social media is just one small part of a larger culture of technology, and frankly not as earth-shaking as our "experts" would have us believe.  And if you don't believe that, go check the price of Facebook's stock.  It hardly even matters when you read this, that will likely hold true. And this is a company that gets non-stop, 24-hour, free advertising on television, radio, web sites, business cards... I could go on for awhile here.  But still you can't gain any traction in the market?

The only thing that surprised me about the Facebook stock plummet was how quickly it happened. I figured the bubble would hold for at least a couple months after the IPO. But despite the hype around the launch, corporations, and in particular publicly-owned corporations, are about profit and the Instagram purchase was just one of many cracks in the facade of Facebook's potential profitability.  Which reminds me, you should really check out The State of the Web, Spring 2012 on The Oatmeal.

But I digress, the whole Facebook thing has been nauseating and really worth an entry of it's own, but at the heart of the problem is the over-inflated sense of importance of social media in general.  Social media has it's place and people have found ingenious ways to make use of it.  But it's not the end-all, be-all of the evolution of human technology.  And let's not ignore how many relationships and I'm sure even lives have been destroyed with the help of social media.

Are you an organization struggling to figure out how to employ social media?  Maybe you don't have to; maybe there are other tools you should be investing in altogether.  Social media has become the hammer, and now everything is a nail.  As a consultant offering software development services, I've gotten some strange requests related to social media.  The funniest one was an organization who provides services to people with involvement in the criminal justice system asking if I could set up Facebook accounts for all of their clients that could be controlled and accessed by the organization.  Um, no, and more importantly, why on earth would you want to do that?

Oh yeah, and social media is not new.  I'm not sure how people have been convinced of this, but I'm sure everyone involved in the early development of Bulletin Board Systems in the late 1970s and early 80s cringes when they hear how "revolutionary" this all is.


Monday, February 6, 2012

You know it's bad when you forget about chocolate

A week ago I bought myself a dark chocolate bar.  It was very much an impulse buy one morning in the midst of the Client Cutover From Hell.  I slipped that bar of chocolate into my purse... and today I remember it's there.  That chocolate bar that I had a week ago decided might give some brief chemical reprieve.  And now as I sit at the tail-end of the Client Cutover From Hell, I celebrate with my chocolate.  It really tells you more about this week than much else I could say.  And yet I will.

I have a few gripes after going through this process.  These are things I have long been sick of but now that they've become the cause of much sleep deprivation, I therefore declare war on....
  • Crap software companies that over-charge non-profits with promises of technology like you haven't seen since the mid-90s.  I'm looking at you, Convio Luminate!  This I think is worth a post in and of itself.
  • Crap IT support who don't have any clue what they're doing. Some of my difficulties ended up being the result of having to reverse engineer what turned out to be a completely asinine domain configuration by this organization's IT support consultant.  They've already expressed dissatisfaction with this guy, and I'm going to recommend that they drop him altogether.
  • Visualforce reRenders.  I used to think it was me, that there were just nuances to reRendering in Visualforce that I was missing.  And while I'm sure that is the case at times, I'm now also quite sure that the way reRender works in Visualforce can be just plain inconsistent.  I discovered in this project that pages that I wrote months ago that were working perfectly fine, now had to re-written because reRenders which previously worked no longer did.  My fellow developer and contractor has found the same thing and has taken to using a third-party Javascript to do the reRenders in many scenarious.  I'm with her (yup, another female developer!)

Saturday, January 14, 2012

Sweet Breaded Chicken

Necessity is the mother of invention and this recipe came about when I realized I didn't have an egg to stick the breading to my chicken.  I actually prefer this method now.  I use this chicken either in stir fry or as chicken fingers with some fries.  I'm considering incorporating parmesan into the breading and garlic into the soy sauce next time I do this, and may update the recipe if that works out.

Ingredients
1 lb Chicken Cutlets
3 Tablespoons Flour
1 Cup Soy Sauce
3/4 Cup Plain Breadcrumbs
1/2 Cup Canola (or other vegetable) Oil
Pepper to taste

  1. First set up your station so you're ready when you're hands are covered with chicken and goo- one bowl with flour, another bowl with the soy sauce and a plate covered in a layer of bread crumbs.
  2. Cut the chicken up into either nugget size or finger size pieces.
  3. Coat the chicken pieces with flour.
  4. Dunk the chicken into the soy sauce but don't leave it too long so you don't lose all of the flour on it, this helps make it sticky for the next step.
  5. Press the chicken into bread crumbs so they're completely coated. 
  6. Heat up a pan over medium heat with the oil.  I don't deep fry, I just use enough oil to coat the pan and fry one side of the chicken at a time.
  7. Don't add the chicken to the pan until the oil is nice and hot or the breading will just end up mush.  The chicken should sizzle a bit when you add it.
  8. Pepper the exposed side of the chicken.
  9. Leave the chicken alone while one side of it browns- the time will vary based on the size and thickness of the pieces, but the finger-sized pieces I did took about 4-5 minutes per side.
  10. The chicken is ready to flip when the side that's frying is verging on dark brown - the sides should look lightly browned. Tongs are the easiest way to flip them.
  11. Pepper the newly exposed side.
  12. Brown the other side. 
  13. Remove the chicken from the pan and let it drain on a plate with a paper towel beneath it.

Wednesday, January 11, 2012

Random Book and Movie Reviews


Movies
Contagion- I told you if you left Gwyneth Paltrow to her own devices long enough that something like this would happen.  I liked this movie, though it just re-focused me on my fear of the impending plague.  The characters were well written and performed and it kept me engaged, unlike...

Tinker Tailor Soldier Spy- This is a movie of old white men looking at each other awkwardly and staring across rooms and out windows.  Every once in awhile a woman will look across a room with an expectant dismay on her face.  The men never seem to expect anything until it's too late and unfortunately it's on them who we focus.  The time jumps were confusing and each scene I had to check for grey hairs to get an idea of when it was.  There was a slew of scenes with a character who had been shot earlier that I assumed were flashbacks but it turned out he just hadn't died. I guess Gary Oldman delivered the 10 or so lines he had really well, but mostly he just stared.  If you couldn't tell already, I was not as enamored with this movie as the critics.

Rise of the Planet of the Apes- The writers spent time endearing the apes to me, but there was no need; I was with them from the beginning.  Get 'em, gorilla!  Along those same lines, we were flipping through the channels the other day and happened upon a scene at a bull fight of a bull jumping into the stands of people who had come to watch him be tortured.  Havoc was wreaked. Fair play to the bull. 

Book
Pym, A Novel by Mat Johnson- It's hard for a book that goes into the realm of complete implausibility to fully engage me the way this one did.  The main character, formerly a professor of African American studies, becomes convinced that the novel The Narrative of Arthur Gordon Pym of Nantucket, by Edgar Allen Poe is actually based on a true story and sets off to prove it, an adventure that eventually lands him in Antartica.  It's a very entertaining read, I recommend it.

Sunday, January 8, 2012

Visualforce to Word


I've been having lots of fights with Microsoft applications lately.  Perhaps as a web developer, this goes without saying.  I just won a fight getting a Visualforce page to display correctly when converted to a Word file.  I was generating a file of certificates with each certificate on a separate page and a very specific layout that had to be followed. Thought I'd share a few tips on what I learned.

Basics
First off, how to get a Visualforce page to generate as a Word file.  In the "page" tag, include the following:

<apex:page contentType="application/msword#FileName.doc" showHeader="false" standardStylesheets="false">

Page Formatting
  • Save a Word doc as HTML
    Not sure how to get the font, border, layout, etc specified in Visualforce to display correctly in Word?  Do it in Word and then save it as an HTML file.  The file it generates is a bit of a beast, but if you scroll down, you'll find <style> tags with Word-specific attributes you can use in <style> tags in Visualforce. You can also copy and paste paragraphs from the html into your Visualforce, though I wouldn't recommend trying to paste the whole file, there's just too much crap (very technical term) in there and Salesforce won't even let you save it the way Word writes the html tags.

    For my document, I grabbed these styles to create the following in Visualforce; note the orientation style to set the page as landscape:
    <style>
            @page Section1{
                size:11.0in 8.5in;
                mso-page-orientation:landscape;
                margin:.25in .25in .25in .25in ;
                mso-header-margin:.5in;
                mso-footer-margin:.6in;
            }
            div.Section1
            {
                 page:Section1;
            }
            body {
                font-family:"Gotham-Book";
            }
    </style>
    For the style above to work, you'll need to surround your content in Visualforce with: 
<div class="Section1">your content</div>
  • Use "in" (inches) for widths to layout the page
    Word is designed to render documents for printing, so widths should be specified in inches (and I'm guessing centimeters for everyone that's abandoned our archaic measuring system).  Just a note that "px" does work in Word (in specific contexts) but if you're trying to fit something to a page like a border that goes around everything, save yourself the trouble and just do it in inches.
  • Use "pt" for font sizes
    Setting the font size using style="font-size:12pt;" in VF is the equivalent of selecting 12 for the font size in Word.
  • Margin instead of Padding Padding wasn't doing the trick to space out lines of text but margin works.  Here's a sample:

  • <p style="font-size:22.0pt;font-family:Gotham-Medium;margin-top:12pt;margin-bottom:.0001pt;">
                    Some text here
    </p>
  • Page breaks
    Wherever you want a page break, insert this:

    <br clear="all" style="mso-special-character:line-break;page-break-before:always;"/>

    Note that I tried using "page-break-before:always;" as the style for a div and that was a no go, Word wants it in a break or paragraph tag.
Including Images
  • Don't use a secure URL (https) as the image URL
    I came across forum posts where people were able to successfully use the secure Salesforce URL (i.e. https://na2.salesforce.com/...) to link to images in Word, so my guess is that this varies based on the version of Word you have.  I have Word 2007 and the images would not display until I switched to a non-secure URL, which I did using Salesforce Sites.  It's probably a good idea to do this even if the secure URL works for you because it may not work for others.  
  • Use the full URL
    If your image is stored in Salesforce Documents, you can generate that URL like this:
    http://yourdomain.force.com/servlet/servlet.ImageServer?id=015D0000000Dpwc&oid=00DD0000000FJbGyourdomain = 
    Your Salesforce Sites domain
    id= The Id of the Document
    oid= Your Organization Id, which you can find in Setup in Company Information

    You can also store the image as a Static Resource and link to it that way.
Exclude "Visualforce" from your web searches
Still need help formatting your Visualforce page for Word? When it comes to figuring out formatting in Word, Google (as is often the case) is your friend and no doubt how you arrived here, but don't limit your searches by including "Visualforce" in the search terms. People use html to generate Word files in other languages, and some of the best tips I found were on PHP and ASP forums.

Tuesday, January 3, 2012

African Groundnut Stew

I somewhat randomly selected this as our New Year's Eve dinner, and was happy I did.  This is based on a recipe from a slow cooker pamphlet I no longer have, so I can't properly attribute it.  Nor do I know for sure if it's actually based on an African recipe as was claimed.  Whatever, it's yummy.

Ingredients
1/2 to 3/4 lbs Chicken Cutlet

1 Medium Potato
1 Medium Sweet Potato
1/4 Medium Onion
1 Tablespoon Peanut Butter
1 Tablespoon Tomato Paste
1 teaspoon Ground Ginger (or dice up some fresh ginger)
Salt and pepper to taste

  1. Cut up the chicken, potatoes and onion into stew sized pieces.  Make the sweet potato pieces bigger than the regular potato pieces as they cook through faster.
  2. Mix all ingredients into the slow cooker.
  3. Turn on the slow cooker (I have a rather simple unit that's either on or off, but if you need a temperature setting, I'm guessing it will be about median of your options).
  4. Let it cook at least 2 1/2 hours, stirring occasionally.

Salesforce general sentiments


Right now my business is based almost exclusively on developing on the Salesforce.com platform, but I'm not one of those Marc Benioff idolizing, Salesforce can do no wrong type of people (those people are annoying).  I think Salesforce has done a good job of creating an extensible CRM that allows for non-technical people to customize it up to a point, and developers to customize it beyond that point.

For awhile, the supposed plan for Salesforce was to become a true platform- they handle the storage and maintenance and provide the framework on which everyone could build or install pre-built applications.  Included in that pitch was the statement that they wouldn't build enhancements specific to industry verticals or job functions, that would be left to the customers and third-party developers.  Good bye Salesforce.com, hello Force.com.  I liked this plan. 

This is clearly no longer the plan.  Salesforce continues to build functionality for sales and other verticals, and has gone the Microsofty route of acquiring companies who have built Salesforce solutions and incorporating those solutions into the platform.  This plan I don't like for the same reason I never cared for Microsoft doing it.  Trying to incorporate software into a platform that was not specifically engineered to be incorporated creates complexities (i.e. opportunities for bugs) and inefficiencies that could have been avoided by engineering something from the ground up to actually be a part of the platform.  It's a big part (though not the only reason) of why Windows is the giant bloated piece of crap that is today.  Microsoft has managed to prosper despite this by having a strong foothold in the market, particularly corporate sector of the market, and because computers have progressed to be able to handle the bloatiness of Windows and still function. 

As of late, we've seen some instability in the Salesforce sandbox environment. There was that week this past fall where one of the sandboxes was dead to the world, and just last night the entire sandbox environment, in North America anyway, was hiccuping.  Do I know for sure that these glitches were the result of the way they've expanded the platform?  Absolutely not.  But they were no real surprise to me when they hit and I suspect we'll see more; it's as hard for Salesforce to avoid as a painfully long boot-up time is for MS Windows.  Be careful there Icarus, your wings are looking a little gooey.