And I say to ASHX for SharePoint: make me a folder.
Sometimes, I do get to do some on-premises farm stuff. Because deep in my soul, I'm a hardcore dev. Muahaha.
Scenario
In InfoPath, we want to be able to:
- Send people to a folder within a document library, for them to upload attachments.
- Each form has its own unique ID, say "1234". The folder will be <site>/Attachments/1234/
- The folder doesn't need to be created when it's not used. That is, it would be great to create the folder ON DEMAND
- Finally, InfoPath is quite dumb. It only has a hyperlink.
Solution
- Create a HTTP Handler that takes this URL: <site/sitecollection>/_layouts/InfoPathHelper/InfoPathHandler.ashx?folder=<site>/attachments/<ID>
- Create a folder on demand, and then respond via a HTTP Redirect.
Steps
- Add a ashx handler to your SharePoint solution. CKSDev has great template for this.
- Add code to ProcessRequest
public void ProcessRequest(HttpContext context)
{
if (!string.IsNullOrEmpty(context.Request.QueryString["folder"]))
{
CreateFolderAndRedirectResponse(context);
return;
}
}
- Add a function to check for the folder, create it if we need it, and end with a redirect.
private void CreateFolderAndRedirectResponse(HttpContext context)
{
// <site-collection>/_layouts/InfoPathHelper/InfoPathHandler.ashx?folder=<site>/var path = context.Request.QueryString["folder"];
var server = new Uri(SPContext.Current.Web.Url);
var url = string.Format("{0}://{1}{2}", server.Scheme, server.Authority, path);// elevate permission to create the folder.
SPSecurity.RunWithElevatedPrivileges(() =>
{
try
{
using (var site = new SPSite(url))
{
using (var web = site.OpenWeb())
{
SPFolder folder = web.GetFolder(path);
SPFolder f = folder;
List<SPFolder> folders = new List<SPFolder>();
SPDocumentLibrary library = folder.DocumentLibrary;
if (library == null)
{
return;
}
while(f.Url.ToLower() != library.RootFolder.Url.ToLower()){
if (f.Exists || string.IsNullOrEmpty(f.Url))
{
break;
}folders.Add(f);
f = f.ParentFolder;
if (f == null)
{
// if this happens we're in trouble
return;
}
}
if (folders.Count > 0)
{
// we are in a GET request - need to allow unsafe updates
web.AllowUnsafeUpdates = true;
folders.Reverse();
foreach (SPFolder f1 in folders)
{
if (!f1.Exists)
{
f1.ParentFolder.SubFolders.Add(f1.Name);
}
}
web.AllowUnsafeUpdates = false;
}
}
}
}
catch (Exception ex)
{}
});context.Response.Redirect(path, true);
}
- Wait what's all the strange looking nested folder stuff? That's right, as a bonus, if you specify nested folder within the document library, the HTTP Handler will create those too!
folder=/attachments/1234/1235/ - InfoPath is super simple, just add a Hyperlink to the URL
Link to Data Source:concat("/_layouts/InfoPathHelper/InfoPathHandler.ashx?attachments?folder=", my:ID)
See it running