Sharepoint webpart for Silverlight 2.0

March 31, 2008

Before the last release of Silverlight 2.0 beta 1 embedding a silverlight application in a webpart was a somewhat tedious thing to do. Basically, you had to deploy your silverlight.dll and .xaml file within your Sharepoint solution as well as a bunch of javascript files that are called from your sharepoint webpart to create your silverlight content.

With the last release of Silverlight 2.0 all this steps are no longer necessary and we can easily host Silverlight Applications in Sharepoint Webparts just by including Silverlight controls and deploying .xap files with our solution.

For this example, I’ll create a generic webpart that will be able to load any .xap file accessible at our server. It will have a XapUrl property where we will be able to write a Url where the webpart will search for the xap file. Obviously this means that the .xap file must be deployed in our server, I usually do that by including the file in my Sharepoint Solution and deploying it in the _layouts folder of my server.

So following, is the code of my Silverlight Webpart


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Runtime.InteropServices;
using System.Web.UI;

namespace Examples.SilverLight
{
    public class SilverLightWebpart : System.Web.UI.WebControls.WebParts.WebPart
    {
        private ScriptManager _scriptHandler;
        private System.Web.UI.SilverlightControls.Silverlight _silverlightControl;

        private string _xapUrl = string.Empty;
        [WebBrowsable(true), Personalizable(true)]
        public string XapUrl
        {
            get { return _xapUrl; }
            set { _xapUrl = value; }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            _scriptHandler = ScriptManager.GetCurrent(this.Page);
            if (_scriptHandler == null)
            {
                _scriptHandler = new ScriptManager();
                _scriptHandler.ID = "ScriptManager1";
                this.Controls.Add(_scriptHandler);
            }

            if (!string.IsNullOrEmpty(_xapUrl))
            {
                _silverlightControl = new System.Web.UI.SilverlightControls.Silverlight();
                _silverlightControl.ID = "Xaml1";
                _silverlightControl.Source = _xapUrl;
                _silverlightControl.Version = "2.0";
                this.Controls.Add(_silverlightControl);
            }
        }



    }
}

Basically what we are doing is adding a ScriptManager and a Silverlight controls to our class. The only things we have to take care of are to ensure that there is only one instance of a ScriptManager in our current page (we do that with the call ScriptManager.GetCurrent(this.Page);) and that we must create the controls in the OnInit event of our webpart instead of the usual CreateChildControls event if not, we will get an exception from ScriptManager.RegisterScriptControl telling “Script controls may not be registered before PreRender

And that’s all, see how simple is now to host silverlight applications in our sharepoint server !


SPWebConfigModification: configure applicationSettings with a Feature

March 5, 2008

Features are one of the coolest additions in WSS 3.0 if not the most. Not only they help us as software developers to encapsulate our code in redistributable packages but also simplify mainteinance tasks and deployment to server administrators. Ultimately, we’d like to be able to deploy our site definition with just a walk-through installer that would activate the required features.

Quite often I have to make some changes in the web.config files of the web applications where my solutions are to be deployed. Manifest.xml files of Sharepoint Solutions helps us with this task providing us with some easy ways to alter the web.config files, unfortunately, we are limited at registering safe controls and alter security policies which sometimes is not enough.

What I usually want to do, is to deploy a particular applicationSettings section for an assembly used in my solution, in this example, I have an assembly that uses an httpHandler to retrieve some Xml and I have configured the url of the handler in a settings file, this way, I’ll be able to change it if needed. This is the settings sections I need to include in the web.config file of the web application in order to do that:

<configuration>
  <configSections>
    <sectionGroup name=”applicationSettings” type=”System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″> <section name=”MyAssembly.Properties.Settings” type=”System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ requirePermission=”false” /></sectionGroup>
  </configSections>
  <applicationSettings>
    <MyAssembly.Properties.Settings>
      <setting name=”HttpHandlerUrl” serializeAs=”String”>
        <value>https://oricode.wordpress.com</value&gt;
      </setting>
    </MyAssembly.Properties.Settings>
  </applicationSettings>
</configuration>

There is no easy way to automate this process. The usual procedure would be to deploy the solution and then ask the administrators to change the web.config files of all the involved web applications manually but this is what I was trying to avoid. I decided to take a look at the new SPWebConfigModification objects in the SPWebApplication object and use them in a Feature Receiver that would modify the web.config file.

At first sight, it seems that this objects are specially suited for this task and that it would be an easy thing to do, but it is not. SPWebConfigModification class is usefull when adding and replacing sections in the configuration files but it’s not easy to modify existing ones by adding new child sections. Basically, what you do is to select a section by providing a xpath and provide the new xml value for the section, but what happens when we don’t want to replace the section but just append new child nodes to it ?

For example in my case, I had to consider the following scenarios:

1. The webconfig file didn’t had the applicationSettings section defined (which is something very usual)
2. The webconfig file already had the applicationSettings section defined (by another assembly) and I had to append the new section for my assembly in it

So I couldn’t just replace the whole section with the xml for my assembly because this might have an impact in any already configured assembly used in the web application that relied in the applicationSettings section.

So here is the code of my FeatureActivated event where I would handle those modifications:

Initialization:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = (SPSite)properties.Feature.Parent;
    SPWebApplication webApp = site.WebApplication;

Since there is no way to know if the applicationSettings section is already defined in the web.config file, my solution is to execute the modifications as if the section was defined and if that rises an exception, it would mean that it is not and then I could add it. I know it is not an elegant approach and I’d really appreciate it if anybody could come up with a better solution !

try
{
    webApp.WebConfigModifications.Clear();

    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"section[@name='MyAssembly.Properties.Settings']";
    configMod.Path = "configuration/configSections/sectionGroup[@name='applicationSettings']";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Add(configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"MyAssembly.Properties.Settings";
    configMod.Path = "configuration/applicationSettings";
    configMod.Sequence = 0;
    configMod.Owner = properties.Feature.DefinitionId.ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "" +
                            "" +
                                "https://oricode.wordpress.com" +
                            "" +
                       "";
    webApp.WebConfigModifications.Add(configMod);

    webApp.Farm.Services.GetValue().WebConfigModifications.Clear();
    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

If the section was already defined, the previous code would launch a <em>SPException</em>. Unfortunately, since the exception is not better typed, there is no way to ensure that the error is caused by the applicationSettings section not being configured (we could always read the exception message that will tell us if so). So if the exception is raised, we have to add the sections as new <em>SPWebConfigModification</em> objects, the previous modifications were added to the <em>WebConfigModifications</em> collection of the <em>SPWebApplication</em> object so there’s no need to add them again.

catch (SPException ex)
{
    SPWebConfigModification configMod = new SPWebConfigModification();
    configMod.Name = @"sectionGroup[@name='applicationSettings']";
    configMod.Path = "configuration/configSections";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = " " +
                      "";
    webApp.WebConfigModifications.Insert(0, configMod);

    configMod = new SPWebConfigModification();
    configMod.Name = @"applicationSettings";
    configMod.Path = "configuration";
    configMod.Sequence = 0;
    configMod.Owner = Guid.NewGuid().ToString();
    configMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    configMod.Value = "";
    webApp.WebConfigModifications.Insert(0, configMod);

    webApp.Update();
    webApp.Farm.Services.GetValue().ApplyWebConfigModifications();
}

Note that this two last configuration sections have their Owner property set to a random Guid string. This is because the owner property is used in the feature deactivating event for removing the web.config changes, however, I can’t remove the applicationSettings sections because after my feature is activated, some other application settings might be registered in those sections by the server administrator and If then my feature was deactivated, all of them would be deactivated as well.

There are also a lot of issues I’ve faced when using SPWebConfigModification objects, like modifications that are persisted in the WebConfigModifications collections of the webApplication object before any call to the update() method is made (this is why I’ve set the Clear() methods of the collection at the start of the code) and other similar issues. Feel free to comment any strange behaviour you might find and I’ll try to help you If possible.

And that’s it, activating this feature will modify the web.config file of the web application as desired. However, I hope in future releases of Sharepoint object model we will see a better way to achieve this.

To end, I’d like to point that this solution should be tested in a farm deployment where multiple servers are involved, use with caution in that scenarios !

That’s all for now 🙂 as always, comments are welcome !


Sharepoint: publish a webservice with your solution

February 29, 2008

In this article I’ll cover how can you publish a WebService with your solution that will be accessible at every site in all the web applications where the solution is deployed. To keep thing simple, I’ve chosen a simple Helloworld webservice but with this approach we could publish any webservice or .aspx pages or a httpHandler

What we will do is to publish the service in the _vti_bin sharepoint folder of our site. To do this, we must first copy the .asmx file of the service in the Common FilesMicrosoft Sharedweb server extensions12ISAPI folder of our server.

<%@ WebService Language=”c#” Class=”MyAssembly.Services.HelloWorldService,MyAssembly.Services”%>

This was the .asmx file we want to distribute and following is the .DDF file from which we’ll generate the solution, I’d like to show that you could also publish a web.config file for the _vti_bin/MyAssembly folder, we would want to do so if, for example, were deploying an httpHandler instead of a webservice or if we need to configure some AppSettings parameters.

; Solution.DDF
FeaturesISAPIMyAssemblyweb.config ISAPIMyAssemblyweb.config
FeaturesISAPIMyAssemblyHerramientas.asmx ISAPIMyAssemblyHelloWorldService.asmx
ServicesbinDebugMyAssembly.Services.dll MyAssembly.Services.dll

Then we must define the manifest.xml file used in the solution deployment:

<RootFiles>
<RootFile Location=ISAPIMyAssemblyweb.config />
< RootFile Location=ISAPIMyAssemblyHelloWorldService.asmx />
</RootFiles>

<Assemblies><Assembly DeploymentTarget=WebApplication Location=MyAssembly.Services.dll /></Assemblies>

Remember to set the appropiate assembly permission at the CodeAccessSecurity section of the manifest.xml file if not running under Full Trust mode or not deploying the assembly in the GAC.

Finally, here is the easy code of our Helloworld service:

namespace MyAssembly.Services
{
    [WebService(Namespace = "http://MyAssembly.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class HelloWorldService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Greetings from this Service !";
        }

    }
}

Now, after deploying the solution we can access the webservice adding _vti_bin/MyAssembly/HelloWorldService.asmx at our site url (for those sites we have deployed the solution to !). However, If we have deployed the solution in the GAC, the service should be able to all the sites in the server.

That’s all for now ! As always, feel free to comment anything you might find interesting !


Deploy a XML file in your Sharepoint Solution to the Web Application Folder

February 27, 2008

This is a small trick I’ve been using these days in a Sharepoint Solution in order to add a xml file with some data which some webpart will read later on a Sharepoint site and make use of it. There are many ways to accomplish this each one with it’s benefits and drawbacks.

The first approach I thought of was to deploy the xml file in the _layouts folder and then query the data using a WebClient request from my webpart, the problem I found is that if anonymous access wasn’t enabled for the site, the request failed with a 401 Unauthorized exception because the ASP.Net credentials where the ones used by default to access the resource. One solution was to enable the impersonation at the web.config file of the application and then, the current user’s credentials would be used instead, but as I didn’t know where my solution would be deployed and didn’t have any control over the web.config file configuration, I rejected this approach.

Finally, I decided to deploy locally the xml file in the bin folder of the application and then accessing it with a simple StreamReader. The first problem was how to deploy the file ? Finally, I found the small trick I was talking about, you can deploy any file from a Sharepoint Solution in the bin folder of the web application just by doing the same as you do with a normal assembly .dll file, with the <Assembly> tag in the manifest.xml file:

<Assemblies><Assembly DeploymentTarget=WebApplication Location=MyData.xml /></Assemblies>

When the Solution is deployed, the xml file will be copied to all the bin folders of the selected web application. This raises the obvious drawback of this approach: There’s one xml file for each web application and there’s no easy way to globally manage them.
Since we will be accessing the xml file from our webpart, and assuming we haven’t set our trust level to Full (because we know it’s a bad thing to do!), we must grant our assembly the FileIOPermission  in order to be able to read the file. We can do this in the manifest.xml with the following section:

<CodeAccessSecurity><PolicyItem><PermissionSet class=NamedPermissionSet version=1 Description=Permission set for OurAssembly><IPermission class=System.Security.Permissions.FileIOPermission version=1 Unrestricted=true /> </PermissionSet><Assemblies><Assembly Name=OurAssembly /></ Assemblies></PolicyItem></CodeAccessSecurity>

Please note that I’ve set the unrestricted=true attribute just to keep it simple but you should set the Read attribute to the appropiate folder.Now, we can read the file from our webpart:

string loc = HttpContext.Current.Server.MapPath("/") + "bin\";
using (StreamReader reader = new StreamReader(loc + @"MyData.xml"))
{
    XDoc = XDocument.Load(reader, LoadOptions.None);
}

And that’s all, with this approach we can deploy all kinds of xml data and even .dll configuration files or any other localresource you might need. I hope it’s helpful and don’t forget to leave a comment if so !


Ajax webpart: Register scripts on UpdatePanel postbacks

February 25, 2008

These days I’m developing custom ajax enabled webparts for a sharepoint solution. I’ve created a base webpart class named AjaxEnabledWebpart from which all the webparts inherit. This webpart has an updatepanel control where all the child webparts are supposed to create the controls they need so finally, everycontrol is rendered inside the updatepanel and the webpart has the expected asp.net ajax behaviour of partial rendering the pages. Some of the webparts have to render content created dinamically by javascript code as you will see later in the code, in this case, content is rendered this way because we are supposed to give an accessible front end in our webpart (this is supporting non-script enabled browsers) so we are using the typical <noscript><script> tag approach. However, the problem we faced is that the content rendering script code (or any script code at all) was not executed during Ajax postbacks of the webpart. The solution we found to this problem was to register the scripts with the Sys.WebForms.PageRequestManager.getInstance().add_endRequest() function. Following is the code we used. First, for reference only, I’ll post the main code of the AjaxEnabledWebpart which somebody might find useful

protected override void CreateChildControls()
{
    base.CreateChildControls();

    EnsurePanelFix();

    _updatePanel = new UpdatePanel();

    _scriptHandler = ScriptManager.GetCurrent(this.Page);
    if (_scriptHandler == null)
    {
        //no script manager registered
        _scriptHandler = new ScriptManager();
        _scriptHandler.ID = "scriptManager";
        this.Controls.Add(_scriptHandler);
    }

    _updatePanel.ID = "updatePanel_";
    _updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
    _updatePanel.ChildrenAsTriggers = true;

    CreatePanelControls(_updatePanel.ContentTemplateContainer.Controls);

    this.Controls.Add(_updatePanel);
}

protected virtual void CreatePanelControls(ControlCollection PanelControls)
{
}

The only relevant code from the AjaxEnabledWebpart is the CreateChildControls method, which creates the UpdatePanel control and calls the virtual method CreatePanelControls passing as a parameter the control collection where child pages are supposed to add the controls. Also, you can find the EnsurePanelFix implementation on my previous post at this linkAfter that, here is the code of a custom HtmlTable used in some webparts which contents are rendered using javascript code (if scripting is enabled in the client browser). The posted code is a function which returns the html code to be inserted in a cell’s InnerHtml property of a table:

private string _getSelectAllHtml(HtmlTableCell cell)
{
    string ret = "";

    ret += "" +
                "select all" +
             "" +
             "" +
                "// " +
            "" +
           "";

    string fn = "" +
                    "//<!&#91;CDATA&#91; n" +
                        "function _loadSelectAllHeader() { " +
                            "cab = '<a href="#" title="CellTitle">" +
                            "<img src="_layouts/img/selectall.gif" alt="select all">';" +
                            "var capa = document.getElementById("" + cell.Attributes&#91;"Id"&#93; + "");" +
                            "capa.innerHTML = cab;n" +
                        " }" +
                    "//&#93;&#93;> " +
                "";

    Page.ClientScript.RegisterClientScriptBlock(typeof(ResultsTableMod47),
                                "_loadSelectAllHeader",
                                fn);

    Page.ClientScript.RegisterStartupScript(typeof(ResultsTableMod47),
                                "initRequestScriptHandler_loadSelectAllHeader",
                                "Sys.WebForms.PageRequestManager.getInstance().add_endRequest(_loadSelectAllHeader);");

    return ret;
}

The relevant lines of the code are the calls to RegisterClientScriptBlock and RegisterStartupScript methods. The first one register our content-rendering script while the second one adds this function to the end_request handler of the PageRequestManager instance. This will ensure that the script is called after each Ajax postback of the pages.Further scripts can be registered in the end_request handler by calling the add_endRequest method for each script we want to be executed after a postback. However, we have to considered that this script will be executed for all the postbacks and not only for the ones generated by this webpart’s updatepanel control, so if more than one updatepanel is present on the page, we might have to include some checking at the startup of our code to ensure that our script must be executed. In this case this was not necessary since the script could be executed again without any problem.

That’s all for now ! I hope it’s useful…. coments are welcome 🙂


Ajax enabled webpart not working on second postback

February 20, 2008

Trying to develope an Ajax webpart with aspx.net 2.0 I came up with this msdn example  which seemed to be exactly what I was looking for. I followed the steps suggested by the article but I ended up with some weird behaviour from the webpart when finally deployed in my Sharepoint server.

Apparently everything was working as expected and when I clicked the webpart the first time, the ajax method was correctly executed and the “Hello! XXX” text succesfully displayed. However, clicking again the button didn’t seem to do anything, also, other page actions that should submit the form and do some action (like the “Edit Page” item from the “Site Actions” menu) stopped working.

I finally found that the problem was in the EnsurePanelFix function of the example which was not working as expected

private void EnsurePanelFix()
{
   if (this.Page.Form != null)
   {
     String fixupScript = @"
     _spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
     function _initFormActionAjax()
     {
       if (_spEscapedFormAction == document.forms[0].action)
       {
         document.forms[0]._initialAction =
         document.forms[0].action;
       }
     }
     var RestoreToOriginalFormActionCore =
       RestoreToOriginalFormAction;
     RestoreToOriginalFormAction = function()
     {
       if (_spOriginalFormAction != null)
       {
         RestoreToOriginalFormActionCore();
         document.forms[0]._initialAction =
         document.forms[0].action;
       }
     }";
   ScriptManager.RegisterStartupScript(this,
     typeof(SayHelloWebPart), "UpdatePanelFixup",
     fixupScript, true);
   }
}

So what I did is to provide my own implementation of that function. What we needed is to invalidate the onSubmit wrapper that Sharepoint calls in order to ensure some callback scenarios, this can be done by setting to true the _spSuppressFormOnSubmitWrapper variable so I replaced the original function by the following implementation

private void EnsurePanelFix()
        {
              ScriptManager.RegisterStartupScript
                (this,
                 typeof(AjaxEnabledWebpart),
                 "UpdatePanelFixup",
                 "_spOriginalFormAction = document.forms[0].action; _spSuppressFormOnSubmitWrapper=true;",
                 true);
}

And that’s it ! my Ajax webpart is working properly no matter how many times we click the Hello World ! button 🙂


Connecting webparts at Object Model

February 8, 2008

Unfortunately there is no way in Sharepoint 2007 you can connect two webparts in a site definition configuration file (such as ONET.xml). I can’t understand why such a feature isn’t provided by the platform since is something you usually need in custom site definitions.

I’m currently developing a custom site definition for an e-Learning portal. One of the pages of the site is composed by a student search filter webpart and a student result list webpart which should be connected as a provider (filter) and consumer (list) of the search criteria of the current search. Obviously, I want this connected to be deployed when the administrator creates a new site of this site definition template however, as I pointed before, such a feature is not available at any configuration file (onet.xml or a feature element) of the site definition.

Researching on this topic, I came up with what seemed a way to solve this issue: implement the webpart connection through Object Model at a site creation event of a site provisioning assembly or a feature receiver class.

The only information I found on this topic was at the following posts by jasonwj and edhilds which suggested that this could be done with a SPLimitedWebPartManager object. Msdn documentation on this topic, although almost inexistent, seems to suggest also that.

However, I didn’t manage to get the expected results. Following is the code I wrote at the FeatureActivated event of the FeatureReceiver class:

SPWeb web = (SPWeb)properties.Feature.Parent;
SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager("search_students.aspx", PersonalizationScope.Shared);

System.Web.UI.WebControls.WebParts.WebPart searchFilter = mgr.WebParts[0];
System.Web.UI.WebControls.WebParts.WebPart searchResults = mgr.WebParts[1];

ProviderConnectionPoint searchFilterConnPoint = mgr.GetProviderConnectionPoints(searchFilter)["SearchProvider_ot"];
ConsumerConnectionPoint searchResultsConnPoint = mgr.GetConsumerConnectionPoints(searchResults)["SearchConsumer_ot"];

SPWebPartConnection conn = mgr.SPConnectWebParts(searchFilter,
                                       		 searchFilterConnPoint,
                                                 searchResults,
                                                 searchResultsConnPoint);

Both webparts where build from this msdn example just for testing purposes.This code didn’t connect both webparts when the feature was activated, apparently nothing seemed to happen but the consumer webpart (searchResults) got all it’s properties reseted and initialized to empty values (for example, it’s title property was set to “Untitled”). Apart from that, nothing else seemed to happen.I’m still researching on this topic, unfortunately, there is not much about it. If anyone has any point on how could we connect two webparts in a site definition or what is happening with my implementation please leave a comment ! I can also give the source code of the feature if you want to try it at your computer.