Showing posts with label visualforce. Show all posts
Showing posts with label visualforce. Show all posts

Friday, March 20, 2015

Visualforce Email Templates

Hello, dear readers.  I have been busy and haven't updated this blog in a while but I came across a couple unusual behaviors that are not well documented.  In fact, one of the behaviors led to my support rep getting a knowledge article published.  The topic for today is visualforce email templates and some things to think about if you are using them.  Trust me, none of this is documented and even though Salesforce says "working as designed", I think you may want to look at your solution carefully.

Imagine Joe Q. Salesperson lands a sweet client one Friday morning, creates an opportunity, generates a quote and sends that quote to his boss for approval.  Joe spends the weekend relaxing the afternoon away and the next day still hasn't gotten approval from his boss.  Joe calls the boss and finds out he never got the email alert that something required his approval.  Well, that's strange because Joe never saw an error and the quote was submitted for approval (he can see it in Salesforce). Furthermore, after his boss approved the email, the status in the email alert still said "Pending".  What was going on?  Joe calls IT and is severely irritated because he couldn't close the deal before the weekend and was seeing data in the email that didn't match what was in Salesforce.

So what is it with VF email templates?

Email Alerts with Visualforce Templates Do Not Always Send (and will not throw error)
Let that sink in for a minute - your trusty email alerts, even when the rule or approval process triggers the alert do not always send the email.  And when they don't send, there isn't an error generated.  Not even in the logs.

According to Salesforce, this is working as designed because Joe's administrator put a field on the VF email template that Joe did not have read-access to.  Support was kind enough to generate some documentation of this behavior for us, after the fact.  It should be noted that if your VF template uses a controller, your users will see an error if you don't give them access to the controller.  It seems to me that the expected behavior here is that field level security should be applied.  If the user has access to the field, display it, otherwise, hide it.  Unfortunately that is not the case.


Email Alerts with Visualforce Templates Do Not Always Get the Latest Field Values
If you have an approval process with an action that performs a field update and sends an email alert, the email will not have the field update value if the template is a VF template.  But, wait Salesforce's documentation says

  • Field updates occur before email alerts, tasks, and outbound messages

It is the first bullet in the Field Update considerations.  Except, it doesn't apply when it comes to VF templates.  Again, support says it is working as designed.  If you have a text or html template, the value from the field update is represented in the email, however, this is not the case with a VF template.

Wednesday, February 12, 2014

Preventing Your Form from Submitting Twice (or more!)

If you have a visualforce page that does any kind of update or insert and you're using a custom method on your controller, be mindful of your aggressive clickers!  They'll get you in trouble by submitting your form more than once, wreaking havoc on all of your hard work.

I just dealt with this and was surprised that I was not able to use the apex:actionstatus and facet combination that I typically use to disable buttons that initiate queries.  Turns out that the important detail is that the actionstatus tag requires us to rerender a component.  When this rerendering occurs, you potentially get another form submission.  It may have to do with the redirect my page was doing after the save.  The good folks over at the stack exchange saved my bacon once again.  Their approach to disable your button is the following:

Button click calls an actionfunction js.  The actionfunction does some javascript to disable the buttons using jquery, then calls the actionfunction component, which describes the controller method to run and what to do when the method completes.

I changed up the accepted stack exchange solution in two ways:

1. The button, while gray and disabled, still behaved like a button (it was still clickable).  I ended up using jquery to remove the btn class (which makes it clickable) and also restore the btn class, after any page messages/errors are displayed:

function buttonsEnabled(enabled) {
        // to disable the button
        if (enabled === false) {
            var $b = jQuery('.btn');
            $b.removeClass('btn');
            $b.addClass('btnDisabled');
            $b.toggleClass('btnDisabled', true).attr('disabled', 'disabled');
        } else {
            var $b = jQuery('.btnDisabled');
            $b.toggleClass('btnDisabled', false).attr('disabled', null);
            $b.removeClass('btnDisabled');
            $b.addClass('btn');
            
        } 
    }

2. If you have some validations on your page, you'll want to be sure you're re-rendering the messages tag in your action function.  In this example, my pagemessages has the id "msgs".

<apex:form id="form">
<apex:actionFunction name="doSomeWorkActionFunction" 
        action="{!mySave}" 
        oncomplete="buttonsEnabled(true);"
        rerender="msgs">
</apex:actionFunction>
<apex:pagemessages escape="false" id="msgs"/>
<apex:pageblock mode="Detail" id="pageblock" >
    <apex:pageblockButtons >
        <apex:commandButton id="mysavebutton" 
                  onclick="return doSomeWork();" value="Save"  />
        <apex:commandButton action="{!myCancel}" value="Cancel" immediate="true" />
    </apex:pageblockButtons>

Friday, October 18, 2013

Draggable and Resizable Modal Popup

One common visualforce solution that I've built for several clients is a modal popup.  At it's core, it is nothing but a hidden outputpanel that is dynamically rendered.  With some styling, you can display the panel "above" your current page, with the current page grayed out.  There are hundreds of blog posts out there covering how to do this: here's one, another, yet another.

