Pages

Wednesday, December 21, 2011

Now you see it... Now you don't!

Part of a recent project requirement was to display a set of fields on a record from a related record.  One possibility could have been formula fields for each related field that needed to be displayed.  Sigh...

Another would have been to open a second window on a second monitor.  Nah... too easy

Of course you could create a Visualforce page that pulls these fields in and add it to the page layout.  That would work, but ah... the panel is part of the page, meaning you scroll - it moves.  Suddenly you're scrolling up and down and you might as well have had two windows open.  Forehead slap!

What do you do?

I'm a huge fan of the Force.com Quick Access Menu; one of the best time savers rolled out in a recent release.

It's just a small menu panel that floats at the side of the page.  The cool part about this is if you scroll, it moves with you.  Now, how in the world are you supposed to recreate that?

Fortunately for me, I didn't want it always displayed.  Because of that, I knew I had access to Custom Links and Custom buttons (read more).  They aren't just good for linking to pages, you can use them for launching onClick Javascripts too!
//Start of Custom Javascript Button Code
{!REQUIRESCRIPT("/soap/ajax/15.0/connection.js")}
 //Let's create a new DIV
var newdiv = document.createElement("div");
var result = sforce.connection.query("Select ID From ObjectID where ID = '{!ObjectID.ID}' AND other criteria if required);

//In my case, not every record would have a related record, so to avoid unnecessary errors we'll add make sure we have a result.  Granted, in your scenario, you may return more than one record.

if(result.size>0){
    var records = result.getArray("records");
    var newdiv = document.createElement('div');
    //I'm just going to build out my URL + parameters here
    var string1 = "apex/yourVFpage?core.apexpages.devmode.url=1&id=";
    var string2 = records[0].Id;
    var string3 = string1 + string2;
    newdiv.setAttribute('id', "thepanel");
    newdiv.style.width = "85%";
    newdiv.style.height = "255px";
    newdiv.style.position = "fixed";
    newdiv.style.left = "220px";
    newdiv.style.top = "0px";
    newdiv.style.background = "#FFFFFF";
    newdiv.style.border = "1px solid #000";
    newdiv.style.zIndex = "1000";
    newdiv.style.padding = "0px";
    //Use a regular old iframe to pull in your VF page
    newdiv.innerHTML = "<iframe src='"+string3+"' width='100%' height='240px' scrolling='false'/>";

//This DIV displays the up arrow graphic and provides the onClick functionality for hiding the panel
    var newdivUp = document.createElement('div');
    newdivUp.setAttribute('id', "UpPanel");
    newdivUp.style.width = "46px";
    newdivUp.style.height = "46px";
    newdivUp.style.position = "fixed";
    newdivUp.style.left = "174px";
    newdivUp.style.top = "0px";
    newdivUp.style.background = "transparent";
    newdivUp.style.zIndex = "1000";
    //Display the image and onClick hide this panel, display the down graphic panel, and hide the info panel
    newdivUp.innerHTML = "<img id='up' src='yourUpGraphic.png' onClick='document.getElementById(\"UpPanel\").style.display=\"none\";document.getElementById(\"DownPanel\").style.display=\"block\";document.getElementById(\"thepanel\").style.display=\"none\";document.getElementById(\"DownPanel\").style.display=\"block\";'/>";

//This DIV displays the down arrow
    var newdivDown = document.createElement('div');
    newdivDown.setAttribute('id', "DownPanel");
    newdivDown.style.width = "46px";
    newdivDown.style.height = "46px";
    newdivDown.style.position = "fixed";
    newdivDown.style.left = "174px";
    newdivDown.style.top = "0px";
    newdivDown.style.background = "transparent";
    newdivDown.style.zIndex = "1000";
    newdivDown.style.display = "none";
    //Displays the image and onClick hide this panel, display the up graphic and the info panel
    newdivDown.innerHTML = "<img src='youDownGraphic.png' id='down' onClick='document.getElementById(\"DownPanel\").style.display=\"none\";document.getElementById(\"UpPanel\").style.display=\"block\";document.getElementById(\"thepanel\").style.display=\"block\";' />";

//These three lines will add the DIVs created above to your page
    document.body.appendChild(newdiv);
    document.body.appendChild(newdivUp);
    document.body.appendChild(newdivDown);
}
//And just in case you don't return any records, display an error message
else if(result.size==0){
    alert('This record is not associated with an \'yourObject\' record. Please check the \'yourObject\' field and try again.');
}

Here's my end result in the "opened" state:

In the closed state:
And since you each DIV's position attribution to be "fixed", the panel, "x," or "+" will float at the top of your page for easy use when related data is needed for comparison purposes.

...or of course you could just open two browser windows.  Your choice.

Monday, October 24, 2011

Automated Phone Calls Via Workflow???

Sure, you've got four workflow actions that you can choose from when setting up your fancy new automated processes.
  • Tasks
  • Email Alerts
  • Field Updates
  • Outbound Messages
What if I said you could extend this to include:
  • Automated Calls
    • Pre-recorded messages
    • Robot voice messages (text-to-speech)
  • Text messages
Pretty sweet right?  Check out Call-em-All.  Let's get it out of the way now... I'm not affiliated with them and they were just a lucky Google search result that I found when searching for broadcast messaging solutions. Sign up for an account and then register for API authorization to get some test credits.

Through the GUI, you can setup call broadcasts; essentially the same as a mass e-mail, but with phone calls.  It's an easy service as all you do is upload your calls list (first name, last name, phone number) and record a message to be played.

They've got a great API that comes loaded with documentation, code samples, and a staging environment. 

You can leverage this to create some Apex that can be initiated by a button/link click, scheduled, or yes...even a trigger.

In the following sample, I will be initiating a call broadcast from my Opportunity by updating the Stage. Utilizing their text-to-speech feature, the call will dynamically include the Opportunity's name and stage. There will be two parts; the class and the trigger.

First, the class (excuse the body as it's lengthy):
public class Call_em {
@future (callout=true)
    public static void callMe(string a, string b){
        string Name = a;
        string stage = b;
        string username = 'username';
        string pin = 'pin';
        string broadcast_type = '1';
        string phone_number_source = '3';
        string broadcast_name = 'SAMPLE API Broadcast';
        string caller_id = 'caller_id';
        string PhoneNumbers = 'phonenumbers';
    string TTSText = 'This is a test call for the' + Name +' opportunity. The stage has been set to ' + stage;
        string proxy = 'http://staging-api.call-em-all.com/webservices/CEAAPI_v2.asmx?WSDL';

        HttpRequest req = new HttpRequest();
        req.setMethod('POST');
        req.setEndpoint('http://staging-api.call-em-all.com/webservices/CEAAPI_v2.asmx?WSDL');
        req.setHeader('Host', 'api.call-em-all.com');
        req.setHeader('Content-Type', 'text/xml; charset=utf-8');
        req.setHeader('SOAPAction', 'http://call-em-all.com/ExtCreateBroadcast');
        string bod = ''+username+''+pin+''+TTSText+''+TTSText+''+broadcast_name+''+broadcast_type+''+caller_ID+''+PhoneNumbers+''+phone_number_source+'';

        req.setbody(bod);

        Http http = new Http();
        HTTPResponse res = http.send(req);

        System.debug('These were the results: ' +res.getBody());
    }
}


And the trigger:
trigger Opportunity on Opportunity (before update){
    for(Opportunity o: trigger.new){
        if(yourcriteriahere!){
            Call_em.callMe(o.Name,o.StageName);
        }
    }
}


Explanations: I've kept the class as simple as possible for now. Ideally, you would probably not want to create a new broadcast every time the trigger was initiated. You would want to create your broadcast in advance and add the new number to it.
Note that the method is annotated with @future and callout=true. This is required as the trigger will make the callout asynchronously. The first part of the class is just setting up some variables. Looking at it, I don't think I used the proxy variable. My trigger will feed it two strings (yum); the Opportunity Name and the Opportunity Stage. I then create the new POST HTTPRequest, decalre the endpoint, and set up a few headers. Notice that my endpoint is set to their "staging" URL. I would recommend that this be set to a variable so that you could toggle between their staging and production environments relatively easily.

The trigger speaks for itself. You can use the "Call_em.callMe(o.Name,o.StageName)" line as an action for a button or even modify it to be used in a schedule Apex class.

Once you get this sample working, dig around in their API a little bit. You should be able to leverage existing broadcasts, modify lists, send text messages and so much more!

API Documentation: http://www.call-em-all.com/dev/docs.aspx

</end sales pitch> ...just kidding

Tuesday, October 18, 2011

Business Hours and Holidays!

The holidays are coming and do you know when your tasks are due?  There's an ongoing issue with Salesforce that I've inquired about throughout the years.  Although you can set business hours (even multiple profiles for hours) and holidays, these aren't taken into consideration when workflow actions are applied or due.

A few years ago, the answer to my inquiry was to research the IdeaExchange.  The idea already existed then and unfortunately is still just an idea today.

IdeaExchange Link:  http://success.salesforce.com/ideaView?c=09a30000000D9xtAAC&id=08730000000Bpq3AAC

What a shame!

Is there anything you can do?  Absolutely, but I hope you like Apex Triggers.

There is a BusinessHours Class that you can read about in the Apex Developer's Guide:  http://www.salesforce.com/us/developer/docs/apexcode/index.htm?businesshours

The first thing you want to do before you start playing around with that, though, is to set up your business hours by going to Setup --> Administration Setup --> Company Profile --> Business Hours

For my example, I'll have a default schedule of Monday through Friday, 8AM to 7PM.

I've also set up some holidays through Setup -->  Administration Setup --> Company Profile --> Holidays.  Don't forget to go back to your Business Hour schedule and add the holidays to it by clicking on the "Add/Remove" button.

Got that configured?  Now you're ready to give it a whirl.

Of course you'll want to work this into a trigger, but for demonstration purposes I've just created a test class and pulled in the system date/time (system.now()).

The VF Page:

<apex:page controller="test_business" >
    <apex:form >
        Current Date: {!oldDate}
            <br /><br />
        New (w/ GMT): {!newDateGMT}
    </apex:form>
</apex:page>

Explanation:  Just two simple getter methods from the test class.
  • oldDate is simply pulling in the current date/time using the system.now() method. 
  • newDateGMT is returning the adjusted date/time according to the line w/in that getter method

The Class:

public with sharing class test_business {
    public DateTime getoldDate(){
        DateTime oldDate = system.now();
    return oldDate;
    }

    public DateTime getnewDateGMT(){
        BusinessHours bh = [SELECT Id FROM BusinessHours WHERE IsDefault=true];
        Datetime oldDate = system.now();
        Datetime newDate = BusinessHours.addGMT(bh.id, oldDate, 3 * 60 * 60 * 1000L);
        return newDate;
    }
}

Class Explanation: 
  • getoldDate just retrieves the system.new() method as explained above.  This is the full GMT value, not adjusted for your time zone.
  • getnewDateGMT has two main parts
  1. The query is creating a BusinessHours object named bh after retrieving the one I created earlier and set as the default.
  2. Where I declare the newDate Datetime, I'm taking advantage of the addGMT method in the businesshours class.  I need to feed to it the ID of a BusinessHours object, a starting date, and my increment (in milliseconds).  Broken down, that looks like this:
    1. 3 - # of hours
    2. 60 - 60 seconds in a minute
    3. 60 - 60 minutes in an hour
    4. 1000L - 1000 milliseconds in second

Monday, October 17, 2011

Summary Report Export: Too Many Details!!!

Say you're perusing a few reports you haven't seen since the dawn of your SF Org, thanks to the new Winter '12 report folder interface, and you come across an oldie but a goodie summary report.

"Awesome," you think as you quickly click on the "Export Details" button to work some Excel magic and show off your report's findings. Wait a second, that once dusty report's Excel file equivalent looks nothing like what you saw in your browser window. What's going on here?

The "Export Details" button does exactly that... it exports the details of the report; meaning every line that makes up your report will be rendered as a new line in your .xls or .csv. This is regardless of whether or not you have clicked on the "Show Details" or "Hide Details" button. Of course, you could rework the resulting spreadsheet and do the grouping yourself, but why would you do that?

Rather than eagerly clicking on the "Export Details button," take the "Printable View" button for a test drive. It will render your report again as an .xls file, but retain the grouping as you see it on your report.

If you want to see the details, click on the "Show Details" button prior to clicking on the "Printable View" button. This will show each line item as well as the summarized headers. If you don't want details, make sure that you've clicked on the "Hide Details" button.

In case you were wondering, this also works for Matrix reports!

Wednesday, October 12, 2011

Error: Invalid API Version... Come Again?

One thing that I love about Salesforce is the sneak peak that we get in our sandboxes prior to each new release. On occasion though, I've worked on something new in one of these developer preview sandboxes and wanted to deploy my code to production. Only to receive an "Invalid API Version" error message.

Right now, my production environment is Summer '11, but my sandbox is Winter '12. If I were to create a new Visualforce page in my production environment, the Salesforce API version would be version 22.0. If I do the same within my sandbox, the API version is 23.0. The dilemma, upon deployment, is trying to push version 23.0 into a platform that only supports up to version 22.0; hence the "Invalid API Version" error message. I've done this with pages, but I imagine it is quite possible to experience with classes, triggers, templates, etc.

How do you fix this? Wait for the update to be applied to your production environment. Just kidding...
Within Salesforce, you can go to "Setup" --> "Develop" --> "Pages" --> and click on your page's name. From there click on the "Edit" button and then on the "Version Settings" tab. Drop your version down to that which matches your production instance. Click on "Save" and try your deployment again.
From within the Force.com IDE/Eclipse, expand your project and browse to the page, class, trigger, whatever file you're having issues deploying. Below that file, you should see an identically named file, appended with -meta.xml... edit that. In the editor pane, you should see an xml file structure. You may need to click on the "Source" tab below. Look for the tags and replace the version number with that which matches your production instance. Save it down and try deployment again.

Example:
<?xml version="1.0" encoding="UTF-8"?>
<ApexPage xmlns="http://soap.sforce.com/2006/04/metadata">
  

<apiVersion>23.0</apiVersion>

<description>Default Force.com Change Password page</description> <label>ChangePassword</label> </ApexPage>

While that should fix your problem, keep in mind you are reducing the API version, some it is possible that if you are utilizing some of the new release's features - your file won't save after the API version number change and those features won't work.

Speaking of version numbers, one question you might have is whether or not you need to go through all of your old code to update the API Version after a new update comes along. Once Winter '12 comes out, am I going to update all of my classes, triggers, and pages to be version 23.0? No - you don't have to and you can leave them as is as Salesforce will maintain backwards compatibility. If you want to use some nifty new tags or features that didn't exist in that version set, you might have to change it down the road - but you'll know when you have to.

Friday, October 7, 2011

Reduce Deployment Pains (pt. 2): E-mail Templates

Here we are, nearing the end of the week, and as promised, here is part 2 of the "Reduce Deployment Pains" series. See what we did there with record types? Guess what? You can use the same idea for e-mail templates too. Say you're working with a method that is responsible for sending an e-mail. You can specify the individual parts of an email, the subject and the body, or you can specify an e-mail template. You'll want to shy away from using an ID again as sometimes you end up in situations where you have an ID mismatch between production and your sandbox.

Instead, like we did for obtaining a record type ID, we'll query the template by its name and use it later within a SingleEmailMessage.

public EmailTemplate template = [select Id,Name,Subject,body from EmailTemplate where name ='Your template's name here'];

...

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setTemplateID(template.id);
...

Thursday, October 6, 2011

Reduce Deployment Pains (pt. 1): Record Types

Let's get this ball rolling right away. One amateur mistake that continually haunts my early code is the hard coding of RecordTypeIDs. Remember, I'm new to the development game and have preached and have been preached at to not hard code anything.

Yeah, well under a tight deadline I was more than happy to throw that to the wind. Now everytime I go to deploy some old code from a sandbox, I'll get nagged with some sort of error message that the RecordTypeID doesn't exist in production.

Ah rats, busted. I created the record type outside of the sandbox or after the sandbox was created and now my record type IDs don't match up. What I would do before is just update the ID, deploy, and be on my merry way.

Wrong.

The better way to specify things like IDs will always be to query them so you have the latest and greatest, despite your environment.

Check out this little ditty:

public list<recordType> RecordTypes= new list<recordType>();
public map<string,id> RecordTypes_Map = new map<string,id>();

public YourClass(ApexPages.StandardController controller) {
    RecordTypes = [select id, name from recordtype where sObjectType = 'YourObjectAPI__c'];
    if(RecordTypes.size()>0){
        for(recordtype r:RecordTypes){
            RecordTypes_Map.put(r.name,r.id);
        }
    }
}

I'm creating a map of all the names and IDs of recordtypes for a particular object (don't forget to chance 'YourObjectAPI__c').

Then within the section of code I want to retrieve the ID, I just use:
NB_RecordTypes_Map.get('Name of Your Record Type'));

The obvious caveat here is if your record type name doesn't exist or has changed between your production and sandbox instances. Salesforce will probably puke all over itself in that event, so it probably wouldn't hurt to include some safe guard catches in your code.

Post Dreamforce Life: Crazy

So it has been a while since my last updates. If you couldn't tell or haven't caught any of the hype that is the monsoon of fun called Dreamforce; it was awesome. If you ever have a chance to go... do it! Worth every penny.

The "9-5" has more often than not been a "9-9+" after the conference due to one of the largest and fastest paced projects the company has taken on. A multi-server hosted platform moved in-house to all new hardware on a brand new network. The teams's still alive and are sorting out the system's "Post Move Blues." However, this blog's not about that the life of an admin; I just wanted to throw out my excuses for being a lazy blogger.

I've got some great Salesforce projects in the pipeline (120+ work related and about 20 non-work related to be precise). I'll be wrapping of this week with a new series on how to make life easier when it comes to deploying a project from a Sandbox to Production.

Onwards and upwards!

Wednesday, August 31, 2011

Day 3: Keynote, Chatter, and Blazing Pages!

Hey all again!

Busy day out there today! Quick shout out to the ! flying solo ! group. Good success and hopefully I'll be meeting with some more of you guys in a few hours before the Gala!

Started out the day by meeting fellow "Flying Solo" group member Aoife for breakfast before making our way over to the keynote. Glad we left when we did; although not a straight on shot of the stage, we had a good profile view and plenty of monitors.

The keynote was great; speakers included Marc Benioff, Parker Harris, and Kraig Swensrud of Salesforce. Guest speakers were Tim Campos, CIO of Facebook, and Angela Ahrendts, CEO of Burberry. Also in attendance, Neil Young and MC Hammer.

Big things are happening for Chatter. IM and screen sharing internal to your org and public groups and meetings with your clients. Quick easy invites to the client gives access to these public groups. This left me with one good question... can it be branded or are we stuck with Salesforce branding?

Another cool concept that was discussed was touch.salesforce.com. This was an iPad app that handled Salesforce.com as an HTML5 app. They were pushing the write once, ready for many philosophies. Can't wait to start playing around with that. Imagine writing an app for the web, that also works on your iPad, Android tablets, and mobile phones, across any platform, all the same.

Service and support gets some new goodies too, aside from the Radian6 buyout, there is a cool new tool that allows Facetime, directly to clients from a case. Pretty sweet idea. Jigsaw has been merged with D&B and now called data.com. No telling if this is included with Salesforce, or still the pay for a premium version model we saw in the past few releases.

Check out the whole thing here: https://www.facebook.com/salesforce

Time for the Cloud Expo, Gala, and Metallica!!!

Tuesday, August 30, 2011

Day 2: Using Chatter to Transform Your Company

This was a great session that practiced what it preached. If learning about Chatter use cases wasn't enough, the presenters utilized the DF Dreamforce Chatter for this Session. Great!

Doing that introduced a nice way to discuss the presentation as it was going on with other audience members. At the end, some questions were read aloud and answered. I'm hoping all of the sessions are like this.

Like a lot of companies that got excited with a pilot of Chatter, we introduced it quickly without proper planning and the attention it deserves. We enabled it for everyone, tracked a bunch of fields on a bunch of objects, effectively creating too much "noise" to do our deployment much good.

An example given during the presentation was to think of Chatter as an old time radio with a tuner dial. You can have all static, some static, or a nice crystal clear signal. It's all about fine tuning for your audience. The record oriented updates didn't mean anything to our users because it was too much of the wrong stuff. For you it might be too many fields, the wrong fields, or the wrong org environment to follow these record "changes."

For my company, I believe Chatter can be an effective communication tool. It will come down to grouping and empowerment. I've disabled the Chatter updates for records within my org and will have a stronger focus on the next attempt. I'll create a basic set of department based groups and elect a leader. The leader will be the employee of the highest position within the company hierarchy. The tip I've heard the most is that adoption of Chatter increases as the higher ups become involved.

With departmental groups, I hope to foster intra-department specialization, increased shared knowledge, and open up communication between the employees in that group.

One challenge will be to establish guidelines of when to use the group and when to pick up the phone.

I'd also like to try an experiment where a group is created for specific projects. I would think that this would help my firm stay on track, be more organized, be more open, and ultimately increase awareness on expectations of those involved.

I'm also thinking that it might be good to have a group to let people teach people. "What do the more experienced users want to share with less experienced employees? HR should be able spread good news like promotions, group managers should be able to post departmental achievements, and more. All work and no play makes Jack a dull boy.

I'll be sure to post more once I have a solidified plan going forward. Open questions after the session were:
1) How do my employees know of groups and hash tags?
2) Compliance related issues: SEC 17a-3 and 4.
3) Future of Chatter in workflow - auto create Chatter; auto-enroll employees into groups
4) Administratively add users to groups - no opt out

Day 2: Exam, n00bs, and Chatter

"Interesting" is probably the best way to describe today. Waking up, I was clear and focused. Then my contact ripped into two while in my eye. Delightful.

I left early and I'm glad I did so that I was able to catch the Community Conference Keynote. Congratulations to all of the newly titled Salesforce MVPs; I hope to join your elite ranks some day. I skipped over breakfast in hopes of finding somewhere inexpensive after yesterday's buffet price shock. I did find a granola bar, banana, OJ, and some coffee.

I was able to slide in some studying time while in the exam check-in line. Maybe I should have got in that line a little bit earlier. I met another guy who had taken the same exam three times without passing; needless to say my optimism skyrocketed.

Unfortunately, I didn't pass, but I've got a much better idea of what is on the exam than what is located here: http://www.salesforce.com/assets/pdf/misc/SG_CertifiedADVDeveloper.pdf

Things that come to mind that I want to check on:
When to system validation rules occur?
Does only the administrator have the ability to execute Anonymous Blocks?
Are Anonymous Blocks executed as the system user or current user?
, including expected parameters
Correct environments to do certain things: config only, developer, and full sandboxes, developer edition
Advantages of managed packages and ANT deployment

I'll try to add more as I think of them.

Anyway, after that little bit (a limited bit of disappointment of course, I'm at Dreamforce. I've never not passed a cert exam before, but then again every other one came with failure making my wallet lighter. I'd rather fail for free than fail for $400.

Next, I quickly grabbed lunch at the "Cloud Dine" and made my way over to the Mascone South for the "New to Dreamforce" session. To the disappointment of more than 100 pre-registered people at the door, they were booked past capacity. Rumors are flooding the Chatter feeds that they didn't scan passes. Major disappointment there.

I ran back to the hotel to grab my laptop and check in with work then quickly left to be early for my next session, "Using Chatter to Transform Your Business" where I'm currently at. To be continued!


Monday, August 29, 2011

Day 1: Getting Started with Customer User Interfaces

Morning came too soon this AM. I got my groggy self together and fed before making my way to the Moscone Center. Registration was quick and easy; according to my badge Professional Capital Services, LLC is a proud sponsor of Dreamforce '11!

Side story: During registration I received an interesting call. Guess who's the apprentice of a former Mr. Olympia? Just an example of how my tech skills are opening doors!

Currently, I'm seated in the Moscone West for my pre-Dreamforce training. We're discussing the basics of the visual aspects of Salesforce. In the back of my mind; tension is building as that exam is looming tomorrow. This session will be a good review of some of the Visualforce side of the exam. This session is led by Peter Chittum who has been VERY responsive to my questions within the Dreamforce app.

Topics discussed:
Pieces of SF considered layout
Differences between page layouts and VF pages
Enabling "Development Mode"
Using the "Component Reference" documentation
Using the inline editor

***Whew... thank you Blogger auto-save - just accidentally closed this window and came back to it to find a saved draft.

Standard Controllers and Actions
Visualforce and Custom tags, namespaces, and attributes
Traversing relationships (up and down)
Databinding
Using other Web Technologies (CSS, Javascript, Flash)
Displaying multiple records/parts of records on a single page
Overriding Buttons
The Page Block Table
Overriding "Edit" Pages
Mass editing list views
Creating VF Templates
...and much much more.

Unfortunately time ran out before the end of the material. We got to Module 5 at a slow/normal pace for the crowd, 5 through 7 were expedited with the exercises joined. Skipped over Module 8: Survey of Additional Visualforce Topics (whomp, whommmmp) altogether and launched into an accelerated talk about Visual Workflow. We then skipped over all the new exciting stuff going on in Siteforce. The week is young, hopefully this isn't the last time I hear of this "Siteforce."

All in all it was a great refresher for the VF side of tomorrow's exam!

Onwards and upwards!




Sunday, August 28, 2011

Almost Here and Almost There

I'm currently sitting in the San Diego airport, waiting for my flight to Los Angeles to get me to San Francisco, recapping my sessions with Frank Zane, and wondering what the next week will bring.

I had a great pump at Zane's home gym yesterday day, both physical and mental. I've got a few changes to implement in my diet and routine, but looks like I'm on schedule with competing in next year's Mr. Natural Philadelphia contest. I'd like to see how many other Salesforcerers can say that!

San Diego was amazing for the three days I was there. I rented a car to get around and doing so was a breeze. Couldn't ask for better weather either; warm and sunny without the humidity. Everything was radically different from the City of Brotherly hate and filth (aka the #1 most toxic city in 2011: Source).

Now San Francisco, I'm not sure what to expect. More hills, unpredictable weather, a larger down town. That's about all I've got.

My plans as of now, fly out of San Diego at 5:40 to arrive before 7 in Los Angeles, then around 8:30 fly the rest of the way to San Francisco. Once I touch down, I'll track down my luggage and then figure out BART for a short train ride to Powell St. Station.

For now though, the Advanced Developer's exam isn't going to study itself. Tuesday's coming awfully fast!








Thursday, August 18, 2011

Metallica in 13!

I found this today. For a little more insight on how Metallica became this year's band, check this out: Metallica: San Francisco Show??

I'd take either as a neighbor!

Tuesday, August 2, 2011

Dreamforce: T-Minus 28 Days!

Only 28 more days until Dreamforce '11. That means 24 more days until my two day training session with former Mr. Olympia Frank Zane, but that's for another blog. There's a lot of activity within the Dreamforce App, so I'll keep this short, but I did want to share my agenda for that week.

8/29

Getting Started w/ Custom user Interfaces

8/30
Advanced Developer Exam
New to Dreamforce
Using Chatter to Transform Your BUsiness
Apex Design Patterns and Best Practices
Data Visualization w/ Flex and AIR
Welcome Reception

8/31
Opening Keynote Session
Blazing Fast Visualforce Pages
Best Practices for Clean Data
Developing Application w/ jQuery
Visual Workflow Makes Business Process Automation Simple!
Dreamforce Global Gala feat. Metallica

9/1
Keynote Session
Platform State of the Union
Do Less and Get More w/ Dynamic and Filtered Dashboards
The New 360: Customer Intimacy Through the Service Cloud
Closing Keynote Session w/ Eric Schmidt

9/2
Hands-On: Force.com Pages (Visualforce) in Disguise for Developers (2.5 hr)
Putting Dreamforce to Work for You

Thursday, July 28, 2011

Visualforce: Hide the In-Line Editor

So you have developer mode on and every page that you visit displays the in-line editor at the bottom of the page. I love that thing, but as handy as it is, when I'm testing pages or trying to demo something for someone, it gets in the way.

Don't you wish there were an easy way to disable that?

Try appending this to your VF page name within your URL:
?core.apexpages.devmode.url=1

Tuesday, July 26, 2011

Visualforce: Render Page as .Doc

It's amazingly easy to create a VF page and have it open as a Word document. I've used this a handful of times when my VF page is a form and I want to offer a printable copy.

VF:
<apex:page contentType="application/msword">

However, this post isn't about that. It's about breaking up your content within that .Doc. Sometimes, you need different data in different sections of separate pages. Easy breezy, just include this every time you want a page break in your .doc file.

VF:
<br clear="all" style="page-break-before:always" />

Example: Setting and Retreiving Cookies

In this example, I'm setting a cookie with its value obtained from the "Name" input box when the user clicks on "Add Cookie." When the user clicks on "Show Cookie," the cookie is retrieved, the panel is refreshed, and the name should be displayed. When the user clicks on "Validate Cookie," the cookie's maxAge is looked at. If the cookie hasn't expired, the user is taken to an Apex page named, "not_expired" and alternately "expired" if the cookie is no longer valid.




VF:
<apex:outputPanel id="CookieTest">
   <apex:form >
      Name:<apex:inputtext value="{!Name}"/>
      <br />
      <apex:commandButton action="{!addtocookie}" value="Add to Cookie"/>
      <apex:commandButton action="{!ShowCookie}" value="ShowCookie" rerender="CookieTest"/>
      <apex:commandButton action="{!ValidateCookie}" value="ValidateCookie"/>
      <br />
      The Name set in the cookie is : {!NameAtCookie}<br />
      Max Age: {!MaxAge}
   </apex:form>
</apex:outputPanel>

Class:
public with sharing class testCookie {
   public string Name{get; set;}
   public string NameAtCookie{get; set;}
   public integer maxAge{get; set;}
   public testCookie(test controller) {
   }
   public Pagereference addToCookie(){
      InsertValueToCookie(Name);
      return null;
   }

   public Pagereference ValidateCookie(){
      SetCookieToNameAtCookies();
      if(NameAtCookie == 'expired'){
         pageReference pageRef = new pageReference('/apex/expired');
         return pageRef;
      }
      else{
         pageReference pageRef = new pageReference('/apex/not_expired');
         return pageRef;
      }
   }
   public Pagereference ShowCookie(){
      SetCookieToNameAtCookies();
      return null;
   }
   public void InsertValueToCookie(string val){
   /* create a new cookie with name 'counter', an initial value of Name at the page,
path 'null', maxAge '-1', and isSecure 'false'.*/
      Cookie myCookies=new Cookie('mycookie',val,null,-1,false);
      ApexPages.currentPage().setCookies(new Cookie[]{myCookies});
   }

   public void SetCookieToNameAtCookies(){
   /* reading the value of the cookie 'mycookie' , and insert him to 'NameAtCookie' field */
      Cookie myCookies = ApexPages.currentPage().getCookies().get('mycookie');
      if(myCookies != null){
         NameAtCookie=myCookies.getValue();
         maxAge=myCookies.getMaxAge();
      }
      else{
         NameAtCookie='expired';
         maxAge=null;
      }
   }
}

Friday, July 22, 2011

Quick Search the Apex and VF Deverloper's Guides

I find myself frequently accessing the HTML version of the Apex and VF Dev Guides. A simple shortcut wasn't cutting it anymore - I wanted to pop-open the page and search all in one step.

Within Firefox, create two bookmarks:
1) http://www.salesforce.com/us/developer/docs/pages/index.htm?%s
2) http://www.salesforce.com/us/developer/docs/apexcode/index.htm?%s

