Paul Galvin's (old) SharePoint space [SharePoint

Just another WordPress.com site

Category Archives: SharePoint Development

Quick and Easy: Send Email Using Gmail SMTP Server in .NET C#

This isn’t exactly a new topic, but when I needed to do it, I found a lot of “why won’t this work for me” and not too many direct answers.  I hope someone finds this useful.

The following bit of code will send an email using my own gmail account to do it, including attachments:

using System.Net.Mail;
using System.Net;

NetworkCredential loginInfo = new NetworkCredential("[My Gmail ID]", "[My Gmail Password]");
MailMessage msg = new MailMessage();
msg.From = new MailAddress("[M Gmail Id]@gmail.com");
msg.To.Add(new MailAddress("paul.galvin@arcovis.com"));
msg.Subject = "Test infopath dev subject";
msg.Body = "<html><body><strong>A strong message.</strong></body></html>";
msg.IsBodyHtml = true;

foreach (string aFile in NIPFD.GetAttachmentNamesAndLocations())
{
    msg.Attachments.Add(new Attachment(aFile));
} // Adding attachments.

SmtpClient client = new SmtpClient("smtp.gmail.com");
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = loginInfo;
client.Port = 587;
client.EnableSsl = true;
client.Send(msg);

 

A few key bits that slowed me down and other observations / notes:

  • The first line that creates the loginInfo object needs to use the gmail ID stripped of “@gmail.com”.  So, if my gmail email address is “sharepoint@gmail.com” and my password is “xyzzy” then the line would look like:

NetworkCredential loginInfo = new NetworkCredential("sharepoint", "xyzzy");

  • My gmail account is set up to use SSL and that wasn’t a problem.
  • There is some conflicting information out there on what port to use.  I used port 587 and it worked fine for me.
  • In my case, I also needed to send attachments.  That NIPFD object has a method that knows where my attachments are.  It’s returning a fully path (e.g. “c:\temp\attachment1.jpg”.  In my test, I had two attachments and they both worked fine.

I used visual studio 2008 to write this code.

</end>

Subscribe to my blog.

Follow me on Twitter at http://www.twitter.com/pagalvin

Technorati Tags: ,,,

Bamboo Calendar Interacting with SharePoint Causes “An unexpected error occurred”

Today, I’ve been working in an environment that uses a Bamboo calendar web part for some improved collaboration.  This a standard medium/small farm with two load balanced WFEs, a “application server” for indexing and InfoPath and a clustered SQL back end.

The client installed some disaster recovery software onto one of the WFEs and that resulted in a broken WFE for a specific site in the site collection.  Whenever load balancing pointed at the affected WFE and that site, users saw a largely blank white screen with the sentence “An unexpected error occurred”.  No other info showed, just that sentence.

They asked me to look at it.  I easily reproduced the problem and then added a ?contents=1 to the end of the URL.  This is how I learned they were using the Bamboo web part.  I went back to the page and now, suddenly, it showed me a nice orderly error message:

 

I don’t know what was happening or what I did to get the controlled error message to display other than appending the ?contents=1 bit of the query string. 

This is probably a very rare edge case but if you get that message, “An unexpected error occurred” go ahead and add ?contents=1 to the query string and see where that leads.

</end>

Subscribe to my blog.

Follow me on Twitter at http://www.twitter.com/pagalvin

Technorati Tags: ,

Using Reflector on Microsoft.SharePoint.dll

Michael Blumenthal has put up an article about alerts and views ("Which views can be used when Creating an Alert ?"), which is interesting on its own merits.  However, I was even more interested to see how he methodically walks through the process of using Redgate’s .NET Reflector.  Step by step, he shows how to use that tool track down how SharePoint determines whether a view on a list should be "subscribable" for lack of a better word (he explains this much better than I do 🙂 ).

At this point in the SharePoint era, you’d almost have to be deaf and blind to have not heard or read about using that tool.  It’s advanced stuff and my own sense is that it’s in many ways a tool of last resort.  However, if you think you need it but don’t really know how, Michael’s write-up seems like a great tutorial on this subject.

</end>

Subscribe to my blog.

Follow me on Twitter at http://www.twitter.com/pagalvin

Sorting Calendar Items in the Calendar View

I’m using a calendar to track panel discussions on a given day.  I have two sessions: morning and afternoon.  I want to create a view that shows morning sessions only listed alphabetically via a session ID.  This is a little tricky to accomplish (unless I missed something real obvious).

I can easily create a filtered view showing just that day’s events.  The calendar will show all those events and if they all take place on the same hour, it lists them side by side in the hour slot.  That’s great.  The tricky part is that, by default, it wants to list them in ID format.  So, if I add "Session 02" and then "Session 01" to the calendar, it shows them in that order (i.e, Session 02 and then Session 01).  To list them in Session ID order, I thought to try these things:

  1. Configure the view using the web user interface.  No luck.  There are no options to sort items this way.

    Since that didn’t work, I am now hoping there’s a CAML query somewhere in there that will allow me to override the sort.  CQWP lets me make that kind of change, maybe calendar will as well?

  2. Edit the web part,  look for an "export" function and … BZZZT!  I can’t export its XML.  There is no export option.

    I’m still holding out hope that I can find and modify some CAML query.  Since I can’t export the web part, that leaves SharePoint Designer.

  3. I add the calendar web part to a sandbox site and open that site using SharePoint Designer.  I look at the markup for the web part and I find what I’m looking for.  It’s encoded madness, but it’s there: "<ListViewXml …. >encoded madness</ListViewXml>".  Specifically, there’s this gem:

<Query>
  <Where>
    <DateRangesOverlap>
      <FieldRef Name="EventDate"/>
      <FieldRef Name="EndDate"/>
      <FieldRef Name="RecurrenceID"/>
      <Value Type="DateTime">
        <Month/>
      </Value>
    </DateRangesOverlap>
  </Where>
  <OrderBy><FieldRef Name="Session_x0020_ID" /></OrderBy>
</Query>

(I’ve decoded the &lt;’s and &gt’s and split them out to multiple lines for clarity’s sake).

Add the <OrderBy> bit and now it sorts by Session ID, not the internal list item ID.

GIGANTIC tip o’ the hat to Becky Isserman for her comment to Isha Sagi’s blog post: http://www.sharepoint-tips.com/2008/07/caml-sorting-by-file-name.html.  I wouldn’t have put the <OrderBy> in the right place without her comment.

I hope to write this up more clearly with screen shots soon, but in case I never do, at least I did this much.

</end>

SharePoint Features & Solutions Management — Don’t Forget the U in CRUD (or the D for that matter)

We collectively spend a great deal of time thinking about SharePoint solutions — how to create them, which tool to use, what happens when they fail to deploy, timer jobs, scopes, etc.  We spend so much time thinking about the up-front bits that it’s easy to forget that we need to retract them as well.  Retracting solutions is probably more difficult, from a conceptual design perspective, than deploying them.  Deployment is basically a cookbook affair.  Typically, install a feature, maybe have a feature receiver load some data into a list, that sort of thing.  However, retracting is potentially more complex.

A given solution may create artifacts like these:

  • Content Type
  • List Definition
  • Site Definition
  • Data in a list
  • Even receivers
  • InfoPath forms

The list goes on.

While it’s obviously important to design a solution that instantiates those artifacts correctly, it’s just as important to consider the update and delete cases.  If your solution creates a new list and populates that list with data, what happens when the solution is retracted?  In some cases, the list should be deleted.  In other cases, it should be left intact for historical purposes.  Your business requirements will guide you to the right decision.

To help with this, create a matrix that lists each artifact your solution deploys to SharePoint.  List three columns per artifact, one for create, update and delete.  For each case, determine the correct outcome for that operation.

This sort of analysis is obviously best done before the solution is ever deployed to a SharePoint farm.  However, like smoking, it’s never too late to start doing things correctly.  Create that matrix and develop a plan to address the missing update/delete scenarios.  It may be a hard problem to solve, but at least you’ll have put a box around the problem.

</end>

Subscribe to my blog.

Follow me on Twitter at http://www.twitter.com/pagalvin

Technorati Tags:

Quick Fix: Web Services that Interact with SharePoint, InvalidOperationException

A million years ago, I helped developed a web service that was invoked via a custom action for a SharePoint Designer workflow.  This week, the client wanted to move it to production (finally!) so we did.

The custom action worked fine, but the web service it invoked did not, giving us this error:

System.InvalidOperationException: This operation can be performed only on a computer that is joined to a server farm by users who have permissions in SQL Server to read from the configuration database. To connect this server to the server farm, use the SharePoint Products and Technologies Configuration Wizard, located on the Start menu in Administrative Tools.
   at Microsoft.SharePoint.Administration.SPWebApplication.Lookup(Uri requestUri)

Turns out that I forgot to add the service to the SharePoint application pool in IIS.  Once I did that, it worked fine.

This MSDN forum posting gave me the clue I needed: http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/2c97c004-7118-4e06-a62c-b2b0ac07ac99

</end>

 Subscribe to my blog.

Follow me on twitter: http://twitter.com/pagalvin

Technorati Tags:

Quick and Easy: Get SPFolder of SPItemList

I keep running into this problem and the Google never seems to understand what I want to do, so I figured I would write this down.

I’ve been doing a lot of event receiver debugging in the last week or two. The ER is defined against a document library.  The individual items in the document library are tightly related to their parent folders.  So, I am always getting the folder of the item for various manipulations.  While debugging, I needed to update the metadata of a folder for a specific item whose ID I know.

Here’s a little console application (designed to run on server in the farm) that takes two arguments: the ID of an item and a value to assign to a field, "Approval Status".  It hard codes a lot of stuff and has no error checking.

The code looks up a hard coded site, gets a hard coded document library and then finds the indicated item.  It then finds the parent folder of that item and assigns the status value.

The key lesson here for me is that SPItem doesn’t get you access to the folder.  You need to use SPListItem.File.

If anyone cares to offer a critique or suggest a better way to get the folder of an item, please leave a comment.

<code>
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Collections;

namespace Conchango
{
    /// <summary>
    /// </summary>
    class ManualFolderUpdate
    {
        static void Main(string[] args)
        {
            string msh = "ManualFolderUpdate (v1.0): "; // msh = "Message Header"

            Console.WriteLine(msh + "Starting up.  I was last modified on 08/04/08.");

            string url = http://localhost/xyzzy;

            using (SPSite oSPSite = new SPSite(url))
            {

                using (SPWeb oSPWeb = oSPSite.OpenWeb())
                {
                    SPList docLib = oSPWeb.Lists["Documents"];

                    Console.WriteLine(msh + "Got the document library.");

                    Console.WriteLine(msh + "Doc lib item count: [" + docLib.ItemCount + "].");

                    int FolderID = 0;
                    string NewStatus = "xyzzy";

                    FolderID = System.Convert.ToInt32(args[0].ToString());
                    Console.WriteLine("Seeking folder for item: [" + FolderID + "].");

                    SPListItem li = docLib.GetItemById(FolderID);

                    SPFolder thisItemFolder = li.File.ParentFolder;

                    Console.WriteLine(msh + "Got the parent folder.");

                    NewStatus = args[1].ToString();
                    Console.WriteLine("Setting status to [" + NewStatus + "].");

                    Console.WriteLine("Press return to commit the update or CTRL-C to abort.");

                    Console.ReadLine();

                    thisItemFolder.Item["Approval Status"] = NewStatus;
                    thisItemFolder.Item.Update();

                    Console.WriteLine(msh + "Finished updating the folder.  Exiting.");

                } // using SPWeb

            } // using SPSite

            Console.WriteLine(msh + "Finished.");

        } // Main

    } // class foldersync
} // namespace
</code>

</end>

Subscribe to my blog.

Technorati Tags:

Hiding Custom Actions in SharePoint

Several months ago, while working on a project for a client, I created a feature.  Among other things, that feature defined a custom action (these appear under site settings).  The only purpose for this custom action was to demonstrate to me that the feature activation was working as expected.  Basically, I used it as a crude form of debugging.

Much to my embarrassment, this debugging artifact made its way from dev to UAT and to production before someone got around to asking, "what is this xyzzy link?"

By now, we’re in production and I really don’t want to uninstall the feature, fix elements.xml and then reinstall. 

Todd Bleaker pointed out an easy way to handle it.  In his words:

"CustomActions are not copied into the database, they are read directly from the Elements.xml file and copied into application state at runtime. So, you don’t need to deactivate, uninstall, reinstall, and reactivate the Feature to remove the CustomAction. Just remove it from the Elements.xml file and recycle the application pool(s) effected. The next time the Feature is in scope, you won’t see the CustomAction anymore."

I followed his instructions and, of course, they work.

In my case, this is a department level installation with just a single WFE.  If you find yourself with this problem in future and with multiple WFE’s, I believe you’ll have to make the change in each.

</end>

Subscribe to my blog.

Quick Fix: Accessing SharePoint Site Throws [HttpException (0x80004005): Request timed out.]

One of my developer colleagues was working on a project this week and ran into a timeout problem while working on building some crazy web part.  His web part was fine, but "suddenly" an unrelated site became very slow and frequently timed out with this error:

[HttpException (0x80004005): Request timed out.]

I logged in and saw that several other sites were just fine.  I suspected that there were some hidden web parts on the page and using the trusty ?contents=1 debug technique, I did in fact find 11 web parts on the page, only two of which were visible.  Even better (from a let’s-hope-I-find-something-ugly-here-that-I-can-fix perspective), three of those closed web parts had a name of "Error".

I deleted those web parts (which itself took a surprisingly long time) and that solved the problem.  For today 🙂

</end>

Subscribe to my blog.

Technorati Tags:

Invoking SSRS Web Services From WSS / MOSS in FBA Environment

We needed to invoke the "CreateSubscription" method on an SSRS web service that is hosted in an FBA managed MOSS environment from a custom web part.  We kept getting variations of:

  • 401: Not authorized
  • Object Moved

The "object moved" message was most interesting because it was saying that the "object" (our SSRS service) had "moved" to login.aspx.  This clearly meant we had some kind of authentication problem.

I eventually realized that I had bookmarked a blog entry by Robert Garret that described how to invoke a general purpose WSS/MOSS web service living inside an FBA environment.  Note that I can’t link directly to the article (as of 06/09/08) because it wants to authenticate.  The link I provide brings you to an "all posts" view and you can locate the specific article by searching for "Accessing MOSS Web Services using Forms Based Authentication".

Here’s the code that worked for us:

ReportingService2006 rs = null; 
// Authenticate Authentication auth = new Authentication(); 
auth.Url = "http://URL/_vti_bin/Authentication.asmx";
auth.CookieContainer =
new CookieContainer();
LoginResult result = auth.Login("userid", "password");
if (result.ErrorCode == LoginErrorCode.NoError) 
{
// No error, so get the cookies.
CookieCollection cookies = auth.CookieContainer.GetCookies(new Uri(auth.Url));
Cookie authCookie = cookies[result.CookieName];
rs =
new ReportingService2006();
rs.Url =
"http://server/_vti_bin/ReportServer/ReportService2006.asmx";
rs.CookieContainer =
new CookieContainer();
rs.CookieContainer.Add(authCookie);
}
try
{
  rs.CreateSubscription(report, extSettings, desc, eventType, matchData, parameters1);
}
catch (Exception ex)
{
  Console.WriteLine(ex.Message.ToString());
}

I interpret things to work like this:

  • Our web part needs to dial up the authentication service and say, "Hey, Tony, it’s me!".
  • Authentication service replies saying, "Hey, I know you.  How are the kids?  Here’s a token."
  • We call up the SSRS service and say, "Tony sent me, here’s the token."

</end>

Subscribe to my blog.