Archives

Archives / 2009 / February
  • SharePoint 14 delayed until 2010

    Tags: SharePoint, Microsoft Office

    Not that unexpected, but Microsoft chief executive office Steve Ballmer has confirmed that the new Office 14 clients and servers (read SharePoint) will not be released during 2009. The products will be “generally available” during 2010.

    Generally available may indicate (I’m always positive) that we have a release for volume licensees and partners in late 2009 and in the shelves during 2010 (just like the previous version).

    Read SharePoint Daily Special Edition for more information.

    I’m not surprised since the lid has been on for so long and for such a significant server product such as SharePoint it really needs time for a beta phase. The adoption of WSS 3.0 and MOSS 2007 has really boosted and having a to short period for extensive testing and upgrade preparation would not be appreciated.

    Now we all can wait for the beta period to start, and hope for a universal change in the number of hours per day, I sure need some more…

  • Custom code with SharePoint Online and Windows Azure

    Tags: .NET, C#, SharePoint, Visual Studio, Microsoft Office, Office Open XML, Windows Azure

    When I first heard about SharePoint Online at the PDC 2008 I was a bit disappointed that you could not use custom code but had to rely on the built-in functionality and the things you could do with SharePoint Designer (which is quite powerful anyway, especially with jQuery).

    To read more about SharePoint online, head over to Tobias Zimmergrens blog.

    But with some clever techniques you can take advantage of the Windows Azure Hosted Services and create your custom code. I will show you how to create some custom code, which normally is done by SharePoint event receivers or timer jobs, using a Worker Role in Windows Azure.

    The post is quite lengthy but I have skipped some of the code snippets to make it easier to read. You will find downloads of the C# code at the end of this post.

    Scenario

    This sample assumes that we have a Document Library in SharePoint Online containing Word 2007 documents for Proposals. The library is called Proposals. We want to make sure that our Proposals has all comments removed before we send it away to our clients. This comment removal procedure will be initiated by a workflow.

    The Document Library

    Action column The Proposals Document Library is a standard SharePoint Document Library with versioning enabled. First we add a new column to the library called Action. This column will contain the status of the comment removal procedure. It has the type Choice and three possible values; RemoveComments, Processing and Done.

    The workflow

    Using SharePoint Designer we create a new workflow, called Prepare Proposal. The workflow has three stages. The workflow is initiated manually

    1. SPD Workflow designerSet the Action column to the RemoveComments value
    2. Wait for Action column to get the value Done
    3. Send e-mail the the author

    Pretty simple workflow but all using the out-of-the-box SharePoint Designer activities.

    So far everything is basic SharePoint Online “development”, but now we head on over to the custom coding parts.

    Create the Hosted Service Worker Role

    Using Visual Studio 2008 and the Windows Azure SDK we can create a new project of the type Worker Cloud Service. This cloud service will contain all our code for removing the comments from our Word documents.

    Cloud Service templates

    When the project is created, actually a solution with two projects, we add two references to our Worker Role project; WindowsBase.dll and DocumentFormat.OpenXml.dll (from Open XML SDK 1.0). The Open XML reference must also have the Copy Local property set to true, so that the assembly is deployed with the hosted service App Package.

    Note: Version 2 of Open XML SDK can not be used since it does not have the AllowPartiallyTrustedCallers attribute, which is required for all referenced assemblies in Windows Azure.

    Add a service reference

    The worker role will use the SharePoint Online web services to read and update data so we need to add a Service Reference to our SharePoint online site and the Lists.asmx web service. When this is done an App.config file is created, just delete it - we have to set our bindings and endpoints in the code so it is fully trusted.

    In your worker role Start method you create code as follows:

      1: BasicHttpBinding binding = new BasicHttpBinding();
    
      2: binding.Security.Mode = BasicHttpSecurityMode.Transport;
    
      3: binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    
      4: binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
    
      5: binding.Security.Transport.Realm = WorkerRole.Realm;
    
      6: binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

    WorkerRole.Realm contains a string with your SharePoint Online Realm with a value something like this: “XXXXXmicrosoftonlinecom-1.sharepoint.microsoftonline.com”.

    Let’s code!

    The worker role will poll the Proposals library for new documents which has the RemoveComments value in the Action column. This is done with the GetListItems method and a query selecting only those documents.

      1: XElement query = new XElement("Query",
    
      2:     new XElement("Where",
    
      3:         new XElement("Eq",
    
      4:             new XElement("FieldRef",
    
      5:                 new XAttribute("Name", "Action")
    
      6:                 ),
    
      7:             new XElement("Value",
    
      8:                 new XAttribute("Type", "Choice"),
    
      9:                 "RemoveComments"))));
    
     10: 
    
     11: XElement queryOptions = new XElement("queryOptions");
    
     12: 
    
     13: var itemsXml = client.GetListItems("Proposals", string.Empty, query.GetXmlElement(), null, string.Empty, queryOptions.GetXmlElement(), string.Empty);
    
     14: 
    
     15: XNamespace s = "http://schemas.microsoft.com/sharepoint/soap/";
    
     16: XNamespace rs = "urn:schemas-microsoft-com:rowset";
    
     17: XNamespace z = "#RowsetSchema";
    
     18: 
    
     19: var docs = itemsXml.
    
     20:     GetXElement().
    
     21:     Descendants(z + "row").
    
     22:     Select(x => new {
    
     23:         Title = (string)x.Attribute("ows_LinkFilename"),
    
     24:         Id = (string)x.Attribute("ows_ID"),
    
     25:         Url = WorkerRole.Url + "/Proposals/" + (string)x.Attribute("ows_LinkFilename")
    
     26:     });
    
     27: 

    Now we have all our proposals in the docs variable and we can iterate over it.

      1: foreach (var doc in docs) {
    
      2:     RoleManager.WriteToLog("Information", "Processing: " + doc.Title);
    
      3:     setStatusOnDocument(client, doc.Id, "Processing");
    
      4: 
    
      5:     WebRequest request = HttpWebRequest.Create(doc.Url);
    
      6:     request.Credentials = new NetworkCredential(WorkerRole.Username, WorkerRole.Password);
    
      7: 
    
      8:     MemoryStream mems = new MemoryStream();
    
      9: 
    
     10:     using (WebResponse response = request.GetResponse()) {
    
     11:         using (Stream responseStream = response.GetResponseStream()) {
    
     12:             byte[] buffer = new byte[8192];
    
     13:             int offset = 0;
    
     14:             int count;
    
     15:             do {
    
     16:                 count = responseStream.Read(buffer, 0, 8192);
    
     17:                 offset += count;
    
     18:                 mems.Write(buffer, 0, count);
    
     19:             } while (count != 0);
    
     20:             using (WordprocessingDocument wpDoc = WordprocessingDocument.Open(mems, true)) {
    
     21:                 RemoveComments(wpDoc);
    
     22:             }
    
     23:             using (WebClient wc = new WebClient()) {
    
     24:                 wc.Credentials = new NetworkCredential(WorkerRole.Username, WorkerRole.Password);
    
     25:                 wc.UploadData(doc.Url, "PUT", mems.ToArray());
    
     26:             }
    
     27:         }
    
     28:     }
    
     29:     setStatusOnDocument(client, doc.Id, "Done");
    
     30:     RoleManager.WriteToLog("Information", "Comments removed from: " + doc.Title);
    
     31: }
    
     32: 

    First of all we have a method which updates the Action column of the item to Processing to indicate to the users that we are currently processing this document. Then we read the document into a stream and pass it into a WordprocessingDocument object. On that object we do the RemoveComments method which removes all comments in the Word 2007 document.

    Note: Have a look at Eric Whites blog for some nifty operations on your Open Xml documents, such as the remove comments function.

    When all comments are removed we upload the file back to SharePoint and set the status to Done, and we are all set.

    As you can see I use three different techniques to read from the SharePoint Online site; the SharePoint web services, request-response and a simple WebClient. Isn’t coding fun!

    Test

    One of Windows Azures beauties is that you can test it on your local machine using the Azure Development Fabric. Just hit F5 in Visual Studio.

    Development Fabric

    This allows you to debug and fine tune your code and see how it scales out.

    Deploy to Windows Azure Staging environment

    Hosted Service When you are sure that there are no bugs just choose Publish in Visual Studio and it will open up a web browser which directs you to the Azure Services Developer Portal, there you can create a new Hosted Service Project or choose an existing one to use for this Proposals service.

    To deploy this solution we have to upload two files; the  App Package containing all your code and references and the Configuration Settings file, both automatically created by Visual Studio.

    Deploying

    When this is done you have to wait a few minutes so Windows Azure can allocate resources for you and prepare the environment. Once this is done you are ready to test your service in the cloud.

    In staging

    Just hit the Run button and after a minute or so the Proposals Service is running - in the cloud!

    Deploy to Production

    For us used to the SharePoint environment and the mess it is to get from development to staging to production environment it’s so much fun to just hit Deploy in Windows Azure to move the Staging service to Production.

    Production

    As you can see from the image above this solution is now in production and started with one worker role.

    Run the workflow

    Now we can test our complete solution by uploading a document containing some comments. Then initiate the Prepare Proposals Workflow.

    Prepare Proposal

    The document will be updated with a new value in the Action column.

    In Progress

    After a few seconds the document is updated by the cloud worker role we created, the workflow completes, there are no more comments in the document and the author should have received an e-mail with information that the workflow is done.

     Done

    Summary

    This was a short demonstration on what you can do with the standard functionalities of Microsoft's cloud offerings such as SharePoint Online and Windows Azure. The scenario was quite simple but using the same techniques you can elevate your SharePoint Online experience and functionality a lot.

    Using Windows Azure is one possibility to host your online custom code, hosting it on on-premise servers is another option and it works just as fine.

    Note: I have not made this sample so it can scale out nor does it handle any exceptions. Take it what its for.

    If you would like to look at the code you can find it here.

    Happy coding!

  • Web Part Properties - part 6 - Complex Properties

    Tags: .NET, SharePoint, Web Parts

    To end my series of Web Part properties I would like to show how to store more complex values than just strings or integers. What happens if you would like to store a more complex object; an array or a coordinate etc?

    Editing these properties with the standard generated interface using the WebBrowsable and Personalizable attributes will not work, since it only accepts basic types, shown in part 1. To make these properties editable you have to (almost…continue reading) create an EditorPart, shown in part 2, and control the properties in the SyncChanges and ApplyChanges methods.

    Note: For more information on which basic types that can be used in the standard toolbar see the PropertyGridEditorPart documentation on MSDN.

    SharePoint Designer and complex properties

    If you are using SharePoint Designer to edit your Web Parts, you can manage your complex properties a little bit more. For example if you store have string array, it will not be visible in the web interface, but it can be edited in SharePoint Designer.

      1: [WebBrowsable]
    
      2: [Personalizable]
    
      3: public string[] AnArray {
    
      4:     get;
    
      5:     set;
    
      6: }

    The property above will not be seen in the web interface but in SharePoint Designer it will look like this:

    SPD

    If you click on the ellipsis button you will get a dialog in which you can edit the string array.

    Custom objects without EditorParts

    But if you still don’t want to create your own EditorPart you can use some .NET development techniques, familiar to you who have been working a lot with WinForms.

    Let’s say that we want to have a Coordinate property that we would like to use in our web part, the class should look like this:

      1: public class Coordinate {
    
      2:     public float Latiude;
    
      3:     public float Longitude;
    
      4:     public override string ToString() {
    
      5:         return this.Latiude.ToString(CultureInfo.CurrentCulture) + 
    
      6:                ":" + 
    
      7:                this.Longitude.ToString(CultureInfo.CurrentCulture);
    
      8:     }
    
      9: }

    And we expose it through our web part like this:

      1:  [WebBrowsable]
    
      2:  [Personalizable]
    
      3:  public Coordinate Coordinate {
    
      4:     get;
    
      5:     set;
    
      6: }

    This property will not be shown in the web interface and will not be editable in SharePoint Designer. Now to get this to work, editable in the web interface as well as in SPD, we have to create an implementation of the ExpandableObjectConverter class. We will override four methods so we can convert a Coordinate to and from a string representation. The string representation is implemented in the ToString() in the code above. The whole implementation of the class looks like this:

      1: public class CoordinateConverter : ExpandableObjectConverter {
    
      2:     public override bool CanConvertTo(ITypeDescriptorContext context, 
    
      3:                          System.Type destinationType) {
    
      4:         if (destinationType == typeof(Coordinate))
    
      5:             return true;
    
      6:         return base.CanConvertTo(context, destinationType);
    
      7:     }
    
      8:     public override object ConvertTo(ITypeDescriptorContext context, 
    
      9:                            CultureInfo culture, object value, 
    
     10:                            System.Type destinationType) {
    
     11:         if (destinationType == typeof(System.String) && 
    
     12:             value is Coordinate) {
    
     13:             Coordinate coord = (Coordinate)value;
    
     14:             return coord.ToString();
    
     15:         }
    
     16:         return base.ConvertTo(context, culture, value, destinationType);
    
     17:     }
    
     18: 
    
     19:     public override bool CanConvertFrom(ITypeDescriptorContext context, 
    
     20:                          System.Type sourceType) {
    
     21:         if (sourceType == typeof(string))
    
     22:             return true;
    
     23:         return base.CanConvertFrom(context, sourceType);
    
     24:     }
    
     25:     public override object ConvertFrom(ITypeDescriptorContext context, 
    
     26:                            CultureInfo culture, object value) {
    
     27:         if (value is string) {
    
     28:             try {
    
     29:                 string s = (string)value;
    
     30:                 string[] arr = s.Split(':');
    
     31:                 Coordinate coord = new Coordinate();
    
     32:                 coord.Latiude = float.Parse(arr[0], 
    
     33:                                 CultureInfo.CurrentCulture);
    
     34:                 coord.Longitude = float.Parse(arr[1], 
    
     35:                                   CultureInfo.CurrentCulture);
    
     36:                 return coord;
    
     37:             }
    
     38:             catch {
    
     39:                 throw new ArgumentException("Can not convert '" + 
    
     40:                           (string)value + "' to type a Coordinate");
    
     41:             }
    
     42:         }
    
     43:         return base.ConvertFrom(context, culture, value);
    
     44:     }
    
     45: }

    To make SharePoint and SharePoint Designer aware of that we now can convert our Coordinate class to a string representation we have to set the TypeConverter attribute on our Coordinate class. The final Coordinate class should look like this:

      1: [TypeConverter(typeof(CoordinateConverter))]
    
      2: public class Coordinate {
    
      3:     public float Latiude;
    
      4:     public float Longitude;
    
      5:     public override string ToString() {
    
      6:         return this.Latiude.ToString(CultureInfo.CurrentCulture) + 
    
      7:                ":" + 
    
      8:                this.Longitude.ToString(CultureInfo.CurrentCulture);
    
      9:     }
    
     10: }

    If we now modify our web part we will see this in the web interface, sweet huh?

    Coordinate

    If we try to set it to any other value than we allow in our ConvertFrom method above, then we will get a nice error:

    Coordinate error

    The Coordinate property can also be edited in SharePoint Designer:

    Coordinate editing in SPD

    Other methods

    There are other ways to make your web part handle complex properties. Wesley Bakker has a nice write up on how to use JSON notation to store and edit the values.

    That was all folks…

  • Once SharePoint, Always SharePoint - what's wrong with that?

    Tags: SharePoint

    Computer Sweden has an article in today's issue, also published online yesterday, called “Impossible to get rid of the cash cow of Microsoft”. To sum it up briefly it discusses how hard it is to get rid of Microsoft SharePoint once you have it installed in your environment and that the licensing costs flies away. An interviewed CTO states that companies he met don’t have control of their SharePoint installations and that they had to step back and look at the ownership and licenses.

    The article has some substance, but is is only viewed from one point.

    First of all one thing I agree with and that I have experience of - SharePoint installation madness. SharePoint is a complicated product/application/platform and should not be installed or launched if you do not have time or experience of it, period. You should have someone to help you out with these projects so that you have control of your installation, storage, security, governance etc etc. Planning - this is the most basic rule of any SharePoint installation.

    Second to think of is the article byline: “Once SharePoint, always SharePoint”. This is in the article read with a negative emphasis. If you invest and plan a SharePoint installation you should of course look into the future, but that future is probably not measured in months it’s most probably the platform you company will use over a foreseeable future. Once again the keyword here is Planning, and this has nothing to do with the platform. Every platform of this magnitude; WebSphere, Alfresco, DotNetNuke whatever you choose requires that you plan your installation and maintenance.

    If you one day discover that you need to change platform, this will cost and take time, independent of your choice of platform. Just make sure that it’s well documented and supports standards. SharePoint currently supports several standards, it will support the new CMIS (Content Management Interoperability Services) and thanks to Microsoft's new openness it’s quite well documented.

    Pricing and licensing is always a tough area to discuss. Yes, using SharePoint will cost you in server licenses and CALs but it’s not more expensive than comparable competitors. If you want to publish a web site and use all the powerful enterprise features of SharePoint you can’t expect the licenses to be the same as for a simple CMS system, in that case you choose the wrong platform to start with - Planning.

    I truly believe that if you invest in SharePoint you will definitely have a platform that you will use for a long time and not wanting to phase out.

  • Sweden SharePoint User Group meeting 9th of February 2009

    Tags: SharePoint

    The Sweden SharePoint User Group had the first meeting of this year this evening and it was a great and interesting meeting. I really enjoyed the session by Rickard Löfberg from Credit Suisse when he talked about their globally rolled out collaboration platform, how they handled the massive amounts of data and customized the platform to have a smooth transition to upcoming versions of SharePoint.

    I did two short sessions; one about Web Part development with 10 tips and a very short introduction to the Geneva Framework. You can find the presentations for download below, currently in Swedish - but if you beg I can finish up the translation to English.

    * I did two updates to the slide; corrected the sentence about Web Part connections and added a note on CAS, which we discussed.

    I’m now looking forward to the next meeting.

  • SPDiag - a SharePoint IT-Pro necessity

    Tags: SharePoint

    The latest version of the SharePoint Administration Toolkit has been released and it contains a new feature called SPDiag (SharePoint Diagnostics Tool).

    It’s a new tool for IT-Pros (or admins and developers for that matter) that helps you have a look at all of your SharePoint server (including hardware) configurations in a nice application. You can check everything from log file locations to web.config content to database configurations to…

     SPDiag

    imageYou have two views, one tree view in which you can drill down in your server/farm configuration and read all of the data and one Trends view in which you analyze and examine the usage of your farm and sites using the different logs in SharePoint and Windows. Unfortunately I did not get the graphs to show on my test machine (yes it’s a Windows 7 with WSS :-), see error message on the right.

    I don’t see this as just a IT-Pro tool, I think it will be great for developers when debugging and looking for problems, since you have a great view of the log files.

    Anyway, I guess you would like to download it all now…

  • Swedish SharePoint bloggers master feed

    Tags: SharePoint

    For the SharePoint Community Sweden community site I have created a master feed containing interesting blog feeds from Swedish SharePoint bloggers, with posts both in Swedish and English.

    This master feed can be found at: http://pipes.yahoo.com/wictorwilen/spcs_all?_render=rss

    Add this feed to your subscriptions if you are interested about how we Swedes think and elaborate on SharePoint.

    It’s built using the amazing Yahoo Pipes.

  • Web Part Properties - part 5 - localization

    Tags: SharePoint, Web Parts

    If you are building a reusable web part that you would like to sell or give away you should look into localizing your web part. The localization support is great with .NET 2.0, using resource files, and there are tons of built-in functionality for this. If you are creating an Editor Part for you web part, then your life is pretty easy, just use the standard techniques. But if you are using the approach by tagging the properties of your web part with the Personalizable attributes, then you are out of luck. Take a look at this property for example:

      1: [WebBrowsable]
    
      2: [Category("Look and feel")]
    
      3: [WebDisplayName("Use custom palette")]
    
      4: [WebDescription("Check this to use a custom palette")]
    
      5: [Personalizable(PersonalizationScope.Shared)]
    
      6: public bool UseCustomPalette {
    
      7:     get;
    
      8:     set;
    
      9: }
    
     10: 

    All these attribute values are hardcoded into you assembly, and you have to build different assemblies for different languages.

    Fortunately the .NET framework is extensible so you can extend these attributes (Category, WebDisplayName and WebDescription) so they take advantage of the localization features.

    All we have to do is to implement three classes: LocalizedCategoryAttribute, LocalizedWebDisplayNameAttribute and LocalizedWebDescriptionAttribute.

    They basically look the same and here is the code for LocalizedWebDisplayNameAttribute:

      1: [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    
      2: public sealed class LocalizedWebDisplayNameAttribute
    
      3:     : WebDisplayNameAttribute {
    
      4: 
    
      5:     bool m_isLocalized ;
    
      6: 
    
      7:     public LocalizedWebDisplayNameAttribute(string displayName)
    
      8:         : base(displayName) {
    
      9:     }
    
     10: 
    
     11:     public override string DisplayName {
    
     12:         get {
    
     13:             if (!m_isLocalized) {
    
     14:                 this.DisplayNameValue = 
    
     15:                     Resources.ResourceManager.GetString(
    
     16:                         base.DisplayName, Resources.Culture);
    
     17:                 m_isLocalized = true;
    
     18:             }
    
     19:             return base.DisplayName;
    
     20:         }
    
     21:     }
    
     22: }
    
     23: 

    What we do here is to override the DisplayName property and instead of just returning the value, we use the ResourceManager object and retrieve the value from our localized resources.

    After this we can change our original property code to something like this:

      1: [WebBrowsable]
    
      2: [LocalizedCategory("LookAndFeel")]
    
      3: [LocalizedWebDisplayName("UseCustomPalette")]
    
      4: [LocalizedWebDescription("UseCustomPaletteDesc")]
    
      5: [Personalizable(PersonalizationScope.Shared)]
    
      6: public bool UseCustomPalette {
    
      7:     get;
    
      8:     set;
    
      9: }

    To get this to work just add the three attribute values (LookAndFeel, UseCustomPalette, UseCustomPaletteDesc in the sample) to our resource file and you are set and done.

About Wictor...

Wictor Wilén is a Director and SharePoint Architect working at Connecta AB. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for four consecutive years.

And a word from our sponsors...

SharePoint 2010 Web Parts in Action