Integrating Windows Form Click Once Application into SharePoint 2007–Part 4 of 4

(This is a series of posts covering how to include a WinForm app inside a SharePoint 2007 application, which allows users to upload batches of files to a SharePoint document library. For further info, please see Posts One, Two, and Three. All of the code can be downloaded in Post Two.)

Just a quick review of our solution, as detailed in the first three posts of this series: a Custom Action adds a menu choice to a document library’s Upload menu, which links to a Windows Form application deployed using Click Once.  The WinForms app allows the user to select files, label them with meta data, and then upload them to a SharePoint document library.  This post will cover what happens after the files are uploaded and also how we packaged everything into one SharePoint solution file, using WSPBuilder.

Web Services

If you remember from Post Two, our WinForms app calls several web service methods.  (The web methods are contained in the ListService.cs file, and are the ClickOnceSharePoint.WebService class.)

WebServicesFiles

If you look at the graphic above with the solution’s files, you’ll see several files in the WebServiceCode folder.  There’s the web service itself in the ListService.cs file, there’s a dataset used to store content type info named GetContentTypes.xsd.  The other files, ContentType.cs, FieldDefinition.cs, and FileInformation.cs, are custom objects that are used to pass information between the client and the web service.

When we open the ListService.cs file, we see these web methods:

        [WebMethod(Description = "Returns a list of Content Types for the given SharePoint List")]
        public List<ContentType> GetContentTypes(String ListName, String SiteName)

        [WebMethod(Description = "Returns a list of Fields for the given Content Type for the given SharePoint List")]
        public List<FieldDefinition> GetFieldDefinitions(String ContentTypeId, String ListName, String SiteName)

        [WebMethod(Description = "Updates the meta data for a set of files in a document library")]
        public bool SetFileInformation(String ListName, String SiteName, FileInformation myFileInformation, String FolderName)

        [WebMethod(Description = "Creates a folder in the document library, or does nothing if one of that name already exists")]
        public bool CreateFolder(String ListName, String SiteName, String FolderName)

The first web method, GetContentTypes(), returns the available content types for the doc library, then once the user chooses a content type, GetFieldDefinitions() is called to get the fields (aka: columns in the SharePoint doc library; aka: meta data) and their corresponding data types. The app then populates the choices on the screen with the information that it retrieves.

Our user fills in the meta data, selects the files to upload, and then clicks upload.  The app uploads each file and then calls the SetFileInformation() web method, which requires a list of FileInformation objects.  Here is the FileInformation class:

using System.Collections.Generic;
using System.Collections;
using System;

namespace Express.StorePortal.WebService
{
    public class FileInformation
    {

        public List<string> FileName { get; set; }

        public List<FileField> FileFields { get; set; }

        public string ContentTypeId { get; set; }
        public string ContentTypeName { get; set; }

        public bool AllViewersCanAccess { get; set; }
        public List<String> RolesThatCanView { get; set; }
    }

    public class FileField
    {
        public string DisplayName { get; set; }
        public string InternalName { get; set; }
        public string Value { get; set; }
    }

}

As you can see, the list of data sent to the web service includes the filename, the SharePoint columns (FileField), and the security permissions that the file should have. (See Post Two of this series for more information about the application.)

If there is an error, or the user is uploading a lot of files, there is a possibility that another user would see these uploaded files in the library before the web service has had a chance to populate their columns.  To handle this, we configured the views on our list to only display files that had their values set.  We also didn’t allow our users to create their own views for this library, so we reduced the chances that users would see files that aren’t labeled.

Packaging everything in a WSP

The last part of solution is deployment.  We used WSPBuilder, integrated with Visual Studio 2008, to organize and build the code into SharePoint solutions files (*.wsp).  We have two projects in our solution.  The first project is “ClickOnceApp”, which as the name would suggest, is the Windows Form click once application that runs on the client.  The second project is “ClickOnceSharePoint” which contains the rest of the code and artifacts (custom actions, web services, images, etc).  It also contains the compiled version of the ClickOnceApp, in the 12/Template/Layouts/ClickOnceApp folder:

ClickOnceAppDeployed

We did this by telling the ClickOnceApp to save the build output to this folder in the second project.  This is done either in the project properties or in the first screen of the publish wizard:

PublishWizard1

When we tell WSPBuilder to build the .wsp file, it includes the ClickOnceApp files in this folder.  This allows us to deploy the click once app right along side any other artifacts and .dll’s and it works flawlessly.

Final Summary

My first suggestion to anyone thinking of doing a similar solution, is to look into Silverlight first. Silverlight is easier to deploy and is also supported across many platforms.  One of the bigger issues we had to deal with was ensuring that the appropriate .Net runtime was already on the client computers that would need to run this app.

You might also want to think about having a separate .wsp for the click once app.  This would allow you to deploy an updated click once app without having to include new web service code.

And with that, our Integrating Windows Form Click Once Application into SharePoint 2007 blog post series is concluded.

How to organize your desktop and keep it that way

I’ve been using a utility for the last couple of years that organizes my desktop – Stardock Fences – and I was going to write up a review saying how cool and useful it is.  However, others have already done that here and here (and many more via Google).

So instead of rehashing those reviews, I’ll just say this: I find that the hiding icons feature and the screen resize features are great when I’m using my laptop for presentations. I’ve used this utility on Windows XP, Windows Server 2003 R2, and Windows 7 x64 – all with no problems.

(Note: I received no compensation for this product endorsement Smile )

Google Search tips & tricks

Over the course of using Google for the past ten years, I’ve found several tips and tricks to using the search engine.  This post covers the ones that I’ve found most useful.

Limit your search to one site

Have you ever tried to find something that you know must exist on a particular site?  This happens to me when I’m looking for info on Microsoft’s site.  So, I’ll often enter a search term into Google and then add “site:microsoft.com” .  Google will then only return hits that are on the *.microsoft.com site.

Find sites that have links to your site

Do you have your own website/blog and want to know if any other websites have links to your site? Just enter “link:mydomain.com” into Google and it will return pages that have links to your site.

Measurement conversions

Ever needed to know how many feet are in 6 miles?  Ask Google – enter “convert 6 miles to feet” and see what the results are (or click the link).  Go ahead, I’ll wait.

Pretty cool, eh?  It also works if you need to convert from English to metric (mile to kilometers, etc).

Google Calculator

Google also does math calculations.  When you enter 1+1 you’ll get the answer “2” along with an option to search for 1 + 1.

GoogleCalculator

Integrating Windows Form Click Once Application into SharePoint 2007–Part 3 of 4

(This is a series of posts covering how to include a WinForm app inside a SharePoint 2007 application.  For further info, please see Posts One and Two. All of the code can be downloaded in Post Two.)

As I promised several months ago, I’ll cover the Custom Action piece of our solution.  The custom action is used to allow the users to launch our Windows Forms application using Click Once deployment.  Our custom action adds a new link to the Action menu of a document library’s toolbar:

CustomUploadActionMenu

Adding a custom action is relatively simple.  First, you create a Feature in Visual Studio.  The feature.xml for my example was this:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <Feature  Id="f33bf219-4185-4be1-8aa6-4220933320a0"
   3:            Title="Custom Action"
   4:            Description="Adds a custom action for document uploads"
   5:            Version="12.0.0.0"
   6:            Hidden="FALSE"
   7:            Scope="Web"
   8:            DefaultResourceFile="core"
   9:            xmlns="http://schemas.microsoft.com/sharepoint/">
  10:    <ElementManifests>
  11:      <ElementManifest Location="elements.xml"/>    
  12:    </ElementManifests>
  13:  </Feature>

This is a pretty standard feature.xml file.

Second, you have the elements file (referenced in line #11 above):

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:    <!-- Document Library Toolbar Upload Menu Dropdown -->
   4:    <CustomAction Id="81F9EBB9-B6D1-497f-9587-95C30B3B00A7"
   5:      GroupId="UploadMenu"
   6:      Location="Microsoft.SharePoint.StandardMenu"
   7:      Sequence="3"
   8:      ControlAssembly="ClickOnceSharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ff21f702ff80f059"
   9:      ControlClass="ClickOnceSharePoint.Controls.UploadCustomAction">
  10:    </CustomAction>
  11:  </Elements>

Let’s look at the elements file above.  Line #5 and Line #6 tell SharePoint where to add our choice to the menu.  These values can be found on this page on MSDN. (The complete schema reference for the CustomAction is here.)

Lines #8 and #9 tell SharePoint that when a user selects our custom action, it should look to the ControlAssembly to find the ControlClass.  (As you can tell by Line 8, our assembly is in the Global Assembly Cache.)

At first, I tried using just a URL here, instead of a custom control.  The <URLAction> tag allows you to define a static url to direct the user to.  However, for my scenario this wouldn’t work because I needed to generate the url on the fly – hence the need for a custom control.

Our custom control is going to generate a dynamic URL with some query string parameters.  This URL is the link to the Windows Form Click Once application and the query string parameters are used to pass information from SharePoint to the app when it starts on the client. 

Here’s the UploadCustomAction class:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Web;
   5:  using System.Web.UI;
   6:  using System.Web.UI.WebControls;
   7:  using Microsoft.SharePoint;
   8:  using Microsoft.SharePoint.WebControls;
   9:  using Microsoft.SharePoint.Administration;
  10:   
  11:  namespace ClickOnceSharePoint.Controls
  12:  {
  13:      public class UploadCustomAction : System.Web.UI.WebControls.WebControl
  14:      {
  15:          protected override void CreateChildControls()
  16:          {
  17:              SPList myList = null;
  18:   
  19:              //TODO: Change this list dynamically based on the web part calling it
  20:              //for this example, we're going to just assume it is the Shared Documents library
  21:              myList = SPContext.Current.Web.Lists["Shared Documents"];
  22:   
  23:              //Do security check
  24:              if (myList.DoesUserHavePermissions(SPBasePermissions.AddListItems))
  25:              {
  26:                  MenuItemTemplate myCustomUploadAction = new MenuItemTemplate();
  27:                  myCustomUploadAction.Text = "Custom Upload";
  28:                  myCustomUploadAction.Description = "Assign permissions and meta data values while uploading multiple documents";
  29:                  myCustomUploadAction.ImageUrl = "/_layouts/Images/CustomUploadIcon.jpg";
  30:   
  31:                  myCustomUploadAction.ClientOnClickNavigateUrl = ActionUrl("SharedDocuments");
  32:   
  33:                  this.Controls.Add(myCustomUploadAction);
  34:              }
  35:              else
  36:              {
  37:                  this.Parent.Visible = false;
  38:              }
  39:   
  40:   
  41:          }
  42:   
  43:          private string ActionUrl(string docLibrary)
  44:          {
  45:              //These parameters will be passed to the ClickOnceApp using the query string
  46:              //  
  47:              int _clientDownloadSpeed = 768000; //Highest speed clients are guarranteed to have (in bytes)
  48:              SPSite site = SPContext.Current.Site;
  49:              string server = this.Context.Request.Url.Host;
  50:              string secure = "N";
  51:              string siteName = SPContext.Current.Web.Name;
  52:   
  53:              string Url = SPContext.Current.Site.Url.ToUpper();
  54:              string maxFileSize= SPContext.Current.Site.WebApplication.MaximumFileSize.ToString();
  55:   
  56:              if (Url.StartsWith("HTTPS:"))
  57:              {
  58:                  secure = "Y";
  59:              }
  60:   
  61:              return string.Format("/_layouts/ClickOnceApp/ClickOnceApp.application?Srv={0}&Sec={1}&Doc={2}&SiteName={3}&Speed={4}&Max={5}",
  62:                  server, secure, docLibrary, siteName, _clientDownloadSpeed, maxFileSize);
  63:          }
  64:   
  65:      }
  66:  }

Let’s walk through this class.

The only required method in this class is the CreateChildControls() method on Line 15. When SharePoint adds our custom action to the menu, it executes this method.  The code checks the document library to verify that the user has permission to upload documents (lines 21-24). 

Please note that the document library is hard coded in line 21.  For production, we have code that determines the document library being used based on the URL of the browser.

The next step is to build the control that will be added to the menu.  The control is of type “Microsoft.SharePoint.WebControls.MenuItemTemplate” (line 26) and allows us to set the text that users see (line 27), the description that users also see (line 28), and the image/icon that users see (line 29).  BTW, this image was included in our solution file.

The last property to set for our MenuItemTemplate control is the “ClientOnClickNavigateUrl”.  This is the URL that SharePoint will send users to (line 31).  Since we’re building our URL dynamically, this line calls our ActionUrl() method.

The ActionUrl() method (line 43) does the final work of putting the URL together by concatenating strings.

Some notes about the parameters:

  • _clientDownloadSpeed This is a value that the WinForm app uses to let uploaders know how long a particular document may take for end users to download.  By letting them know how long it would take, in minutes/seconds, they were better able to judge file size.
  • maxFileSize This value is the maximum file size that the SharePoint Farm is configured to accept.  The value is set in Central Admin.  It allows our app to let the user know that the file is too big before they even try to upload it (and get some strange error message).
  • secure This setting lets the app know whether it needs to upload files to an HTTP or HTTPS address.
  • server, sitename, docLibary These values are used by the WinForm app to build the upload URL.

By using these parameters, we were able to reuse this custom action with different document libraries and different sites within SharePoint.

That’s it for the custom action.  In Part 4, I’ll detail the web service which sets the meta data values of the documents and also how we packaged all of this into one SharePoint Solution file (.wsp).

Windows Live Sync….being renamed to Windows Live Mesh

Microsoft announced that they going to change the Windows Live Sync service in the future, answering one of my complaints from my earlier post.  After the beta, Microsoft is going to rename Windows Live Sync to Windows Live Mesh, but more importantly, increase the size of the SkyDrive size from 2GB to 5!

(Courtesy of Paul Thurrott and the Inside Windows Live blog)

Windows Live Sync (Beta)

SyncLogoI decided to install the new beta of Windows Live Essentials, mostly for the new Sync app.  I also got Live Writer, which has the newer Office style ribbon User Interface. (Here’s the Windows Live blog announcing the beta in June. There was a beta refresh last week.)

I’ve been using sync apps for a while now, starting as far back as 2004 (IIRC) with Folder Sync WindowsLiveFolderShareLogo(a company/app which was later purchased by Microsoft).  When Live Mesh appeared, I switched to it, primarily so I could not only sync to other computers, but also to the Mesh Desktop.  This gave me a way to have an off site backup, which I used for my daily working set of files.

The way Sync works, is that you install a client application on the computer, or computers, that you want to synchronize files between.  You set specific folders to sync, and when a file changes in the folder on one of the computers, it will then send the new version to the other computers.  There is not only a client for Windows, but also one for Mac (OS 10.5).

You also get to Sync to Microsoft’s SkyDrive, which is storage space on Microsoft’s servers storage_icon(sometimes referred to as the “cloud”).  You can access the SkyDrive using a web browser, which makes it handy when you want to get to your files but you’re not using one of your computers (say at the library or school or work…).

I’ve been holding off from switching to the new Sync, because the Mesh Desktop has 5gigs of storage, but Sync’s SkyDrive only allows 2gig worth of data to be sync’d (the Live Blog announcement states that 2 gigs covers most people while keeping Microsoft’s costs down to the point where they can offer the service for free).

But, after rebuilding my laptop a little over a week ago, I finally decided to switch rather than install Mesh again.

Since I want to sync more than 2 gigs, I decided to install Sync on my server and a Mac at home.  After installing and designating which folders to sync, I sat back and watched as the different machines all started sending files to each other.  I had quite a few files, so it took some time, but within a couple of hours everything was sync’d up.

Calendar syncing

I’ve been syncing my calendars for years.  If you’re interested in why I am using Google, then read the history section, otherwise skip down to the how to section of this post.

History

For past six years or so, I’ve been using Plaxo to synchronize my calendar and contacts between my work computer and my home computer.  My work computers have been Windows XP/Vista/7 with Outlook 2003/2007/2010, so I used the Plaxo Outlook Toolbar to sync calendars and contacts.  At home, our main family computer has been Mac G3/G4/Mini running OSX, which sync’d to iCal and the Address Book.  I also had a non work Windows Mobile phone that I would sync through a cradle with the Mac.

This solution worked well for several years.  My wife could see what my work schedule was from the Mac, I could see her and the kids’ schedule using Outlook, and it was all on my phone.  There were a few hiccups over the years, duplicates would get created somehow when I would add/remove a machine to the mix, but those were manageable. Of course, the best part was the fact that it was free.

At some point, I knew this free service would go away.  Last year, it finally happened.  Plaxo would move the Outlook toolbar from free to their Premium (paid) service.  Really, it makes sense.  Most Outlook users are business users.  Surely these people would be willing to pay for a Premium service.  I was one of them for the past year.  I paid for a year’s subscription, at a sharp introductory discount ($47).  I was extremely busy at work and didn’t want to investigate other solutions to my syncing problem, so I paid for a year.

Well, a year later, some things have changed.  Outlook 2010 was released and being a geek who makes a living working with Microsoft products, I started using Outlook 2010 while it was still in beta.  It installed fine, but I got a rather rude surprise when I found out that the Plaxo toolbar didn’t work with 2010 (32 or 64 bit). SO, as a quick fix, I spun up a spare Virtual Machine (on my Hyper-V server), installed Outlook 2007 w/Plaxo and let it run.  It’s sole purpose was syncing while I waited for Plaxo to release a 2010 toolbar.

The next thing to change was my cell phone.  I got a cool HTC phone with a data plan. No longer would I need to cradle my phone with a machine, instead it sync's over the air.

The other change was the price.  When I noticed that my Outlook toolbar was no longer working (I don’t think I got a notice), I found that the price would be $59.  Although it’s not that much more, it had crossed a line.  (Plaxo should listen up to this:) I would gladly pay $20 annually for the convenience and I wouldn’t have thought much about it, almost an impulse buy. At $60, it is now worth my time to study alternatives.

Turns out, I can now use Google’s calendar service as my hub for syncing my calendars. SO, I’ve now replaced a Premium Plaxo service with a free Google service.

How to sync calendars using Google


First, let me describe my setup:

Laptop

  • Windows 7
  • Outlook 2010 (32bit, but 64bit will work too)
  • One calendar with work events
  • Google Calendar Sync (download here)

Mac Mini

  • MacOS X (10.5.8)
  • Apple's iCal
  • One calendar with family events

The first thing I did was install the Google Calendar Sync utility.  It connected my primary calendar in Outlook with my Google calendar.  I set it to do a one way push of events from Outlook to Google, but it does allow two way syncing.

 image

That takes care of getting my work calendar to Google. (BTW, this calendar is hosted on my work Exchange server.) Next step is publish my family calendar from the Mac to Google. Google has the step by step instructions here.  I added my work calendar from Google, as well as the separate family calendar.

One issue I did have was that the calendar events already in iCal didn’t appear to be uploading, so I exported that calendar and used Google Calendar’s web interface to upload the events.

At this point, my work calendar is now on Google and on the Mac, while the family calendar was on the Mac and on Google.  The last step was to get the family calendar on my Outlook.

For this step, I decided to just use Outlook’s internet calendar function.  You start by going to Google Calendar, into the settings, and getting the “ical” URL for the particular calendar you want.  I then entered that URL into Outlook (Go to calendars in Outlook, and switch to the “Folder” tab – then select Open Calendar –> From Internet).  My family calendar then appeared in Outlook.

SharePoint 2010 Administrator Training

I recently passed both of the Microsoft SharePoint 2010 Administration exams (70-667 & 70-668) and I’ve been asked what I used to study for them.  In order to help answer this question in the future, I put this post together.

First, here are some free resources that I used:

Up to this point, I’ve been focusing on the infrastructure track.  I personally prefer instructor led (or video) training, combined with hands on experience.  So as I’ve been watching the videos, I’ll stop and jump on my server (a server I have running Win 2008 and Hyper-V) to try things out.

Through Sogeti (my employer), I also have access to Books 24x7 (www.books24x7.com)  – there are currently six 2010 books there and I’m working my way through two so far:

I also want to look into these:

I’m sure I’m just scratching the surface when it comes to 2010 books.  The authors have done a great job in getting books published quickly after SP2010’s RTM.  (SharePoint 2007 was a much different story – very few books were available the first six months of its release.)

How to move an off screen window back to main screen on Windows 7

Ever had the fun of trying to see a window that is open, but off your screen?  I ran into this again this week.  I have a second monitor hooked up to my laptop most of the time, but when I undock my laptop, I’ll inadvertently leave windows open on the second monitor.  It’ll also happen when I open an application and it launches on the second window (because that’s where it was last open).

The fix for this is to get the window to move back to the main screen.  In Windows XP, you’d simply right click on the window in the taskbar, select “Move” and then use your arrow keys to move the window back.  However, in Windows 7, this has changed.

In Win7, when you right click on an application in the task bar, you get the new jump menu:

JumpMenu

Well, that won’t work.

Instead, try holding the SHIFT key when you right click:

ShiftRightClickMenu

Now, you can select “Move”, and then use the arrow keys to move the window to where you want it.

Firewire drive slow in Windows 7

I rebuilt my laptop this week (it was time) and I ran into this issue again, so I’m documenting it here for my next rebuild.

When I installed Windows 7, my external Firewire hard drive (Seagate 500gb)  is slow, for whatever reason, but there is a fix.  You can revert to the legacy driver. (!)  I’ve seen a significant reduction in the time it takes to copy files.  I found this answer here: http://social.technet.microsoft.com/Forums/en-US/w7itprohardware/thread/65cb7a25-2a84-4875-aa27-b084537e8da1

Here are the steps outlined in that news group post:

1- Click the Start Button, type devmgmt.msc in the “Start Search” box and press Enter.
2- Expand the "IEEE 1394 Bus Host Controllers" node in the device tree on the right hand pane
3- Right click the host controller node select "Update driver software ..."
4- Select "Browse my computer for driver software"
5- Select "let me pick from a list of device driver on my computer ..." and Check the box before “Show compatible hardware”.
6. Choose the second option---1394 OHCI Compliant Host Controller (Legacy), and click next to update the driver.

Access denied error when adding new server to existing SharePoint 2007 Farm

I recently got an error when adding a new SharePoint 2007 (SP2) server to our existing MOSS farm.  I had run the installation fine, and was walking through the SharePoint Configuration Wizard, when I got an error on step 2: “Resource retrieved id PostSetupConfigurationFailedEventLog is Configuration of SharePoint Products and Technologies failed.”

I searched the net, but didn’t really find anything.

I then remembered that I had forgotten to run the latest SharePoint updates (for us, the latest applied was December 2009).  I installed the WSS update, and then the MOSS update and both worked fine.  I then ran the Configuration wizard again and it worked without any errors.

Integrating Windows Form Click Once Application into SharePoint 2007 – Part 2 of 4

In my last post, I explained why we decided to use a Click Once application to solve our business problem. To quickly review, we needed a way for our business users to upload documents to a SharePoint 2007 document library in mass, set the meta data, set the permissions per document, and to do so easily.

Let’s look at the pieces that make up our solution.  First, we have the Windows Form application.  This app is deployed using Click Once and calls SharePoint web services in order to upload files and then calls web services to set the meta data (SharePoint columns and permissions).  Second, we have a custom action.  The custom action is responsible for providing our users a link that will launch the Windows app, as well as passing values to it via the query string.  And lastly, we have the web services that the Windows Form application calls.  For our solution, we used both out of the box web services and a custom web service in order to set the column values in the document library as well as the permissions on the documents.

Now, let’s look at the technical details of each of these pieces.  (All of the code is downloadable from here: )

 

Windows Form application deployed via Click Once

The Windows Form application, called “Custom Upload”, has just a few classes in it:

  • Custom Upload -- the form
  • FileList.xsd -- the dataset used to track the names of the files and their meta data values
  • SharePointUpload -- this class handles uploading the file

SharePointUpload uses an HttpWebRequest to transfer the file to the web server. We had to change this code from a WebClient object to the HttpWebRequest object, because we needed to be able to set the time out value. 

        public bool UploadDocument(string localFilename, string remoteFilename)
        {
            bool result = true;

            //Need to use an HttpWebRequest object instead of a WebClient object
            //  so we can set the timeout (WebClient doesn't allow you to set the timeout!)
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(remoteFilename);

            try
            {
                req.Method = "PUT";
                req.Timeout = 60 * 1000; //convert seconds to milliseconds
                req.AllowWriteStreamBuffering = true;
                req.Credentials = System.Net.CredentialCache.DefaultCredentials;
                req.SendChunked = false;
                req.KeepAlive = true;

                Stream reqStream = req.GetRequestStream();
                FileStream rdr = new FileStream(localFilename, FileMode.Open, FileAccess.Read);
                byte[] inData = new byte[4096];
                int bytesRead = rdr.Read(inData, 0, inData.Length);

                while (bytesRead > 0)
                {
                    reqStream.Write(inData, 0, bytesRead);
                    bytesRead = rdr.Read(inData, 0, inData.Length);
                }

                reqStream.Close();
                rdr.Close();

                System.Net.HttpWebResponse response = (HttpWebResponse)req.GetResponse();
                if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)
                {
                    String msg = String.Format("An error occurred while uploading this file: {0}\n\nError response code: {1}",
                        System.IO.Path.GetFileName(localFilename),
                        response.StatusCode.ToString());

                    LogWarning(msg, "2ACFFCCA-59BA-40c8-A9AB-05FA3331D223");

                    result = false;
                }
            }
            catch (Exception ex)
            {
                LogException(ex, "{E9D62A93-D298-470d-A6BA-19AAB237978A}");
                result = false;
            }

            return result;
        }

The class also contains the LogException() and LogWarning() methods.

When the application is launched, it parses the query string for some initial values.  The query string looks like this:

                string queryString = "Srv=clickonce&Sec=N&Doc=DMI&SiteName=&Speed=128000&Max=50";

This Srv is the path to the server (my Virtual Machine is name “clickonce”), the Sec is short for security – meaning HTTPS or HTTP, the Doc is the shortcut for which document library to use, and SiteName is the name of the SharePoint site.  Speed is used to calculate an estimate for download speed for each file.  We added this so our users uploading documents would realize how long it might take for clients in remote locations (using slow WAN connections) to download the documents.

The last value, Max, is the maximum size that the SharePoint site will allow documents to be.  This allowed us to give users a warning that a file is too large before we even attempt to upload it.

Another critical piece is the meta data collection.  We organized our site using SharePoint content types, so when the app loads, it gets a list of the document library’s content types.  The user then select one of the content types from the drop down list, and then we query SharePoint to get a list of the fields that make up that content type.  We used both an out of the box web service, and one that we custom built, in order to get these values.

Once we have the content type fields, we then add controls to the form.  Which type of control we add depends on the data type of the field.  (DateTime pickers for date/time fields, etc)  We didn’t write code to cover every data type, since we were working with a limited set of content types and field data types.

