Create a loop section in your worlflow with SharePoint Designer 2010

OOTB, SharePoint Designer doesn’t offer you the ability to create a workflow with a loop.

Fortunately by manipulating the .oxml file of the workflow, everything become possible !

First take care that SharePoint’s web.config has this line as bellow:

<authorizedType Assembly=”System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″ Namespace=”System.Workflow.*” TypeName=”WhileActivity” Authorized=”True” />

Next, you can start to create your workflow as you need replacing the expected loop section by an if condition section and encapsulate your activity inside the if in a section called “while do” or something you can find easily later.

Image

Take care to design your workflow correctly and test it now because after the manipulation, you won’t be able to design the workflow any more because SharePoint Designer can’t display a while in the design view.

Save and publish your workflow.

Close the workflow and go to “all files/workflow/your workflow name” and open its .xoml file with Note Pad.

Image

In the XML file, search the section you have created called “While Do” or something else and modify the content of the section as bellow replacing in-red section:

Image

By the in-red section bellow:

Image

Save the bloc note, click on the file .oxml to open the workflow and Save again. Click No when confirm Save.

Image

Restart SharePoint Designer and open the workflow, Make a Check Errors and Publish.

Image

The workflow is ready to be used. Fortunately if you need to update the workflow you have the make the reverse change again in the .oxml file as you see SharePoint Designer can’t display your workflow loop in design mode :

Image

Create/Update a SharePoint timer job on Web Application level

You can create a timer job only on the web application level. This is to not impact all your farm but just a site collection.

First, create your timer job as usually. Delete eventual already existing occurrence of the timer job, instanciate a new one and configure it:

Image

Next configure your feature as a Web application level:

Image

Deploy your solution.

Go to your central administration, manage web application, select yours and go to manage feature:

Image

Activate manually the feature. Now your timer job is launch and you can follow it on the timer job history page on the central administration.

Image

If you have make an update of the timer job, please run the following commands to restart the SharePoint timer:

Image

Get Google Domain Shared Contacts or Google Private Contacts in VB.NET

This is how to retrieve Google Domain Shared Contacts and Private Contacts.

Code:

Dim _ContactsService As ContactsService = New ContactsService([AppName])
_ContactsService.setUserCredentials(p_Username, p_Password)
Dim query As ContactsQuery = New ContactsQuery(ContactsQuery.CreateContactsUri([PARAMETER]))
Dim feed As ContactsFeed = _ContactsService.Query(query)
Dim listOfContacts As List(Of ContactEntry) = New List(Of ContactEntry)
For Each c As ContactEntry In feed.Entries
     listOfContacts.Add(c)
Next

For private contacts method: [PARAMETER] = [USER_ID]

For domain shared contacts method: [PARAMETER] = [DOMAIN]

Create an administration page on SharePoint 2010

I have created my first page on the SharePoint central administration.

This example simply lists the sites of a selected site collection and also lets to select some of them. It’s completely useless but it explains how to create your administration page and to use a persistent object to save the selected sites.

First of all create a new ‘Empty SharePoint Project’

Choose your central administration site to debug, validate and finish.

Your solution explorer has to seem like this one:

To begin create a persistant object to save the sites selected by the administrator.

The class ‘Persistant’ inherits from ‘SPPersistedObject’, has an persisted object tagged with [Persisted] tag and constructors.

public class PersistedSettings : SPPersistedObject
    { 
            [Persisted] 
            public List<Guid> SiteCollectionsEnabled = new List<Guid>(); 
            public PersistedSettings() { } 
            public PersistedSettings(string name, SPPersistedObject parent, Guid id) 
                : base(name, parent, id) 
            { 
            } 
    }

Add new ‘SharePoint Mapped’ folder called ‘ADMIN’. This will contain our ASPX administration page.

This page is an ‘Application Page’

This new page is put in a new ‘Layouts’ folder. Move it to the ‘ADMIN’ folder and delete the ‘Layouts’ one.

We will create a page as the following one:

There is three important parts:

  • ‘InputFormSection’ and ‘ButtonSection’ are used SharePoint-pages. It’s to use the SharePoint layout easily and you just have to add your controls in. Please go to Karine Bosch’s post for more explanations about these controls.
  • ‘SharePoint:WebApplicationSelector’ is a control useful to select the site collection.
  • ‘ASP:Repeater’ is used to list the site list of the selected site collection.
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" Src="/_controltemplates/InputFormSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="/_controltemplates/ButtonSection.ascx" %> 
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
</asp:Content>
<asp:Content ID="PageDescription" ContentPlaceHolderID="PlaceHolderPageDescription" runat="server">
  This page allows you to display the site list of a site collection. 
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<table width="100%" class="propertysheet" cellpadding="0" cellspacing="0" border="0">
    <tr><td class="ms-error"><asp:Literal ID="ErrorMessageLiteral" runat="server" EnableViewState="false" /> </td></tr>
  </table>
  <table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tr>
      <td>
        <!-- web application selector -->
        <wssuc:InputFormSection runat="server"
                    Title="<%$Resources:spadmin, multipages_webapplication_title%>"
                    Description="<%$Resources:spadmin, multipages_webapplication_desc%>" >
          <Template_InputFormControls>
            <tr>
              <td>
                <SharePoint:WebApplicationSelector id="WebAppSelector" runat="server"  
                  OnContextChange="WebAppSelector_OnContextChange" />
              </td>
            </tr>
          </Template_InputFormControls>
        </wssuc:InputFormSection>
        <!-- site collection selector -->
        <wssuc:InputFormSection runat="server"
                                  Title="Site Collections"
                                  Description="Select the site collections to enable or disable the warmup job. Only the top level site's homepage within the site collection is hit.">
          <Template_InputFormControls>
            <tr valign="top">
              <td>
                <asp:Repeater ID="SiteCollectionRepeater" runat="server"  
                OnItemDataBound="SiteCollectionRepeater_OnItemDataBound">
                  <ItemTemplate>
                    <asp:TextBox runat="server" Visible="false" ID="SiteCollectionIdTextBox" />
                    <asp:CheckBox ID="Checked" runat="server" class="ms-descriptionText" RepeatDirection="Horizontal" />
                    <asp:Literal runat="server" ID="SiteCollectionLiteral" /><br />                  
                  </ItemTemplate>
                  <SeparatorTemplate><br /></SeparatorTemplate>
                </asp:Repeater>
              </td>
            </tr>
          </Template_InputFormControls>
        </wssuc:InputFormSection>
        <wssuc:ButtonSection runat="server">
          <template_buttons>
            <asp:Button id="SaveButton" runat="server" class="ms-ButtonHeightWidth" Text="OK"  
            OnClick="SaveButton_OnClick" />
          </template_buttons>
        </wssuc:ButtonSection>
      </td>
    </tr>
  </table>
</asp:Content>
<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Site List By Site Collection 
</asp:Content>
<asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
Site List By Site Collection 
</asp:Content>

This is the code behind. There is three principal event:

  • WebAppSelector_OnContextChange: to get the site collection complete list.
  • SiteCollectionRepeater_OnItemDataBound : to retrieve the site of the selected site collection and check wich one are selected
  • SaveButton_OnClick : to save the new list of selected sites.