These blog posts are incredibly helpful and have inspired me to pass it forward and share the incremental bits that I can.  One thing that I've wanted to do that I just got working in my sandbox was to make these modal popups draggable and/or resizable.  As I've come to learn, jquery makes this ridiculously easy.

If you look at the source code for the draggable example on jquery's site, you'll see that it's 3 parts:
  1. The references to the jquery library
  2. The jquery function
  3. The div that you want to make draggable
Applying this to Salesforce, there are the same 3 parts:

1. A reference to the jquery libraries.  You should probably use static resources, but if you're just testing it out, something like this needs to be on your page:

<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/>
<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"/>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" type="text/css" media="all" />

2. Add the jquery script and don't forget the noConflict() requirement.  In my example, I have a div called "pop" that I want to make draggable and resizable.

<script type="text/javascript">
    $j = jQuery.noConflict();
    $j(function() {
    $j( "[id$='pop']" ).draggable().resizable();
});
</script>

3. The div/outputpanel becomes something like this:

<apex:outputPanel id="popupBackground" styleClass="popupBackground" layout="block" rendered="{!displayPopUp}"/>
        <apex:outputPanel id="custPopup"  layout="block" rendered="{!displayPopUp}" >
        <div id="pop" Class="custPopup">
        <!-- your form/pageblock/fields/tables go here-->


Part 3 depends on your styling but it should be pretty easy to apply to your modal popup.  If all goes well you should be able to move your panel around and resize it.  Enjoy!

Thursday, October 3, 2013

Salesforce Quotes


There is no doubt that the Salesforce Quote functionality is useful.  But could this object be any more specialized?  We've run into so many issues with customizations.  Here's a list of some issues or limitations I've recently come across:

1. You cannot override the standard Quote view with a custom visualforce page.  The option to override View is not available.
  • I suspect that because the object is so specialized (oppty sync, PDF creation, etc) that this is not likely to change.  If you need custom quote functionality, you may need to build your own from the object up.

2. The standard Discount field is not available as a merge field in Email templates.
  • As a workaround, you can create a formula field that references the out-of-box field and then use the formula field in your email template.

3. You cannot roll up list price from the line item.
  • Apparently, list price is a reference to the PricebookEntry object and so it is not a field that can be summarized.  To proceed, you need to create another currency field and populate it with a workflow on the QLI based on your business requirements.

4. You can only control edit access to Sales Price from a Profile-level parameter.
  • It's not so bad to have to use this parameter, since you can also put it in a permission set, but it's pretty inconsistent from the way the rest of the profile/page layouts behave.

5. If your record is locked as the outcome an approval process, you will not be able to save your pdf to the quote using the Create PDF button.
  • Speaking of inconsistent... on all other objects that are locked, you can almost always add an attachment.  To get around this, I changed our approvals to unlock the record, then used validation rules to keep the QLI from being changed.


Tuesday, August 13, 2013

Required InputTextArea

I was asked to make an inputtextarea required on a visualforce page and thought, "ok, no problem, 2 minutes!".  In reality, this was kind of a nightmare to implement.  My vf page looked something like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield"}"
...

So the first issue is that the iconic redline next to the required field does not display for text areas.  To fix this, you have to wrap the tag with an outputpanel like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:outputPanel styleClass="requiredInput" layout="block">
<apex:outputPanel styleClass="requiredBlock" layout="block"/>
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield"}"
</apex:outputPanel>
....


When unit testing, the error that is displayed is something like:

pageBlock:pageBlockSec:j_id38:bigfield: Validation Error: Value is required.

This is not a message a user would understand, even with the text area marked with a red line.  Some of the initial searches turned up crazy solutions like using jquery to clean up the message, or rewriting the validation to occur within the controller.  So, it took a while but the solution was buried in this thread.

To remove the garbage text in the error, you have to provide the textarea a label attribute.  Your final markup will look something like this:

<apex:pageblock id="pageBlock">
<apex: pagblocksection id="pageBlockSec">
<apex:pageblocksectionitem>
<apex:outputlabel value="Big Field">
<apex:outputPanel styleClass="requiredInput" layout="block">
<apex:outputPanel styleClass="requiredBlock" layout="block"/>
<apex:inputtextarea value="{!myObject__c.Big_Field__c}" required = true id="bigfield" label = "Big Field"}"
</apex:outputPanel>
....

Thursday, August 1, 2013

Visualforce Page with ContentType in MultiByte Language

I'm working on a project where there are several multibyte languages, like Chinese and Arabic, that are supported.  One issue that we discovered in a new feature is related to exporting some content as a Word file.  The issue was in specifying the filename.  As you probably know, the syntax for something like this is:


<apex:page Controller="yourController" contenttype="application/msword#yourfilename" .. />

This works great but if you are substituting yourfilename with something from the controller, one symptom you could see is the file generated with the name of your VF page.  So, let's say you were doing something like this:

<apex:page Controller="yourController" contenttype="application/msword#{!someVar}" .. />

If {!someVar} is a value that is in Chinese or Arabic, your file name will probably look like "YourVFPage.doc" instead of "{!someVar}.doc".

There wasn't much in the Salesforce support community so I've come up with a workaround:

