Welcome to MSDN Blogs Sign in | Join | Help

You just want the error message to be displayed on the default SharePoint error page. Is this an easy

In Microsoft.SharePoint.Utilities.SPUtility there are 2 functions:

  • SPUtility.TransferToErrorPage - this transfers the user to the administrative layouts page for error messages
  • SPUtility.TransferToSuccessPage - this transfers the user to the page one would see after something such as a long running transaction

There are a few overloads for each and they come in handy when developing custom admin pages or custom lists.

This one is an awesome feature to add if you've got an application running a workflow asynchronously and on-the-fly (see post on programmatically launching a workflow) and you want to coordinate the results of said workflow synchronously, so.you have to wait for the workflow to complete.

image

Check out Microsoft.SharePoint.SPLongOperation. This object allows you to begin and complete the long operation quite easily.

SPLongOperation operation = new SPLongOperation(this) {
  LeadingHTML = "This is a bold description of my operation",
  TrailingHTML = "This is a normal sub-description of my operation"
};

operation.Begin();
Thread.Sleep(5000);
operation.End("/redirectpage.aspx");

Following the lead of Sara Ford, I think I want to do a 'How do I' piece for SharePoint developers. I don't know how long this will last, but this is the first post.

First off, layout pages require the following directives. Try as I may, I have never been able to get around whatever permission demand is blocking layout pages from inheriting a specific class.

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page language="C#" MasterPageFile="~/_layouts/application.master" inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

The files must be placed in the %12-hive%\Template\Layouts directory-I usually create a custom folder underneath based on my specific feature to separate my custom code from SharePoint code. Incidentally, I recommend putting stylesheets, JavaScripts, and generic HTTP handlers here as well (the HttpHandler does allow you to create a code behind compile and deploy to this directory, which is cool). On your layouts pages you can add any web parts and custom controls you wish, you just have to add them manually as you don't have web part zones or personalization.

Note: By putting pages in the layouts directory, you may be breaching your security for the application as layouts pages are available to any authenticated user (not in a publishing scenario, though). Ensure that your pages are secured using controls such as the SPSecurityTrimmedControl.

In case you missed hearing about the new SDK, here's a great post on what's in it.

http://blogs.msdn.com/randalli/archive/2008/08/28/just-published-wss-and-moss-sdk-1-4-download-and-online-msdn-library-8-29-2008.aspx

I had enough trying to find this code, so I'm putting what I've pieced together to activate a workflow programmatically (specifically a SharePoint Designer Workflow)

public static string StartWorkflow(ContractListItem contract, string workflowName) {
            SPListItem wfListItem = contract.ListItem;
            SPWorkflowAssociationCollection wfAssocs = wfListItem.ParentList.WorkflowAssociations;
            SPWorkflowAssociation activeWorkflowAssoc = null;
            string errorMessage = string.Empty;

            foreach (SPWorkflowAssociation wfAssoc in wfAssocs) {
                if (wfAssoc.Name.Equals(workflowName)) {
                    activeWorkflowAssoc = wfAssoc;
                    break;
                }
            }

            // if the workflow exists, start the workflow
            SPWorkflow activeWorkflow = null;
            if (activeWorkflowAssoc != null) {
                try {
                    activeWorkflow = SPContext.Current.Site.WorkflowManager.StartWorkflow(
                        wfListItem,
                        activeWorkflowAssoc,
                        "<Data></Data>");
                }
                catch (Exception ex) {
                    ErrorHandler.LogError(Resources.ActionBarWebPart_WorkflowNotStarted, ex);

                    if (activeWorkflow != null)
                        SPWorkflowManager.CancelWorkflow(activeWorkflow);

                    errorMessage = Resources.ActionBarWebPart_WorkflowNotStarted;
                }
            }
            else {
                ErrorHandler.LogError(Resources.ActionBarWebPart_WorkflowDoesNotExist);
                errorMessage = Resources.ActionBarWebPart_WorkflowDoesNotExist;
            }
            return errorMessage;
        }
  }

I hate fishing around to get the publickeytoken for my strong named assemblies, so I fished around for a way to do it in Visual Studio. Here's what I did:

  1. Create a new External Tool - I called mine Get PublicKeyToken
  2. Map to the sn.exe file in the Windows SDK <DRIVE:\>Program Files\Microsoft SDKs\Windows\v6.0A\Bin\sn.exe
  3. Add the command you want to execute, in this case, it's -T $(TargetPath) - you can get the TargetPath monniker from the flyout.
  4. Check the Use Output window checkbox.

Get PublicKeyToken

Now, to execute it, select a *.dll file. You can select the file in the bin directory of your dev project, go to Tools->Get PublicKeyToken and the output will be rendered to the Output window.

So, I scoured the internet (all of it, I swear) to find some information on readonly fields in SharePoint. Apparently, SharePoint workflows can write to readonly columns. The only problem I'm having now is getting the column to show up in a columns list.

private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e) {
  SPList list = workflowProperties.List;

  if (!list.Fields.ContainsField(FIELD_NAME))
    list.Fields.Add(FIELD_NAME, SPFieldType.User, false);

  SPFieldUser field = (SPFieldUser)list.Fields[FIELD_NAME];
  field.Title = FIELD_NAME;
  field.AllowDisplay = true;
  field.ReadOnlyField = true;
  field.ShowInDisplayForm = true;
  field.ShowInEditForm = true;
  field.ShowInDisplayForm = true;
  field.ShowInEditForm = true;
  field.ShowInListSettings = true;
  field.ShowInNewForm = true;
  field.ShowInVersionHistory = true;
  field.ShowInViewForms = true;
  field.Update();
}

private void codeActivity1_ExecuteCode(object sender, EventArgs e) {
  workflowProperties.Item["restricted"] = "test: " + DateTime.Now.ToShortTimeString();
  workflowProperties.Item.Update();
}

Woo- hoo! It works!

I just bought a video camera online, so I figured I'd check out the Live Cashback program and see if it would work out well. It totally rocked. I found the camera I wanted at a better price than I could find on other online shopping search sites, then I found I would get 4% back on the camera price and 4% back on the warranty to accompany it.

This deal totally rocks!

I just got my Navigon 7100 GPS and I have to say I'm impressed. I really like the navigation quality of the GPS, especially compared to my TomTom ONE. I like the TomTom's ease of use--in fact, I like it better than my friends Garmin nuvi, but the TomTom ONE doesn't have some really important features that I need for my weekly trek's about the east coast.

Anyway, the Navigon has icons that match the chain restaurants and hotels, Zagat ratings (including the reviews), a built-in traffic receiver, and more POIs than my TomTom.

So, here's the bad:

If you've never had a GPS before, the UI is a little cumbersome. It comes with a stylus because some of the icons/buttons are too small to hit with your finger--almost frustrating enough to send back.

In the end, I think I love it because of its actual navigation quality. It takes the same routes I would take (which neither Garmin nor TomTom would do). I also love the RealityView when you're on a major highway with a lot of lane decisions, it shows signs on a highway and an arrow for which lane you should be in. Finally, I love the fact that it allows me to extremely easily choose a POI or interim stop without making me rebuild my navigation course.

I presented a Silverlight 1.0/2.0 demonstration yesterday at CodeCamp and it went quite well. Most importantly, it compiled; hey, that's half the battle.

The talk started out about how POX is not truly a ReST service because POX services are not usually provided as post-able services that control and manage information processing. With POX services, the idea is really to share the information with the user interface. So, I put together a service that provided customer order information from the AdventureWorks catalog. The information was provided in JSON format to the front end Silverlight 1.0 and 2.0 interface.

The Silverlight 1.0 portion of the demo went really fast, mainly because I kept the JavaScript in code snippets so I wouldn't end up debugging any of it. The Silverlight 2.0 piece took some time because I had to write all the code from scratch because I forgot to save the code in snippets. It did look like I knew what I was doing, though.

Anyways, thanks to Mike Snell, Craig Oaks, and Dave Hoerster for hosting CodeCamp! It was a fun time and I got to eat burritos.

I ran into a problem when trying to use Peschka's PartCheck code for My Site autoconfiguration to modify some boolean fields on the Colleague Tracker web part and found some modifications you'll need to make in order to set boolean fields on controls with this code:

private object GetPropertySetterValue(string Value) {
  ...
  switch {
    ...
    default:
      bool result;
      if (bool.TryParse(Value, out result))
        return result;

      break;
  }
  ...
}

This will allow the boolean value to be passed correctly to the control.

I'm speaking at Pittsburgh's Code Camp on April 12th located at University of Pittsburgh's Sennott Square. I'll be presenting POX services using WCF and ReST (although many will agree that POX is not truly ReST) to supply data to a Silverlight 1.0 application and I will use the same services with a Silverlight 2.0 application.

Depending on your hardware configuration IIS 6.0 does have a potential limit to the number of application pools. The TechNet FAQ for IIS stated here says:

The answer varies depending on the hardware and software configurations of your server as well as the types of content it hosts. If you are setting up your application pools with unique identities, depending on the applications and memory resources of your server, you will reach a limit of about 60 application pools. There are finite limits to some system resources that are allocated with each new logon session. This means that 60 processes can run concurrently as distinct accounts. IIS 6.0 supports running these processes in a single shared desktop, at a cost of sharing a single encapsulation of a user session among all parties.

There is a way to scale far beyond 60 worker processes, but why would anyone want to do that?

I posted these to Joel Oleson's MSDN blog, but since he's moving the blog, I didn't want to lose them.

Uber-nerd SharePoint versions of Star Wars lines:

  • These aren't the content databases you are looking for. (compliments Eric Charran)
  • If you only knew the power of the stsadm -o installfeature -force.
  • There will be a substantial reward to the Site Collection Admin that finds the deleted document, but I want it restored, no disintegration's!
  • What is thy bidding, my Master page?
  • Search your index, you know it to be true.

Enjoy!

Unfortunately, you cannot natively migrate web part pages to WCM publishing pages. The benefit of publishing pages manifests itself in the centralized page layouts. Web part pages cannot be modified universally, but publishing page layouts can be thusly modified.

So, to build a utility for migrating web part pages, it's a messy recipe. The result is only publishing pages with web parts intact, but not in the proper location nor is the page layout maintained; however, it is a better deal than to have to migrate web parts individually.

Start by making sure the web is a PublishingWeb:

if (PublishingSite.IsPublishingWeb)
    return PublishingWeb.GetPublishingWeb(web);

Get all web part pages by looping through all document libraries and getting web part pages:

foreach (SPListItem item in doclib.Items) {
    if (item.File.Url.EndsWith(".aspx")) {
        WL("Adding web part page: {0}", item.File.Url);
        webPartPages.Add(item.File.Url, item);
        count++;
    }
}

Next, create a new publishing page from a selected page layout (BlankWebPartPage.aspx is a good one):

// get the blank web part page layout
List<PageLayout> layouts = new List<PageLayout>(pubWeb.GetAvailablePageLayouts());
PageLayout layout = layouts.Find(
    delegate(PageLayout l) {
        return l.Name.Equals(pageLayoutName, StringComparison.CurrentCultureIgnoreCase);
    });

// get the pages collection and add the new publishing page
PublishingPageCollection pages = pubWeb.GetPublishingPages();

// change the name of the new page if it already exists
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='Text'>" + item.File.Name + "</Value></Eq></Where>";
SPListItemCollection items = pubWeb.PagesList.GetItems(query);
string pageName = item.File.Name;
if (items.Count > 0) {
    string fileExtension = DateTime.Now.ToString("_MMddyy_hhmmss");
    pageName = pageName.Replace(".aspx", fileExtension+".aspx");
}

// create the new page
PublishingPage publishingPage = pages.Add(pageName, layout);
publishingPage.Title = item.Title;

Get a few SPLimitedWebPartManagers to read the old web parts and store them and to create new web parts on the publishing page:

SPLimitedWebPartManager wppWpm = ((SPWeb)site).GetLimitedWebPartManager(item.File.Url, PersonalizationScope.Shared);
SPLimitedWebPartManager publishingWpm = ((SPWeb)site).GetLimitedWebPartManager(item.File.Url, PersonalizationScope.Shared);

Now you've got to copy the web parts and save the publishing page:

foreach (WebPart oldWP in oldPageWpm.WebParts) {
    // don't move the Title bar, it's already built into the page
    if (oldWP.ToString().Equals("Microsoft.SharePoint.WebPartPages.TitleBarWebPart")) {
        TitleBarWebPart titleBar = (TitleBarWebPart)oldWP;

        // do steal the title bar property data, though
        publishingPage.Title = titleBar.HeaderTitle;
        publishingPage.Description = titleBar.HeaderDescription;
    }

StringBuilder sb = new StringBuilder();
try {
    // export the web part to a stream
    oldWP.ExportMode = WebPartExportMode.All;
    XmlWriter xwriter = XmlTextWriter.Create(sb);
    wppWpm.ExportWebPart(oldWP, xwriter);
    xwriter.Close();

    // import the web part from the stream
    string output = sb.ToString();
    XmlReader xreader = XmlTextReader.Create(new StringReader(output));
    string errorMessage;
    WebPart newWP = publishingWpm.ImportWebPart(xreader, out errorMessage);
    xreader.Close();

    // check for import errors
    if (!string.IsNullOrEmpty(errorMessage)) {
        UpgradeLog(errorMessage, EventLogEntryType.Error);
    }

    // add the web part to the page
    publishingWpm.AddWebPart(newWP, "Header", oldWP.ZoneIndex);
}
catch (Exception ex) {
    Debug.WriteLine(ex.ToString());
}
}
publishingPage.Update();

More Posts Next page »
 
Page view tracker