public partial class SiteListBySiteCollectionPage : LayoutsPageBase
    { 
        protected Literal ErrorMessageLiteral; 
        protected WebApplicationSelector WebAppSelector; 
        protected Repeater SiteCollectionRepeater; 
        protected void Page_Load(object sender, EventArgs e) 
        { 
            SPContext.Current.Web.AllowUnsafeUpdates = true; 
        } 
        protected void WebAppSelector_OnContextChange(object sender, EventArgs e) 
        { 
            InitSiteCollectionSelector(); 
        } 
        private void InitSiteCollectionSelector() 
        { 
            if (this.WebAppSelector.CurrentItem.Sites.Count > 0) 
            { 
                this.SiteCollectionRepeater.DataSource = this.WebAppSelector.CurrentItem.Sites; 
                this.SiteCollectionRepeater.DataBind(); 
            } 
        } 
        protected void SiteCollectionRepeater_OnItemDataBound(object sender, RepeaterItemEventArgs e) 
        { 
            if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) 
            { 
                SPSite siteCollection = e.Item.DataItem as SPSite; 
                TextBox siteCollectionIdTextBox = e.Item.FindControl("SiteCollectionIdTextBox"as TextBox; 
                siteCollectionIdTextBox.Text = siteCollection.ID.ToString(); 
                Literal siteCollectionName = e.Item.FindControl("SiteCollectionLiteral"as Literal; 
                siteCollectionName.Text = siteCollection.Url; 
                CheckBox checkbox = e.Item.FindControl("Checked"as CheckBox; 
                if (SiteCollectionHasWarmupJobConfigured(siteCollection)) 
                    checkbox.Checked = true; 
                else
                    checkbox.Checked = false; 
            } 
        } 
        private bool SiteCollectionHasWarmupJobConfigured(SPSite siteCollection) 
        { 
            try
            { 
                PersistedSettings settings = this.WebAppSelector.CurrentItem.GetChild<PersistedSettings>("SiteListSelected"); 
                // If no settings previously created, create them now.
                if (settings == null) 
                { 
                    SPPersistedObject parent = this.WebAppSelector.CurrentItem; 
                    settings = new PersistedSettings("SiteListSelected", parent, Guid.NewGuid()); 
                    settings.Update(); 
                } 
                return settings.SiteCollectionsEnabled.Contains(siteCollection.ID);                           } 
            catch (Exception ex) 
            { 
                ErrorMessageLiteral.Text = ex.Message + "A new storage location had to be created. Please go back to the Application Management page and come back in before doing any work."; 
                return false; 
            } 
        } 
        protected void SaveButton_OnClick(object sender, EventArgs e) 
        { 
            PersistedSettings settings = this.WebAppSelector.CurrentItem.GetChild<PersistedSettings>("SiteListSelected"); 
            // Purge the configuration data for the current Web application.
            settings.SiteCollectionsEnabled.Clear(); 
            settings.Update(); 
            // Get a list of all the site collections that were requested to be enabled.
            List<Guid> selectedSiteCollections = GetSelectedSiteCollections(); 
            if (selectedSiteCollections.Count > 0) 
            { 
                // Add the settings.
                foreach (Guid siteCollectionID in selectedSiteCollections) 
                { 
                    settings.SiteCollectionsEnabled.Add(siteCollectionID); 
                } 
                settings.Update(); 
            } 
        } 
        private List<Guid> GetSelectedSiteCollections() 
        { 
            List<Guid> selectedSiteCollections = new List<Guid>(); 
            TextBox siteCollectionIdTextBox; 
            CheckBox chk; 
            foreach (RepeaterItem item in SiteCollectionRepeater.Items) 
            { 
                siteCollectionIdTextBox = item.FindControl("SiteCollectionIdTextBox"as TextBox; 
                chk = item.FindControl("Checked"as CheckBox; 
                if (chk.Checked) 
                    selectedSiteCollections.Add(new Guid(siteCollectionIdTextBox.Text)); 
            } 
            return selectedSiteCollections; 
        } 
    } 

The page is created and now we just have to deploy it as a new feature. For this, add a new module:

And add this code:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="04B5F622-AF31-481A-891D-0032B72F84DA" GroupId="SiteCollections" 
      Location="Microsoft.SharePoint.Administration.Applications" 
      Sequence="20" RequiredAdmin="Delegated" 
      Title="Site List" Description="">
    <UrlAction Url="_admin/SiteListBySiteCollection/SiteListBySiteCollectionPage.aspx" />
  </CustomAction>
</Elements>

It says that the page link will be on the ‘Microsoft.SharePoint.Administration.Applications’ page under the ‘SiteCollections’ section. Get all the custom actions available here.

To finish, add a new feature and add the module in.

Your solution explorer has to seem like this one right now:

You can deploy your solution.

And visit the central administration to see the link as expected.

One the page and you can choose a site collection and list and check its sites.

Infopath 2010 : Maintain a form name

When you submit an Infopath form you need to choose a filename often calculated to be sure is unique. When you submit a form which already exists you need to know the file name to resubmit with the correct name. With Infopath there is no way to know the name of the current form.

To bypass this disadvantage, I use a hidden field named “filename” which will contain the name of the file. I set this field value at the first submit and reuse it for all following submit.

For this example, the field is visible but usually I hide it.

I add a new data connection to the list where I submit the form and set the file name to the value of my field Filename.

In submit option, I choose to perform some action rules on submit.

And I setup the following action rules: First I look at Filename field value, if it’s blank I set it to a new value “My file”. Next, I submit the form with the data connection created just before.

I publish my form to the SharePoint Server.

Set the server name.

Publish as a site content type.

Create or update a content type.

Choose a form library where my form template will be uploaded.

Add some fields which will be added to the site content type.

I add the Filename to make it available on the list.

And I publish my form.

On the SharePoint list settings where the forms will be submit I add a new content type.

I add the content type created previously in Infopath.

On Sharepoint Designer I add a new workflow on the list to maintain Filename field.

The workflow is to set Filename to the name of the file.

I check to start the workflow when an item is created or changed to maintain the Filename on each event.

Now, when you resubmit a form you replace the preceding version because you use the same name. And when you renamed it and resubmit it, the Filename field is update and you replace the old version too.

Infopath 2010 : Display one item at a time from a repeating group

Suppose you have a repeating group and you want to display only one item from the group at a time. Moreover, you want to be able to add a new item displaying only this one.

For this you need to know the currently displayed item and be able to identify uniquely each item.

Create the data source like this. With in particular a “CurrentItemID” to know the ID of the item to display and a reapeting group with a unique identifier “ItemID”.

Set the “CurrentItemID” default value to 1 to display for the first element on load.

The default value of the “ItemID” field must be an incrementale number. Therefore calculate the value from the previous field + 1. For this use the function button and insert the following formula: “count(../preceding-sibling::my:RepeatingGroup) + 1”.

Next add a rule on the repeating group which will be triggered when a new item will be inserted. This rule set the current item id to the value of the new item id.

For this select the repeating group in the field section and choose “Home”/”Manage Rules” on the and add a new action rule :

Next construct your form like this:

The repeating group is inserted two times : The first to list the item available and the second to display the current item properties.

On the first occurrence, I set the default value of the button to the current item id following by the current item name.

You must add a new action rule on the button. This will change the current item ID to the selected item id.

On the second occurrence, you must add a formatting rule to only display the current item properties.

Launch the preview. The list of the available items is on the left and the current item is on the right.

When you choose an item by click on the left, the properties of the item appears.

When you choose to “Insert item”, on the right a new item appears and you can complete the properties.