SharePoint – Creating dynamic or static document URL
Links are meant to be broken, at least if you move files or change file names of documents in SharePoint.
I had a customer that required links not to be broken when file name was changed or a document was moved. They also wanted to be able to create links to a specific version, current version and latest major version. The idea was by using the document context menu, users would have the option to create these URL’s.
I wrote a post about creating URL handler for document URLs, this time we will focus on how we can reuse that URL handler to create dynamic and static URLs to documents and document-versions directly from the GUI.
Microsoft has implemented a great way to reach documents by the document id, but it seems that they didn’t fully implement it’s potential into SharePoint. Therefor I would like to show you how we implemented a useful way to reach document versions by using the document id.
This implementation requires the document id feature in SharePoint 2010 enabled. It can be found under Site Collection Features.
Creating the context menu feature
Adding a new option to the context menu is quite easy, it’s all XML. Just add a new Module to your project, I called mine CustomContextMenuActions. Then, by editing the Elements.xml, add a new CustomAction like this. By using the query-string IsDlg=1 we tell SharePoint to remove stuff from the master-page that isn’t relevant to the dialog.
1 2 3 4 5 6 7 8 9 10 11 |
<CustomAction Id="UserInterfaceCustomActions.ECBItemToobar" RegistrationType="List" RegistrationId="101" Location="EditControlBlock" Sequence="898" Title="Create url" Description="Create a dynamic or static document url" ImageUrl="/_layouts/images/LINKTOPAGE.GIF"> <UrlAction Url="javascript:openCreateUrl('{SiteUrl}/_layouts/customapplicationpages/createurl.aspx?IsDlg=1&ItemUrl={ItemUrl}')"/> </CustomAction> |
It would create an action like this.
Before we can use this, we have to create the application page called createurl.aspx and the JavaScript function openCreateUrl.
Creating the application page
The action redirect will send us to an application page called createurl.aspx. This page will contain the layout and logic that is needed for the creation of the URLs.
First we have to add an application page in our solution. I placed it in /_layouts/customapplicationpages/ and named it createurl.apx.
We want the page to be shown as a ModalDialog. The reason we use a ModalDialog is because it looks clean and we don’t need to navigate away from the current view.
This it what the ModalDialog would look like
Markup
Here is an example of the markup for the application page, with inline styling. You can of course move this into your CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server"> <div style="border:1px solid;width:315px"> <div style="padding:5px;background-color:#ffffdd;border-bottom:1px solid;" Create a dynamic or static URL from document <div> <asp:Label ID="DocLabel" runat="server" style="font-weight:bold"/> </div> </div> <div style="margin:5px;width:305px;"> <div> Choose a version </div> <div> <div style="float: left; margin-top:15px;" id="versionslist"> <asp:DropDownList ID="VersionsDropDown" runat="server" DataValueField="value" DataTextField="key"/> </div> <div id="copyUrlBtn" style="background-color:#FDEEB3;border:1px solid #F1C435;float:right;padding:6px;cursor:pointer"> <img alt="" src="/_layouts/images/allcontent32.png"/> <span style="bottom: 10px; position:relative;"> Copy URL </span> </div> </div> <div> <input type="text" id="urlInptut" style="width:300px;margin-top:10px;" disabled=""/> </div> </div> </div> </asp:Content> |
Code Behind
This will add the File-name to DocLabel and populate VersionsDropDown with version as text and the URL as value. The extension method TryGetDocumentId can be found in this post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
public partial class CreateDocumentUrl : LayoutsPageBase { protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { string itemUrl = Request.QueryString["ItemUrl"]; if (!string.IsNullOrEmpty(itemUrl)) { //Get the SPFile. SPFile file = SPContext.Current.Web.GetFile(itemUrl); string docId; if (file.Exists && file.Item.TryGetDocumentId(out docId)) { this.DocLabel.Text = file.Name; //Create the full URL with DNS string docUrl = SPContext.Current.Site.MakeFullUrl(SPContext.Current.Web.ServerRelativeUrl) + "/getdocument?docid=" + docId; //Create the DataSource for the dropdown Dictionary<string, string> versions = new Dictionary<string, string>(); //Add the URL for the latest version versions.Add("Latest version", docUrl); //Add the URL for the latest major version versions.Add("Latest major", docUrl + "&version=major"); //Add the current version versions.Add(file.UIVersionLabel, docUrl + "&version=" + file.UIVersionLabel); //Add all version from the version history SPFileVersionCollection fileVersions = file.Versions; for (int i = fileVersions.Count - 1; i > 0; i--) { string versionLabel = fileVersions[i].VersionLabel; versions.Add(versionLabel, docUrl + "&version=" + versionLabel); } this.VersionsDropDown.DataSource = versions; this.VersionsDropDown.DataBind(); } else { SPUtility.TransferToErrorPage("No document was found"); } } else { SPUtility.TransferToErrorPage("No document was found"); } } } } |
JavaScript
This JavaScript requires jQuery. I decided to handle rest of the GUI logic in JavaScript, no more code-behind. This JavaScript includes code for the ModalDialog and parent window.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
$(document).ready(function () { var urlInput = $("#urlInptut"); var versionList = $("#versionslist select"); //Set the value to the url input. urlInput.val($(":selected", versionList).val()); moveToEnd(urlInput[0]); //When dopdown changes, set the new value to the url input. versionList.change(function () { urlInput.val($(":selected", versionList).val()); moveToEnd(urlInput[0]); }); //Try to add the url input to the clipboard. $("#copyUrlBtn").bind("click", function () { var urlToCopy = urlInput.val(); if (copyUrl(urlToCopy)) { //use the parent window to close the dialog. parent.closeModalDialog(false, "URL was copied"); } else { //select the text if other browser than ie. urlInput.removeAttr('disabled'); urlInput.select(); } }); }); //Move to end of input text and select all. function moveToEnd(el) { var len = el.value.length || 0; if (len) { if ('setSelectionRange' in el) el.setSelectionRange(len, len); else if ('createTextRange' in el) { var range = el.createTextRange(); range.moveStart('character', len); range.select(); } } } //Adds the URL to the windows.clipbord. Only supported by IE function copyUrl(url) { if ($.browser.msie) { window.clipboardData.setData('text', url); return true; } else { alert("Your browser does not support this function. Copy manually."); return false; } } //Closes the child modal dialog window. function closeModalDialog(refresh, notification) { SP.UI.ModalDialog.commonModalDialogClose(); //Refresh the page after closing. if (refresh) { SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK); } //Show notification if (notification != null) { SP.UI.Notify.addNotification(notification); } } //Opens a ModalDialog with the right size for the application page. function openCreateUrl(url) { JSRequest.EnsureSetup(); var options = { url: url, height: 165, width: 335 }; SP.UI.ModalDialog.showModalDialog(options); } |
This will create a quite handy feature that will generate the URL’s and present them to the user in a way that is easy to understand. This feature was very appreciated by my customer, they use it to build baselines and add hyperlinks in TFS.