SharePoint 2013 - Get SPListItem versions via REST - sharepoint

I have a SharePoint 2013 List with versioning enabled.
I need to to get SPListItem versions list via REST.
I can get SPListItem by that request: http://spbreportportal/Projects/_api/lists/getbytitle('Projects')/Items(1)
But I can't find in documentation and in response how to retrieve all versions of this item.
Is it possible?

It does not seem possible to get versions for a List Item via REST/CSOM APIs, but there are alternative options
Using Versions.aspx application page
The idea is to perform a get request to Versions page: http://<server>/<site>/_layouts/versions.aspx?list={litsID}&ID=<itemID>
function getItemVersions(url,listId,itemId,success)
{
var versionsUrl = url + '/_layouts/versions.aspx?list=' + listId + '&ID=' + itemId;
$.get( versionsUrl, function( data ) {
var versionEntries = parseVersionList(data);
success(versionEntries);
});
}
function parseVersionList(data){
var entries = {};
var versionList = $(data).find('table.ms-settingsframe');
versionList.find('tbody > tr').each(function(i){
if(i > 0 && (i-1) % 2 == 0) {
var verRow = $(this); //get version row
var propsRow = verRow.next(); //get properties row
var versionLabel = verRow.find('td:first').html().trim();
entries[versionLabel] = {};
//extract item properties from propsRow goes here
//...
}
});
return entries;
}
//Usage
var webUrl = _spPageContextInfo.webAbsoluteUrl;
var listId = _spPageContextInfo.pageListId;
var listItemId = 1;
getItemVersions(webUrl,listId,listItemId,function(versionEntries){
console.log(versionEntries);
});
Using Lists SharePoint Web Services
Another option would be to utilize Lists SharePoint Web Services that exposes Lists.GetVersionCollection Method to return version information for the specified field in a SharePoint list
SPServices example:
$().SPServices({
operation: "GetVersionCollection",
async: false,
strlistID: "Projects",
strlistItemID: 1,
strFieldName: "Description",
completefunc: function (xData, Status) {
$(xData.responseText).find("Version").each(function(i) {
console.log("Name: " + $(this).attr("Description") + " Modified: " + $(this).attr("Modified"));
});
}
});

Note: This doesn't seem to work in 2013. I have verified this working in SharePoint Online and it may work in 2016+ but I have not verified the latter.
The situation may have changed since this question was originally posted, but it is now possible to use the REST API to get version history for any list/library item:
https://url/to/site/_api/web/Lists/getbytitle('MyListName')/items(ITEMID)/versions
This will return a series of results for the current version and all past versions, with the item's column values from each version.
As with other REST endpoints, you can use $select, $filter, etc. to further manipulate the results.

In the REST API, you can select the property OData__UIVersionString. It also supports OData__ModerationStatus
Ex:
GET http://site url/_api/web/lists/GetByTitle(‘Test')/items(item id)?$select=OData__UIVersionString,OData__ModerationStatus
More infos : https://msdn.microsoft.com/en-us/library/office/dn292552.aspx
It's not a solution to get all the versions or a specific version, but it's more info on the version.

To add to #Vadim Gremyachev's Excellent answer to use "GetversionCollection": This interface can also be reached using old school SOAP. Unfortunately it only returns one field at the time (so we use a lot of calls ...). The C# snippet is below.
//https://blogs.msdn.microsoft.com/pinch-perfect/2016/06/04/sharepoint-web-services-read-version-history-for-column-changes/
//http://www.indy.gov/eGov/City/DCE/Permits/Signs/_vti_bin/lists.asmx?op=GetVersionCollection
//https://www.codeproject.com/Articles/26338/Using-the-GetListItems-GetVersionCollection-and-Up
string strSite =
string strListGuid =
string strListItemID =
string strFieldName = "Title" // or some other field name
string requestXML = "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>" +
"<soap:Body>" +
"<GetVersionCollection xmlns='http://schemas.microsoft.com/sharepoint/soap/'>" +
"<strlistID>"+ strListGuid + "</strlistID><strlistItemID>" + strListItemID + "</strlistItemID>" +
"<strFieldName>"+ strFieldName +"</strFieldName>" +
"</GetVersionCollection>" +
"</soap:Body>" +
"</soap:Envelope>";
object xmlRequestObj = Activator.CreateInstance(Type.GetTypeFromProgID("Microsoft.XMLHTTP"));
MSXML2.XMLHTTP xmlRequest = (MSXML2.XMLHTTP)xmlRequestObj;
xmlRequest.open("Get", strSite + "/_vti_bin/Lists.asmx", false, null, null);
xmlRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/GetVersionCollection");
xmlRequest.send(requestXML);
string responseText = xmlRequest.responseText;

