Like the title says, I'd like to know how to update the name of an attachment from a list item. The document name.
I've figured out how to get the attachment from a list item (see code below), on the condition that it is a list item that was made by the current user and that the list item has one or more attachments, but can't seem to find a way to update the name of an attachment. What I'm trying to do is to "rename" the existing attachments in list items. So if I have a list item, and it has an attachment named "Attachment.docx", I want to rename that attachment to "John Doe Attachment.docx".
My client really wants this and it would really contribute to the list and would make it much more user-friendly (long story to explain why, but it has to do with my client wanting each and every attachment to have a unique name).
$().SPServices({
operation: "GetListItems",
async: false,
webURL: 'https://site-name-here.com/Subsite',
listName: 'My list name',
CAMLViewFields: "<ViewFields >" +
"<FieldRef Name='Author'></FieldRef>" +
"<FieldRef Name='Attachments'></FieldRef>" +
"</ViewFields>",
completefunc: function (xData, Status) {
$(xData.responseXML).SPFilterNode("z:row").each(function() {
// Name of the author of the list item:
var tempName = $(this).attr("ows_Author").substring($(this).attr("ows_Author").indexOf(";#") + 2);
// Compare the name stored in the session to the name from the author of the list item,
// and check if the list item has attachments:
if ((tempName == sessionStorage.getItem("sessionItemUserName", userName)) && ($(this).attr("ows_Attachments") != null)) {
var id = $(this).attr("ows_ID");
$().SPServices({
operation: "GetAttachmentCollection",
listName: "My list name",
ID: id,
CAMLViewFields: "<ViewFields >" +
"<FieldRef Name='Title'></FieldRef>" +
"</ViewFields>",
completefunc: function(xData, Status) {
$(xData.responseXML).find("Attachments > Attachment").each(function(i, el) {
var $node = $(this);
var filePath = $node.text();
var arrString = filePath.split("/");
var fileName = arrString[arrString.length - 1];
// New file name, now starting with the name of the author:
var newFileName = tempName + " " + fileName;
// Below is commented because I haven't figured out how to do this part yet:
/*$().SPServices({
operation: 'UpdateListItems',
webURL: 'https://site-name-here.com/Subsite',
listName: 'My list name/Attachments',
//...
completefunc: function (xData, Status) {
//...
}
});*/
}); // end each
} // end completefunc
}); // end SPServices
} // end if
}); // end each
} // end completefunc
}); //end spservices
I also thought about trying to copy the existing attachments and create a new attachment based on the existing ones, and then make sure that the new attachment has a different name before saving it. But I'd much rather just find a way to rename an existing attachment.
I've been searching on this for days and I really don't want to disappoint my client.
Any help would be greatly appreciated!
-EDIT: SOLVED-
Based on the code provided in wjervis' answer and the suggestion of user1065557 to put one more expand in the request (and thus getting the attachments information in the first request), I changed my code to the following:
function changeAttachmentName() {
var attachmentID = "",
attachmentAuthor = "",
statusAttachment = 0,
statusAuthor = 0,
authorMinified,
itemArray = [],
lastCreatedByCurrentUser = [],
rightNow = new Date();
var url = "/_api/Web/Lists/getByTitle('My List Name')/Items",
qs = "?$filter=Attachments eq 1&$select=ID,Author/Title,*&$expand=Author,AttachmentFiles",
siteUrl = "https://site-name-here.com/Subsite";
$.ajax( {
url : siteUrl + url + qs,
type : 'GET',
headers : {
'accept' : 'application/json;odata=nometadata',
'content-type' : 'application/json;odata=nometadata'
},
success : successHandler,
fail : failHandler
} );
function successHandler( data ) {
$().SPServices({
operation: "GetListItems",
async: false,
webURL: 'https://site-name-here.com/Subsite',
listName: 'My List Name',
CAMLViewFields: "<ViewFields >" +
"<FieldRef Name='Author'></FieldRef>" +
"<FieldRef Name='Attachments'></FieldRef>" +
"<FieldRef Name='Created'></FieldRef>" +
"</ViewFields>",
completefunc: function (xData, Status) {
$(xData.responseXML).SPFilterNode("z:row").each(function() {
if ($(this).attr("ows_Attachments") != 0) {
attachmentAuthor = $(this).attr("ows_Author").substring($(this).attr("ows_Author").indexOf(";#") + 2);
if (attachmentAuthor == sessionStorage.getItem("sessionItemUserName")) {
// Array of items with attachments that were made by current user:
itemArray.push([$(this).attr("ows_ID"), attachmentAuthor, $(this).attr("ows_Created")]);
}
}
});
}
});
for (var x = 0; x < itemArray.length; x++) { // If there are items with attachments that were made by current user
var backThen = itemArray[x][2]; // Save date of item in itemArray
if (Date.parse(rightNow)>Date.parse(backThen)){ // If current date is bigger than date of item in itemArray
if (lastCreatedByCurrentUser.length == 0) { // If lastCreatedByCurrentUser is still empty
lastCreatedByCurrentUser = itemArray[x]; // Assign last created item to lastCreatedByCurrentUser
}
else { // If lastCreatedByCurrentUser is not empty
if (Date.parse(backThen)>Date.parse(lastCreatedByCurrentUser[2])){ // Check if the date from current item is bigger than the date of item in lastCreatedByCurrentUser
lastCreatedByCurrentUser = itemArray[x]; // Assign new last created item to lastCreatedByCurrentUser
}
}
}
}
if (lastCreatedByCurrentUser.length != 0) { // If lastCreatedByCurrentUser is not empty, meaning if there is an item
for ( var i = 0; i < $(data).find("feed")[0].childNodes.length; i++ ) {
if ($(data).find("feed")[0].childNodes[i].tagName == "entry") { // (Entry can only be found in items with attachments)
if ($(data).find("feed")[0].childNodes[i].childNodes[0].tagName == "id") {
attachmentID = $(data).find("feed")[0].childNodes[i].childNodes[0].innerHTML;
attachmentItemID = attachmentID.substring(attachmentID.indexOf("Items(") + 6);
attachmentItemID = attachmentItemID.substring(0, attachmentItemID.indexOf(")")); // ID of the list item that holds this attachment
if (attachmentItemID == lastCreatedByCurrentUser[0]) {
authorMinified = lastCreatedByCurrentUser[1].latinise();
for ( var j = 0; j < $(data).find("feed")[0].childNodes[i].childNodes.length; j++ ) {
if ($(data).find("feed")[0].childNodes[i].childNodes[j].getAttribute("title") == "AttachmentFiles") {
var oldName = $(data).find("feed")[0].childNodes[i].childNodes[j].childNodes[0].childNodes[0].lastChild.lastChild.childNodes[0].childNodes[0].innerHTML,
newName = 'TWL_' + authorMinified + '_' + '(ID' + attachmentItemID + ')' + oldName.substring(oldName.lastIndexOf("."));
newName = newName.replace(/ /g, "_");
var oldUrl = $(data).find("feed")[0].childNodes[i].childNodes[j].childNodes[0].childNodes[0].lastChild.lastChild.childNodes[0].childNodes[1].innerHTML,
newUrl = oldUrl.replace( oldName, newName );
oDataUrl = siteUrl + "/_api/web/getfilebyserverrelativeurl('" + oldUrl + "')/moveto(newurl='" + newUrl + "', flags=1)";
}
}
$.ajax( {
url : oDataUrl,
type : 'POST',
headers : {
'accept' : 'application/json;odata=verbose',
'content-type' : 'application/json;odata=verbose',
'X-HTTP-Method' : 'PUT',
'X-RequestDigest' : $( '#__REQUESTDIGEST' ).val()
},
success : function( ) { console.log( 'Attachment renamed' ); },
fail : failHandler
} );
}
}
}
function failHandler( data, errCode, errMessage ) {
console.log( 'Error: ' + errMessage );
}
}
}
}
function failHandler( data, errCode, errMessage ) {
console.log( 'Error: ' + errMessage );
}
}
This code will run when a new item has been added to the list, and only if that new item was made by the current user and has an attachment. If there already are items in the list made by the same user that also have attachments, then it will check the dates of all items and only change the name of the attachment from the item of which the date is closest to the current date (meaning that the attachment of the latest added item by the current user will be renamed).
I also used a function to latinise the name of a user, in case it contains special characters. Attachments now get renamed to the following format: "TWL_Firstname_Lastname_(ID##)".
Thank you so much guys for your help, I couldn't have done it without you. :)
moveTo()
function ofSP.File
. You just need to specify the new filename in the destination url.