Thursday 20 June 2013

Naming your feature

In SharePoint you use feature manifests to deploy the components that you've built. Now this might seem trivial, but you'd be amazed at the silly naming conventions I've come across.

So lets get started, first let's take a look at the solution explorer:


now make note of three things:

  • Project Name: FeatureDemo
  • Feature Name: FeatureName.WS
  • WS: the ws at the end of the name stands for WebScoped

Now let's open up our actual manifest and take a look inside

You'll notice three things

  • Title: This what you'll see when insepcting feature through site settings
  • Description: This will accompany the title
  • Scope: in this case we will be web scoped, there are four options, I'm only going to focus on site and web in this post.

If you navigate to site settings

Think of "Site" as your entire site, and think of a "Web" as a specific sub web of your site, so all of your webs make up your site.

so back to our example, if I where to deploy our project and look in the Web scoped features of our site I'd find

Notice that our Feature title and description from our Feature manifest are listed.

if you where to jump into your hive C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\ and look for your feature you'd find.

Notice that the folder name is made up of your ProjectName_FeatureName.

this is why generally you like to preface your project name and feature title with some sort of consistent acronym,  this will group your custom features in both your hive, and feature lists.

Friday 14 June 2013

Content Type Event Receiver

With all this talk of workflows it's important to make note of another automating tool and that tool is event receivers. We are going to make an event receiver for a category list to ensure that the user does not enter duplicate entries accidentally.

1.) To get started, create a content type with two OOTB site columns title and description: if you have the cksDev tools, just add a content type with an event receiver. 

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!-- Parent ContentType: Item (0x01) -->
  <ContentType ID="0x0100764cabc1856e471db2475d3c1414c779"
               Name="Ct_eventRecievers - Simple_CT"
               Group="Custom Content Types"
               Description="My Content Type"
               Inherits="TRUE"
               Version="0">
    <FieldRefs>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" 
                Name="Title" 
                DisplayName="Category" />
      <FieldRef ID="{92bba27e-eef6-41aa-b728-6dd9caf2bde2}" 
                Name="Description" 
                DisplayName="Category Description" />
    </FieldRefs>
    <!--Insert xmlDocuments snippet here-->
  </ContentType>
</Elements>

2.) After that you're going to have to add the following xmlDocuments

<XmlDocuments>
  <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events">
    <Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events">
      <Receiver>
        <Name>Simple_EventHandler</Name>
        <Type>ItemAdding</Type>
        <SequenceNumber>10000</SequenceNumber>
        <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
        <Class>$SharePoint.Type.d46d2530-cb14-4f80-9853-fe51ecf97d9e.FullName$</Class>
        <Data></Data>
        <Filter></Filter>
      </Receiver>
    </Receivers>
  </XmlDocument>
</XmlDocuments>

if you're curious about the two highlighted lines, check out msdn Replaceable Parameters

3.) Now add a regular c# code file
name it whatever you please.

4.) open it up and make the following changes:
  • add the following libraries
    • using System;
    • using System.Runtime.InteropServices;
    • using Microsoft.SharePoint;
  • Add the guid you used before as a class attribut
    • [Guid("d46d2530-cb14-4f80-9853-fe51ecf97d9e")]
  • inherit from the SPItemEventReciever class
  • set the class to public
You should end up with:

using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
 
namespace Ct_eventRecievers.Simple_CT
{
    [Guid("df45d565-73d2-447f-a463-5a362d2fc50d")]
    public class Simple_EventReceiver : SPItemEventReceiver
    {
    }
}

5.) Now add you're event, the ones that end in (ing) are synchronous, the ones that end in (ed) are asynchronous, the difference is that the former waits for the result before moving on and the latter does not. I'm going to add ItemAdding, notice that I specified it in the xml in my elements file.

public override void ItemAdding(SPItemEventProperties properties)
{
    base.ItemAdding(properties);
}

remove the base.ItemAdding(properties); and add your own logic