To add more information reagrding on how to obtain all version history from a SharePoint list:
//Get ID of the Dossier in SP list
strID = items(i - 1).getAttribute("ows_ID")
Debug.Print strID
//Get all Versions of the ID in SP list as a XML
URL1: https://path to site collection/_vti_bin/owssvr.dll?Cmd=Display&List={LIstID}&XMLDATA=TRUE&Query=*&IncludeVersions=TRUE
XDoc3.Load (URL1 & "&FilterField1=ID&FilterOp1=eq&FilterValue1=" & strID)
Set Item = XDoc3.SelectNodes("//rs:data/*")
Set temp3 = XDoc3.SelectNodes("//rs:data/*")

Related

How to get a list of internal id in netsuite

Is there a proper way to get a list of all internal id in a Netsuite Record?
var record = nlapiLoadRecord('customer', 1249);
var string = JSON.stringify(record);
nlapiLogExecution('DEBUG', 'string ', string );
I can get most of the id using json string but i am looking for a proper way. It prints all the data including internal id. Is there any Api or any other way ?
I actually developed a Chrome extension that displays this information in a convenient pop-up.
I ran into the same issue of nlobjRecord not stringifying properly, and what I ended up doing was manually requesting and parsing the same AJAX page NS uses internally when you call nlapiLoadRecord()
var id = nlapiGetRecordId();
var type = nlapiGetRecordType();
var url = '/app/common/scripting/nlapihandler.nl';
var payload = '<nlapiRequest type="nlapiLoadRecord" id="' + id + '" recordType="' + type + '"/>';
var response = nlapiRequestURL(url, payload);
nlapiLogExecution('debug', response.getBody());
You can have a look at the full source code on GitHub
https://github.com/michoelchaikin/netsuite-field-explorer/
The getAllFields() function will return an array of all field names, standard and custom, for a given record.
var customer = nlapiLoadRecord('customer', 5069);
var fields = customer.getAllFields();
fields.forEach(function(field) {
nlapiLogExecution('debug', field);
})

(401) Unauthorized exception while downloading file from SharePoint

