nonprofitCRM.org is produced by members of the NPSF (nonprofit salesforce.com) community. We are Salesforce.com administrators and consultants working to help nonprofits understand, better use and leverage Salesforce.com for their organizations. Read More
In conversation with a staff person at a local Boston-based nonprofit, we were talking about wanting to hire a consultant. Her initial reaction was strongly negative, based on a past experience she’d had. But when we started talking things over, it became clear that hiring a consultant might make her life considerably easier, and finally move forward a long-standing under-resourced project (their Salesforce.com implementation) forward. So here are a few reasons why it could make sense to hire someone:
I hope that helps people think about the different roles a consultant might play if you decide to hire them. As a reminder, the list of Nonprofit Salesforce Consultants is on the Foundation’s website. You can also find consultants on the redesigned AppExchange, but I don’t think that list is vetted by the Foundation. In a future post, I’ll talk about the process of finding and vetting a consultant.
I posted recently about Batch Apex, which I used to solve an interesting problem for data summary across lots of records. That code was working great before Winter 10 was released – unfortunately, Batch Apex is no longer working properly since the update. See this discussion for information on the problems people are encountering. The basic issue is that if your query is complex or returns a large number of records (the whole point of batch apex), it fails. I some cases you get an error, in others the query simply returns no records and reports that it was successful at running 0 batches.
If you were thinking of using Batch Apex in your own code, you will want to wait until they have a chance to iron out the bugs. I have submitted a case to support, and will post here when I learn more.
UPDATE 10/30: A release came out this week that appears to have fixed the issue of large queries. I haven’t had a chance to test all our use cases, but it looks promising.
Most of you have probably started receiving email notifications about your Winter 10 upgrade. I browsed through the release notes and highlighted a few of the many new features which will be rolled out in this release.
This is by no means a comprehensive list. I would strongly encourage you to review the full release notes for more details on any of the above items as well as the comprehensive list of Winter 10 release features.
Does your organization maintain a group calendar of staff events, trainings, room rentals, or any other set of dates that are also stored somewhere in Salesforce? Do you wish there were a way to display them on a calendar?
One option would be to use workflow to create an event on every user’s calendar whenever an object is saved. But that creates noise for users, and isn’t nearly as intuitive as having a separate calendar for a given purpose. What you really want is a group calendar available to everyone that displays only events pertaining to one thing.
The use case
My client, a theater that sells blocks of tickets to its shows using Salesforce, needed a place to track the status of all their bookings on a calendar. To meet their requirement, I created a public calendar in their account. (See my general post on group/public calendars on the NPower Seattle blog for how to do this.)
Getting the events on the calendar
My first thought was to use workflow to put events on that new calendar whenever an opp was moved to the right stage. It turns out you can’t do that – although a public calendar can be the “owner” of an event, you can’t select a public calendar as the user for a workflow task or event. Besides, the client needed not only to put this event on the calendar in the first place, but to update it automatically anytime the stage of the opportunity changes.
To maintain the events on the calendar, I created the following trigger:
trigger oppChangeAfter on Opportunity (after delete, after insert, after undelete, after update) { // trigger to manage events on the shared theater Show Calendar // written by Evan Callahan, copyright (c) 2009 NPower Seattle // released under the GNU General Public License, http://www.gnu.org/licenses/gpl.html // need to know the salesforce ID of our public calendar final id publicCalendarId = '02380000000YHci'; // this flag allows us to bypass all queries if the opp is not the type that goes on the calendar boolean createEvents = false; // get acct names to put them in the events we create set oppAccts = new set(); map acctMap; if (!trigger.isDelete) { set acctIds = new set(); for (opportunity o : trigger.new) { if (o.stageName != 'Quote' && o.stageName != 'Closed Lost' && o.show_date__c != null && o.show_date__c >= system.today().addMonths(-1) && o.theater_city__c != null) { createEvents = true; oppAccts.add(o.accountId); } } if (createEvents) acctMap = new map([select id, name from account where id in : oppAccts]); } // get opp ids in this trigger set set oppIds = (trigger.isDelete ? trigger.oldmap : trigger.newmap).keyset(); // get contacts for these opps so we can attach them to the events map oppCons = new map(); if (createEvents) { for (opportunityContactRole ocr : [select opportunityid, contactid from opportunityContactRole where opportunityId in : oppIds and isPrimary = true]) { oppCons.put(ocr.opportunityId, ocr.contactId); } } // get existing events for these opps - need to update them map tzShowEventMap = new map(); for (event e : [Select id, e.WhoId, e.WhatId, e.Subject, e.OwnerId, e.IsReminderSet, e.IsAllDayEvent, e.ActivityDate From Event e where whatId in : oppIds and ownerId =: publicCalendarId ]) { tzShowEventMap.put(e.whatId, e); } event[] eventsToDelete = new event[0]; if (trigger.isDelete) { eventsToDelete = tzShowEventMap.values(); } else if (createEvents) { // add an event for the show for (opportunity o : trigger.new) { if (o.stageName != 'Quote' && o.stageName != 'Closed Lost' && o.show_date__c != null && o.show_date__c >= system.today().addMonths(-1) && o.theater_city__c != null) { // create the event or get the existing one event e = tzShowEventMap.containsKey(o.id) ? tzShowEventMap.get(o.id) : new Event(); e.isReminderSet = false; e.isAllDayEvent = true; // connect to the contact, if any e.whoId = oppCons.containsKey(o.id) ? oppCons.get(o.id) : null; // connect to the opp e.whatId = o.id; // get the acct name string acctName = (o.accountId==null || !acctMap.containsKey(o.accountId)) ? '[Unknown]' : acctMap.get(o.accountid).name; if (acctName.length() > 50) acctName = acctName.substring(0, 50); // the subject of the event includes the opp stage, acct name, and number of tickets purchased // example - 45 Booked: NPower Seattle e.Subject = ((o.total_tickets__c != null && o.total_tickets__c > 0) ? o.total_tickets__c.format() + ' ': '') + (o.StageName == 'Closed Won' ? 'Closed' : o.StageName) + ': ' + acctName; // the date of the show is in a custom field e.activityDate = o.show_date__c; // to put it on the right calendar, set the owner ID to the calendar ID e.ownerId = publicCalendarId; // put this event on the map tzShowEventMap.put(o.id, e); } else { // delete events with opps that no longer fit the bill if (tzShowEventMap.containsKey(o.id)) eventsToDelete.add(tzShowEventMap.get(o.id)); } } // add and update! upsert tzShowEventMap.values(); } if (!eventsToDelete.isEmpty()) delete eventsToDelete; }
Another option for a truly public calendar
This calendar is working well – we created a link to it for all users, and they can hover over events on the calendar to see the details or link directly to the opportunity or associated contact. However, the calendar is not publicly available outside Salesforce.
Another option we considered was using the Google Apps API to add the opportunities to an external Google calendar. That would allow us to share the calendar with employees or partners who don’t have Salesforce accounts – or even to the general public. After taking a peek at the API on the developer site, I don’t think it would be much more effort. The Google calendar wouldn’t offer direct links to the Salesforce objects, but it would be a lot more flexible than the calendar in Salesforce.
FYI, this came across the inbox from a nonprofit I work with:
9/14/09 Constant Contact releases InfoTransfer for Salesforce Plug-In – Good news for our customers who use Salesforce.com’s customer relationship management service! We now offer a plug-in that makes it a breeze to upload and synchronize contacts in Salesforce.com with your Constant Contact account. You can rest assured, knowing your contacts are up to date, and comply with CAN-SPAM unsubscribe guidelines. Log into your Constant Contact account, click on the “Contacts” tab, and under “My Contacts” you’ll see the “Import Tools” link. After a call to their support line for more info:
So that’s the info. Not sure if this sways anyone off of Vertical Response, but it will be nice to have another mass-email option, especially for orgs that are already using CC.
Ever since this feature was requested by our old pal Steve Andersen, I’ve been waiting to play around with this. This feature makes looking at a specific Campaign wayyyy more useful. First thing you should do (if you haven’t already) is add the CampaignMember Related List to the Campaign Page Layout. Ta-da! You can now see which Leads or Contacts are members of your Campaign without running that sucky, uncustomizable Campaign Call Down Report. But wait! You can now customize that Campaign Call Down Report too! Woo-hoo!
Okay, so now you’ve got the report of your Campaign Members. Wouldn’t you love to indicate payment information on each of those members? Of course you do! Sure, you could customize the Campaign Member statuses to indicate RVSP, Paid, etc. But I’m sure you’d love to capture their payment details. Well, now you can go ahead and add custom fields to the Campaign Member object, like a Check #, Credit Card #, Amount, etc. Then, whenever you have events that you want to see if people have paid for, you can use the Campaign Member customization you just did. There are all sorts of ways to use this, and I’ve only touched on one. How are you using Campaign Member customization? Do you like the new Campaign Membership Management?
I’ve been working on a project that involves the use of CMSForce and since that’s a fairly new offering from Force.com Labs, thought I’d share my experiences so far. The install itself is easy enough, but there are a few post-install instructions that need to be followed for it to work, so be sure to download the documentation available on the AppExchange and read through it.
To use CMSForce, you’ll either need to use one of the Page Templates that comes with the application, or create your own, which involves Visualforce. The idea behind CMSForce, as with any CMS application, is that those templates can be created by someone more technical, and then anyone can go in and create new pages or adjust existing pages without needing a background in Visualforce, HTML, or style sheets. CMSForce definitely accomplishes this, and anyone willing to spend half an hour to an hour getting up to speed on it should quickly be able to start making use of the Sites functionality now native in the Enterprise version of Salesforce.
For the graphic design of the Site I did, my client already had a non-Salesforce web portal whose style they wanted to mimic, so all I had to do was create a Zip file of the CSS and image files (maintaining the folder structure on the web server), upload that as a Static Resource and then refer to the style sheets in the Visualforce Template. And the voila, standard HTML tags like <h1> (heading 1) now display with my client’s branding.
The Page Template is what it sounds like, it provides the styles and the frame for the page, which can include header, footer, menus, etc. In the Visualforce for the Template, you can insert Content Block components which are the placeholders for the content that users will be able to provide. From there you create Page records, which become individual pages in the site. Users then have the ability to update the content in each Content Block within the Page using a GUI editor. One page can be identified as the Home page and is the default when users go to your Site URL without specifying a specific page.
Once you have a Page Template and one or more Pages with Content Blocks created, you’re ready to set up a Site to go public. Once again, just read through the CMSForce documentation for all the configuration that needs to be done.
I’ve hit 2 issues that I’m still trying to resolve in this process. First, the content within the Content Blocks looks great in the preview but isn’t displaying in the public site. This I’m sure is a permission issue, and fortunately one of the Sites PMs from Salesforce will be helping me resolve that tomorrow. If this is a shortcoming in the documentation, I’ll be sure to post the resolution here. The other is that I created Visualforce page to display data from Salesforce and used that as a Page Template, and it isn’t functioning as expected in Sites. I suspect this has to do with the fact that my page is embedded as a component in another page, so once I figure that out I’ll post what I’ve learned here. Revision 8/26: both of these issues were, predictably, due to user error- I hadn’t realized that CMSForce wasn’t deployed yet. Once deployed, everything works as expected!
All in all, another solid application from Force.com Labs.
(With apologies to Stanley Kubrick) We’re about to begin a Salesforce rollout to a new team of users and I thought I’d share the steps I’m taking to prepare everyone for the big change that’s about to happen:
Stay tuned for a future post about monitoring user adoption. For more on the topic of managing technology change in your org, I refer you to one of the best sessions at this year’s Nonprofit Technology Conference, “Technology Ch-Ch-Change: Managing Technological Change in Your Organization” and to Dahna Goldstein’s chapter in the NTEN book. Enjoy!
On many occasions, I talked to organizations new to Salesforce that were worried about storing their data online. At a very high level, this post presents the security in place with Salesforce.
When it comes to data, Salesforce and the cloud computing model mean that you are not storing your data on your own server and that multiple clients’ data could be stored on the same server. What it does not mean is that you give up control of who can access your data or that your data is more vulnerable. It is indeed the contrary. There are two levels of security in place. The first one comes to you out of the box and ensures the protection of your database. The second level provides you with tools to implement your specific permission requirements.
Because of the shared infrastructure, Salesforce has higher level of security than most organizations are able to implement independently. You can find a great description of this here but know it includes:
Obviously, any level of security requires consideration of different elements: user training, password policy, user permission, backup, etc. And it is has to do as much with the tool you are using as the policy you put in place (we all know the old story of the monitor with a password written on a post- it).
This is an overview of the basic security controls Salesforce has to offer:
Salesforce provides a secured platform with ways to implement specific permissions which is usually sufficient for most. However each organization should decide whether or not this is adequate and I hope this post will help you understand some of the security controls available. More information can also be found in the Best Practices from Salesforce.
Here is a 5-minute demo of the nifty Visualforce interface I created for running batch apex (see my earlier post on Batch Apex and why we are using it). I needed to create the custom page because: