Skip to content

Instantly share code, notes, and snippets.

@tiagoduarte
Last active September 7, 2020 16:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tiagoduarte/7c24de4ca66f455ef5bdc743d39d77b1 to your computer and use it in GitHub Desktop.
Save tiagoduarte/7c24de4ca66f455ef5bdc743d39d77b1 to your computer and use it in GitHub Desktop.
Release SharePoint file lock through html+js
<!--
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, '&lt;').replace(/>/g, '&gt;'));
$.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