Right-click on the book marks, choose properties, and update the "Keyword" box with "vf" or "apex" accordingly.

Now within the URL bar of FF, you can type something like "apex outbound" or "vf commandbutton" to quickly pull up the reference material.




For Chrome, Jeff Douglas has an excellent tool, here: http://blog.jeffdouglas.com/force-com-utility-belt/

Clean Up Classes w/ Hard-Coded SF URLS

Whether you are building something for an internal VF page or an external site, you'll be instantiating instances of the pageReference class.  <-- Did I say that right?  May not have mentioned this, but I have no background in traditional coding, just what I've picked up through my SF work.

For a while, I found my self doing something like this:
pageReference pageRef = new pageReference('https://na4.salesforce.com/apex/mypagename');

Obviously, those links would break when pushed from a sandbox to production and I would have to modify the code.

To save time, I came up with this to prevent those changes:
String hostname = ApexPages.currentPage().getHeaders().get('Host');
pageReference pageRef = new pageReference ('https://'+hostname +'/yourpagenamehere’);
system.debug('pageRef was:  ';+ pageref);
return pageRef;

VF Pages and Emails: Dynamic Copyright Date

"Thanks for getting that e-mail out so quickly... but the footer still says 2008!"

I hate hearing that and luckily there is an easy way around it.  I can't tell you how many email templates or VF pages I have in my SF instance that had hard-coded dates.  The most frequent culprit was a duplicated VF email template with a standard copyright footer. 

Rather than hardcode something like:
Copyright ©2005-2011 Acme, LLC. All Rights Reserved

You could specify a floating year like this:
Copyright ©2005-{!year(today())} Acme, LLC. All Rights Reserved

Blog Purpose

I'm starting this blog (inspired by Jeff Douglas) to share some tips that I've picked up.  I began using Salesforce in 2007 when I began working for my current employer as a help desk analyst.  My role has shifted numerous times over the last few years and around mid-2010, I began working heavily with Salesforce.  I'd love to become one of the best (or most versatile) Salesforce admins in the area.

There probably won't be a lot of huge posts, detailing large projects, but just little tid-bits that might help out my fellow SF'ers.