Here’s a screen shot of the Form, before and after someone has selected the content types and our code has added the custom controls:

CustomUploadBefore  CustomUploadAfter  

The other piece of meta data we collect is the in the upper right corner of the app, “Users with access”.  This box lists the different SharePoint Groups that we have set up and by checking the boxes, the user can set the permissions on the uploaded documents.

All of this meta data is collected and submitted to our custom web service, which then sets the values on the documents on the list.  We’ll look at these web services in a future post.

In the next post, we’ll walk through the Custom Action we built.

Integrating Windows Form Click Once Application into SharePoint 2007 – Part 1 of 4

Last year, I had the opportunity to build a solution that involved integrating a Windows Form application into a SharePoint 2007 (WSS version 3.0). In this post, I’ll layout our architecture thinking and in part two, I’ll describe the technical details.

Business Case

Our challenge was this: we needed an easy way for a small group of our users to upload documents, in batches.  They also needed to quickly set the meta data values, as well as set security on individual files.

Using the out of the box uploads just didn’t fit.  The single file upload allows set the meta data, but our users would be uploading dozens of files.  The multiple upload would allow our users to upload batches of files, but it doesn’t allow them to set the meta data during upload.  Also, neither upload method allows the users to set the permissions on the file.

Our Solution

We looked into building a web control of some kind, but ruled that out due to security complexities (if I remember correctly).  Another option would have been using a technology like Silverlight (or Flash?), but our team didn’t have the skills necessary to build with these.