public override void ItemAdding(SPItemEventProperties properties)
{
    string title = properties.AfterProperties["Title"].ToString();
 
    using (SPWeb web = properties.OpenWeb())
    {
        SPList categoryList = web.Lists[properties.ListId];
 
        SPQuery query = new SPQuery();
        query.Query = string.Format(@"
            <Where>
                <Eq>
                    <FieldRef Name='Title' />
                    <Value Type='Text'>{0}</Value>
                </Eq>
            </Where>", title);
 
        SPListItemCollection items = categoryList.GetItems(query);
 
        if (items != null && items.Count > 0)
        {
            properties.ErrorMessage = string.Format(
                @"An item with the title {0} already exists.", title);
            properties.Status = SPEventReceiverStatus.CancelWithError;
            properties.Cancel = true;
        }
    }
}


I added a quick search for items that contain the same title as submitted and cancel the process if any are returned.

6.) make a list definition and instance from this content type using declarative XML, the event receiver will not fire if you simply add the content type to a generic list. you can however create the list definition and then use the UI to create your instance. anyway lets fast forward this part. (List definition & Instance) just use the Content type from this post instead.

OK,so you have your list definition and instance created, now in the UI add an item with the title test, after you've added one add a second one with the same title.

and you should get the following error, notice the message.

and that's it you've made your first content type with an even receiver, again I can't stress this enough you need to have a list template/definition (one and the same) built in your visual studio project.



Monday 10 June 2013

Rewire Data Connection on activate

Previously we built an InfoPath form that uses data connections to communicate with our web application, and then we converted the data connections into files and deployed them to a data connection library using a module. the only down side is when we open our data connection files to inspect the xml we very quickly notice that we have server specific paths to library's we are posting to, lists we are pulling from, soap services we are using and so on.

<?xml version="1.0" encoding="UTF-8"?>
<?MicrosoftWindowsSharePointServices ContentTypeID="0x010100B4CBD48E029A4ad8B62CB0E41868F2B0"?>
<udc:DataSource MajorVersion="2" MinorVersion="0" xmlns:udc="http://schemas.microsoft.com/office/infopath/2006/udc">
 <udc:Name>submitNewEmployee</udc:Name>
 <udc:Description>Format: UDC V2; Connection Type: SharePointLibrary; Purpose: WriteOnly; Generated by Microsoft InfoPath 2010 on 2013-05-30 at 14:27:52 by SSRAP7\Administrator.</udc:Description>
 <udc:Type MajorVersion="2" MinorVersion="0" Type="SharePointLibrary">
  <udc:SubType MajorVersion="0" MinorVersion="0" Type=""/>
 </udc:Type>
 <udc:ConnectionInfo Purpose="WriteOnly" AltDataSource="">
  <udc:WsdlUrl/>
  <udc:SelectCommand>
   <udc:ListId/>
   <udc:WebUrl/>
   <udc:ConnectionString/>
   <udc:ServiceUrl UseFormsServiceProxy="false"/>
   <udc:SoapAction/>
   <udc:Query/>
  </udc:SelectCommand>
  <udc:UpdateCommand>
   <udc:ServiceUrl UseFormsServiceProxy="false"/>
   <udc:SoapAction/>
   <udc:Submit/>
   <udc:FileName>Specify a filename or formula</udc:FileName>
   <udc:FolderName AllowOverwrite="0">http://server/New Employee/</udc:FolderName>
  </udc:UpdateCommand>
  <!--udc:Authentication><udc:SSO AppId='' CredentialType='' /></udc:Authentication-->
 </udc:ConnectionInfo>
</udc:DataSource>

so what to do about this? well there really is only one thing that I could think of, and that's to create a feature receiver and on the feature activated event:

  • traverse your data connection files
  • open them using the xmldocument class
  • update that server paths
  • save the files
  • check them back into the library

and that's it, you're done. simple right?

first lets traverse the list
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPWeb web = properties.Feature.Parent as SPWeb)
        {  
            SPList dcs = web.Lists["Data Connections"];
            foreach (SPListItem dc in dcs.Items)
            {
                if (dc.Properties["ConnectionType"].Equals("SharePointLibrary"))
                {
                    handleSharePointLibrary(web, dc);
                }
            }
        }
    });
}