I have generated an access token using OAuth mechanism for SharePoint Online server. I am using this token to create ClientContext using CSOM. While I am able to access all the sites, libraries, and folders seamlessly, I get error
The remote server returned an error: (401) Unauthorized.
while downloading the file from SharePoint Online. Below is the code that I am using for file download:
var clientContext = TokenHelper.GetClientContextWithAccessToken("https://adventurer.sharepoint.com/Subsite1", accessToken);
var list = clientContext.Web.Lists.GetByTitle("SubSite 1 Library 1");
string vquery = #"<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='UniqueId' /><Value Type='Lookup'>" + "6718053d-a785-489c-877f-5a4b88dcb2a7" + "</Value></Eq></Where></Query></View>";
CamlQuery query = new CamlQuery();
query.ViewXml = vquery;
var listItems = list.GetItems(query);
clientContext.Load(listItems, items => items.Take(1).Include(item => item.File));
clientContext.ExecuteQuery();
var fileRef = listItems[0].File.ServerRelativeUrl;
var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(clientContext, fileRef);
I don't understand the root cause of this error, as I am passing client context with right access token. I want to know if OpenBinaryDirect has a limitation to work with access tokens? If not, what is wrong with above code? Is there any other alternative that can be used to download using access token?
After trying a lot of alternatives, I have come to conclusion that OpenBinaryDirect() cannot be used with OAuth tokens. I was able to download file from SharePoint Online using two other approaches. I am posting the answers here so that it might help someone:
Approach 1 (OpenBinaryStream):
var file = clientContext.Web.GetFileByServerRelativeUrl(fileRef);
clientContext.Load(file);
clientContext.ExecuteQuery();
ClientResult<Stream> streamResult = file.OpenBinaryStream();
clientContext.ExecuteQuery();
While this approach works perfectly, OpenBinaryStream is not available in Microsoft.SharePoint.Client.dll <= v 14.0.0.0.
Approach 2 (WebClient or Other Http Requests):
string downloadUrl = HostURL + "/_api/web/getfilebyserverrelativeurl('" + fileRef + "')/$value";
WebClient client = new WebClient();
client.Headers.Add("Authorization", "Bearer " + accessToken);
client.DownloadFile(downloadUrl, filePath);
Note the download URL that I have used for WebClient. Normal file URL will not work to download files from SharePoint Online.
I am using C# and here is how I am currently retrieving documents from SharePoint Online. I am showing the user a list of their documents in a gridview, so I populate a DataTable with the documents. I am unsure of a way using an Access Token, but if you are able to use a Service Account like I am, then hopefully this helps you.
Namespaces
using Microsoft.SharePoint.Client;
using SP = Microsoft.SharePoint.Client;
Object Attributes
SecureString securePassword = new SecureString();
private string username = "";
ClientContext context = new SP.ClientContext("https://<root>.sharepoint.com/<site collection (unless root)>/<site>");
Constructor (This is how I am authenticating)
public SharePoint()
{
securePassword = convertToSecureString(System.Web.Configuration.WebConfigurationManager.AppSettings["O365PW"]);
username = System.Web.Configuration.WebConfigurationManager.AppSettings["O365UN"];
context.Credentials = new SharePointOnlineCredentials(username, securePassword);
}
Method to get documents
public DataTable GetDocuments(int changeID)
{
DataTable dt = new DataTable("ChangeDocuments");
DataRow dr = dt.NewRow();
dt.Columns.Add("Title");
dt.Columns.Add("URL");
dt.Columns.Add("ChangeID");
dt.Columns.Add("Modified");
dt.Columns.Add("ID");
// The SharePoint web at the URL.
Web web = context.Web;
// We want to retrieve the web's properties.
context.Load(web);
// We must call ExecuteQuery before enumerate list.Fields.
context.ExecuteQuery();
// Assume the web has a list named "Announcements".
SP.List oList = context.Web.Lists.GetByTitle("Name of your document library");
// This creates a CamlQuery that has a RowLimit of 100, and also specifies Scope="RecursiveAll"
// so that it grabs all list items, regardless of the folder they are in.
CamlQuery query = CamlQuery.CreateAllItemsQuery(100);
query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='ChangeID'/>" +
"<Value Type='Number'>" + changeID + "</Value></Eq></Where></Query><RowLimit>100</RowLimit></View>";
SP.ListItemCollection items = oList.GetItems(query);
// Retrieve all items in the ListItemCollection from List.GetItems(Query).
context.Load(items);
context.ExecuteQuery();
foreach (Microsoft.SharePoint.Client.ListItem listItem in items)
{
// We have all the list item data. For example, Title.
dr = dt.NewRow();
dr["Title"] = listItem["FileLeafRef"];
if (String.IsNullOrEmpty(listItem["ServerRedirectedEmbedUrl"].ToString()))
{
dr["URL"] = "<root>/<site>/<document library>" + listItem["FileLeafRef"].ToString();
}
else
{
dr["URL"] = listItem["ServerRedirectedEmbedUrl"];
}
dr["ChangeID"] = listItem["ChangeID"];
dr["Modified"] = Convert.ToDateTime(listItem["Modified"]).ToString("MMM.dd,yyyy h:mm tt");
dr["ID"] = listItem["ID"];
dt.Rows.Add(dr);
}
return dt;
}
Method to convert password to secure string
private SecureString convertToSecureString(string strPassword)
{
var secureStr = new SecureString();
if (strPassword.Length > 0)
{
foreach (var c in strPassword.ToCharArray()) secureStr.AppendChar(c);
}
return secureStr;
}

How Do I get a page's URL using JSOM

