Sunday, September 10, 2017

How To Download All Your Pictures From Smileflingr

My family and I went to Space Camp recently, and there as at other attractions in the area they have professional photographers snapping photos. If you buy the photos, they post them to a site called Smileflingr. This site has a rather unpleasant flaw however: when you go to view your photos, there is no "Download All" button to download all the photos to your computer. The only option given is to download the photos one by one, which is untenable if you have hundreds of photos in there. It will definitely cause you to fling them a frown.

Fortunately, their photo page is written in such a way that it's fairly easy to download all your photos. Here's how.


First, you'll want to be using the Google Chrome browser to do this, and I'll be running the command line commands (you'll see) in a Windows command console although a similar method will work on a Mac terminal.

1. Open your photos page in Smileflingr with whatever code they emailed you.




2. Go to the 3 dots icon at the top right and select More Tools->Developer Tools.

3. In the Developer Tools click on the Network tab at the top. Now go back to your browser window with Smileflingr in it. A bunch of network traces will appear - the page downloads all your pictures up front!


4. Right-click (or two-finger click on a Mac) on one of the network traces and select Copy->Copy All as cURL (use cmd for Windows and bash for Mac).

5. Paste that into a text editor. Delete any line that is not downloading from their Amazon S3 bucket - any line that does not look like this:

curl "http://mm-storage2.s3-website-us-east-1.amazonaws.com/web/2017/07/06/SSUS_2017xxxxx-0500_be624b79-9ec4-40e6-aa6e-666d818c296.jpg" --compressed &

There will be some excess lines at the top and some at the bottom.

6. Now find and replace the word "curl" and replace it with "curl -O" - so now you should have a big file with a bunch of lines that look like this:

curl -O "http://mm-storage2.s3-website-us-east-1.amazonaws.com/web/2017/07/06/SSUS_201707xxxxx-0500_be624b79-9ec4-40e6-aa6e-666d818c296.jpg" --compressed & 

7. Save this file as a batch file (name it like dl_pictures.bat in Windows, or dl_pictures.sh on a Mac). Now double-click the file you just created to run the batch file, and it'll download all of your pictures into whatever directory you put the batch file in.


Voila! You have downloaded all the pictures.

Monday, September 2, 2013

Service Cloud Basics: Web To Case

People often ask me about Web To Case -- how it's used, what it can do, and its limitations.  I thought I'd take a moment today to discuss it.

First, let's start with the basics.

What is Web To Case and how does it work?

Web To Case is a means by which you can post a simple, unauthenticated web page that allows your customers to submit cases directly to your Salesforce.com instance.  This means that you can post a public case submission page on your own website with your own branding and styling.  In a nutshell, Web To Case works by generating for you a snippet of HTML.  This HTML is a good old-fashioned HTML form that you can put on any page.  When your customer presses Submit, the information on this form is posted directly to a Salesforce.com server, which handles the information, converts it to a case, and redirects the customer's browser back to a page of your choosing.

Setting up Web To Case requires a couple of steps; rather than regurgitating them all here, I will refer to the excellent documentation on the subject.

When your customer posts a case via Web To Case, a few fields are generally required, namely the name and the email address.  These values will be stored in the newly created Case in the Web Name and Web Email fields.  If that email address happens to be associated with a Contact in your system, then Web To Case will automatically associate that case with the contact who has that email address, and with the account associated to that contact.  If that email address is not found, or Web To Case discovers more than one contact with that email address, then Web To Case will not know which contact to associate to the case.  In that instance, it will leave the Contact and Account fields on the case blank and allow you to fill them (which you can generally find using those Web Name and Web Email fields).

What is Web To Case useful for?

Let's say that you're doing a lot of support by email.  Emails are an unstructured medium, so even if you're automating the process with Email To Case , someone generally still has to look at the cases to classify them -- it's not easy to perform workflow on plain text.  Web To Case allows your customers to submit the information directly into the individual fields on your case, so you can perform a great deal of automation, like workflow, assignment, and auto-response rules, immediately.  That can save you a lot of man hours.

Meanwhile, Web To Case is a very quick way to put up a web form to accept cases.  You can generally have a quick-and-dirty page up in minutes, and because it's standard HTML, it's easy to embed into an existing page.

What are Web To Case's limitations?

First, Web To Case is limited to receiving 500 cases per day.  This is to keep things like spambots from beating up your org (and beating up Salesforce.com's servers).  Speaking of spam, Web To Case does not perform any spam filtering.  You could use workflow to filter out cases that appear to be spam, but Web To Case itself does not apply any particular intelligence -- it just takes the cases.

Web To Case cannot handle attachments. 


This is a side effect of the simplicity of its setup, and of the simple transactional nature of HTTP.  Taking attachments via the web is a remarkably complicated process.  For instance, try attaching a document in Gmail.  You'll notice that you first have to browse to a file; it posts that file immediately to the server.  In the background, it associates your in-progress email with the file it just posted, so that when you finally go to send the email, it knows which attachments go with which email.  So there are two posts involved here, plus a fair bit of backend logic.  Web To Case, on the other hand, is a simple form with a single post that you can embed anywhere.  Attachments don't play nice with that.

Out of the box, if Web To Case does not find a contact to associate a case to, it will not autocreate one.  However, a previous blog post discusses how, with a simple Apex trigger, you can set your org up to do so.

Finally, Web To Case is solely a tool for the initial submission of cases.  If your customers need to be able to track their cases, or add information to them after the initial submission, then you'll want to look into the Self Service Portal or the Customer Portal -- those are authenticated products that allow your customers to file cases, but also to follow up on them, browse the knowledge base, and in the case of the Customer Portal, lots of other things as well.

How can I work around the volume and attachment limitations?

If you need to take more than 500 cases per day, or you want to be able to take attachments, you can now do so using Salesforce.com's facility for unauthenticated access called Force.com Sites.  There's a quick start you can follow on how to create a custom Web To Case form using Sites.

Monday, August 26, 2013

Service Cloud Basics: Auto-Response Rules

Every now and then I like to use this blog to take our readers back to basics and visit parts of the Salesforce.com application that they may have overlooked.  Today's topic is none other than good old Auto-Response Rules.

Auto-Response Rules are a type of workflow that are available on Case and Lead.  Broadly speaking, their function is to generate an email automatically in response to an incoming contact from a customer.  They are often used in conjunction with Web To Case, Email To Case, and Web To Lead. 

The great thing about auto-response rules is that they can automate that first response to the customer and provide that customer with a branded, professional-looking email that makes that customer feel that he has been heard. There can sometimes be a bit of confusion around when to use auto-response rules and when to use workflow email alerts.  It can be fairly said that you can accomplish some of the same things with both.  Auto-response rules have workflow-esque criteria, and like an email alert, they generate an email based on an email template.  In some ways, workflow email alerts are more powerful in that they allow you to choose who to send the email to from a list of people who are related to the object, whereas auto-response rules only email the Contact on the case or the email address of the Lead.

Indeed, they are much alike, but auto-response rules are specially tailored for the use case of the first response to a customer contact, and they do have two very powerful features to that end.  First of all, auto-response rules are processed sequentially, whereas workflow rules are processed in parallel.  What does this mean?

Let's say I have two levels of support, Premier and Basic, and two products, Product A and Product B.  If a Premier Support customer creates a case, I want to email him back with my Premier Support template, from my Premier Support email address.  If the customer is Basic support, however, I want to send him an email from Basic Support using a template that differs by product.

If I were to try to model this using standard workflow rules, I would end up with at least 3 different rules, and with a convoluted set of criteria to ensure that I don't end up sending the email multiple times.  Auto-response emails are different, though -- because they're processed sequentially, they are guaranteed to send at most one email.  An auto-response rule will try each rule entry in sequence until it hits some criteria that matches, and it will send the email from that rule entry and stop processing the remainder of the rules. Here's how my auto-response rule might look in this example:





While it can seem at times that workflow email alerts and auto-response rules are identical, use cases like this one illustrate the differences.  Happy emailing!

Monday, August 19, 2013

The Contention-Proof Case Accept Button

The case accept button is a handy thing.  It shows up on queue views to allow members of that queue to mass-accept cases (actually, in the Agent Console it shows up in the Mass Action dropdown rather than as a button, but it works the same). If you have a large queue, though, with lots of agents accepting cases at the same time, they may sometimes step on each others' toes.  If two agents accept the same cases at almost the same time, then the last one will win, and the first agent will not actually own the cases he thinks he just accepted.

Here's some custom button code I wrote which addresses this issue.  It only accepts cases that are still assigned to a queue; if the cases have been accepted by a user already, those cases will remain owned by the users who accepted them. 


To add this code, go to Setup->Cases->Buttons and Links and make a new custom button called Accept Cases (or whatever label you'd like to use for this).  Its Display Type should be set to List Button, its Behavior to Execute JavaScript, and its Content Source to OnClick JavaScript.  Paste the code from this link into the OnClick JavaScript field.  

Now you'll have to add this button to the Case list view.  To do this, go to Setup->Cases->Search Layouts.  Click Edit next to the Cases List View entry, and add your new button from the Available Buttons section to the Selected Buttons section.

So how did I do this? Quite easily really.  Perhaps the most important part is the line that invokes {!GETRECORDIDS($ObjectType.Case)}.  This little-known merge field allows you to get a list of all the IDs that are selected in a list view or related list -- really handy when you're trying to make buttons that act upon a large number of records.  Once I have the IDs of the selected records, I run them through the also little-known retrieve function to get more information about them.  The key to that retrieve call is getting Owner.Type -- that will return a string for each case containing either "Queue" or "User" depending on what the type of each case's owner is.  A good lesson here is that GETRECORDIDS plus retrieve makes for a quick and powerful combination.

Now I divide the cases into two piles: the "accept" pile for cases that are currently owned by a queue that we're going to allow this user to accept, and the "reject" pile of cases that have already been accepted by other users.  If there are any cases in the "accept" pile I call update on it to update those cases, and then I give some feedback to the user of the changes that were made (and the changes that were rejected). And there we have it: The Contention-Proof Case Accept Button.  Note that with just a couple of minor changes you can adapt this same code for use with leads and custom objects -- any object that is ownable by a queue.

Monday, August 12, 2013

How To Turn Off The Interaction Log In The Service Cloud Console

Today I needed to turn off the Interaction Log in the Service Cloud Console.  It was remarkably hard to figure out how to do so!  As such I thought I'd document it here.

Interaction Logs are enabled on a per-page layout basis.  As such, you'll need to edit each page layout to turn them off.  To do so, edit the page layout of a primary tab object (in my example I'm using Contact).  You'll find a button called Layout Properties at the top.


Click that button and you'll find a place where you can turn off the Interaction Log.





Press OK.  Don't forget to press Save on the page layout!  

Now go back to the Console.  If you already had a contact open (or whatever object you were editing the page layout of), you may find that it still has the Interaction Log on it.  Don't be alarmed -- just close that tab and reopen it.  Any new object of that type will be rendered without the Interaction Log.

Bonus for advanced users:

If you've got lots and lots of page layouts and you don't want to go through all of them turning off the Interaction Log for every one, use the Force.com IDE instead.  Set it to download all the relevant page layouts.  Then do a find-and-replace across all files for:

<showInteractionLogPanel>true</showInteractionLogPanel>

And change it to:

<showInteractionLogPanel>false</showInteractionLogPanel>

Now save them all, and voila, the Interaction Log is off in all your layouts.






Monday, August 5, 2013

Reporting On Percentage Of Cases By Origin

The original version of this blog post comes courtesy of guest blogger and all-around Salesforce.com guru Jay Thayer (I've since modified it to apply to the new Report Builder).

Let's say you're trying to build a common report, Cases By Origin, but with a twist -- you want to know what percent of your interactions are coming via each channel.  Well, you could build a dashboard with it and eyeball it, but that's not very satisfying -- sometimes you just want cold, hard numbers.  The good news is that there is in fact a way to do it using some little-known capabilities of custom summary fields.

First, start by making yourself a standard Cases By Origin report by creating a custom report.  Search for the report type simply called Cases -- we don't need any related objects for this report.  Next make it a Summary Report by choosing that as the Format:



Now drag the Case Origin field onto the "Drop a field here to select a grouping" header.  This will group your cases by origin, so we're nearly there.


On the top of the left hand side you'll notice an Add Formula icon.  Drag that right on top of your Case Origin grouping (in the blue area).

This type of field will allow us to do math using both a summary of the group items and a summary of the whole report -- that's exactly what we need to calculate our percentages.  First, at the top of the page, give the formula field a name, and set its Format to Percent.  Now set it to display at Grouping 1: Case Origin, because that's the granularity at which we want this calculation to occur.


Now let's go to the formula builder and build it up. In order to calculate the percentage for each group, we need the record count for that group, and then we have to divide that by the total number of records in the report.  To get the record count for the group, we use the handy-dandy Summary Fields > dropdown, where you'll find that the top item is called Record Count.  That inserts an item that says "RowCount." That was easy enough.

Now we have to divide that value by the record count of the entire report.  For that, we can use one of the Custom Summary Functions -- to view them all, click the Functions dropdown.  Here we see a function called PARENTGROUPVAL.  This is exactly what we need, because in this case the parent group is the entire report.  Our only grouping summary is Grand Summary, which is fine because that's what we want. 


Double-clicking PARENTGROUPVAL adds the following to our report: 

PARENTGROUPVAL(summary_field, GRAND_SUMMARY).  

Now we just have to select the summary field.  Select that "summary_field" part of the above, and replace it with RowCount, so it says: 

PARENTGROUPVAL(RowCount, GRAND_SUMMARY) 

So your final formula should read: 

RowCount/PARENTGROUPVAL(RowCount, GRAND_SUMMARY) 

That will divide the rows in the current group by the rows in the entire report.  The completed formula field should look like this:

Save your custom summary formula field by pressing OK, and proceed with the report wizard as usual, selecting and ordering your columns and entering criteria.  When you run the report, you'll see that it's grouped by Case Origin, and each group is tagged with the percentage of total cases that came from that origin: Eureka!



Monday, July 29, 2013

Allowing Customer Portal Users To Edit Their Own Contact Information

Customer Portal users have the ability to edit some information about themselves via the My Profile page.  That page allows them to edit their own User record, but not their own Contact or Account record.  However, the User record does have some fields in common with Contact and Account, and with a quick bit of Apex, you can make it such that when the portal user updates his information, that gets reflected in his Contact or Account record as well.

To allow portal users to see the My Profile link, go to Setup->Home->Home Page Layouts.  Make a separate home page layout for your Customer Portal (if youhaven't already done so) and add the Customer Portal Welcome componentto the top of it.  That will render a little area in the sidebar with a My Profile link (which will allow them to edit their user information) and a Logout link.

Now to synchronize that User information with the Contact information.  The example given here shows that synchronization just with the Contact, but it can easily be modified to apply to an Account or a Person Account.  While the code itself is simple, it's not as straightforward as it seems.  As this page of the Apex docs indicates, you can't just make a User trigger that updates a Contact or an Account, because it's forbidden to modify those "non-setup" objects from a "setup" object like User.  Fortunately there is a simple solution: the @future annotation .  The @future annotation allows you to create Apex that runs asynchronously at some point in the future (in my tests I've found that it runs immediately, or at least very soon after the trigger executes).  Methods that use @future are subject to different limits than normal trigger operations because @future methods don't hold up the trigger (and therefore the entire user experience) while they're working.  Therefore @future methods are often used to perform long-running web service callouts from triggers asynchronously.  However, they can also be handy for a case like ours, where we want to update an object that we're not normally eligible to update. Here I'll show some snippets of code that allow me to accomplish this synchronization.


You can download the full trigger and class including test methods at the Developing with Service Cloud page here.

Calling a method that has been marked as @future is just like calling any other static method.  Here's my trigger, short and sweet:

trigger UpdateContactFromPortalUser on User (after update) {
    //We only want to run on the single item that the user edited
    if (Trigger.new.size()==1)
    {
        User u =_ Trigger.new[0];
        //And only if it's a portal user
        if (u.ContactId!=null) {
            UpdateContactFromPortalUser.updateContacts(u.Id);
        }
    }
}


My @future method is named UpdateContactFromPortalUser.updateContacts.  Let's see how that works. 

In the example given here, I just update a couple of fields from the user record: the name, the email address, and the title.

global class UpdateContactFromPortalUser {
    @future public static void updateContacts(String userId) {
        User u = [select ContactId,Email,FirstName,LastName,Title
                    from User
                    where Id=:userId];

        if (u!=null && u.ContactId!=null) {
            Contact c = new Contact (Id=u.ContactId,Email=u.Email,FirstName=u.FirstName,LastName=u.LastName,Title=u.Title);
            update c;
        }
    }
   
    ...
}


(note that the above is just a snippet -- download the whole class here)

By putting the @future above my method, I'm telling the system that when I call this method, it should be run asynchronously.  This allows me to bypass those pesky trigger restrictions and update my contact.  Note that the @future method does impose some constraints: the method has to be static, return void, and cannot take sObjects as parameters (which is why I passed the user ID in instead of the User sObject itself).

That's all there is to it!