this will ensure that we are only looking at data connections that submit to a library, in this next snippet we filter by the data connection name/title to ensure that we are in fact rewiring the one we are concerned with. After we rewire the file we check it back into the library.

private void handleSharePointLibrary(SPWeb web, SPListItem dc)
{
    string listURL = null;
 
    switch (dc.Properties["vti_title"].ToString())
    {
        case "submitNewEmployee":
            SPList lst = web.Lists["New Employee"];
            listURL = string.Format("{0}/{1}/", lst.ParentWeb.Url, lst.RootFolder.Url);
            break;
    }
 
    if (!string.IsNullOrEmpty(listURL))
    {
        SPFile dataConnectionFile = web.GetFile(dc.Url);
        RewireUpdateCommand(dataConnectionFile, listURL);
        dataConnectionFile.Approve("Approved via event reciever");
    }
}

Now for the magical part, in the above snippet you see a call to the RewireUpdateCommand function, lets take a look inside that sucker.

private void RewireUpdateCommand(SPFile dataConnectionFile, string listURL)
{
    string LibraryURL_xPath = "/udc:DataSource/udc:ConnectionInfo/udc:UpdateCommand/udc:FolderName";
 
    XmlDocument doc = new XmlDocument();
    doc.Load(dataConnectionFile.OpenBinaryStream());
 
    XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(doc.NameTable);
    nameSpaceManager.AddNamespace("udc""http://schemas.microsoft.com/office/infopath/2006/udc");
 
    XmlElement root = doc.DocumentElement;
 
    XmlNode ListId = root.SelectSingleNode(LibraryURL_xPath, nameSpaceManager);
    ListId.InnerText = listURL;
 
    var encoding = new System.Text.ASCIIEncoding();
    dataConnectionFile.SaveBinary(encoding.GetBytes(doc.InnerXml));
}

if you need an explanation xml document msdn

Thursday 6 June 2013

Deploy Data Connection File Via Feature

In our previous post we created an InfoPath form with two data connection files, we did this to separate our form from our server instance. We do this to attain a one step deployment process, that is we want our WSP package to deploy everything, I mean come on connecting to your production environment to deploy an info path form... kinda rookie.

So first off lets create a module to deploy our data connection file, download the one you put into your data connection Library. I'm just going to do the submitNewEmployee.udcx.

In your project add a module, remove the sample.txt file and add your submitNewEmployee.udcx file to the module.

With that complete, open up the elements file for your module, it should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="DataConnectionModule">
  <File Path="DataConnectionModule\submitNewEmployee.udcx" 
        Url="DataConnectionModule/submitNewEmployee.udcx" />
</Module>
</Elements>
We're going to make a couple changes to it:

  • Add the URL and Path to the module
  • Remove the Path from the file
  • Simplify the URL for the file, you specify it in the Module "Lists/DataConnections"
  • Add the Type and ignore properties to the file
  • Add a property node to the file

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="DataConnectionModule" Url="Lists/DataConnections" Path="DataConnectionModule">
    <File Url="submitNewEmployee.udcx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
      <Property Name="Title" Value="submitNewEmployee" />
    </File>
  </Module>
</Elements>
changes are highlighted in yellow.

Make sure, that your module is in the feature manifest, the right hand column.

After you deploy your project open up your data connection library to verify that your file was deployed. check the modified date to insure that you're looking at the most latest version and not just the one you uploaded from InfoPath.

now go ahead and download your submitNewEmployee.udcx file, open it up and take a look inside. the one thing that should stick out and burn your eyes is highlighted.
<?xml version="1.0"?>
<?MicrosoftWindowsSharePointServices 
ContentTypeID="0x010100B4CBD48E029A4AD8B62CB0E41868F2B0004AFDC87700F120458D1316B308B71A11"?>
<udc:DataSource MajorVersion="2" MinorVersion="0" xmlns:udc="http://schemas.microsoft.com/office/infopath/2006/udc">
  <udc:Name>submitNewEmployee</udc:Name>
  <udc:Description>Format: UDC V2; Connection Type: SharePointLibrary; Purpose: WriteOnly; Generated by Microsoft InfoPath 2010 on 2013-05-30 at 14:27:52 by yoursite\Administrator.</udc:Description>
  <udc:Type MajorVersion="2" MinorVersion="0" Type="SharePointLibrary">
    <udc:SubType MajorVersion="0" MinorVersion="0" Type=""/>
  </udc:Type>
  <udc:ConnectionInfo Purpose="WriteOnly" AltDataSource="">
    <udc:WsdlUrl/>
    <udc:SelectCommand>
      <udc:ListId/>
      <udc:WebUrl/>
      <udc:ConnectionString/>
      <udc:ServiceUrl UseFormsServiceProxy="false"/>
      <udc:SoapAction/>
      <udc:Query/>
    </udc:SelectCommand>
    <udc:UpdateCommand>
      <udc:ServiceUrl UseFormsServiceProxy="false"/>
      <udc:SoapAction/>
      <udc:Submit/>
      <udc:FileName>Specify a filename or formula</udc:FileName>
      <udc:FolderName AllowOverwrite="0">http://yoursite/New Employee/</udc:FolderName>
    </udc:UpdateCommand>
    <!--udc:Authentication><udc:SSO AppId='' CredentialType='' /></udc:Authentication-->
  </udc:ConnectionInfo>
</udc:DataSource>
Now that does not seem very dynamic, and that's because it's not, what we are going to have to do is in our event receiver open this file up, update the url and save it up again. I'll tackle that in my next post.

Continued

Wednesday 5 June 2013

InfoPath Data Connections

In this post We're going to create a InfoPath form that reads job titles from a SharePoint list and submits itself to a SharePoint Forms Library, but we are going to store our Data Connection in a SharePoint Data Connection Library. We have Three prerequisites for this:


What do I mean that our forms library is only going to be a place holder? in InfoPath to create a Submit to Library Data Connection you actually need to have this document library on your site. For now just make a Document Library through the SharePoint UI and call it FormLibrary, in a subsequent post I'll demonstrate how to deploy the Form we are making here via a WSP package.

Make Note of the forms URL, http://spm/FormLibrary
With the above three complete, open up InfoPath and create the following form.

With your form complete it's time to make your data connections, Go to the Data Connection Tab and click the Data Connections button.

This will bring up the Data Connections Window, it looks like this

What you're going to do is click the add button, the first thing we're going to do is specify that we want to retrieve data.

Next we are going to specify that we're going to retrieve our data from a SharePoint Library or list, in our case it'll be that Job Titles list we made previously.

Next select the site where the list you wish to pull data form resides.

Pick the columns you need in your form and the order you want them sorted. In our case we are going to need the Title Column and we should sort it in ascending order.

In the next window we can choose to store a copy of the data in our form, I leave this unchecked because I'll be using a web based form. If you utilize infopath filler then you might want to check this off... i guess...

Now give your data connection a unique name, I generally like to use camel case

When you click finish you'll be brought back to the data connection window, with your new data connection listed what we are going to do is select our getJobTitles Data connection and convert it into a Data connection file, at this point if you don't have a Data Connection Library this will not work.

Next you're going to specify the location of your Data connection library and append it with the file name you want the data connection to have. Hit OK and you're Job titles Connection file is created.

Now you should be back at your Data connection window, if you want you could open your data connection library in your browser to inspect the file you just created.
Next let's run through that entire process again, but this time let's build a data connection for the form submit. Back at the Data Connections window click the add button one more time.
Again you'll be asked whether you want to submit or receive data, this time pick submit.
next pick "To a document library on a SharePoint site"
Give your Data Connection a name
Click finish, you should go back to the Data Connections window, select your newly made data connection and convert it to a data connection file as well.
Just Like before specify your data connection library url and append it with the file name you want your data connection to have.

again go to your Data Connection library in your browser to see the newly added data connection file.
Now save both data connection files to your hard drive, we are going to move them into our projects, also publish your form to the same network location, we’re also going to deploy that using our visual studio solution.
And that’s it you've created your form and two Data Connections, One to Submit your form to a library and one to Fetch job types from a SharePoint list.

Continued