Freitag, 2. August 2013

Allow multiple of Workflowsubscription on same ListItem

This applies to the new Workflow 2013 Engine:

By default Sharepoint doesn´t allow to have multiple Instances of a Workflow running on the same ListItem.

This gave me headache because I had a Workflow to start when an Item is updated. However, if the Workflow got updated again before the Workflow of the last Update ended, I was not able to start the Workflow again. Even if I canceled the former Workflow it wasn´t possible, because sometimes the cancellation process lasts some seconds and I weren´t able to start a new Workflow, even if the former was in cancelling state. In the End I had no workflow running at all.

However, there is a solution:

If you deploy the Workflow subscription by declarative XML using Visual Studio, you should have somewhere the File "WorkflowStartAssocioation" in the elements.xml of your workflow. If you attach the property:


<Property Name="ManualStartBypassesActivationLimit" Value="1" />

you should be able to start the workflow again (as long as you start the workflow manually by Hand or by Code using EventReceiver).

This works also, if create the Subscription by Code using the SubscriptionService or even update an existing Subscription. There is a Property "ManualStartBypassesActivationLimit" in the Subscription-Object which you can set to true.



Donnerstag, 18. April 2013

Setting the Dataprovider for Resultscriptwebpart programmatically (eg. for setting the QueryTemplate)

For doing that you have to create a instance of the DataProviderScriptWebpart, initialize it with the JsonData from the ResultScriptWebPart. There you can set the Propertiy you need and write the PropertiesJason-Member back to the ResultScriptWebpart.DataProviderJson. And of course save the Webpart finally.


           var resultWebPart =  
             webPartManager.WebParts.Cast<WebPart>()  
                    .FirstOrDefault(  
                      wp => wp is ResultScriptWebPart) as ResultScriptWebPart;  
   
           var querySettings = new DataProviderScriptWebPart  
             {  
               PropertiesJson =  
                 resultWebPart.DataProviderJSON  
             };  
   
           querySettings.Properties["QueryTemplate"] =  
             "{searchboxquery} (contentclass:STS_ListItem OR IsDocument:True)";  
   
           resultWebPart.DataProviderJSON = querySettings.PropertiesJson;  
   
           webPartManager.SaveChanges(resultWebPart);  

But beware: Altough there is a Property DataProviderScriptWebPart.QueryTemplate which you can set, it is not included if you request the DataProviderScriptWebPart.PropertiesJson. You have to set the property using the  Properties-Collection of the object.

(of course you could also simple edit the Json-String itself, but I prefer this way).

Sonntag, 14. April 2013

Filtering a List by Userfield and Username

I recently had an requirement to get all Items of a List for a specific user. Usually that´s not that problem, using Server-side Code you could simply get the users SPUser-Object by SPWeb.EnsureUser and filter the list based on the SPUsers´ ID. However, I had to do that by using the Client Object Model (in detail, the Javascript API). Here the process of doing an async request to get the SPUser-Object and then do another async Request filtering by its ID didn´t make me happy at all, so I thought of another way doing that.

I found a solution by joining the List with the Hidden Userinfo-List, on that Join I was able to do a filter on the username.

Here´s the View-XML for doing that:

 <View>  
      <Joins>  
           <Join Type='INNER' ListAlias='User Information List'>  
                <Eq>  
                     <FieldRef Name='UserField' RefType='Id' />  
                     <FieldRef List='User Information List' Name='ID' />  
                </Eq>  
           </Join>  
      </Joins>  
      <Query>  
           <Where>  
                <Eq>  
                     <FieldRef Name='MyUserName' />  
                     <Value Type='Text'>Domain\UserName</Value>  
                </Eq>  
           </Where>  
      </Query>  
      <ProjectedFields>  
           <Field Name='MyUserName' Type='Lookup' List='User Information List' ShowField='Name' />  
      </ProjectedFields>  
 </View>  

(Beware: You have to do a double-Backslash for between Domain and Username when setting the XML-String to the query, because it removes one of them)

Additionaly you can also set ViewFields, for minimizing the result for faster response

Donnerstag, 26. Juli 2012

How to deploy custom Masterpage by Code

In our current project we had the advice to deploy a masterpage by a feature.

While deploying the new masterpage to the masterpage-gallery was quite simple (and documented), activating the masterpage for the site(s) was not that straightforward. Most instructions one can find on the web are dealing with that by iterating through all webs and setting the masterpage one by one.

But there is a more simple solution to deal with that, at least when then publishing-feature is activated for the site (what is usually the case if one want to be able to change the masterpage).

Just add below code to the corresponding action of the feature receiver (in my case, the feature-receiver is targeting a Web, but it should also work for feature-receiver targeting the site, then you have to use the RootWeb of the site for the below code)

private const string defaultMasterUrl = "~/_catalogs/masterpage/v4.master";
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            SPWeb web = properties.Feature.Parent as SPWeb;

            if (web != null && PublishingWeb.IsPublishingWeb(web))
            {
                PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
                publishingWeb.CustomMasterUrl.SetValue(customizedMasterUrl, true);
                publishingWeb.MasterUrl.SetValue(customizedMasterUrl, true);
                publishingWeb.AlternateCssUrl.SetValue(CustomCssUrl, true);
                publishingWeb.Update();
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            SPWeb web = properties.Feature.Parent as SPWeb;
            
                 if (web != null && PublishingWeb.IsPublishingWeb(web))
                {
                    PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
                    publishingWeb.CustomMasterUrl.SetValue(defaultMasterUrl, true);
                    publishingWeb.MasterUrl.SetValue(defaultMasterUrl, true);
                    publishingWeb.AlternateCssUrl.SetValue("", true);
                    publishingWeb.Update();
                }
        }
 You don´t have to set all the properties. Usually you just have to set the CustomMasterUrl-property, as the MasterUrl-Property is only used for non-publishing-pages. I just did it to make sure the masterpage is set. Also setting the AlternateCssUrl is optional. The only thing to reminde with the AlternateCssUrl is that the default-Value is an empty string (not a real url as with the masterpage). So if you want to set the CSS-Class back to the default one, set this propety to "". If you want to switch back to the default Masterpage, you have to set the url stated above.

Montag, 2. Juli 2012

How to ajaxify Visual Webpart using Telerik

First of all, you have to init (or reuse if already existing) a RadAjaxManager on the Webpage. You can do it in the <WebPart>.cs-File or (which I recommend if you want to use it in multiple Webparts) you create a custom Extension of WebPart like i did:

   public class TelerikAjaxifiedWebPart : WebPart
    {
        RadAjaxManager ajaxManager;

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

            SetUpAjaxManagerOnPage();

            EnsureChildControls();
        }

        protected void SetUpAjaxManagerOnPage()
        {
            RadAjaxManager currentAjaxManager = RadAjaxManager.GetCurrent(Page);

            if (currentAjaxManager == null)
            {
                Page.Form.Controls.AddAt(0, AjaxManager);
                Page.Items.Add(typeof(RadAjaxManager), AjaxManager);
            }
        }

        protected virtual RadAjaxManager AjaxManager
        {
            get
            {
                if (ajaxManager == null)
                {
                    ajaxManager = RadAjaxManager.GetCurrent(Page);

                    if (ajaxManager == null)
                    {
                        ajaxManager = new RadAjaxManager() { ID = "RadAjaxManager1" };
                    }
                }

                return ajaxManager;
            }
        }
    }
 Instead of Deriving from "WebPart" you can now derive from your custom Class and be sure that the RadAjaxManager is there and useable.

Now you simply have to add


            RadAjaxManager manager = RadAjaxManager.GetCurrent(Page);
            if (manager != null)
                manager.AjaxSettings.AddAjaxSetting(RadGrid1, RadGrid1, RadAjaxLoadingPanel1);


in the Page_Load Method of your  <UserControl>.cs file. (The if-Condition just to be sure, usually there shouldn´t be null). You have to add all the controls you want to ajaxify in code (because the markup doesn´t know about the AjaxManager-Control). In the Markup of your userControl you simply have to add your AjaxLoadingPanel you want to use for that control as well as your Controls (like the RadGrid in my example).

As you are using ONE RadAjaxManager for the whole Web-Page, you should not use the RadAjaxManager.DefaultLoadingPanel-Property, instead of that use the Overload of the AddAjaxSetting-Method as I did, which is setting the control firing the AjaxRequest, the one which is getting updated and the LoadingPanel that is used. So you can be sure that the right AjaxLoadingPanel is shown during the AjaxRequest.

Note:
I recommend using the same version of the Telerik-Dll in all of your WebParts (at least all Webparts situated on the same site). I encountered troubles with non-working Javascripts, when they are mixed.