Jan 6

To check if a user is in a SharePoint Group (which also checks if the user is in an AD Group within that SharePoint group), use the following code:

 using System.DirectoryServices.AccountManagement;

        public bool IsUserInSharePointGroup(string webUrl, string groupName, string username)
        {
            bool userIsInGroup = false;

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                try
                {
                    SPWeb web = SPContext.Current.Web;

                    // Find the group
                    SPGroup group = web.SiteGroups[groupName];
                    string upperCaseUserName = username.ToUpper();

                    foreach (SPUser user in group.Users)
                    {
                        // Check if this is an AD Group
                        if (!user.IsDomainGroup)
                        {
                            // Verify if the user name matches the user name in group
                            if (user.LoginName.ToUpper().Equals(upperCaseUserName))
                            {
                                userIsInGroup = true;
                                return;
                            }
                        }
                        else
                        {
                            // this is an AD group
                            var pc = new PrincipalContext(ContextType.Domain);
                            var myuser = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, username);
                            var mygroup = GroupPrincipal.FindByIdentity(pc, user.LoginName);
                            if (myuser.IsMemberOf(mygroup))
                            {
                                userIsInGroup = true;
                                return;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    //Trace error
                }
            });
            return userIsInGroup;
        }

Dec 8

There is a slight difference to looping through discussion board items as compared to standard list items. The main difference is having to use SPList.Folders and not SPList.Items

As a result of that, extracting any attachments to the SPList.Folders is not very obvious.

I had been trying to get the attachment as an SPFile object so I can convert it into binary and store it in the SQL Server database as an "image" (binary object). However I seem to only be able to get the filename as a string and not as an SPFile object.

Here's the code I used:

SPWeb mySite = SPContext.Current.Web;
SPList oList = mySite.Lists["DiscussionBoardListName"];
SPListItemCollection oListItems = oList.Folders;

for (int i = 0; i < oListItems.Count; i++)
{
     SPListItem item = oListItems[i];

     if (item.Attachments.Count > 0)
     {
          for (int j = 0; j < item.Attachments.Count; j++)
          {
               // item.Attachments.UrlPrefix - gives the path to the attachment as a string
               // item.Attachments[j]; - gives the attachment name as a string
               // together I couldn't get it into a SPFile object.
               // I tried to use the URL method to read the file Url into stream,
               // but I get an access denied error
          }
     }
}

To get the attachments into SPFile format, I had to get the parent folder into an SPFolder object and loop through the SPFolder and get the contents as SPFile objects.

for (int i = 0; i < oListItems.Count; i++)
{
     SPListItem item = oListItems[i];

     SPFolder attachmentsFolder = item.ParentList.RootFolder.SubFolders["Attachments"].SubFolders[item.ID.ToString()];

     if (item.Attachments.Count > 0)
     {
          for (int j = 0; j < item.Attachments.Count; j++)
          {
               SPFile file = attachmentsFolder.Files[j];
               // then do what you want with the SPFile object
          }
     }
}

Aug 31
Setting process priority
Posted by Wei in C# on 08 31st, 2011| | No Comments »

If you are developing something that requires use of 3rd party programs, it usually is a good idea to change the priority of the process to something below normal otherwise it may take up all server resources which could result in performance issues to other users.

The process priority can only be set through System.Diagnostics.Process:

System.Diagnostics.Process myProcess = null;
myProcess = System.Diagnostics.Process.Start(@"C:\\path\\to\\some\\program", "-param1 value1 -param2 value2...");
myProcess.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;

However, if the process that is being started is an assembly such as Microsoft.Office.Interop.Word where you would use Word.Application oWord = new Word.Application(), setting the priority class is a little more difficult.

In my research, I was not able to find an easy way of setting the process to below normal, so I had to settle with a work around.

System.Diagnostics.Process[] thisProc = System.Diagnostics.Process.GetProcessesByName("WINWORD");
foreach (System.Diagnostics.Process proc in thisProc)
{
    proc.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
}

This probably needs to be run with elevated privileges, so the full code would be:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
    System.Diagnostics.Process[] thisProc = System.Diagnostics.Process.GetProcessesByName("WINWORD");
    foreach (System.Diagnostics.Process proc in thisProc)
    {
        proc.PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
    }
});

Jul 28
Editing InfoPath 2010 xml with C#
Posted by Wei in C#, InfoPath, SharePoint on 07 28th, 2011| | No Comments »