So, after looking at what was technically possible, and also what skills our team had, we settled on a Windows Form application.  We also decided to deliver it to the clients via Click Once, so we would have the ability to easily update the application in the future.

Lessons Learned

After deploying our solution, we’ve learned a few lessons.  First, you’ll need to have the .Net Framework installed on the client computers.  We knew this, but we still ran into issues making sure our users had the proper framework version installed.  Second, we had issues with authentication.  Our issues were due to our testing domain being a separate Active Directory domain from the domain that our end users and their workstations were members of.  (See my earlier post about Clearing Saved Passwords for the fix to our problem).

Our third issue was how we dealt with uploading files that were named the same.  Our application would replace the existing file with the new file, which is the way we expected it to work.  However, our users wanted to upload weekly reports, named the same as the previous week.  We solved this by using folders within the document library to keep the sets of reports separate from previous weeks.

One last thing to consider before implementing a solution like this, is what browsers and platforms your users will be working from.  We only needed to support IE and Windows, which works fine.  However, if you need to support Firefox, there are add-ons that allow Click Once to work with Firefox.  This is still a Windows only solution though.  In order to support Macs, you’d have to focus on either browser techniques (AJAX?) or Silverlight/Flash.

Summary

Our users are happy with the Click Once app.  It allowed them to move all of their content to our SharePoint site in under a couple hours, which they were thrilled with.  We’re happy because we can easily deploy updates, our development time was small, and we met all of our business requirements.

Corrupted Views when migrating document libraries from SharePoint 2003 to 2007

A coworker of mine ran into this error recently, while migrating a document library from SharePoint 2003 to 2007:

“A WebPartZone can only exist on a page which contains a SPWebPartManager. The SPWebPartManager must be placed before any WebPartZones on the page.”

He saw this when he tried to see the All Documents view for the library.

After looking into it, we figured out what had happened.  He was migrating documents using the Explorer View in SharePoint.  He had copied the contents of the library from one server (a remote server that we didn’t have administrative access to) to his desktop.  He then opened an Explorer View of the new library and copied the files to it.  Well, it turns out he had copied the hidden “Forms” folder, which contained the files necessary to display the different views for the library. (He had set his explorer to show hidden files, which made them visible.)

explorerview

So, he had copied the 2003 forms to the 2007 library, which are incompatible.

We fixed it, by simply deleting the new document library, recreating it, and then copied everything except that hidden Forms folder.  Another option might have been to create a new document library on 2007, and copy the Forms folder from it to the broken library.  Since we didn’t need to save anything in the broken

<UPDATE>

Based on the feedback I received, we've confirmed that just moving the Forms folder from a newly created library to the broken one fixes it.  Just keep in mind, that any custom forms will need to be recreated (using a tool like SharePoint Designer).  If your broken library is just an out of the box library, you'll be fine.

</UPDATE>

BTW, I confirmed my suspicion with this blog post: http://palmettotq.com/blog/?p=54

SharePoint 2007 Parser Error after updating master page

A few weeks ago I was updating the master page for a SharePoint 2007 (WSS) site.  The client wanted the site updated to reflect the new look and feel that is being applied to another set of sites in the organization.

I created a new theme and master page, which I already wrote about here and here.  It worked well, except for a few pages on a subsite.  On those pages, I got the following error:

Server Error in '/' Application.

Parser Error

Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.

Parser Error Message: Code blocks are not allowed in this file.

 

I decided to go comb through my new master page and compare it to the existing master page that was already working.  After going through them line by line several times, I had no clue what would be causing the error because they were basically the same!

It turns out, it was a combination of two things.  First, on a few of the pages in the site, there was some include code (basically an <% EVAL()%> snippet).  This was the code that was triggering my error “Code blocks are not allowed in this file”. However, this code was working fine with the previous master page.

I decided to then try doing a full deployment of the site with the new master page, and it worked fine!  Apparently, if the master page is deployed using a Feature, then it is granted permission to allow code blocks, but if you upload pages either using web UI or SharePoint Designer, then the pages won’t be able to use code blocks.

I haven’t been able to pin down the rules or official info about this, but I thought others might find it useful anyway.

<UPDATE>

I recently read the Inside WSS 3.0 book which explains the reason for this issue. Pages deployed through the UI or SharePoint Designer are considered customized/unghosted/whatever you want to call it. As such they are not allowed to have script blocks outside of web part zones. This is intentional design by Microsoft so that potentially untrusted users aren't able to run malicious code on your server. Pages deployed through features and WSPs are non-customized/ghosted/etc and are allowed to have script blocks anywhere on the page.

-from the comments - Thanks Brian!

</UPDATE>