As I wrote previously the one of my recent projects was to create a new upload page for specific Document Library (based on custom list template) that will use Telerik Upload component (Silverlight) and will entirely replace OOB upload.aspx page. Looking for some suggestions how to do this I found few solutions but none of them met my criteria:
- Using SharePoint Designer for customizing default upload page - I'm not sure if this could apply to document library; I also rejected this option from start as non-programmatic approach because of problems in future development and maintenance
- New document template redirection trick - looks simple but this wasn't enough elegant solution for me (I know - who cares, it's SharePoint after all... ;-)
- Creating a custom action in ribbon and hiding the old one with javascript - the first part looks quite nice but the second is another hack; also, "Add new document" link at the bottom of default document library view still points to the standard /_layouts/Upload.aspx page...
- Creating a custom action in ribbon with custom rendering template for ribbon - very nice solution when you want to change upload pages for all lists on farm, but this is not applicable in my case; still "Add new document" link at the bottom remains
There were also other approaches like changing all related links with jQuery on client side but I would prefer some simple, elegant and server side solution that will not cause any problems on migration to the next version of SharePoint.
Because document libraries does not have the New form (new documents are uploaded or created from Office templates), setting links in list template schema in Forms section won't work:
<Forms> <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" /> <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" /> <Form Type="NewForm" Url="MyCustomUploadPage.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" /> </Forms>
For the same reason setting New form template in custom content type (inherited from Document CT) definition:
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"> <Display>DocumentLibraryForm</Display> <Edit>DocumentLibraryForm</Edit> <New>DocumentLibraryForm</New> </FormTemplates>
will not change the upload page. Both methods still works for regular list or CTs not inherited from Document.
This may sound obvious for experienced SharePoint developers, but it was not for me. When I understood this I've started looking for methods of changing upload links (marked on picture below) both in ribbon and in default list view.
The first one - ribbon button - looks fairly simple. I left it for future development after finding some articles describing creation of Custom Action and hiding the existing buttons. Of course finally I will have to do this.
The second - '+ Add new document' link - was definitely more challenging for me. First I've looked for the CT definition for Document Library and I found Toolbar definition in default view:
<Views>
<View BaseViewID="0" Type="HTML" MobileView="TRUE" TabularView="FALSE" FreeForm="TRUE">
...
<Toolbar Position="After" Type="Freeform">
<IfHasRights>
<RightsChoices>
<RightsGroup PermAddListItems="required" />
</RightsChoices>
<Then>
<Switch>
<Expr>
<GetVar Name="MasterVersion" />
</Expr>
<Case Value="4"><HTML><![CDATA[<div class="tb"><img src="/_layouts/images/caladd.gif" alt="" /> <a class="ms-addnew" id="idAddNewDoc" href="]]></HTML>
<HttpVDir /><HTML><![CDATA[/_layouts/Upload.aspx?List=]]></HTML>
<ListProperty Select="Name" /><HTML><![CDATA[&RootFolder=]]></HTML>
<GetVar Name="RootFolder" URLEncode="TRUE" /><HTML><![CDATA[" onclick="javascript:NewItem(']]></HTML>
<ScriptQuote NotAddingQuote="TRUE">
<HttpVDir />
</ScriptQuote><HTML><![CDATA[/_layouts/Upload.aspx?List=]]></HTML>
<ListProperty Select="Name" /><HTML><![CDATA[&RootFolder=]]></HTML>
<GetVar Name="RootFolder" URLEncode="TRUE" /><HTML><![CDATA[', true);javascript:return false;" target="_self">]]></HTML><HTML>$Resources:core,Add_New_Document;</HTML><HTML><![CDATA[</a></div>]]></HTML>
</Case>
<Default><HTML><![CDATA[ <table width="100%" cellpadding="0" cellspacing="0" border="0" > <tr> <td colspan="2" class="ms-partline"><img src="/_layouts/images/blank.gif" width='1' height='1' alt="" /></td> </tr> <tr> <td class="ms-addnew" style="padding-bottom: 3px"> <img src="/_layouts/images/rect.gif" alt="" /> <a class="ms-addnew" id="idAddNewDoc" href="]]></HTML>
<HttpVDir /><HTML><![CDATA[/_layouts/Upload.aspx?List=]]></HTML>
<ListProperty Select="Name" /><HTML><![CDATA[&RootFolder=]]></HTML>
<GetVar Name="RootFolder" URLEncode="TRUE" /><HTML><![CDATA[" onclick="javascript:NewItem(']]></HTML>
<ScriptQuote NotAddingQuote="TRUE">
<HttpVDir />
</ScriptQuote><HTML><![CDATA[/_layouts/Upload.aspx?List=]]></HTML>
<ListProperty Select="Name" /><HTML><![CDATA[&RootFolder=]]></HTML>
<GetVar Name="RootFolder" URLEncode="TRUE" /><HTML><![CDATA[', true);javascript:return false;" target="_self">]]></HTML><HTML>$Resources:core,Add_New_Document;</HTML><HTML><![CDATA[</a> </td> </tr> <tr><td><img src="/_layouts/images/blank.gif" width='1' height='5' alt="" /></td></tr> </table>]]></HTML>
</Default>
</Switch>
</Then>
</IfHasRights>
</Toolbar>
...
</View>
...
</Views>
which was promising but I've noticed that 'Add new document' in the view that is actually displayed is different than the one defined above (it calls NewItem2() JS function instead of NewItem()).
After next few hours of searching and digging in 14 SharePoint folder I finally found the answer why the link at the bottom of Document Library default view is rendered in such way. In SharePoint 2010 every List View is actually XsltListViewWebPart which is well described in MSDN (this was also something new for me as for SP rookie). As the name suggest rendering is based on (quite complex) XSL transformations that for default views are defined in vwstyles.xsl imported in main.xsl which is linked in the following line in View definition of list template:
<XslLink Default="TRUE">main.xsl</XslLink>
In case of 'Add new document' link transformations end on this template:
<xsl:template name="Freeform">
...
<xsl:variable name="Url">
<xsl:choose>
<xsl:when test="List/@TemplateType='119'"><xsl:value-of select="$HttpVDir"/>/_layouts/CreateWebPage.aspx?List=<xsl:value-of select="$List"/>&RootFolder=<xsl:value-of select="$XmlDefinition/List/@RootFolder"/></xsl:when>
<xsl:when test="$IsDocLib"><xsl:value-of select="$HttpVDir"/>/_layouts/Upload.aspx?List=<xsl:value-of select="$List"/>&RootFolder=<xsl:value-of select="$XmlDefinition/List/@RootFolder"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$ENCODED_FORM_NEW"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
...
</xsl:template>
which renders the link to default Upload.aspx page in _layouts folder. As you see for Document Libraries this link is always the same, no matter what you will put in New form in CT definition or list template.
The simple, programmatic and server side solution for changing the link I was looking for and which worked for me was to deploy my own xsl (i.e. custom_views.xsl):
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0"
exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:sppchemas-microsoft-com:xslt"
xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" ddwrt:oob="true">
<xsl:include href="/_layouts/xsl/main.xsl"/>
<xsl:include href="/_layouts/xsl/internal.xsl"/>
<xsl:template name="Freeform">
<xsl:param name="AddNewText"/>
<xsl:param name="ID"/>
<xsl:variable name="Url">
<xsl:choose>
<xsl:when test="List/@TemplateType='119'">
<xsl:value-of select="$HttpVDir"/>/_layouts/CreateWebPage.aspx?List=<xsl:value-of select="$List"/>&RootFolder=<xsl:value-of select="$XmlDefinition/List/@RootFolder"/>
</xsl:when>
<xsl:when test="$IsDocLib">
<xsl:value-of select="$HttpVDir"/>/_layouts/LargeFileUploadWithSLToSP/LibraryUpload.aspx?documentLibraryId=<xsl:value-of select="$List"/>&RootFolder=<xsl:value-of select="$XmlDefinition/List/@RootFolder"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$ENCODED_FORM_NEW"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
...
</xsl:template>
</xsl:stylesheet>
in a mapped VS solution folder (which will overwrite only the "Freeform" template) to Layouts and put the link to it in XslLink in View definition:
<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,All_Documents;" DefaultView="TRUE" DefaultViewForContentType="TRUE" MobileView="True" MobileDefaultView="True" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/dlicon.png" Url="Forms/AllItems.aspx"> <XslLink Default="true">custom_views.xsl</XslLink> ... </View>
Here is a sample Visual Studio 2010 solution structure.
The results (with Telerik Silverlight upload component) are shown below.
This satisfies all my requirements: it's a full programmatic server side solution, it does not require any javascript tricks (high risk that in next version of SP will not work, when for example the css style names will change), it can be applied selectively to specific Document Libraries based on custom list templates (no need to change the default behavior on entire WFE) and it is simple.
Flawless victory :-)
I hope this short article will spare your time.
The VS solution with code samples for this article is available on GoogleCode SVN: http://byteloom-codesamples.googlecode.com/svn/trunk/LargeFileUploadWithSLToSP/
Great post! but could you please tell me detail information about your solution in visual studio. For example i don't know how to add DataModel and UploadClient. I will be be very grateful if you answer me here or send me on email some piece of your code or may be your solution. Thanks)
ReplyDeleteHello ksusha!
DeleteThank you for your comment.
In one of my next posts I'll describe in some details how I used the Telerik upload component to build the custom upload form. I will try to answer in it the question about UploadClient Silverlight project.
I'm not sure if I understand correctly the question about DataModel folder in project - could you tell me something more about what do you need (definition of content type or list template / something else)?
Best regards
Marek
Well, I must do custom upload form for my library. I don't know may be it'll be better to use just list and then save new list element as doc. Ideally, my form should consist of lots of fields, so I want to make several pages (on the form will be paginator). So, person fill all parts of new element, save it. And all these fields with filled data should be written in Word document and saved in the document library. So may be I should think about list custom create form...Don't sure that you understand me..nevertheless, thank you in advance)
ReplyDeleteIf you don't have any specific requirement to the upload itself (like pause/resume or chunking) the reimplementation of upload form does not seem to be necessary.
DeleteIf you want to have personalized edit form to fill all required meta-data for document in library after it's uploaded you can just change the default rendering template for specific doc.lib. edit form (where you will be redirected automatically if your content type has any field marked as required). This is the standard document library approach.
I recommend this article: http://pioneeringsharepoint.blogspot.com/2009/11/correct-way-to-customize-forms.html
With this approach you should be able to implement pagination (or some kind of dialog-based wizard) in edit form.
In my case the change of upload form itself was required for resumability and chunking (uploading large files, like 2GB). Also the standard upload form does not have a progress bar.
Hello Marek.
ReplyDeleteThis is exactly the solution I'm looking for at my work currently. But most of your posting here is "what cannot be done", and then you have a screenshot on what has been done.
Could you please share the code with us. I'm struggling to get this finished. Your help is appreciated.
Thank you in advance.
Pinaki.
www.ePinaki.com
Hello Pinaki,
DeleteI agree, the description of the solution in my post could me more straightforward. I'm on holidays and I don't have my VM with SP2010 image with me. I'll try to upload the VS solution on github as I return.
For now this is the procedure I followed to achive what is shown on screenshots:
1. Deploy a custom application page with upload form (with Silvelight app embeded)
2. Deploy XSL (custom_views.xsl in example)
3. Refer to the new XSL in view definition in schema.xml of your custom list template
Best regards
Marek
Hello Marek.
DeleteWondering if you are back, but I could really use some help. I have a dedline to achieve, and this is my task. I'm also fighting on this a bit. Can you help?. I need a VS solution to start with.
Hello Pinaki,
DeleteI've just uploaded the solution to GoogleCode (SVN):
https://byteloom-codesamples.googlecode.com/svn/trunk/LargeFileUploadWithSLToSP/
Best regards
Marek
Sorry, I've gave you the URL for contributors (it requires authentication on CO). Here is the public (read-only): http://byteloom-codesamples.googlecode.com/svn/trunk/LargeFileUploadWithSLToSP/
DeleteThis worked for me. Thanks a lot!
ReplyDelete