Here's a code snippet to edit nodes in an existing InfoPath 2010 Xml document from a selected Form Library in SharePoint 2010:

 SPWeb mySite = SPContext.Current.Web;
 SPList oList = mySite.Lists["FormLibraryName"];
 SPQuery oQuery = new SPQuery();
 oQuery.Query = "<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='Text'>TargetFile.xml</Value></Eq></Where>";
 SPListItemCollection listItems = oList.GetItems(oQuery);

 if (listItems.Count > 0)
 {
  SPListItem item = listItems[0];

  MemoryStream oMemoryStream = new MemoryStream(item.File.OpenBinary());
  XmlTextReader oReader = new XmlTextReader(oMemoryStream);

  XmlDocument oDoc = new XmlDocument();
  oDoc.Load(oReader);

  oReader.Close();
  oMemoryStream.Close();

  XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(oDoc.NameTable);
  nameSpaceManager.AddNamespace("my", oDoc.DocumentElement.NamespaceURI);

  oDoc.DocumentElement.SelectSingleNode("my:NodeToUpdate", nameSpaceManager).InnerText = "Update text";
  oDoc.DocumentElement.SelectSingleNode("my:Node/my:SubNode", nameSpaceManager).InnerText = "Update text";

  mySite.AllowUnsafeUpdates = true;

  System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  SPFile oSPFile = mySite.Folders[mwp.FormLibraryName].Files.Add(item.File.Name, (encoding.GetBytes(oDoc.OuterXml)), true);
 }

Apr 12
More notes on 404 error page for .exe files
Posted by Wei in C# on 04 12th, 2011| | No Comments »

It has been brought to my attention that my previous post to modify IIS to enable users to click on .exe files and download them is flawed as it will disable quite a bit of other functionality in SharePoint. One big one is the inability to use SharePoint designer to navigate to subsites. Big problem there if you have SPD workflows or custom page layouts etc on those subsites.

So in the end, I had to undo the solution as discussed and create a custom web part to add to the document library in question that would modify the div (using Javascript) with the same Javascript link that is used to download the file.

It's not the neatest piece of code since the client needed it in a hurry so I didn't really think about the best way to do things. I just made it work.

The code will prompt you to download anything in the document library. So if the document is a .html file or a .aspx file, it won't open it, it will instead prompt you to download it. You can modify the code to distinguish between files that should be opened vs files that should be downloaded though with a simple if statement.

In addition, the code works for items that have been grouped. If the document library is not grouped, then the code can be simplified. However, the way it is now will work whether the document library is grouped or not.

Here's the javascript part of things:

<script language='javascript'>
function changeDownloadLink(id,NewHtml) {
    // check that the element exists
    if (document.getElementById(id)) {
        // check that the element does not yet contain the string stsnavigate
        // if it doesn't then replace the html
        if (document.getElementById(id).innerHTML.indexOf(""STSNavigate"") == -1) document.getElementById(id).innerHTML=NewHtml;
    }
}

items = new Array();

function saveArray(item) {
    items[items.length]=item;
}

function resetLinks() {
    // loop through the items array
    for (var i=0;i<items.length;i++) {
        if (i%2!=0) {
            changeDownloadLink(items[(i-1)],items[i]
);
            //if (i==1) alert(items[(i-1)] + ' - ' + items[i]
);
        }
    }
}

window.setInterval(function()
{  
   resetLinks();
}, 1000);

</script>

And here is the C# Render method (DocLibName) is the name of the document library that contains all the files that should be downloaded.

           try
            {
                SPWeb mySite = SPContext.Current.Web;
                SPList oList = mySite.Lists[DocLibName];

                SPListItemCollection listItems = oList.Items;

                if (listItems.Count > 0)
                {
                    for (int i = 0; i < listItems.Count; i++)
                    {
                        SPListItem item = listItems[i];

                        writer.Write("<script>changeDownloadLink('" + item["ID"].ToString() + "','<a href=\"#\" onclick=\"STSNavigate(\\'" + mySite.Url + "/_layouts/download.aspx?SourceUrl=" + HttpUtility.UrlEncode(item.File.ServerRelativeUrl) + "\\')\">" + item.DisplayName + "</a>')</script>");

                        string url = "<a href=\"#\" onclick=\"STSNavigate(\\'" + mySite.Url + "/_layouts/download.aspx?SourceUrl=" + HttpUtility.UrlEncode(item.File.ServerRelativeUrl) + "\\')\">" + item.DisplayName + "</a>";
                        writer.Write("<script>saveArray('" + item["ID"].ToString() + "');</script>");
                        writer.Write("<script>saveArray('" + url + "');</script>");
                    }
                }
                //writer.Write("All Done");
            }
            catch (Exception ex)
            {
                writer.Write(ex.ToString());
            }

It's not the prettiest code, but it works and the clients are happy.

« Previous Entries