Nishand's Blog

Welcome to my personal blog, All the information in this bogs is my ideas,findings and thoughts on .Net, Asp.Net and SharePoint.
ALL POSTING ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND

  • SPWebPartManager doesn't honour the WebPartCancelEventArgs.Cancel value while deleting the webpart

    Some scenarios developers may want to restrict users from deleting webparts from  a page, the scenarios varies.
    SPWebPartManager has got a event named ‘OnWebPartDeleting’.The issue here is, the deletion of the webpart cannot be
    avoided by setting the WebPartCancelEventArgs.Cancel = true; within the   ‘OnWebPartDeleting’ event.
    So the SPWebPartManager’s ‘OnWebPartDeleting’event is not going to help us in accomplishing our requirement, here I have a workaround, it may help you! 

     

     

    ……….
    <script runat="server">
      protected void On_WebPartDeleting(object sender, WebPartCancelEventArgs e)
      {
          Page.Response.Write("Sorry! This webpart cannot be deleted!<br>");  
           e.Cancel = true;    
      }
    </script>
      <form >

     
    <WebPartPages:SPWebPartManager OnWebPartDeleting="On_WebPartDeleting"  id="m" runat="Server"/>

     

    As a workaround your can implement this, not sure you like this approach:)

    Develop a custom HttpModule to trace the WebPart delete events,  basically tracing the ‘__EVENTARGUMENT’ value.

    See below the implementation.

       1: void application_PostAuthorizeRequest(object sender, EventArgs e)
       2: {
       3: HttpContext context = HttpContext.Current;
       4:  
       5: if (context != null && context.Request.Params["__EVENTARGUMENT"] != null && Convert.ToString(context.Request.Params["__EVENTARGUMENT"]).Length > 1)
       6: {
       7: string contextValue = context.Request.Params["__EVENTARGUMENT"];
       8:  
       9: if (contextValue.Contains("MSOMenu_Delete"))
      10: {
      11: try
      12: {
      13: string[] keyValue = contextValue.Split(new char[] { ';' });
      14: Guid guid = new Guid(keyValue[0]);
      15: SPWeb web = SPContext.Current.Web;
      16: SPLimitedWebPartManager manager = web.GetLimitedWebPartManager(context.Request.RawUrl, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
      17: System.Web.UI.WebControls.WebParts.WebPart webpart = manager.WebParts[guid];
      18: context.Response.Write("<h5><font color=red>Sorry, you do not have the rights to delete this webpart. Please contact your admin.</font></h5><br><a href=" + context.Request.RawUrl+ ">Back</a>");
      19:  
      20: context.Response.Flush(); 
      21: context.Response.End();
      22: }
      23: catch (Exception ex)
      24: {
      25: }
      26: }
      27: }

     

    This is a tricky workaround, however if you are looking for options to achieve this functionality then this would help.

    Enjoy!

  • Setting the property 'AllowMultipleValues' resets the control's type to "Lookup" when it is inherited from SPFieldLookup class.

    When you develop a custom field type that inherits from SPFieldLookUp/SPFieldUser(not sure about any other controls) .
    The derived field type has its own rendering controls. The issue arises when multiple values are to be stored in a field of the custom fieldtype.
    In order to save multiple values to an SPFieldLookUp the property "AllowMultipleValues" should be set to true . When "AllowMultipleValues" property is
    set to true, the field rendering control is overridden by MultipleLookupField. So the custom field rendering control is never instantiated/render if the field is
    set to store multiple values(AllowMultipleValues=true). As a workaround what I did is overridden the ‘OnAdded’ function and edit the schema to append the ‘Mult’ attribute.

       1: using System;
       2: using System.Collections.Generic;
       3: using System.Text;
       4: using Microsoft.SharePoint;
       5: using Microsoft.SharePoint.WebControls;
       6: using System.Web.UI;
       7: using System.Xml;
       8:  
       9: namespace CustLookuColumn
      10: { 
      11:     public class MultiLookUp : SPFieldLookup  
      12:     {        
      13:        
      14:        public MultiLookUp(SPFieldCollection fields, string fieldName)
      15:             : base(fields, fieldName)
      16:         {          
      17:                    
      18:         }
      19:  
      20:        public override void OnAdded(SPAddFieldOptions op)
      21:        {
      22:            base.OnAdded(op);        
      23:            XmlDocument doc = new XmlDocument();
      24:            doc.LoadXml(this.SchemaXml);
      25:            XmlNode element = doc.FirstChild;
      26:            XmlAttribute attr = doc.CreateAttribute("Mult");
      27:            attr.Value = "TRUE";
      28:            element.Attributes.Append(attr);
      29:            this.SchemaXml = doc.OuterXml;
      30:            base.Update();
      31:  
      32:        }
      33:        public MultiLookUp(SPFieldCollection fields, string typeName, string displayName)
      34:             : base(fields, typeName, displayName)
      35:         {
      36:            
      37:         }        
      38:        
      39:         public override BaseFieldControl FieldRenderingControl
      40:         {
      41:             get
      42:             {
      43:                 BaseFieldControl fieldControl = null;
      44:                 fieldControl = new RenderFieldControl(); //See the below code snippet                               
      45:                 fieldControl.FieldName = InternalName;
      46:                 return fieldControl;
      47:             }
      48:         }
      49:     }   
      50: }

     

       1: using System;
       2: using System.Runtime.InteropServices;
       3: using Microsoft.SharePoint;
       4: using Microsoft.SharePoint.WebControls;
       5: using System.Web.UI;
       6: using System.Web.UI.WebControls;
       7: using System.Web.UI.HtmlControls;
       8:  
       9: //This is just a skeleton, I haven't included the entire functionality. 
      10:  
      11: namespace CustLookuColumn
      12: {
      13: public class RenderFieldControl : BaseFieldControl
      14:    {      
      15:        protected override void CreateChildControls()
      16:        {
      17:            base.CreateChildControls();
      18:         }
      19:  
      20:     }
      21:  
      22: }

     

       1: <?xml version="1.0" encoding="utf-8"?>
       2: <FieldTypes>
       3:   <FieldType>
       4:     <Field Name="TypeName">MultiLookUp</Field>
       5:     <Field Name="TypeDisplayName">MultiLookUpField</Field>
       6:     <Field Name="TypeShortDescription">Custom Multi LookUp Field</Field>
       7:     <Field Name="ParentType">LookupMulti</Field>
       8:     <Field Name="UserCreatable">TRUE</Field>
       9:     <Field Name="FieldTypeClass">CustLookuColumn.MultiLookUp,CustLookuColumn , Version=1.1.1.1, Culture=neutral, PublicKeyToken=6332f1922196b6de</Field>
      10:     <Field Name="FieldEditorUserControl">/_controltemplates/YOUR_LookupFieldEditor.ascx</Field>
      11:     <!-- 
      12:         YOUR_LookupFieldEditor.ascx is not included it would be identical to the ascx control under ..\12\TEMPLATE\CONTROLTEMPLATES\LookupFieldEditor.ascx
      13:     -->
      14:   </FieldType>
      15: </FieldTypes>

     

    Note: The samples provided within this blog may not work, my intention was to provide you a workaround when you hit with this issue.

    Happy customization!!!

  • Issue with Multi Field/Group values and SPWorkflowTaskProperties.ExtendedProperties

    I encountered an issue when assigning a multi-user data to a workflow task field from within the workfflow.

    There may be various reasons anyone wants to create a User/Group field with multiple values and set it within in the workflow as below,

    SPWorkflowTaskProperties.ExtendedProperties["CustomAssignToUsers"] = "domain\user1;domain\user2";

    SharePoint checks the property collection and strips off the multiple user data from the property for type SPFieldUser.

    As a workaround, we can create a custom field which inherits from the SPFieldUser, this new field type bypass the control type checks within SharePoint.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using System.Xml;
     
    namespace MyCurrentUserFieldNS
    {
        public class MyCurrentUserField : SPFieldUser
        {
            public MyCurrentUserField(SPFieldCollection fields, string fieldName)
                : base(fields, fieldName)
            {
                this.Presence = true;         
            }
            
            public override bool AllowMultipleValues
            {
                get
                {
                    return true;
                }
                set
                {
                    base.AllowMultipleValues = true;
                }
            }
     
            //There is an issue with the SPFieldUser,setting the 'AllowMultipleValues' resets the
            //Control 'Type'. As a workaround, I override this function and update the field schema
            public override void OnAdded(SPAddFieldOptions op)
            {
                base.OnAdded(op);
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(this.SchemaXml);
                XmlNode element = doc.FirstChild;
                XmlAttribute attr = doc.CreateAttribute("Mult");
                attr.Value = "TRUE";
                element.Attributes.Append(attr);
                this.SchemaXml = doc.OuterXml;         
                base.Update();
     
            }        
            public MyCurrentUserField(SPFieldCollection fields, string typeName, string displayName)
                : base(fields, typeName, displayName)
            {
                this.Presence = true;
            }
            public override Type FieldValueType
            {
                get
                {
                    return typeof(SPFieldUserValueCollection);
                }
            }
            
            public override BaseFieldControl FieldRenderingControl
            {
                get
                {              
                    BaseFieldControl fldControl = new UserField();
                    fldControl.FieldName = InternalName;
                    return fldControl;             
                }
            }
        }
    }
    <FieldTypes>
        <FieldType>
            <Field Name="TypeName">MyCurrentUserField</Field>
            <Field Name="ParentType">LookupMulti</Field>
            <Field Name="TypeDisplayName">My Current User</Field>
            <Field Name="TypeShortDescription">My Current User</Field>
            <Field Name="Mult">TRUE</Field>
            <Field Name="UserCreatable">TRUE</Field>
            <Field Name="ShowInListCreate">TRUE</Field>
            <Field Name="ShowInSurveyCreate">TRUE</Field>
            <Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
            <Field Name="ShowInColumnTemplateCreate">TRUE</Field>
            <Field Name="FieldTypeClass">MyCurrentUserFieldNS.MyCurrentUserField, MyCurrentUserField, Version=1.0.0.0, Culture=neutral, PublicKeyToken=79330dc34f5433ec</Field>
            <Field Name="FieldEditorUserControl">/_controltemplates/UserFieldEditor.ascx</Field>
         </FieldTypes>

    Save the second markup as ‘FLDTYPES_MyCurrentUserField.xml’ and paste it under ..\12\TEMPLATE\XML folder

    Build an assembly using the code snippet provided below, strong name it.

    Ensure that you change the ‘FLDTYPES_MyCurrentUserField.xml’ according to the binary version and the publictokenkey.

    Other Resources

     

    Custom Field Types

    http://msdn.microsoft.com/en-us/library/ms446361.aspx

    Create a custom field type and  a field Control

    http://msdn.microsoft.com/en-us/library/bb417414.aspx

     

    Enjoy!

  • FullTextSqlQuery with QueryText length greater than 4096 characters throws ArgumentOutOfRange exception.

    The max number of characters that can be passed through FullTextSqlQuery is 4096 characters, beyond this length throws an exception ArgumentOutOfRangeException. You will get the same behavior when using the webservice QueryEx(XmlNode) as well.

       1: private void ExecQuery()
       2: {
       3: ResultTableCollection resultCollection = null;
       4: SPSite site = new SPSite("http://server/");
       5: FullTextSqlQuery fullText = new FullTextSqlQuery(site);
       6: fullText.ResultTypes = ResultType.RelevantResults;
       7: fullText.QueryText = "SELECT Title, Path, Description, Write, Rank, Size FROM Scope()"; //max 4096
       8: fullText.KeywordInclusion =KeywordInclusion.AnyKeyword;
       9: resultCollection = fullText.Execute();
      10: ResultTable results = resultCollection[ResultType.RelevantResults];
      11: dataGridView1.DataSource = null;
      12: if (results.RowCount > 0)
      13: {
      14: DataTable dtresults = new DataTable();
      15: dtresults.TableName = "Result"
      16: dtresults.Load(results, LoadOption.OverwriteChanges);
      17: DataSet ds = new DataSet("All_Results");
      18: ds.Tables.Add(dtresults);
      19: dataGridView1.DataSource = ds;
      20: dataGridView1.DataMember = "Result";
      21: }
      22: }

    Hope this helps.

     

  • How to implement custom User Profile handler?

       1: namespace MyNewProfileHandler
       2: {
       3: public class MyUserProfileHandler : MySiteProfileHandler 
       4: {
       5: public override void SetMySiteOwner(Microsoft.Office.Server.ServerContext context, UserProfile profile, string newOwner)
       6: { 
       7: base.SetMySiteOwner(context, profile, newOwner);
       8: } 
       9:  
      10: public override bool PreProfileDeleted(Microsoft.Office.Server.ServerContext context, UserProfile profile)
      11: { 
      12: return base.PreProfileDeleted(context, profile);
      13: } 
      14: }
      15: }
      16:  

    Installation steps:

    1) Strong name this assembly and register it to GAC

    2) Run the stsadm tool  with the below command and parameter

    stsadm -o profiledeletehandler -type "MyNewProfileHandler.MyUserProfileHandler, MyNewProfileHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6dc6c93b86ce3656"

    3) Reset the timer services

     

    How to test this custom handler?

    1) Do a full user profile import through SSP

    2) Delete a user from Active Directory

    3) Do an incremental import, you could see the custom handler gets called.

     

    Enjoy...

  • How to add, remove and start workflow programmatically?

    Removing Association

     

       1: SPSite site = new SPSite("http://localhost");
       2: SPWeb web = site.OpenWeb();
       3: SPList list = web.Lists["Shared Documents"];
       4: SPWorkflowAssociation _association = null;
       5: foreach (SPWorkflowAssociation association in list.WorkflowAssociations)
       6: {
       7: if (association.Name == "Approval")
       8: {
       9: _association = association;
      10: break; 
      11: }
      12: }
      13: if(_association !=null)
      14: list.RemoveWorkflowAssociation(_association);

     

    Adding workflow to the library

       1: SPSite site = new SPSite("http://localhost");
       2: SPWeb web = site.OpenWeb();
       3: SPList list = web.Lists["Shared Documents"];
       4: SPList taskList = web.Lists["Tasks"];
       5: SPList historyList = web.Lists["Workflow History"]; 
       6: SPWorkflowTemplate _template = null;
       7: foreach (SPWorkflowTemplate template in web.WorkflowTemplates)
       8: {
       9: if (template.Name == "Approval")
      10: {
      11: _template = template;
      12: break; 
      13: } 
      14: }
      15: SPWorkflowAssociation asso = SPWorkflowAssociation.CreateListAssociation(_template, _template.Name, taskList, historyList); 
      16: list.AddWorkflowAssociation(asso);

    Starting workflow

     

       1: SPSite site = new SPSite("http://localhost");
       2: SPWeb web = site.OpenWeb();
       3: SPList list = web.Lists["Shared Documents"];
       4: SPWorkflowAssociation association = AddWorkflow(); // Above function to get the SPWorlflowAssociation object
       5: SPWorkflowManager manager = site.WorkflowManager; 
       6: for (int idx = 0; idx < 10 ; idx++) // I'm assuming there would be atleast 10 items within the library
       7: {
       8: SPListItem item = list.Items[idx];
       9: manager.StartWorkflow(item,association, "you can pass any data here",true);
      10: }
  • How to change the Workflow History list programmatically?

       1: SPSite spSite = new SPSite("http://localhost");
       2: SPWeb spWeb = spSite.OpenWeb();
       3: SPList spList = spWeb.Lists["Shared Documents"];
       4: SPList workflowlist = spWeb.Lists["NewHistoryLibary"];
       5: SPWorkflowAssociation association = spList.WorkflowAssociations[0];
       6: association.SetHistoryList(workflowlist);
       7: spList.UpdateWorkflowAssociation(association); 
  • Updating 'ModerationStatus' field

    You might have experienced a road block when trying to the update the ‘ModerationStatus’ field with other fields. Any attempt to do so will result in an exception “You cannot change moderation status and set other item properties at that same time”. This is an expected behavior since the document deals with stream, promotion and demotion and for non document libraries we are not touching the streams, so it allows updating other fields with “ModerationStatus”.

    Here is the code: In this sample code, we are trying to update the 'Editor' field along with the 'ModerationStatus’.

          

       1: SPSite oSite = new SPSite("http://localhost:100/sites/Internetsite/");
       2:         SPWeb oWeb = oSite.OpenWeb(); 
       3:         SPList oList = oWeb.Lists["Shared Documents"]; 
       4:         SPListItem oListItem = oList.Items[0]; 
       5:         SPUser oUser = oWeb.AllUsers[@"domain\user123"]; 
       6:         SPFieldUserValue oUserValue = new SPFieldUserValue(oWeb, oUser.ID, oUser.LoginName); 
       7:         oListItem["Editor"] = oUserValue;                
       8:         oListItem.ModerationInformation.Status = SPModerationStatusType.Approved; 
       9:         oListItem.Update();

     

    Note: This behaviour is only with libraries of type document libraries(SPBaseType.DocumentLibrary).

    Good Luck!!!

  • SPWeb.GetObject() throws an exception when site context changes.

    Here is the scenario, you may want to get an object(SPListItem) without iterating through the list item collection, if you know the URL of the file the GetObject() returns the item.

    Section#1

       1: 1. SPSite site = new SPSite("http://nishand:100/sites/RootSite/");
       2: 2. SPWeb web = site.OpenWeb();
       3: 3. object ob1 = web.GetObject("http://nishand:100/sites/RootSite/SubSite/Shared%20Documents/Three.txt");
       4: 4. object ob2 = web.GetObject("http://nishand:100/sites/RootSite/SubSite/Shared%20Documents/Four.txt");
       5: 5. object ob3 = web.GetObject("http://nishand:100/sites/RootSite/Shared%20Documents/One.txt");

    You will get an exception when you try calling the …One.txt file(line # 5)

    Microsoft.SharePoint.SPException: Incorrect function. (Exception from HRESULT: 0x80070001) ---> System.Runtime.InteropServices.COMException (0x80070001): Incorrect function. (Exception from HRESULT: 0x80070001)

    Workaround:

    Section#2

       1: object ob2 = web.GetObject("http://nishand:100/sites/RootSite/SubSite/Shared%20Documents/Three.txt");
       2: web1 = site.OpenWeb();
       3: object ob2 = web.GetObject("http://nishand:100/sites/RootSite/SubSite/Shared%20Documents/Four.txt");
       4: web1 = site.OpenWeb();
       5: object ob3 = web.GetObject("http://nishand:100/sites/RootSite/Shared%20Documents/One.txt");

    Note: MOSS 2007 SP1 restricts the user from accessing a resource outside the current context web, so in section#1 -line#3 will throw an exception, because the web context is http://nishand:100/sites/RootSite/ and  the line#3 is trying to get an item from outside the current web.

     

    Happy coding!!!!

  • How to delete SPRecycleBin items?

       1: SPSite spSite = new SPSite("http://nishand:100/sites/InternetSite/");
       2: SPRecycleBinItemCollection collection = spSite.RecycleBin; 
       3:  
       4: for (int x = 0; x < collection.Count ; x++) 
       5: {           
       6:               collection[x].Delete(); x = x-1;                               
       7: }
       8:  

    The above code works only for the items deleted by the user who runs the application/current user.
    Any attempt to delete a file deleted by other user from the recycle bin file will result in an exception "Operation is not valid due to the current state of the object."

    We do have an overridden Delete() function, that helps to delete all RecycleBin items.

    SPRecycleBinItemCollection.Delete(Guid [] guids)

    Hope this works for you!

  • What does the SPWeb.ParserEnabled property do?

    When we set SPWeb.ParserEnabled to false, it disables the document parser (there will not be any property demotion and promotion), it means that the file properties and the WSS properties will not be in sync. So when you search for a custom file property value, the search result won't return you the file.

  • Changing the SPWorkflowAssociation.AutoCleanupDays property

    By default the SPWorkflowAssociation.AutoCleanupDays property value is set to ‘60’ days, to set this property we need to call the SPList.UpdateWorkflowAssociation(SPWorkflowAssociation) function, SPWeb.Update() won’t work here.

     

       1: SPSite site = new SPSite("<http://nishandv3:100/sites/DevSite/>");
       2: SPWeb web = site.OpenWeb();
       3: SPWorkflowTemplateCollection collection = web.WorkflowTemplates;
       4: SPList list = web.Lists["Shared Documents"];
       5: SPWorkflowAssociation _asso = null;
       6: foreach (SPWorkflowAssociation asso in list.WorkflowAssociations)
       7: {
       8: if (asso.Name == "Approval")
       9: {
      10: asso.AutoCleanupDays = 100;
      11: _asso = asso;
      12: }
      13: }
      14: list.UpdateWorkflowAssociation(_asso); 

     

    Hope this works for you!

  • How to hide subsite's GlobalNavigation nodes/tabs through SharePoint API?

     

    When we create a site either through UI or OM, it will not come as a tab unless you check the option ‘Site Actions-->Site Setting-->Navigation-->Show subsites to true’.

    On checking the option results in displaying all the subsites as ‘Tabs’. If you want to hide some of them programmatically, you need to change the ‘__GlobalNavigationExcludes’ property from the property bag of SPWeb object.

    Consider I have a site with 10 subsites, I enabled the option Site Actions--> Site Settings-->Navigation--> Show subsites to 'true, I can see now 10 addtional tabs, If I say SPWeb.Navigation.GlobalNodes.Count wont return a value approx equal to 10.

     

    SPSite site = new SPSite("http://nishandv3:100/Sites/DevSite/");

    SPWeb web = site.OpenWeb();

    string strGlobalNavigationExcludes = web.AllProperties["__GlobalNavigationExcludes"].ToString();

    web.AllProperties["__GlobalNavigationExcludes"] = strGlobalNavigationExcludes + "{569F3E30-F9AF-44d5-A52C-47670FC3FEAC}";

    // 569F3E30-F9AF-44d5-A52C-47670FC3FEAC" is the GUID of the web you want to hide from the navigation.

    web.Update();

     

    Hope this helps!

  • How to create SearchAlert Programmatically?

    Note: Make sure that you have enabled search based alerts SSP-> Search Settings -> Search Based Alerts

       1: SPSite spSite = new SPSite("http://nishand:300/sites/PubSite/Search/");
       2: SPWeb web = spSite.OpenWeb();
       3: Query alertQuery = new KeywordQuery(spSite);
       4: alertQuery.QueryText = "Hello" // Keyword
       5: SearchAlert searchAlert = new SearchAlert(spSite, alertQuery); 
       6: searchAlert.InnerAlert.AlertFrequency = SPAlertFrequency.Daily; 
       7: searchAlert.InnerAlert.Title = "Alert#2"
       8: searchAlert.InnerAlert.User = web.Users["domain\\user"]; 
       9: searchAlert.Update();

    How to check whether an alert named 'Alert#2' has been created?

      Go to Site Actions ->  Site Settings - User Alerts -> Select 'user name' and click on 'update' button.

  • How to set user permissions programmatically?

       1: SPSite site = null;
       2: SPWeb web = null;
       3: site = new SPSite("http://server:100/sites/DevSite/");
       4: web = site.OpenWeb();
       5: SPRoleAssignment roleAssignment = new SPRoleAssignment("domain\\user","alias@domain.com","Nishand","Simple Test For You!");
       6: SPRoleDefinition roleDefinition = web.RoleDefinitions.GetByType(SPRoleType.Contributor);
       7: roleAssignment.RoleDefinitionBindings.Add(roleDefinition);
       8: web.RoleAssignments.Add(roleAssignment); 

     

    Enjoy!!!

More Posts Next page »

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker