tag:blogger.com,1999:blog-61840628212255946102024-03-13T20:24:25.163-07:00improveit! 360From the development team behind the improveit! 360 products, a blog for salesforce.com users and developers, by salesforce.com users and developers.The improveit! 360 Development Teamhttp://www.blogger.com/profile/11333495960262631783noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-6184062821225594610.post-84343141156910534352012-10-31T13:54:00.000-07:002012-10-31T14:03:58.251-07:00Invincible 'Dashboard' using Visualforce<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">One of our clients wanted to build a 'dashboard'</span> <span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">to display in their lobby, to show some key metrics about how the business was doing for all employees to see without having to go run reports. Here are the requirements:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Update at least every 10 minutes </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Secure from the outside world (so competitors can't find and watch it)</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Don't need to babysit it</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Work across multiple sites</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Have custom branding </span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">We decided to build it as a custom Visualforce page and put it in a public facing site.</span> <span style="font-family: Arial, Helvetica, sans-serif;">We then limited the access setting for the sites profile to only allow certain IP ranges that the client owned. </span><br />
<br />
<span style="font-family: Arial, Helvetica, sans-serif;">The Visualforce nature lets us easily grab the information direct from their database and build it with custom branding. Building into a public site takes out all the headache and security issue that go along with authentication, and makes it so that the unattended PC it is running on is not a security issue. We can keep out people that shouldn't be looking at it by restricting the IP range to the sites that are going to use it. Finally, it is pretty easy to update it as often as needed by using an <span style="font-family: "Courier New",Courier,monospace;"><apex:actionPoller /></span>.</span><br />
<br />
<span style="font-family: Arial, Helvetica, sans-serif;">This let us fulfill all the requirements except for the babysitting requirement</span>. <span style="font-family: Arial, Helvetica, sans-serif;">If the computer lost internet for a few minutes, or if the Salesforce servers went down for maintenance overnight, the page would throw an error, and the actionPoller would stop runnng. Then someone would have to find the computer and hit 'refresh' to get it to start up again. </span><br />
<br />
<span style="font-family: Arial, Helvetica, sans-serif;">To get around this, we built a <i>second </i>Visualforce page and used it to wrap the page with the actionPoller on it. We then just use javascript to refresh the frame that the real page lives in. The result is a page that is virtually indestructible. Because the full page is not re-rendering, it will self-heal, even if the wrapped page goes down or the internet gets disconnected for a bit.</span><br />
<blockquote class="tr_bq">
<span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;"><apex:page showHeader="false" sidebar="false" cache="false"><br /><script><br /> setInterval ( "refreshFrame()", 600000 );/<span style="font-size: x-small;">/ Self-heal every 1<span style="font-size: x-small;">0</span> min</span><br /><br /> function refreshFrame ( )<br /> {<br /> // (refresh the dashboard)<br /> window.DashboardFrame.location = "/apex/DigitalDashboard";<br /> }<br /><br /></script><br /><iframe src="/apex/DigitalDashboard" name="DashboardFrame" id="DashboardFrame" scrolling="no" height="768" width="1024"/><br /></apex:page></span></span></span></blockquote>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To summarize, here are the steps to build your own 'dashboard':</span><br />
<ul>
<li><span style="font-family: Arial,Helvetica,sans-serif;">Build a Visualforce page that displays whatever data you want. </span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">Build the wrapper page above, replacing 'DigitalDashboard' with the page name you just built</span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">Create a Site, and add both pages to the site</span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">In your site, modify the 'Public Access Settings' and add the IP ranges that you want to view the site. If you want it totally public, just skip this step.</span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">On the computer you want to show the dashboard on, browse to the site URL, and hit F11 to put it in fullscreen. </span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">Walk away! The dashboard should run forever.</span></li>
</ul>
<h4>
<span style="font-family: Arial,Helvetica,sans-serif;">Considerations:</span></h4>
<ul>
<li><span style="font-family: Arial,Helvetica,sans-serif;">I realize it seems odd to wrap a refreshing page in another page that also refreshes. The key difference is that one uses an AJAX actionPoller, which does not 'flicker' when re-loading, and the other uses a frame refresh, which might flicker when re-loading. If you don't mind flickering, you don't need to use an actionPoller at all. </span></li>
<li><span style="font-family: Arial,Helvetica,sans-serif;">A Salesforce Site has limited bandwidth. It is easy to check by just going to the Site details in Setup; limits and usage are listed at the bottom of the page. If this page is going to run 24/7, which is the point, you should turn the refresh rate down to make sure you don't blow over the limits. </span></li>
</ul>
<br />
<br />
<br />
<br />Jason Hartfieldhttp://www.blogger.com/profile/01113400037782608591noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-56670071365677443122011-12-19T12:29:00.000-08:002011-12-19T13:47:30.704-08:00Mike the Customer<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSblb6hh8aVAGu0e7IO6IAL-uGhgYgj4noNNo3jhUjwQflR0TzH7zm3ceKBkQb3N4GJ7K9N2bvbXXwl1s-2Fuw0SPXAeflrGFFKwmNVxMCtsLc0ftDPI0AfdCMXpdFinACXpVt0KDod_A/s1600/Mikes+Details.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSblb6hh8aVAGu0e7IO6IAL-uGhgYgj4noNNo3jhUjwQflR0TzH7zm3ceKBkQb3N4GJ7K9N2bvbXXwl1s-2Fuw0SPXAeflrGFFKwmNVxMCtsLc0ftDPI0AfdCMXpdFinACXpVt0KDod_A/s320/Mikes+Details.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5687940522701302562" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVWDpsMEngxL0KaahLAAh_e4Any6OIdaFmwATgMzJOXejZLAmXDibu7zn0wrbGsikXD-05bNKGo6dGUbhL6D_fI-R6P9s2xZyNXifUd2pxK1wUUHtyiqxB2s_hrU2WuIyRMdaevOx1FC8/s1600/Mike+the+Customer.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVWDpsMEngxL0KaahLAAh_e4Any6OIdaFmwATgMzJOXejZLAmXDibu7zn0wrbGsikXD-05bNKGo6dGUbhL6D_fI-R6P9s2xZyNXifUd2pxK1wUUHtyiqxB2s_hrU2WuIyRMdaevOx1FC8/s320/Mike+the+Customer.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5687940422737407186" /></a><br /><div><br /></div><div>The other day we got together with Marketing, remodeling magazines, some scissors, and some colorful markers and created our customer. It was a fun way to end the week.</div><div><br /></div><div>The goal of the project was to create who we thought our customer was. </div><div><ul><li>What are his interests?</li><li>What is important to him in business?</li><li>What is important to him in his personal life?</li></ul><div>"Mike" is now apart of the improve<i><b>it!</b></i> 360 team. When we are developing new products or features we will turn to "Mike" to see if it is something he would be interested in.</div></div>Katie M.http://www.blogger.com/profile/06542537507555112313noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-76501488313759323552011-01-21T12:47:00.000-08:002011-01-24T11:24:48.936-08:00Dreamforce for DummiesI realize this is a technical blog, but I have special talents when it comes to reviewing (imagine an analyst being good at writing reviews.... shocker!!). So, here's my recap / review of Dreamforce for any of you fellow techies who are considering attending in the future.<br />
<br />
This year was my first year attending Dreamforce. I'd heard a lot about it from co-workers and acquaintances who had attended in years past. I expected a large conference where, everyone attends sessions during the day, has a few hours of downtime in the evening, then possibly a dinner party or networking event. <b>Reality did not meet my expectations. </b>This was not a bad thing.<br />
<br />
<i>Transportation from the Airport: </i>There are several ways to get from the airport to your hotel (and vice versa). One is to take a shuttle / taxi (I recommend this). The other option is to take the BART. Bart is a rickety (very loud) old train that takes you back into the 1970's through creepy tunnels and bad looking neighborhoods. If your hotel is near market street, it does have a stop that is relatively close. It is not worth a dollar more that the $7.50 it costs to take it one way.<br />
<br />
<i>Hotels:</i> Book early and try to stay around 4th and Market. There are several hotels in this area, and it is in very close proximity to the Moscone center. We stayed at the <a href="http://www.themosser.com/">Mosser</a>. This is a very unique hotel, that offers some standard hotel rooms, and other hostel type rooms with shared restrooms. Economical, not fancy, got the job done. Keep in mind you will literally only be at the hotel to sleep.<br />
<br />
<i>The Conference</i>: While "cult-ish", with uniformed Salesforce people and their Harry Potter scarves swarming the streets and conference buildings with walkie talkies and clip boards, this is to be expected at any conference. Clouds are taken to a new extreme. A generation that has no idea what a bean bag chair is will soon be educated upon attending a Dreamforce conference.<br />
<br />
The keynotes were lots of marketing, but definitely worth attending. There were announcements galore on new features, live demos, and lots of 'people getting excited getting you excited' type stuff. I attended about 3 sessions that I would call <i>great.</i> Otherwise, the sessions were not overly impressive. Some are poorly named and described, so it's not until half way through the session you realize it has nothing to do with your job or function. There are ad hoc sessions that are put together as new features are announced. I <i>highly</i> recommend attending these, as the SME's are doing the presentations and all the information is new. In addition, stay after the sessions and ask questions - you will assuredly get more out of the sessions if you do this.<br />
<br />
Also, there is a huge developer zone, where you can basically take any development questions and ask a Salesforce developer how to solve it. This is hugely beneficial. They also have a plethora of valuable development resources, so pack a light bag. You will be bringing lots of valuable freebies home. Some of them are heavy (force.com fundamentals book, etc). <br />
<br />
<i>Food:</i> If you require more food that a 5 year old child, bring snacks. There were no snacks or beverages available between sessions. I ended up going to a Walgreens and stocking up on Diet Coke, Tea, and Honey Roasted peanuts.<br />
<br />
<i>Nightlife:</i> Be prepared to not sleep. We had partner events galore, and when not attending those, we were meeting our Salesforce reps out. The relationship building and networking aspect of the conference is phenomenal - your biggest benefit from the conference will be to take advantage of this.<br />
<br />
In conclusion, Dreamforce for me was sort of like the technical version of the social event of the year. The most I got out of the conference was from talking to people - speaking with presenters after the sessions, asking questions, networking, and meeting the individuals we do business with from a distance the other 361 days of the year.<br />
<br />
The one thing I will most definitely do differently when attending Dreamforce next year is to wear comfortable shoes.Alyssa Bhttp://www.blogger.com/profile/12642652836450768781noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-39980131535296517922010-11-19T12:09:00.000-08:002010-11-19T12:09:51.969-08:00Troubleshooting Lead & License Creation Issues in the LMA<div class="MsoNormal" style="margin: 0in 0in 10pt;"><span style="font-family: Calibri;">We ran into an issue a few months back where licenses and leads were not being created in our LMA (License Management Application) for new trials. Without licenses, we have no way to activate a trial in the event that a trial user subscribes. We tried several workarounds before finally figuring out the issue - which was frustratingly obvious. </span><br />
<br />
<span style="font-family: Calibri;">First, we attempted reinstalling our package over the trial, in hopes that installing the package would cause the lead and license to be created. Unfortunately, this didn't work. We now know it didn't work becuase it was an issue in our LMA.</span><br />
<br />
<span style="font-family: Calibri;">Next, We attempted manually creating licenses, since we knew the Org IDs of the trials that had no licenses. Unfortunately, Salesforce doesn’t seem to make the association in the background. I think it may have something to do with the Package License ID, which is automatically populated on a license that’s created from creating a trial. Our manually created licenses were missing this value.</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt;"><span style="font-family: Calibri;">Finally, our technical evalgenlist (salesforce support couldn't identify the issue) was able to walk us through a few critical checks in the Org that our LMA was installed in that did eventually lead us to finding the issue and resolving it. I couldn’t find this information anywhere online, hence the reason for this blog post.</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt;"><span style="font-family: Calibri;">So, if you’re having issues with licenses and leads being created in your LMA, try the following:</span></div><div class="MsoListParagraphCxSpFirst" style="margin: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1; text-indent: -0.25in;"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;"><span style="font-family: Calibri;">1.</span><span style="font-family: "Times New Roman";"> </span></span></span><span style="font-family: Calibri;">Make sure your package is registered and listed properly on AppExchange</span></div><ul><li><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 1in; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -0.25in;"><span style="font-family: Calibri;">The package version of your trial you are creating is listed</span></div></li>
<li><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 1in; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -0.25in;"><span style="font-family: Calibri;">In the leads tab on your app exchange listing, the org that contains the LMA is designated under the “Send Leads to” field.</span></div></li>
</ul><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1; text-indent: -0.25in;"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;"><span style="font-family: Calibri;">2.</span><span style="font-family: "Times New Roman";"> </span></span></span><span style="font-family: Calibri;">In your org where your LMA is installed, check the following:</span></div><ul><li><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 1in; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -0.25in;"><span style="font-family: Calibri;">There are not required fields on the Lead object that aren’t being populated. If there are, this could be causing the creation of the lead to fail in the background and prevent the license from being created.</span></div></li>
<li><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 1in; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -0.25in;"><span style="font-family: Calibri;">There are no custom assignment rules executing when the lead is being created that are assigning that lead to an inactive user.</span></div></li>
<li><div class="MsoListParagraphCxSpMiddle" style="margin: 0in 0in 0pt 1in; mso-add-space: auto; mso-list: l0 level2 lfo1; text-indent: -0.25in;"><span style="font-family: Calibri;">The <b style="mso-bidi-font-weight: normal;">Lead Manager</b> on the package that the trial is for is an active user. This turned out to be our issue .</span></div></li>
</ul>Alyssa Bhttp://www.blogger.com/profile/12642652836450768781noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-57539312947489755402010-09-24T07:58:00.000-07:002010-09-24T08:11:42.022-07:00Using the Old Report BuilderThe new report builder is flashy and has increased functionality, but there are times when using the old one is just quicker for some functions.<br /><br />When the new report builder showed up you had the option to use either method to build or edit a report. Then one day my ability to use the old one just went away. Here's what happened:<br /><br />There is a new permission on profiles called 'Report Builder' (under General User Permissions). When this is checked, you can only access the new report builder. To access the old one, you have to uncheck it. Here's the trick: System Administrators automatically have all permissions checked and you can't change that. So if you need access to the old way of doing things, you won't be able to do it with a user who is an admin.<br /><br />Support suggested cloning the admin profile and then unchecking the option, but I have not performed that exercise.JBhttp://www.blogger.com/profile/16040562406356627333noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-85859885680373808972010-09-20T18:46:00.000-07:002010-09-21T06:32:46.677-07:00Storing a Website URL for use in Custom Email Templates<div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">In Salesforce setup, most users are familiar with the “Organization” object, which stores basic organizational information such as address, time zone, and phone numbers. Well it’s my understanding whoever created this object was designing applications for companies stuck in 1980 who don’t have websites. The URL for an organization’s website is a critical piece of organizational information that most companies want to have available on emails and other system templates.</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">Unfortunately, custom fields cannot be added to the organization object, making it impossible to do what would seem most intuitive - create a URL field to store a company website on the Organization object. A workaround we’ve used to circumvent this limitation is to do the following:</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><b><span style="font-family: Calibri;">Step 1: Create a Custom Setting to Store the Company Website URL</span></b></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">Simply create a new custom setting. Keep the default setting type of hierarchy and set the visibility to Public. Once the setting has been created, add a new custom field to the setting. It should be created as type “URL” (it will look something like the image below when you’re done).</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS3cnZ0JW1PlZSiSbRYDTB5IpdcqpJSzh4ihayP5uE_tcMVjegyl_JpbKdMOJHcd2UHlAr71NlLpfXsY5KWqMuUM17ZNOyoBxU5OFknmLhdxVy6MpCkzOyg8zbO4SwBkJLNk-JIylpziw/s1600/CustomSetting.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" qx="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS3cnZ0JW1PlZSiSbRYDTB5IpdcqpJSzh4ihayP5uE_tcMVjegyl_JpbKdMOJHcd2UHlAr71NlLpfXsY5KWqMuUM17ZNOyoBxU5OFknmLhdxVy6MpCkzOyg8zbO4SwBkJLNk-JIylpziw/s400/CustomSetting.png" width="400" /></a></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><br />
</div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">Don’t forget to save an actual website URL in the field once you’ve saved it.</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><b><span style="font-family: Calibri;">Step 2: Add a Custom Field to the User Object to Reference the Custom Setting</span></b></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">Next, simply go to the user object and add a custom field of type “formula”. In the simple formula area, reference the custom setting that was created in step 1. Your reference formula should be in the format of:</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="color: #333333; font-family: "Courier New"; font-size: 9pt; line-height: 115%;">$Setup.<i>CustomSettingAPIName.CustomSettingFieldAPIName</i></span></div><div class="MsoNormal" style="margin: 0in 0in 10pt;"><span style="font-family: Calibri;"> Save the custom field when you’re finished.</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt;"><span style="font-family: Calibri;"> <b>Step 3: Reference the User Website Field in a Template</b></span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"><span style="font-family: Calibri;">Now, referencing the company website URL in an email template is easy! Since each user in the system now has the website stored in a custom field, the website URL can be accessed through any system user. Luckily, a field type called “Sending User” is available for use on any template. You can now place a website URL in a template by using the following merge field:</span></div><div class="MsoNormal" style="margin: 0in 0in 10pt 0.5in;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9Kz5wnWOzK6Cp-VG1frJfymdNwvr57Yduhfxz_M3dnuRg-IR34hTQbgnwVIa2Doy1yn3y4nTP8o7ept-SQx3Cdt6S2mp-VXd6mx5WB2r222Fem9X0l5667yzxx_C18Hdja3sonIZt1w/s1600/MergeField.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="51" qx="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9Kz5wnWOzK6Cp-VG1frJfymdNwvr57Yduhfxz_M3dnuRg-IR34hTQbgnwVIa2Doy1yn3y4nTP8o7ept-SQx3Cdt6S2mp-VXd6mx5WB2r222Fem9X0l5667yzxx_C18Hdja3sonIZt1w/s400/MergeField.png" width="400" /></a></div>Alyssa Bhttp://www.blogger.com/profile/12642652836450768781noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-19259022710406662802010-09-20T08:59:00.001-07:002010-09-21T06:45:06.745-07:00Implementing Quicksort to sort a list of SelectOptions<p>A common task when building Visualforce pages is displaying a dropdown list that is populated by a list of SelectOptions, and making sure the values in the dropdown are ordered alphabetically. The most efficient ways to sort in Force.com are to either ask the SoQL database to do it for you by using <span style="font-family:courier new;">ORDER BY</span>, or by using the List object's .sort() function. Unfortunately <span style="font-family:courier new;">.sort()</span> only supports primitive datatypes, which <a href="http://www.salesforce.com/us/developer/docs/pages/Content/apex_pages_selectoption.htm" target="_blank">SelectOption</a> is not. And if the list didn’t come from a simple SoQL query that you can tweak the <span style="font-family:courier new;">ORDER BY</span> statement on, or if you just want to save a query, then it's time to bite the bullet and sort the list yourself.</p> <p>Sorting a list is one of the most fundamental problems in computer science, and <a href="http://en.wikipedia.org/wiki/Sorting_algorithm" target="_blank">there are many ways to do it</a>. We chose to use one of the most popular algorithms, <a href="http://en.wikipedia.org/wiki/Quicksort" target="_blank">Quicksort</a>, because it doesn’t require a lot of memory, has high performance, and is easy to implement. To us, that means our sorting won't use up too many script statements and we wont' need to spend 4 days writing and testing the sorting code.</p> <p>Below is the implementation of Quicksort that we use to sort out SelectOption lists. Note that this implementation does not do a true in-place sort, so the memory usage is a bit higher than it could be, but it has served our purposes very well.<br /></p><p>You should be able to simply drop this in a class and use it as is!</p> <div class="csharpcode"><pre class="alt"> <span class="rem">// This is a simple quicksort algorithm to sort a SelectOption list (dropdown) </span></pre><pre> <span class="rem">// by label alphabetically.</span></pre><pre class="alt"> global <span class="kwrd">static</span> List<SelectOption> SortOptionList(List<SelectOption> ListToSort)</pre><pre> {</pre><pre class="alt"> <span class="kwrd">if</span>(ListToSort == <span class="kwrd">null</span> || ListToSort.size() <= 1)</pre><pre> <span class="kwrd">return</span> ListToSort;</pre><pre class="alt"> </pre><pre> List<SelectOption> Less = <span class="kwrd">new</span> List<SelectOption>();</pre><pre class="alt"> List<SelectOption> Greater = <span class="kwrd">new</span> List<SelectOption>();</pre><pre> integer pivot = ListToSort.size() / 2;</pre><pre class="alt"> </pre><pre> <span class="rem">// save the pivot and remove it from the list</span></pre><pre class="alt"> SelectOption pivotValue = ListToSort[pivot];</pre><pre> ListToSort.remove(pivot);</pre><pre class="alt"> </pre><pre> <span class="kwrd">for</span>(SelectOption x : ListToSort)</pre><pre class="alt"> {</pre><pre> <span class="kwrd">if</span>(x.getLabel() <= pivotValue.getLabel())</pre><pre class="alt"> Less.add(x);</pre><pre> <span class="kwrd">else</span> <span class="kwrd">if</span>(x.getLabel() > pivotValue.getLabel()) Greater.add(x); </pre><pre class="alt"> }</pre><pre> List<SelectOption> returnList = <span class="kwrd">new</span> List<SelectOption> ();</pre><pre class="alt"> returnList.addAll(SortOptionList(Less));</pre><pre> returnList.add(pivotValue);</pre><pre class="alt"> returnList.addAll(SortOptionList(Greater));</pre><pre> <span class="kwrd">return</span> returnList; </pre><pre class="alt"> } </pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre{ font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em;}.csharpcode .lnum { color: #606060; }</style>Jason Hartfieldhttp://www.blogger.com/profile/01113400037782608591noreply@blogger.com0tag:blogger.com,1999:blog-6184062821225594610.post-19181089512149011632010-09-17T12:50:00.001-07:002010-09-17T13:08:54.896-07:00‘Gotchas’: Using jQueryUI to make popups in Visualforce pages<style type="text/css">.csharpcode, .csharpcode pre{font-size: small;color: black; font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }</style><p>Awhile back, I decided to use <a href="http://jquery.com/" target="_blank">JQuery</a> and <a href="http://jqueryui.com/" target="_blank">JQueryUI</a> to help add some spice to our custom Visualforce pages. One of the features we use quite regularly is <a href="http://jqueryui.com/demos/dialog/" target="_blank">JQueryUI’s ‘Dialog’ widget</a> to display interactive modal dialog boxes. You can view some great examples that cover the basics <a href="http://jqueryui.com/demos/dialog/" target="_blank">here</a> along with the source code that will get you up and using the dialog widget quickly. </p> <p>When we tried to use them in Visualforce the popups <em>basically</em> worked immediately, except that we found several critical ‘gotchas’ that took awhile to figure out. This article focuses on those issues and how we solved them.</p> <h4 style="color: rgb(204, 0, 0);"><span style="font-size:130%;">Gotcha #1: jQuery conflicts with Salesforce’s built in javascript libraries</span></h4> <p>Salesforce uses a competing javascript library known as <a href="http://www.prototypejs.org/" target="_blank">Prototype</a> which is loaded up on all Visualforce pages. Both Prototype and jQuery use the ‘$’ character as a shortcut to their libraries. To avoid this, we redefine jQuery to use ‘j$’ instead. Make sure to always paste the following code into the beginning of any page you will be using jQuery on:</p> <div class="csharpcode"><pre class="alt">var j$ = jQuery.noConflict();</pre></div><p>Note that this means you will always use ‘j$’ to reference the jQuery libraries, rather than just ‘$’.</p><h4 style="color: rgb(204, 0, 0);"><span style="font-size:130%;">Gotcha #2: The ‘:’ character is special in jQuery</span></h4><p>When rendered in html, the ID given to a Visualforce element nearly always uses the ‘:’ character, which is beyond our control. However in jQuery, the ‘:’ is a special character and needs to be escaped. For example, the following lookup that tries to find a page element named ‘<span style="font-family:Courier;">ThePageID:TheFormID</span>’ will not work:</p><br /><div class="csharpcode"><pre class="alt">j$(<span class="str">"#ThePageID:TheFormID"</span>);</pre></div><p>To fix this, escape any colons with two backslashes:</p><br /><div class="csharpcode"><pre class="alt">j$(<span class="str">"#ThePageID\\:TheFormID"</span>);</pre></div><h4 style="color: rgb(204, 0, 0);"><span style="font-size:130%;"><br />Gotcha #3: .dialog() will move your element outside of the form</span></h4><p>This was perhaps the most annoying ‘feature’ of the jQueryUI dialog widget. Whenever you invoke the .dialog() method on a page element, the element gets moved to the bottom of the <body> section, and outside of the form it was in! For example if your HTML looked like this:</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">html</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">body</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">form id="ThePageID:TheFormID"</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">div</span> <span class="attr">id</span><span class="kwrd">="MyPopup"</span><span class="kwrd">></span>Hi!<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span><br /> <span class="kwrd"></</span><span class="html">form</span><span class="kwrd">></span><br /> <span class="kwrd"></</span><span class="html">body</span><span class="kwrd">></span><br /><span class="kwrd"></</span><span class="html">html</span><span class="kwrd">></span></pre><p>And then you ran the following javascript to make ‘MyPopup’ a modal popup:</p><div class="csharpcode"><pre class="alt">j$(<span class="str">"#MyPopup"</span>).dialog({modal: <span class="kwrd">true</span>, autoOpen: <span class="kwrd">false</span> });</pre></div><br /><p>Your source HTML would now look something like this:</p><pre class="csharpcode"><span class="kwrd"><</span><span class="html">html</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">body</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">form id="ThePageID:TheFormID"</span><span class="kwrd">></span><br /> <br /> <span class="kwrd"></</span><span class="html">form</span><span class="kwrd">></span><br /> <span class="kwrd"><</span><span class="html">div</span> <span class="attr">id</span><span class="kwrd">="MyPopup"</span><span class="kwrd">></span>Hi!<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span><br /> <span class="kwrd"></</span><span class="html">body</span><span class="kwrd">></span><br /><span class="kwrd"></</span><span class="html">html</span><span class="kwrd">></span></pre>The popup will still display just fine, but if your popup has any form elements on it (buttons, fields, etc.), they will <em><strong>need</strong></em> to be inside their original form, or else the data in them won’t get posted back to the server. This causes hair pulling when buttons aren’t working and values aren’t getting populated on the server side. <p>To fix this, make sure your <apex:form> tag has an ID specified. Then use jQuery to find it and put the popup back inside the <form> section. So, assuming my <form> tag has an id that renders as ‘<span style="font-family:Courier;">ThePageID:TheFormID</span>’ the code would look like this:</p><div class="csharpcode"><pre class="alt">j$(<span class="str">"#MyPopup"</span>).dialog({modal: <span class="kwrd">true</span>, autoOpen: <span class="kwrd">false</span> }).parent().appendTo(j$(<span class="str">"#ThePageID\\:TheFormID"</span>));</pre></div><br /><p>Note that this also must be done any time you invoke <span style="font-family:Courier;">.dialog()</span>, including when you open or close the dialog using <span style="font-family:Courier;">.dialog(‘open’)</span> or <span style="font-family:Courier;">.dialog(‘close’).</span></p><h4 style="color: rgb(204, 0, 0);"><span style="font-size:130%;">Gotcha #4: Salesforce datepicker displays <em>behind</em> popups</span></h4><p>I beat my head against a wall for too long on this one before figuring out what was going on. You know the little calendar that pops up with any date entry field?</p><p><a href="http://lh5.ggpht.com/_EdjjqRZlAZw/TJPGj2nCBII/AAAAAAAAlq4/qskGKeHL71c/s1600-h/image%5B2%5D.png"><img style="border: 0px none; display: inline;" title="image" alt="image" src="http://lh5.ggpht.com/_EdjjqRZlAZw/TJPGkNu5EyI/AAAAAAAAlrA/7HyMQ_ygWbE/image_thumb.png?imgmax=800" border="0" height="199" width="231" /></a> </p><p>If you put one of these in a jQuery popup, the calendar doesn’t display. The text box is there, and works fine, but the calendar popup is never displayed. As it turns out, it is because the calendar pops up <em>behind</em> the jQuery popup. To fix this, there is a zIndex property you can set when initializing the dialog. After some investigation and a bit of trial and error, I found the magic zIndex number to be 9. So make sure that when you initialize your dialog, you include ‘zIndex: 9’, like so:</p><div class="csharpcode"><pre class="alt">j$(<span class="str">"#MyPopup"</span>).dialog({modal: <span class="kwrd">true</span>, zIndex: 9 });</pre></div><p></p><h4><span style="color: rgb(204, 0, 0);font-size:130%;" >Gotcha #5: Handling validations on popups (Coming soon)</span><br /></h4><p>There is one last gotcha that deals with handling validations on jQuery popups. It is a bit tricky, so I decided it needs it’s own separate post. Check back soon!</p>Jason Hartfieldhttp://www.blogger.com/profile/01113400037782608591noreply@blogger.com7