1. I added a charset identifier to the contenttype attribute like so:

<apex:page Controller="yourController" contenttype="application/msword#{!someVar};charset=utf-8" .. />

When you try to view your file, you get a little closer - your filename will likely look like: "------.doc".

2. The following thread, gave me the idea for the fix for ---- characters.  In the controller, we just encode the someVar value like so:

  String someVar = EncodingUtil.urlEncode(myString, 'UTF-8'); 
  return someVar;

When you try to view your file, you'll see the multibyte value in the filename.  You may have to do some substitution to remove any other characters, but this should get you closer to a user acceptable solution.

Tuesday, June 18, 2013

MultiSelect and JQuery/Javascript

I had a requirement to give users a way to quickly select a couple of values in a multi-select picklist based on some other value they had previously selected on the form.  In our case, if the selected Language was X, then pick Region 1, Region 3, and Region 4 from the multi-select picklist automatically.

This one took a while to piece together so hopefully this will help someone out.

On my VF page, I created an anchor tag to act like a "Command Link":


  <a href="javascript:void(0);" id="selectAllRegions">[Select All Regions for Language]</a></span>

Using some jquery, I catch the click as follows:

  j('#selectAllRegions').click(function () {
        selectRegions('{!$Component.regionsMS}');

        });

The regionsMS id is the id of your multiselect apex:inputfield.

The "selectRegions" function does the following:

function selectRegions(objId){
    var multiSelect;
    var unSelectedId = objId + "_unselected";
    var selectedId = objId + "_selected";
   
    multiSelect = document.getElementById(unSelectedId);
        for (i = 0; i < multiSelect.options.length; i++) {
            if(multiSelect.options[i].text == "Region 1"){
            multiSelect.options[i].selected = true;}
        }

        javascript:MultiSelectPicklist.handleMSPSelect(objId);
}

The key to making this work was this last function, which came up in a search here (there's some other NSFW stuff there, just fyi).  If you use firebug or chrome's developer tools, you'll see how the script interacts with the elements that make up the multiselect control.

And there you have it - when you click the link "Select All Regions for Language", Region 1 is selected.  All that needs to be done now, is evaluate the selected language and then change which region values are selected.

Friday, June 7, 2013

Rich Text Editors

On a recent project, I was asked to implement a visualforce page with rich text editing capability.  As I've come to learn, when you put an <apex:inputTextArea> tag inside a visualforce page, you don't get a rich text editor.  You get a text area.  No toolbars for formatting, unlike the standard layouts.  The standard layout editor appears to be ckeditor but if you're using visualforce, you have to put in the rich text editor yourself.  Fortunately, there are lots of editors out there.  I had no idea.

Based on the project needs and features, I ended up implementing the following for user evaluation:

  1. TinyMCE
  2. CKEditor
  3. NicEdit
  4. Redactor

TinyMCE ended up being the selected editor, mostly because of the ICE plugin.  I use strikingly to power my website and strikingly uses TinyMCE as it's editor.  It's fairly straightforward to implement:


  1. Download TinyMCE and upload the zip it as a static resource
    1. If you're using ICE, you'll need to include it in your plugin directory, rezip, and upload
  2. Update you visualforce page as follows
<apex:includeScript value="{!URLFOR($Resource.tinymce, 'tinymce/jscripts/tiny_mce/tiny_mce.js')}"/> 
...

<apex:inputTextArea value="{!TEST__c.Some_Field__c}" id="somefield" style="width:100%;" styleclass="mceEditor"/>
<!--this is the initialization required for TINYMCE  -->
                <script type="text/javascript">
                   tinymce.init({
                            mode : "textareas",
                            editor_selector :"mceEditor",
                            theme : "advanced",
                            plugins : "ice,icesearchreplace,spellchecker,pagebreak,style,layer,table,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,visualchars,wordcount",
                            theme_advanced_buttons1: 'ice_togglechanges,ice_toggleshowchanges,iceacceptall,icerejectall,iceaccept,icereject,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect',
                            theme_advanced_buttons2: 'spellchecker,cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,forecolor,backcolor',
                            theme_advanced_buttons3: 'tablecontrols,wordcount',
                            theme_advanced_buttons4: "",
                            theme_advanced_toolbar_location: "top",
                            theme_advanced_toolbar_align: "left",
                            theme_advanced_toolbar_location : "top",
                            theme_advanced_statusbar_location : "bottom",
                            theme_advanced_resizing : true,
                            ice: {
                                          user: { name: '{!$User.Alias}', id: '{!$User.Alias}'},
                                          preserveOnPaste: 'p,a[href],i,em,strong'
                                },
                            width: "100%",
                            height: "200"      
                        });
                </script>

I highlighted a couple sections that are noteworthy:
  • You need to mention that you're using TinyMCE so you'll need the <apex: includeScript> tag
  • You can have multiple TextArea fields on your page and selectively enable TinyMCE using the editor_selector property when you initialize the editor.  Just set the styleClass property on your text area fields that you want to override with TinyMCE.
  • If you're using ICE, the modification here allows you to capture the user who edits the text.
And if all goes well, you should have something like this:

Tuesday, June 19, 2012