I am using SharePoint 2013 workflow.
I am in the Initiation form when my my users clock the Start button to start the workflow.
I am using JSOM to start the workflow but since I am on the Initiation form, I don't know the URL of the page. I do know the list (pages) and the the list id (2).
Can someone help me retrieve the list id's url using JSOM?
Thanks
Tom
How to get Page Url in Initiation Form page:
var listId = getParameterByName('List');
var itemId = getParameterByName('ID');
var ctx = new SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getById(listId);
var listItem = list.getItemById(itemId);
ctx.load(listItem);
ctx.executeQueryAsync(
function () {
var itemUrl = listItem.get_item('FileRef');
console.log(itemUrl);
},
function (sender, args) {
console.log(args.get_message());
}
);
,where
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, " "));
}
is intended for retrieving parameter from query string
Source

List 'created by' in column

I know there is surveylist.get_description and surveylist.get_itemCount. I would like to know if there is a way to get created by fit in a column?
From syntax i guess you are using Javascriot CSOM.
Try this:
surveylist.get_author
Don't forget that you need to explicitly specify which properties you want to retrieve before you can get their values.
Unfortunately List Author property is not exposed via SharePoint CSOM.
How to retrieve List Author property via CSOM?
The idea is to retrieve SPList.SchemaXml property and extract Author property
function getListAuthor(listTitle,OnSuccess,OnError) {
var context = SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
context.load(web);
context.load(list,'SchemaXml');
context.executeQueryAsync(function(sender,args){
var schemaXml = $.parseXML(list.get_schemaXml());
var authorId = parseInt($(schemaXml).find('List').attr('Author')); //SchemaXml contains Author ID only
var listAuthor = web.getUserById(authorId);
context.load(listAuthor);
context.executeQueryAsync(OnSuccess(listAuthor),OnError);
},OnError);
}
//Usage
getListAuthor('Discussions List',
function(author){
console.log('List created by: ' + author.get_loginName())
},
function(sender,args){
console.log('Error occured:' + args.get_message());
}
);

CRM 2011 - setting a default value with JScript

We have CRM 2011 on premise. The Contact entity was customized to use a lookup to a custom entity Country instead of just a text field. When creating a new Contact we would like the country field to be set to Canada by default. I have the following function that does that:
function SetDefaultCountryCode(countryFieldId) {
var _canadaId = "{FC167B4D-1C3B-E111-8904-F2EA3FE25706}";
var countryControl = Xrm.Page.getAttribute(countryFieldId);
// only attempt the code if the control exists on the form
if (countryControl != null) {
var currentCountry = countryControl.getValue();
// if country is not specified, then set it to the default one (Canada)
if (currentCountry == null) {
var defaultCountry = new Object();
defaultCountry.entityType = "cga_country";
defaultCountry.id = _canadaId;
defaultCountry.name = "Canada";
var countryLookupValue = new Array();
countryLookupValue[0] = defaultCountry;
countryControl.setValue(countryLookupValue);
}
}
}
On the form OnLoad I invoke the function like that:
// set Country fields to Canada if not set
SetDefaultCountryCode('cga_address1country');
We have two servers - DEV and TEST. This JScript works fine in DEV. When I run it in TEST it does not work because the Canada in TEST has different id (GUID) - when I create it manually. I was hoping I could export the Country entity values from DEV and import them in TEST preserving their GUIDs. Unfortunately this did not work. I export the data to Excel file and it has the GUIDs of the countries. I also delete any existing Country records in TEST before importing. When I try to import it the import succeeds but does not create any records. If I add a new row in the excel file without specifing a Guid it will import it. It seems to me the import functionality was not meant to preserve the GUIDs of the records. But this also means my script will not work because it depends on the GUIDs.
I have two questions here:
Is it possible to export / import entity data preserving the GUIDs ?
If I cannot have the same GUIDs in DEV and TEST how I can make the JScript to work properly?
Thank you in advance for any help / feedback.
It's very bad practice to hard code your GUIDs and you discovered the problems of it.
As you stated above, we cannot have the same GUIDs but we have the same name. So, we have to query the name of the country using JScript and jQuery to retrieve the GUID.
In order to retireve information from client-side (or Entity Form):
We will use/consume REST Endpoint (testing in browser).
Upload jQuery lib.
Upload Json2 lib.
Use the AJAX function from the jQuery library.
Define your entity, columns and criteria.
Lets, look for querying REST Endpoint.
http://yourHostName/yourOrg/XRMServices/2011/OrganizationData.svc/new_CountrytSet?$select=new_Name,new_CountryId&$filter=new_Name eq 'Canada'
Take this URL, subsitute your actual values and paste it into your browser, you'll find that the response is returned in XML format. If there is any error, please ensure that the Entity name and its attribute are case senisitve.
After seeing your your results, we are going to call this URL using an AJAX call.
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: 'http://yourHostName/yourOrg/XRMServices/2011/OrganizationData.svc/new_CountrytSet?$select=new_Name,new_CountryId&$filter=new_Name eq 'Canada'',
beforeSend: function (XMLHttpRequest) {
//Specifying this header ensures that the results will be returned as JSON.
XMLHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
if (data.d && data.d.results) {
//var _canadaId = "{FC167B4D-1C3B-E111-8904-F2EA3FE25706}"; no longer be used
var _canadaId = data.d.results[0].ContactId;
// now we have the GUID of Canada, now I can continue my process
}
},
error: function (XmlHttpRequest) {
alert("Error : " + XmlHttpRequest.status + ": " + XmlHttpRequest.statusText + ": " + JSON.parse(XmlHttpRequest.responseText).error.message.value);
}
});
But before you copy the code to your form, you have to download the jQuery lib from here
Then upload it as a Web resource, add this web resource to the Form load libs.
Here is the complete code to be put in the form load event handler:
var context = GetGlobalContext();
// retireve the invoice record id (Opened Form)
var invoiceId = context.getQueryStringParameters().id;
var customerId;
//Retrieve the server url, which differs on-premise from on-line and
//shouldn't be hard-coded.
// this will return something like http://yourHostName/yourOrg
var serverUrl = context.getServerUrl();
//The XRM OData end-point
var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
var odataUri = serverUrl + ODATA_ENDPOINT;
function SetDefaultCountryCode(countryFieldId, odataUri) {
odataUri = odataUri + '/ContactSet?$select=ContactId,FullName&$filter=FullName eq \'Ahmed Shawki\'';
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: odataUri,
beforeSend: function (XMLHttpRequest) {
//Specifying this header ensures that the results will be returned as JSON.
XMLHttpRequest.setRequestHeader("Accept", "application/json");
},
success: function (data) {
if (data.d && data.d.results) {
//var _canadaId = "{FC167B4D-1C3B-E111-8904-F2EA3FE25706}"; no longer be used
var _canadaId = data.d.results[0].ContactId;
var countryControl = Xrm.Page.getAttribute(countryFieldId);
// only attempt the code if the control exists on the form
if (countryControl != null) {
var currentCountry = countryControl.getValue();
// if country is not specified, then set it to the default one (Canada)
if (currentCountry == null) {
var defaultCountry = new Object();
defaultCountry.entityType = "cga_country";
defaultCountry.id = _canadaId;
defaultCountry.name = "Canada";
var countryLookupValue = new Array();
countryLookupValue[0] = defaultCountry;
countryControl.setValue(countryLookupValue);
}
}
}
},
error: function (XmlHttpRequest) {
alert("Error : " + XmlHttpRequest.status + ": " + XmlHttpRequest.statusText + ": " + JSON.parse(XmlHttpRequest.responseText).error.message.value);
}
});
}
One more thing, don't forget to check "Pass execution context as first parameter" box on the form properties.
EDIT: Beside adding the jQuery library into the form load event handler, add the Json2 lib as a web resource.
For more information about the REST Endpoint.
It is indeed possible to export and import records along with their guids, just not natively. You'd have to build an app that would export the data for you, then create identical records through the CRM API in the target environment. You just have to clear out fields that aren't valid for create (createdon, statecode, etc.) and just specify the same Guid. CRM will then create the record with that Guid.
The old 4.0 Configuration Data Tool does this. I can't recall if it works against a 2011 org, but it could be a starting point.

Resources