SalesForce for Domino Dogs 1: Profile Documents

Following on from the Initial Session “Salesforce for Domino Dogs” that Paul Mooney and I did at Engage and a modified version of which that has just been presented at #DNUG I figured that a series of dev articles on how you would do a job in Salesforce that you had always taken from granted in Domino might be a good idea, because:

  1. It would give an honest way of comparing features between the 2 systems shorn of the hype/marketing/platform bashing, that frankly gets on my thungers from both sides.
  2. It will hopefully help people trying to integrate the 2 systems.
  3. As IBM are one of the largest Salesforce consultancies in the world, it is something a champion should do.
  4. The Salesforce community is very short on this kind of thing given its size in comparison to traditional IBM communities and with people like René in it I want to try and help improve it.

These articles are not in any order and are not meant to represent any form of training guide.

So lets get started, first up: Profile Documents!!


In Domino you tend to store config settings for an app in a profile document1 for all your one off settings

To get the same features in Salesforce you use a ‘custom setting’ which does exactly the same job and has one huge advantage over using a normal Salesforce custom object that could do the same job.

(It should be noted that Domino profiles are nothing like Salesforce profiles)

To create a custom setting, go into Setup and search for “Custom Settings”


Click on the “New” button, and fill in some sane details, for normal configs i.e. stuff you would use system wide, select a setting type of “List”, if you want to use them for things like default values in fields and formulas then select “Hierarchy”


Click ‘Save’ and you now have a custom setting object, you can add one or more fields to it just as you would any other object in Salesforce

Added all the fields you want?, lets put in some data. if you go back to the list of custom settings you will see that you now have a “manage” link, click this

Then click “New”


Fill in the fields just like you would on a normal form, if this is a Setting there is only going to be one of, I tend to give it the same title as the name of the object to keep things simple, in this case “General Settings”, if you are going to use it multiple times than give it a name that will make sense in the context of your code


All done. now we can use the setting in our code and see the reason why we would use them vs. a normal custom object.

As you can see from the code below, you don’t have to use a Select statement, which means getting the settings wont count against your apex limits, HORRAY!!!

You just have to create a new instance of the setting object then you can just “getInstance” with the Name of the object you created to get the document back.

General_Settings__c generalSettings = General_Settings__c.getInstance('General Settings');
String thirdPartAppURL = '';
if (null != generalSettings) {
     thirdPartAppURL = generalSettings.Third_Part_App_URL__c;
} 

Simples..

  1. Well you are supposed to, but you never do thanks to the evil caching and the fact the are are a sod to just copy around, so you end up with a config document and a view you store them in.[]

Editable salesforce templates’

Editing an email that is generated via an Saleforce email template BEFORE it is sent is something that I have had a few clients grumble over, the feature is baked into the Quote object which makes sense, but you try telling clients they cant have it for Order objects….

So this is a basic solution that gets round this problem and is the basic set-up for expanding it to solve all the issues, in this case I am solving the most common issue I have come access, in that you are generating a order recept or delivery note pdf using the ‘renderAs=”PDF”‘ option in an email template and want to send the email with a custom message to one or more people that you decide at the time of sending.

To do that we are going to create a new visual force page that resembles a normal email and use it to fill in the details before we generate the email template

 

 

1) Create a new email custom object

This is just a basic object with the fields we need for the email, and a lookup field to the parent Order


Note: For brevity I’m leaving out the creation of the layout for the custom object and the adding of the related list to the Order Layout

2) Add some extra Fields to your Order object

These are the temporary fields that we are using to actually send the template email, resist the urge to set the To/CC/BCC as email fields as that will stop you having multiple recipients.

 

Obviously these fields have to be editable to all but not part of the order layouts so they don’t show up.

3) Create a new “Template Editor” Visualforce Page

This page is as simple or complex as you can want it, my one here takes email address separated by a comma but yours can be a really posh contact lookup

 

<apex:page controller="sendOrderPDFEmail">
    <apex:messages />
    <apex:pageBlock title="Email for Order: {!order.Name}">
    <p>Fill out the fields below and click "Send email"</p>
    <apex:form ><br/><br/>
        <apex:outputLabel value="Template To Attach" for="chooseTemplate"/>: <br/>
        <apex:selectList id="chooseTemplate" value="{!template}" size="1">
            <apex:selectOption itemValue="Template1" itemLabel="Template1"/>
            <apex:selectOption itemValue="Template2" itemLabel="Template2"/>
            <apex:selectOption itemValue="Template3" itemLabel="Template3"/>
        </apex:selectList>
        <br/><br/>  
        <apex:outputLabel value="To" for="To"/>: <br/>
        <apex:inputText value="{!to}" id="To" maxlength="255" style="width: 300px;"/>
        <br/><br/>  
        <apex:outputLabel value="CC" for="CC"/>: <br/>
        <apex:inputText value="{!cc}" id="CC" maxlength="255" style="width: 300px;"/>
        <br/><br/> 
        <apex:outputLabel value="BCC" for="BCC"/>: <br/>
        <apex:inputText value="{!bcc}" id="BCC" maxlength="255" style="width: 300px;"/>
        <br/><br/> 
        <apex:outputLabel value="Subject" for="Subject"/>: <br/>
        <apex:inputText value="{!subject}" id="Subject" maxlength="255" style="width: 500px;"/>
        <br/><br/>
        <apex:outputLabel value="Body" for="Body"/>: <br/>        
        <apex:inputTextarea value="{!body}" id="Body" richText="true" rows="20"/>
        <br/><br/>
        <apex:commandButton value="Send Email" action="{!send}"/>
    </apex:form>
    </apex:pageBlock>
</apex:page>

 

4) Create the “Send” code (don’t forget to create your test code).

I have just put comments on the code, as its not complex and follows the flow chart at the top of the blog.

public class sendOrderPDFEmail {
    public String template { get; set; }
    public String cc { get; set; }
    public String bcc { get; set; }
    public String to { get; set; }
    public String subject { get; set; }
    public String body { get; set; }
    public Account contactLookup { get; set; }
    private final Order order;
    public sendOrderPDFEmail() {
        //Get the Id of the order that we are working on
        template = '';
        String OrderId = ApexPages.currentPage().getParameters().get('id');
        order = [SELECT Name, ID
        FROM Order
        WHERE Id = :OrderId];
    }
    public Order getOrder() {
        return order;
    }
    public PageReference send() {
    if( !String.isBlank(cc)) {
        cc = cc.trim();
    }
    if( !String.isBlank(bcc)) {
        bcc = bcc.trim();
    }
    if( !String.isBlank(to)) {
        to = to.trim();
    }
    //Mail email Order object
    orderemail__c con = new orderemail__c(
        Address_CC__c=cc,
        Address_BCC__c=bcc,
        Address_to__c=to,
        Body__c=body,
        parent_order__c=order.id,
        Subject__c=subject,
        Template__c=template
        );
    insert con;
    //Store the temp values in the Order
    order.le_Address_BCC__c=bcc;
    order.le_Address_CC__c=cc;
    order.le_Address_to__c=to;
    order.le_Body__c=body;
    order.le_Subject__c=subject;
    order.le_Template__c=template;
    update order;
    if ( String.isBlank(to) ) {
         to = 'test@test.com';
     }
     // Construct the list of emails we want to send
     List<Messaging.SingleEmailMessage> lstMsgs = new List<Messaging.SingleEmailMessage>();
     Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
     //set the email tempalte from the name chossen on the email form
     EmailTemplate[] emailTemplate = [select id from EmailTemplate where Name=:template];
     if (emailTemplate.size() > 0) {
          msg.setTemplateId( emailTemplate[0].id );
     } else {
         msg.setPlainTextBody('No Template Provided');
     }
     // Trim and convert the comma delimted string to a suitable recipiant list for the email.
     String[] trimmedtoarray;
     if (!String.isBlank(to)) {
        String[] toarray = to.split(',');
        trimmedtoarray = new String[toarray.size()];
        Integer k = 0;
        for (String singlEmail: toarray) {
           trimmedtoarray[k++] = singlEmail.trim();
        }
        msg.setToAddresses(trimmedtoarray);
     }
     if (!String.isBlank(cc)) {
         String[] ccarray = cc.split(',');
         String[] trimmedccarray = new String[ccarray.size()];
         Integer i = 0;
         for (String singlEmail: ccarray) {
             trimmedccarray[i++] = singlEmail.trim();
         }
         msg.setCcAddresses(trimmedccarray);
     }
     if (!String.isBlank(bcc)) {
         String[] bccarray = bcc.split(',');
         String[] trimmedbccarray = new String[bccarray.size()];
         Integer j = 0;
         for (String singlEmail: bccarray) {
            trimmedbccarray[j++] = singlEmail.trim();
         }
         msg.setBccAddresses(trimmedbccarray);
     }
     // Templates do need an object (Contact that he pdf will be used to generate against) for things like first name etc
     // So we are going to pick the first contact on the to list.
     Contact c = [select id, Email from Contact where email = :trimmedtoarray[0] limit 1];
     msg.setWhatId( order.id );
     msg.setTargetObjectId(c.id);
     lstMsgs.add(msg);
     //Send the email
     Messaging.sendEmail(lstMsgs);
    // clear out the temp fields we used to generate the email.
    order.le_Address_CC__c='';
    order.le_Address_BCC__c='';
    order.le_Address_to__c='';
    order.le_Body__c='';
    order.le_Subject__c='';
    order.le_Template__c='';
    update order;
        //Send the user back to the Order
        PageReference backToQuotePage = new PageReference('/' + order.id);
        backToQuotePage.setRedirect(true);
        return backToQuotePage;
    }
}

##### 5) Create a button in the Order object to launch your “Email” screen

##### 6) Update your Email template
Now update your email templates to use the “le_Body__c” and “le_Subject__c” fields for their email contents,

<messaging:emailTemplate recipientType="Contact"
    relatedToType="Order"
    subject="{!RelatedTo.le_Subject__c}"
    replyTo="orders@XXXX.com">
    <messaging:htmlEmailBody >
        <html>
        <c:EmailStyle />
        <body>
            <apex:outputText value="{!RelatedTo.le_Body__c}" escape="false"/>
        </body>
        </html>
    </messaging:htmlEmailBody>
    <messaging:plainTextEmailBody >
    <apex:outputText value="{!RelatedTo.le_Body__c}" escape="false"/>
    </messaging:plainTextEmailBody>
<messaging:attachment renderAs="PDF" filename="GeneratedEmail.pdf">
      <style>
      @page {
      size: A4 landscape;
      margin-top: 1.0cm;
      margin-left: 1.0cm;
      margin-right: 1.0cm;
      margin-bottom: 7cm;
        @bottom-center {
          content: element(footer);
        }
      }
      </style>
        <html>
        <c:EmailStyle />
        <body>
        //POSH PDF STUFF HERE
        </body>
        </html>
</messaging:attachment>
</messaging:emailTemplate>

Note:

So PDF’s generated by Email templates in Salesforce are not saved and you cant get hold of them, so we are not saving them in the custom email object, if you do need that, then you will need to convert your email templates to visual force pages then render as PDFs and save them in the custom objects.

MKV on Xbox One with Plex and Synology

I use the wonderful Plex media library software on my Synology and normally everything is perfect but recently I have been having some issues playing some of the high level (1080p) MKV on the Xbox One, after a bit of head scratching (it works fine on the browser client), it turned out to be because the Xbox One cant play MKV natively so is trying to transcode on the fly which is a bit much for my Synology’s ARM processor, now Plex has the the newish “Optimise” function, but it takes AGEEEESSSS and then it is still buggy (mine keeps re-doing the optimise whenever the library refreshed and a few other things).

Anyway, long story short, I just need to convert the MKV into MP4 which Xbox One will play without any transcoding simply and in a lossless way (I don’t want to recode everything). to do this I use the ffmpeg library in a one line bash script file, this line converts all the files in the current directory and only takes a few seconds.

for i in *mkv; do ffmpeg -i "$i" -vcodec copy -acodec copy "${i%.mkv} Plex Version.mp4"; done

Now the resultant files work perfectly on the Xbox one and its dead easy to convert large batches of files.
Thats all

Update

As you will all know, MP4 does not hold subtitles and is a bit on the limited side compared to the MKV container, so for the videos I had that were full blown Blueray rips with a bunch of different audio tracks and lot os subtitles, i had to find an alternative, this was supplied by HandBrakeCLI the command line version of Handbrake, the following command just re-renders the MKV to a lower spec that my Synology can cope with, not perfect but a solution I can live with till I can buy a dedicated plex server box

for i in *mkv; do HandBrakeCLI --optimize -i "$i" -o "/${i%.mkv} Plex Version.mkv" -E mp3 --preset="Normal" --subtitle scan,1,2,3,4,5,6,7,8,9,10 -a 1,2,3,4,5,6,7,8,9,10 ; done

Salesforce read mode hide-when hack

It seems the need for hacks has not gone away with the move to cloud, but you do have to be more careful, as I have already found out Salesforce can break your custom code at will.

So that being true one of the things that SalesForce does not seem to have that every other framework does is a simple an powerful “hide when”, IMHO this feature should be present in every line button and object on every page and most platforms do,

It is the most requested feature for customisation that I have come across, and this is the hack that I use for read mode documents or forms (which strangely is where the request is most often made) (and yes it is a hack)

We are going to just stuff some JavaScript into a apex page, you can see an example of it below

<apex:page standardController="Case" showHeader="false" sidebar="false">
<script>
myVar = checkforbutton();
function checkforbutton()
{
    if (typeof(parent.document.getElementsByName("searchArticles")[0]) != "undefined" )
    {
               if ( parent.document.title.indexOf("Customer Community") != -1) {        
                   parent.document.getElementsByName("searchArticles")[0].style.display = "none"; 
               }
    }
    else
    {
            window.setTimeout("checkforbutton();",100);
    }
} 
</script>
</apex:page>

Lets break it down
1. We have to use “parent.document” to get JavaScript objects as when we use this page [SalesForce](http://www.salesforce.com/) will embed it inside a Iframe
2. We have to do a wait loop that keeps looking for the object till it finds it (then stops), because [SalesForce](http://www.salesforce.com/) adds button objects in after the page loads so they wont be there when it first opens

    if (typeof(parent.document.getElementsByName("searchArticles")[0]) != "undefined" )
    {
            //do stuff
    }
    else
    {
            window.setTimeout("checkforbutton();",100);
    }

3. When the button is confirmed to be there then we are going to check the page for a condition(s) and if it’s true/false we are going to hide the button/element on the basis of it, in the above example, if we are on the “Customer Community” version of a page, we don’t want to allow the users to search for articles, so we hide the “searchArticles” button, it’s not a security issue, we just don’t want to confuse the user experience by allowing this feature.
So we put this code into a Page

This insert that page into the layout Object layout we want it to effect.

that would normally give a big white space where the inserted form lives, so we make the page a 1% by 1px block

And that’s it, just save the layout, the button/”what ever” will hide when the page loads, a hack but a simple reliable hack
Now as always “hide when” is not security, remember to make sure that you don’t leave functions exposed and think this kind of thing covers you.