Last active
September 7, 2020 16:20
-
-
Save tiagoduarte/7c24de4ca66f455ef5bdc743d39d77b1 to your computer and use it in GitHub Desktop.
Release SharePoint file lock through html+js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
SharePoint document lock release script | |
Tiago Duarte | |
Based on code from Peter Holpar, | |
https://pholpar.wordpress.com/2014/04/07/how-to-use-javascript-to-delete-short-term-locks-from-documents-opened-from-sharepoint/ | |
Page URL: | |
/Pages/UnlockFile.aspx | |
Script URL: | |
/Style Library/js/UnlockFile.inc | |
Features: | |
- detect lock presence | |
- show locked date and auto release date adapted to the current timezone | |
- determine lock type | |
- determine lock user | |
- release option | |
TODO: | |
- upload Unlock-File.inc to style library\js folder | |
- create a page, e.g. UnlockFile.aspx and add a Content Editor Web Part referencing th Unlock-File.inc location, /Style Library/js/UnlockFile.inc | |
USAGE: | |
UnlockFile.aspx?docUrl=http://weburl/library/doc001.docx&webUrl=http://weburl | |
or | |
UnlockFile.aspx | |
* Note * | |
Due to a sort problem in the release web service, the request expects a lower case 'utf-8' parameter in the content type header. | |
However, due to RFC compliance, Chrome and other browsers convert this value to uppercase automatically, causing the service call to throw an error: | |
> 400 (Bad Request) | |
The workaround at the moment is to simply run this code/page in Internet Explorer, until a workaround to ensure the lowercase value is found. | |
e.g.: Content-Type: multipart/related; type="application/xop+xml"; boundary="urn:uuid:8cfcbb22-dd52-4889-b29d-9ff2dcf909b2"; start="<f13ad06d-8530-4af1-8cf3-d6d75c1635d4@tempuri.org>"; start-Info="text/xml; charset=UTF-8" | |
(Fiddler, any of the 3 requests to CellStorageService, Request Headers, Entity | |
--> | |
<div id="locked-doc"> | |
<p> | |
<label>Document URL</label> | |
<br/> | |
<input id="docUrl" type="text" value="" style="width:500px" /> | |
</p> | |
<p> | |
<label>Website URL</label> | |
<br/> | |
<input id="webUrl" type="text" value="" style="width:500px" /> | |
</p> | |
<p> | |
<input type="button" value="UNLOCK" onclick="getLockForDoc();" /> | |
</p> | |
<p></p> | |
<div id="debugInfo"></div> | |
</div> | |
<script type="text/javascript"> | |
//get a parameter from querystring by its name, empty if fails | |
function getParameterByName(name){ | |
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); | |
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), | |
results = regex.exec(location.search); | |
return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); | |
} | |
// define String.format function | |
if (!String.prototype.format){ | |
String.prototype.format = function (){ | |
var args = arguments; | |
return this.replace(/{(\d+)}/g, function (match, number){ | |
return typeof args[number] != 'undefined' | |
? args[number] | |
: match | |
; | |
}); | |
}; | |
} | |
function Properties(source){ | |
this.source = source; | |
this.offspring = []; | |
this.getPropValue = function (propName){ | |
var propValue = ""; | |
var propNameFullLine = '<li>' + propName + '\n'; | |
var startPos = this.source.indexOf(propNameFullLine); | |
if (startPos > -1){ | |
var endPos = this.source.substr(startPos + propNameFullLine.length).indexOf('\n'); | |
if (endPos > 0){ | |
propValue = this.source.substr(startPos + propNameFullLine.length, endPos); | |
// trim leading chars | |
// http://msdn.microsoft.com/en-us/library/office/ms460937(v=office.14).aspx | |
var strDummy = '<li>SW|'; | |
propValue = unescapeEx(propValue.substr(strDummy.length)); | |
} | |
} | |
return propValue; | |
} | |
// this function is intended to be accessible from the object itself (e.g. private) | |
var unescapeEx = function (value){ | |
var result = value.replace(/&#([0-9]|[1-9][0-9]|[[01][0-9][0-9]|2[0-4][0-9]|25[0-5]);/g, function (str, match){ return String.fromCharCode(match); }); | |
return result; | |
} | |
} | |
//get lock details | |
function getLockForDoc(){ | |
var docUrl = $("#docUrl").val(); | |
var webUrl = $("#webUrl").val(); | |
if(docUrl === "" || webUrl === "") | |
{ | |
alert("Invalid parameters!"); | |
return; | |
} | |
if(docUrl.toLowerCase().indexOf("?web=1") !== -1) | |
{ | |
alert("Document URL cannot contain ?Web=1!"); | |
return; | |
} | |
if(docUrl.indexOf('http://http://') != -1 || webUrl.indexOf('http://http://') != -1) | |
{ | |
alert("duplicate http entries!"); | |
return; | |
} | |
var escapedDocUrl = decodeURI(docUrl); | |
$.ajax({ | |
url: $("#webUrl").val() + '/_vti_bin/_vti_aut/author.dll', | |
type: 'POST', | |
contentType: 'application/x-www-form-urlencoded', | |
headers:{ | |
'MIME-Version': '1.0', | |
//'User-Agent': 'MSFrontPage/14.0', | |
'Accept': 'auth/sicily', | |
'X-Vermeer-Content-Type': 'application/x-www-form-urlencoded' | |
}, | |
data: 'method=getDocsMetaInfo%3a14%2e0%2e0%2e6009&url%5flist=%5b' + escapedDocUrl + '%5d&listHiddenDocs=false&listLinkInfo=false', | |
complete: function (result){ | |
if ((result.readyState == 4) && (result.status == 200)){ | |
var rawResponse = result.responseText; | |
//debug | |
$("#debugInfo").html("<b>DEBUG INFO</b><br/>DOCUMENT METADATA:<br/>" + rawResponse); | |
var startPos = rawResponse.indexOf('<li>meta_info=\n<ul>'); | |
if (startPos > 0){ | |
var endPos = rawResponse.substr(startPos).indexOf('</ul>'); | |
if (endPos > 0){ | |
var props = new Properties(rawResponse.substr(startPos, endPos)); | |
var lockId = props.getPropValue('vti_sourcecontrollockid'); | |
var checkedOutBy = props.getPropValue('vti_sourcecontrolcheckedoutby'); | |
var checkedOutDate = props.getPropValue('vti_sourcecontroltimecheckedout'); | |
var checkedOutDateLocalTime = (new Date(checkedOutDate)).toString(); | |
var lockExpires = props.getPropValue('vti_sourcecontrollockexpires'); | |
var lockExpiresLocalTime = (new Date(lockExpires)).toString(); | |
var lockType = props.getPropValue('vti_sourcecontrollocktype'); | |
var lockTypeDesc = lockType == 1 ? "Shared" : "Exclusive"; | |
if (lockId == ""){ | |
alert("File is not locked."); | |
} | |
else{ | |
if (confirm(String.format("File is locked by '{0}'\r\nLocked Date: '{1}'\nLock Expire Date: '{2}'\nLockId = '{3}'\nLock Type = '{4} ({5})'\r\nDo you want to clear the lock?", checkedOutBy, checkedOutDateLocalTime, lockExpiresLocalTime, lockId, lockType, lockTypeDesc))){ | |
releaseLock(escapedDocUrl, lockId); | |
//window.location.href = "http://test/_layouts/15/ECDC.DMS.Core/UnlockDocument.aspx?DocUrl=" + escapedDocUrl + "&WebUrl=" + $("#webUrl").val(); | |
} | |
} | |
} | |
} | |
} | |
} | |
}); | |
} | |
// message template to unlock a document | |
var correlation = "{35E42C96-FE02-41FE-B4D8-F7DEC43AF784}"; | |
var releseLockReq = '\r\n'; | |
releseLockReq += '--urn:uuid:8cfcbb22-dd52-4889-b29d-9ff2dcf909b2\r\n'; | |
releseLockReq += 'Content-ID: <f13ad06d-8530-4af1-8cf3-d6d75c1635d4@tempuri.org>\r\n'; | |
releseLockReq += 'Content-Transfer-Encoding: 8bit\r\n'; | |
releseLockReq += 'Content-Type: application/xop+xml;charset=utf-8;type="text/xml; charset=utf-8"\r\n'; | |
releseLockReq += '\r\n'; | |
//exclusive lock | |
releseLockReq += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><RequestVersion Version="2" MinorVersion="0" xmlns="http://schemas.microsoft.com/sharepoint/soap/"/><RequestCollection CorrelationId="{0}" xmlns="http://schemas.microsoft.com/sharepoint/soap/"><Request Url="{1}" RequestToken="1"><SubRequest Type="ExclusiveLock" SubRequestToken="1"><SubRequestData ExclusiveLockRequestType="ReleaseLock" ExclusiveLockID="{2}"/></SubRequest></Request></RequestCollection></s:Body></s:Envelope>\r\n'; | |
//schemalock | |
//releseLockReq += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><RequestVersion Version="2" MinorVersion="0" xmlns="http://schemas.microsoft.com/sharepoint/soap/"/><RequestCollection CorrelationId="{0}" xmlns="http://schemas.microsoft.com/sharepoint/soap/"><Request Url="{1}" RequestToken="1"><SubRequest Type="SchemaLock" SubRequestToken="1"><SubRequestData SchemaLockRequestType="ReleaseLock" SchemaLockID="{2}"/></SubRequest></Request></RequestCollection></s:Body></s:Envelope>\r\n'; | |
releseLockReq += '--urn:uuid:8cfcbb22-dd52-4889-b29d-9ff2dcf909b2--\r\n'; | |
//release the document lock | |
function releaseLock(escapedDocUrl, lockId){ | |
var dataToSend = String.format(releseLockReq, correlation, escapedDocUrl, "{" + lockId + "}"); | |
var requestUrl = $("#webUrl").val() + "/_vti_bin/cellstorage.svc/CellStorageService"; | |
$("#debugInfo").html("<b>DEBUG INFO</b><br/>SERVER REQUEST:<br/>" + dataToSend.replace(/</g, '<').replace(/>/g, '>')); | |
$.ajax({ | |
url:$("#webUrl").val() + "/_vti_bin/cellstorage.svc/CellStorageService", | |
type:'POST', | |
headers:{ | |
'MIME-Version': "1.0", | |
'SOAPAction': "http://schemas.microsoft.com/sharepoint/soap/ICellStorages/ExecuteCellStorageRequest", | |
'Content-Type': "multipart/related; type=\"application/xop+xml\"; boundary=\"urn:uuid:8cfcbb22-dd52-4889-b29d-9ff2dcf909b2\"; start=\"<f13ad06d-8530-4af1-8cf3-d6d75c1635d4@tempuri.org>\"; start-Info=\"text/xml; charset=utf-8\"", | |
'X-Vermeer-Content-Type': "application/x-www-form-urlencoded" | |
}, | |
data:dataToSend, | |
complete: function(result){ | |
if ((result.readyState == 4) && (result.status == 200)) | |
{ | |
var rawResponse = result.responseText; | |
if(rawResponse.indexOf("ErrorCode=\"Success\"") !== -1) | |
alert("The lock has been released!"); | |
else | |
{ | |
if(rawResponse.indexOf("FileAlreadyLockedOnServer") !== -1) | |
alert("Sorry, only the person who has the lock can unlock this particular document."); | |
else | |
alert("Error " + result.status + ": " + rawResponse); | |
} | |
} | |
else | |
{ | |
//note: some browsers will convert 'utf-8' to "UTF-8" which will break the server code. apparently we must use Internet Explorer for it to work | |
if(result.status == 400) | |
alert("Error 400. Please try using Internet Explorer instead."); | |
else | |
alert("Error " + result.status + ": " + rawResponse); | |
} | |
} | |
}); | |
} | |
//populate input boxes with query string values | |
SP.SOD.executeOrDelayUntilEventNotified(function() | |
{ | |
var webUrl = getParameterByName("webUrl"); | |
if(webUrl !== "") | |
$("#webUrl").val(webUrl); | |
var docUrl = getParameterByName("docUrl"); | |
if(docUrl !== "") | |
$("#docUrl").val(docUrl); | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment