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.

Mar 1

I've removed the .exe file extension from the Blocked File List in SharePoint Central Admin (Security > Define blocked file types). This allowed me to upload .exe files to document libraries but when I click on the .exe file to download it, I get a 404 Page not found error. I can download it if I click the drop down and select download, or if I go into Explorer view, but it's a Page not Found error when I click on the .exe file itself.

The issue is not with SharePoint. I found that the problem is with IIS.

If you are using IIS6, then to resolve, you do the following:

  1. Open IIS
  2. Right click on the website in question
  3. Click Properties
  4. Choose the Home Directory
  5. Look for the Execute Permissions drop down list
  6. Set it to "Scripts Only"

If you are using IIS7, then to resolve, you do the following:

  1. Open IIS
  2. Select the website in question
  3. Double Click on Handler Mappings
  4. Click on Edit feature permissions (on the right navigation)
  5. Uncheck the Execute checkbox
  6. Click OK

This will automatically recycle the app pool and you can now click on the executable file and you will be prompted to download it.

WARNING: There are some shortfalls to this method, click here to read more and for a way to circumvent the shortfalls.

Feb 17
Word 2010 Retrieve and Set Properties
Posted by Wei in C#, SharePoint, VSTO on 02 17th, 2011| | No Comments »

There are 3 types of properties for Word Documents... that's right, 3. Not 2. Initially I thought there are the Built in properties and the custom properties. But then, there are also content type properties.

I'm not sure how to explain this, but I'll do my best. Content Type properties are set in SharePoint. A custom content type is created and this custom content type inherits columns which are also set in SharePoint. I won't go into detail about how that is set up but it requires you to Allow Management of Content Types for the document library.

Once that is done and the columns added for this content type, they will appear as Content Type properties in the Word document. This means that when the user opens the word document, they can see the custom content types available for them to fill in.

Whatever the user enters into these custom content type properties will be saved into the columns of the list when the user saves the document. Note that if the user saves the document as a PDF, the columns will not be populated with the properties.

Anyway, to go back on to my topic of how to retrieve all 3 types of properties, here is the code:

Microsoft.Office.Interop.Word.Document currentDocument = Globals.ThisAddIn.Application.ActiveDocument;

DocumentProperties builtInDocProperties = (DocumentProperties)currentDocument.BuiltInDocumentProperties;
DocumentProperties customDocProperties = (DocumentProperties)currentDocument.CustomDocumentProperties;
MetaProperties contentTypeProperties = (MetaProperties)currentDocument.ContentTypeProperties;

// for each of these properties, I placed them in a Hashtable for ease of querying later
Hashtable htProperties = new Hashtable();

try
{
    foreach (MetaProperty prop in contentTypeProperties)
    {
        try
        {
            htProperties.Add(prop.Name, prop.Value);
        }
        catch { } // some property names/values cause exceptions, which is why I have this here
    }
}
catch
{
    // no content type properties found in this document
}

foreach (DocumentProperty prop in builtInDocProperties)
{
    try
    {
        htProperties.Add(prop.Name, prop.Value);
    }
    catch { }
}

foreach (DocumentProperty prop in customDocProperties)
{
    try
    {
        htProperties.Add(prop.Name, prop.Value);
    }
    catch { }
}

Now all the properties can be queried from the Hashtable htProperties.

For example, to get the title, you would use htProperties["Title"].ToString(). To get a custom property named "Favourite Colour", you would use htProperties["Favourite Colour"].ToString(). These properties are objects and need to be cast to the right variable type. Such as (DateTime)htProperties["BirthDate"].

To set the value of a property via code, you would need to know the name of the property and the type of property, be it a BuiltInDocumentProperty, a CustomDocumentProperty or a MetaProperty (content type property).

MetaProperty mp = contentTypeProperties["Favourite Colour"];
mp.Value = "Blue";

Feb 17

Usually most of my code is run on the server, meaning that the DLLs sit on the server. This means that I can use the Microsoft.SharePoint DLL to query SP lists, SP document libraries, and so on.

My last project required me to code a Word Add-in. This is a client side application that required a connection to SharePoint to query a SP list. This means that I am no longer able to use Microsoft.SharePoint DLL to query the list as that is only for server side code. I was aware that I had to use the Microsoft.SharePoint.Client and Microsoft.SharePoint.Client.Runtime DLLs but they did not exist in the .NET references list, so I was not able to add them (the list is filtered based on the type of app you are writing).

I did notice that the filter was set to ".NET Framework 4 Client Profile". I modified that to ".NET Framework 4" (in the Project Properties) and was then able to add the required DLL references.

The code to query the list on the client side is also quite different to querying the list on the server side.

Here's the code to query a list on the client side:

using SP = Microsoft.SharePoint.Client; // Make sure you have added the reference to Microsoft.SharePoint.Client
string sharePointSite = "http://intranet";
string siteName = "/site/subsite";
string listName = "MyListName";

SP.ClientContext ctx = new SP.ClientContext(sharePointSite + siteName);
var web = ctx.Web;
SP.List oList = web.Lists.GetByTitle(listName);

SP.CamlQuery oQuery = new SP.CamlQuery();
// the following query queries everything in the list ordered by ID and limited to 100 results
oQuery.ViewXml = "<View><Query><OrderBy><FieldRef Name='ID' Ascending='TRUE'/></OrderBy></Query><RowLimit>100</RowLimit></View>";
SP.ListItemCollection oListItems = oList.GetItems(oQuery);

ctx.Load(oListItems);

ctx.ExecuteQuery();

for (int i = 0; i < oListItems.Count; i++)
{
    SP.ListItem oListItem = oListItems[i];

    // do what you want with oListItem as it now contains the values in the list item.
}

Jan 14
To deploy a wsp in SP2010
Posted by Wei in SharePoint Web Parts on 01 14th, 2011| | No Comments »

Start SharePoint 2010 Management Shell (As Administrator) and run the following

Add-SPSolution –LiteralPath C:\WebPart\packagedwebpart.wsp

then you can do this:

Install-SPSolution –Identity packagedwebpart.wsp –WebApplication http://sp2010 -GACDeployment 

or this:

In Central Admin, click on System Settings > Manage farm solutions to see the web part. Click on it and select Deploy to deploy it.

Depending on how the wsp has been created, you may need to enable the web part in the site collection features.

« Previous Entries Next Entries »