SharePoint 2010 - Client Object Model - Add attachment to ListItem - c#

I have a SharePoint List to which I'm adding new ListItems using the Client Object Model.
Adding ListItems is not a problem and works great.
Now I want to add attachments.
I'm using the SaveBinaryDirect in the following manner:
File.SaveBinaryDirect(clientCtx, url.AbsolutePath + "/Attachments/31/" + fileName, inputStream, true);
It works without any problem as long as the item that I'm trying to add the attachment to, already has an attachment that was added through the SharePoint site and not using the Client Object Model.
When I try to add an attachment to a item that doesnt have any attachments yet, I get the following errors (both happen but not with the same files - but those two messages appear consistently):
The remote server returned an error: (409) Conflict
The remote server returned an error: (404) Not Found
I figured that maybe I need to create the attachment folder first for this item.
When I try the following code:
clientCtx.Load(ticketList.RootFolder.Folders);
clientCtx.ExecuteQuery();
clientCtx.Load(ticketList.RootFolder.Folders[1]); // 1 -> Attachment folder
clientCtx.Load(ticketList.RootFolder.Folders[1].Folders);
clientCtx.ExecuteQuery();
Folder folder = ticketList.RootFolder.Folders[1].Folders.Add("33");
clientCtx.ExecuteQuery();
I receive an error message saying:
Cannot create folder "Lists/Ticket System/Attachment/33"
I have full administrator rights for the SharePoint site/list.
Any ideas what I could be doing wrong?
Thanks, Thorben

I struggled for a long time with this problem too, so I thought I'd post a complete code sample showing how to successfully create a list item and add an attachment.
I am using the Client Object API to create the list item, and the SOAP web service to add the attachment. This is because, as noted in other places on the web, the Client Object API can only be used to add attachments to an item where the item's upload directory already exists (eg. if the item already has an attachment). Else it fails with a 409 error or something. The SOAP web service copes with this OK though.
Note that another thing I had to overcome was that even though I added the SOAP reference using the following URL:
https://my.sharepoint.installation/personal/test/_vti_bin/lists.asmx
The URL that VS actually added to the app.config was:
https://my.sharepoint.installation/_vti_bin/lists.asmx
I had to manually change the app.config back to the correct URL, else I would get the error:
List does not exist.
The page you selected contains a list that does not exist. It may have been deleted by another user.
0x82000006
Here is the code:
void CreateWithAttachment()
{
const string listName = "MyListName";
// set up our credentials
var credentials = new NetworkCredential("username", "password", "domain");
// create a soap client
var soapClient = new ListsService.Lists();
soapClient.Credentials = credentials;
// create a client context
var clientContext = new Microsoft.SharePoint.Client.ClientContext("https://my.sharepoint.installation/personal/test");
clientContext.Credentials = credentials;
// create a list item
var list = clientContext.Web.Lists.GetByTitle(listName);
var itemCreateInfo = new ListItemCreationInformation();
var newItem = list.AddItem(itemCreateInfo);
// set its properties
newItem["Title"] = "Created from Client API";
newItem["Status"] = "New";
newItem["_Comments"] = "here are some comments!!";
// commit it
newItem.Update();
clientContext.ExecuteQuery();
// load back the created item so its ID field is available for use below
clientContext.Load(newItem);
clientContext.ExecuteQuery();
// use the soap client to add the attachment
const string path = #"c:\temp\test.txt";
soapClient.AddAttachment(listName, newItem["ID"].ToString(), Path.GetFileName(path),
System.IO.File.ReadAllBytes(path));
}
Hope this helps someone.

I have discussed this question with Microsoft. Looks like that only one way to create attachments remotely is List.asmx web service. I have tried to create this subfolder also and with no success.

With Sharepoint 2010 there was no way to upload a first attachment to a list item using the COM. The recommendation was to use the Lists web service inmstead.
With Sharepoint 2013 it works.
AttachmentCreationInformation newAtt = new AttachmentCreationInformation();
newAtt.FileName = "myAttachment.txt";
// create a file stream
string fileContent = "This file is was ubloaded by client object meodel ";
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] buffer = enc.GetBytes(fileContent);
newAtt.ContentStream = new MemoryStream(buffer);
// att new item or get existing one
ListItem itm = list.GetItemById(itemId);
ctx.Load(itm);
// do not execute query, otherwise a "version conflict" exception is rised, but the file is uploaded
// add file to attachment collection
newAtt.ContentStream = new MemoryStream(buffer);
itm.AttachmentFiles.Add(newAtt);
AttachmentCollection attachments = itm.AttachmentFiles;
ctx.Load(attachments);
ctx.ExecuteQuery();
// see all attachments for list item
// this snippet works if the list item has no attachments
This method is used in http://www.mailtosharepoint.net/

It reflects rather poorly on the Microsoft SharePoint team for not coming forward with an acknowledgement of the issue and a usable suggestion on how to resolve it. Here is how I dealt with it:
I am using the new SharePoint 2010 managed client that ships with the product. Hence, I already have a SharePoint ClientContext with credentials. The following function adds an attachment to a list item:
private void SharePoint2010AddAttachment(ClientContext ctx,
string listName, string itemId,
string fileName, byte[] fileContent)
{
var listsSvc = new sp2010.Lists();
listsSvc.Credentials = _sharePointCtx.Credentials;
listsSvc.Url = _sharePointCtx.Web.Context.Url + "_vti_bin/Lists.asmx";
listsSvc.AddAttachment(listName, itemId, fileName, fileContent);
}
The only prerequisite for the code above is to add to the project (I used Visual Studio 2008) a _web_reference_ I called sp2010 which is created from the URL of: http:///_vti_bin/Lists.asmx
Bon Chance...

HTML:
<asp:FileUpload ID="FileUpload1" runat="server" AllowMultiple="true" />
Event in code behind :
protected void UploadMultipleFiles(object sender, EventArgs e)
{
Common.UploadDocuments(Common.getContext(new Uri(Request.QueryString["SPHostUrl"]),
Request.LogonUserIdentity), FileUpload1.PostedFiles, new CustomerRequirement(), 5);
}
public static List<string> UploadDocuments<T>(ClientContext ctx,IList<HttpPostedFile> selectedFiles, T reqObj, int itemID)
{
List<Attachment> existingFiles = null;
List<string> processedFiles = null;
List<string> unProcessedFiles = null;
ListItem item = null;
FileStream sr = null;
AttachmentCollection attachments = null;
byte[] contents = null;
try
{
existingFiles = new List<Attachment>();
processedFiles = new List<string>();
unProcessedFiles = new List<string>();
//Get the existing item
item = ctx.Web.Lists.GetByTitle(typeof(T).Name).GetItemById(itemID);
//get the Existing attached attachments
attachments = item.AttachmentFiles;
ctx.Load(attachments);
ctx.ExecuteQuery();
//adding into the new List
foreach (Attachment att in attachments)
existingFiles.Add(att);
//For each Files which user has selected
foreach (HttpPostedFile postedFile in selectedFiles)
{
string fileName = Path.GetFileName(postedFile.FileName);
//If selected file not exist in existing item attachment
if (!existingFiles.Any(x => x.FileName == fileName))
{
//Added to Process List
processedFiles.Add(postedFile.FileName);
}
else
unProcessedFiles.Add(fileName);
}
//Foreach process item add it as an attachment
foreach (string path in processedFiles)
{
sr = new FileStream(path, FileMode.Open);
contents = new byte[sr.Length];
sr.Read(contents, 0, (int)sr.Length);
var attInfo = new AttachmentCreationInformation();
attInfo.FileName = Path.GetFileName(path);
attInfo.ContentStream = sr;
item.AttachmentFiles.Add(attInfo);
item.Update();
}
ctx.ExecuteQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
existingFiles = null;
processedFiles = null;
item = null;
sr = null;
attachments = null;
contents = null;
ctx = null;
}
return unProcessedFiles;
}

I've used and tried this one on my CSOM (SharePoint Client Object Model) application and it works for me
using (ClientContext context = new ClientContext("http://spsite2010"))
{
context.Credentials = new NetworkCredential("admin", "password");
Web oWeb = context.Web;
List list = context.Web.Lists.GetByTitle("Tasks");
CamlQuery query = new CamlQuery();
query.ViewXml = "<View><Where><Eq><FieldRef Name = \"Title\"/><Value Type=\"String\">New Task Created</Value></Eq></Where></View>";
ListItemCollection listItems = list.GetItems(query);
context.Load(listItems);
context.ExecuteQuery();
FileStream oFileStream = new FileStream(#"C:\\sample.txt", FileMode.Open);
string attachmentpath = "/Lists/Tasks/Attachments/" + listItems[listItems.Count - 1].Id + "/sample.txt";
Microsoft.SharePoint.Client.File.SaveBinaryDirect(context, attachmentpath, oFileStream, true);
}
Note: Only works if item folder has been created already

Related

Saving multiple responses from foreach c#

Creating C#/.net application that gets services names from AWS using the cluster name. With the AWS .net SDK
Able to retrieve cluster names and save them in a list of strings:
AmazonECSClient client = new AmazonECSClient();
ListClustersRequest listClusterREquest = new ListClustersRequest();
var responseClusterList = await client.ListClustersAsync(listClusterREquest);
List<string> clusterArns = responseClusterList.ClusterArns;
Now trying to retrieve the list of services using the cluster names. I am having issues saving each response back to a list named serviceArns. It only saves the last response to the list called serviceArns. There should be about 20 responses saved to the list.
ListServicesRequest listRequest = new ListServicesRequest();
List<string> serviceArns = null;
ListServicesResponse responsethree = new ListServicesResponse();
foreach (var item in clusterArns)
{
listRequest.Cluster = item;
ListServicesResponse listResponse = await client.ListServicesAsync(listRequest);
serviceArns = listResponse.ServiceArns;
};
You should intialise the list at the start and add elements as needed be. Currently with each iteration, the list gets set, where it should be adding it instead.
ListServicesRequest listRequest = new ListServicesRequest();
List<string> serviceArns = new();
ListServicesResponse responsethree = new ListServicesResponse();
foreach (var item in clusterArns)
{
listRequest.Cluster = item;
ListServicesResponse listResponse = await client.ListServicesAsync(listRequest);
serviceArns.AddRange(listResponse.ServiceArns);
};

Getting files/folders from sharepoint via the api in c#

I'm trying to link a c# application to a sharepoint directory, so I can create folders, download and upload files. However I am strugling with connecting to the correct folder.
I can retrieve the content from allitems.aspx, but I am not sure how to actually get the content from folder.
I have tried using the ClientContext - something like this:
ClientContext cxt = new ClientContext("https://xx.sharepoint.com/sites/");
cxt.Credentials = GetCredentials();
List list = cxt.Web.Lists.GetByTitle("Kontrakter");
var test = list.Views;
var test1 = cxt.Web.Lists;
cxt.Load(test1);
cxt.Load(list);
cxt.Load(test);
var a = 4;
var fullUri = new Uri("https://xx.sharepoint.com/sites/yy/Kontrakter/AllItems.aspx");
//var folder = cxt.Web.GetFolderByServerRelativeUrl(fullUri.AbsolutePath);
using (var rootCtx = new ClientContext(fullUri.GetLeftPart(UriPartial.Authority)))
{
rootCtx.Credentials = GetCredentials();
Uri webUri = Web.WebUrlFromPageUrlDirect(rootCtx, fullUri);
using (var ctx1 = new ClientContext(webUri))
{
ctx1.Credentials = GetCredentials();
var list1 = ctx1.Web.GetList(fullUri.AbsolutePath);
ctx1.Load(list1.RootFolder.Files);
ctx1.ExecuteQuery();
Console.WriteLine(list.RootFolder.Files.Count);
}
}
or via normal api calls like this:
https://xx.sharepoint.com/_api/Web/GetFolderByServerRelativeUrl('Kontrakter/Forms')/Files
The only way I can find some data is if I look into 'Shared documents/Forms'
I'm having problems understanding the directory structure and how I can actually find the content of files/folders.
Thanks in advance :)
Turned out I was missing a /sites in one of my uris.

How to Get TFS WorkItem URL depending on WorkItem ID

i could successfully add bugs into TFS programmatically,
var tfsURI = new Uri("http://test:8080/tfs");
var networkCredential1 = new NetworkCredential("test", "test!");
ICredentials credential = (ICredentials)networkCredential1;
Microsoft.VisualStudio.Services.Common.WindowsCredential winCred = new Microsoft.VisualStudio.Services.Common.WindowsCredential(credential);
VssCredentials vssCredentials = new VssCredentials(winCred);
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(tfsURI, vssCredentials))
{
collection.EnsureAuthenticated();
WorkItemStore workItemStore = collection.GetService<WorkItemStore>();
Project teamProject = workItemStore.Projects["Test"];
WorkItemType workItemType = teamProject.WorkItemTypes["Bug"];
WorkItem Defect = new WorkItem(workItemType);
FileInfo fi = new FileInfo(#"C:\\Document.docx");
Attachment tfsAttachment = new Attachment(fi.FullName);
Defect.Attachments.Add(tfsAttachment);
Defect.Title = "Testing from VS to TFS Bug";
Defect.Description = "Testing from VS to entered Bug in to TFS.";
Defect.Fields["Assigned To"].Value = "Test";
Defect.Save();
}
I have Newly Created WorkItem ID. Defect.ID provides me workitemID.
How can i get the URL of newly created WOrkItem by passing ID.
Let Me know if any API available? i need HTML Link of Newly created WorkItem so that anyone when click on URL, created WorkItem will be Open.
You could use Rest API to directly return a single work item
GET https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/{id}?api-version=5.1
However this will only get a url to the json data for the bug created, not the URL of the HTML page of the bug. Such as below output that I got
https://tfsurl:8080/_apis/wit/workItems/workitemID
When I load the above link in browser, I got JSON data, instead of the HTML page for the work item I created.
Guess you want to fetch the url of HTML in web portal.
https://tfsurl:8080/tfs/DefaultCollection/PatrickProject/_workitems/edit/172/
In my case it was this and here DefaultCollection is the collection name and the PatrickProject is the project name. I used this url and got rid of the id '172' in this case and use the ID of newly created work item. This would return the URL to go to the work item HTML page.
So it's a fixed format, if you have Newly Created WorkItem ID and collection name , project name, you just need to follow above format and change the last value of work item ID. That's it , ignore of which work item type you created.
Hope this Helps!
I found solution after lots of research, may help to someone in future:
var tfsURI = new Uri("http://test:8080/tfs");
var networkCredential1 = new NetworkCredential("test", "test!");
ICredentials credential = (ICredentials)networkCredential1;
Microsoft.VisualStudio.Services.Common.WindowsCredential winCred = new Microsoft.VisualStudio.Services.Common.WindowsCredential(credential);
VssCredentials vssCredentials = new VssCredentials(winCred);
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(tfsURI, vssCredentials))
{
collection.EnsureAuthenticated();
TswaClientHyperlinkService hyperlinkService =
collection.GetService<TswaClientHyperlinkService>();
String TFSurl = hyperlinkService.GetWorkItemEditorUrl(17648).ToString(); //17648 WorkItem ID
}

How to get all the documents in a folder in Google Docs

I can get all the documents in Google Docs using
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
}
But how can I get the documents in a particular folder? I wan to discover the list of folders and then populate the folders in a tree view. On selection of a folder, I shall like to get the documents in that folder.
To get the folder, I am using
public DocumentsFeed GetFolders()
{
FolderQuery query = new FolderQuery("root"); //http://docs.google.com/feeds/documents/private/full
DocumentsFeed feed = service.Query(query);
return feed;
}
For the service, I am using private DocumentsService service;
Can somebody help?
Another guy using the API has described how he does it:
var docService = new DocumentsService("company-app-version");
docService.setUserCredentials("username", "password");
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Documents;
// snipped method declaration etc
var docService = new DocumentsService("company-app-version");
docService.setUserCredentials("username", "password");
var folderList = docService.Query(new FolderQuery());
var fLinks = folderList.Entries.Select(e =>
new
{
// note how to get the document Id of the folder
Id = DocumentsListQuery.DocumentId(e.Id.AbsoluteUri),
Name = e.Title.Text
});
foreach (var folder in fLinks)
{
Console.WriteLine("Folder {0}", folder.Name);
var fileList = docService.Query(
new SpreadsheetQuery()
{
// setting the base address to the folder's URI restricts your results
BaseAddress = DocumentsListQuery.folderBaseUri + folder.Id
});
foreach (var file in fileList.Entries)
{
Console.WriteLine(" - {0}", file.Title.Text);
}
}
Source:
http://jtnlex.com/blog/2010/06/09/google-docs-api-get-all-spreadsheetsdocs-in-a-folder/
Here's how :
instead of typing the name of the folder , use the resourceID of the folder query = new FolderQuery(FolderEntry.ResourceId);
But first you need to get ALL documents in the root and enable showing folders : query.ShowFolders = true; , that's how you get the resourceId's of the docs in the root and
folders!
Hope this helps !

SharePoint : How can I programmatically add items to a custom list instance

I am really looking for either a small code snippet.
I have a C# console app that I will use to somehow add list items to my custom list. I have created a custom content type too. So not sure if I need to create an C# class from this content type too. Perhaps not.
I think these both blog post should help you solving your problem.
http://blog.the-dargans.co.uk/2007/04/programmatically-adding-items-to.html
http://asadewa.wordpress.com/2007/11/19/adding-a-custom-content-type-specific-item-on-a-sharepoint-list/
Short walk through:
Get a instance of the list you want to add the item to.
Add a new item to the list:
SPListItem newItem = list.AddItem();
To bind you new item to a content type you have to set the content type id for the new item:
newItem["ContentTypeId"] = <Id of the content type>;
Set the fields specified within your content type.
Commit your changes:
newItem.Update();
To put it simple you will need to follow the step.
You need to reference the Microsoft.SharePoint.dll to the application.
Assuming the List Name is Test and it has only one Field "Title" here is the code.
using (SPSite oSite=new SPSite("http://mysharepoint"))
{
using (SPWeb oWeb=oSite.RootWeb)
{
SPList oList = oWeb.Lists["Test"];
SPListItem oSPListItem = oList.Items.Add();
oSPListItem["Title"] = "Hello SharePoint";
oSPListItem.Update();
}
}
Note that you need to run this application in the Same server where the SharePoint is installed.
You dont need to create a Custom Class for Custom Content Type
You can create an item in your custom SharePoint list doing something like this:
using (SPSite site = new SPSite("http://sharepoint"))
{
using (SPWeb web = site.RootWeb)
{
SPList list = web.Lists["My List"];
SPListItem listItem = list.AddItem();
listItem["Title"] = "The Title";
listItem["CustomColumn"] = "I am custom";
listItem.Update();
}
}
Using list.AddItem() should save the lists items being enumerated.
This is how it was on the Microsoft site, with me just tweaking the SPSite and SPWeb since these might vary from environment to environment and it helps not to have to hard-code these:
using (SPSite oSiteCollection = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb oWeb = oSiteCollection.OpenWeb(SPContext.Current.Web))
{
SPList oList = oWeb.Lists["Announcements"];
// You may also use
// SPList oList = oWeb.GetList("/Lists/Announcements");
// to avoid querying all of the sites' lists
SPListItem oListItem = oList.Items.Add();
oListItem["Title"] = "My Item";
oListItem["Created"] = new DateTime(2004, 1, 23);
oListItem["Modified"] = new DateTime(2005, 10, 1);
oListItem["Author"] = 3;
oListItem["Editor"] = 3;
oListItem.Update();
}
}
Source:
SPListItemClass (Microsoft.SharePoint). (2012). Retrieved February 22, 2012, from http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.splistitem.aspx.
I had a similar problem and was able to solve it by following the below approach (similar to other answers but needed credentials too),
1- add Microsoft.SharePointOnline.CSOM by tools->NuGet Package Manager->Manage NuGet Packages for solution->Browse-> select and install
2- Add "using Microsoft.SharePoint.Client; "
then the below code
string siteUrl = "https://yourcompany.sharepoint.com/sites/Yoursite";
SecureString passWord = new SecureString();
var password = "Your password here";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
ClientContext clientContext = new ClientContext(siteUrl);
clientContext.Credentials = new SharePointOnlineCredentials("Username#domain.nz", securePassword);/*passWord*/
List oList = clientContext.Web.Lists.GetByTitle("The name of your list here");
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem oListItem = oList.AddItem(itemCreateInfo);
oListItem["PK"] = "1";
oListItem["Precinct"] = "Mangere";
oListItem["Title"] = "Innovation";
oListItem["Project_x0020_Name"] = "test from C#";
oListItem["Project_x0020_ID"] = "ID_123_from C#";
oListItem["Project_x0020_start_x0020_date"] = "2020-05-01 01:01:01";
oListItem.Update();
clientContext.ExecuteQuery();
Remember that your fields may be different with what you see, for example in my list I see "Project Name", while the actual value is "Project_x0020_ID". How to get these values (i.e. internal filed values)?
A few approaches:
1- Use MS flow and see them
2- https://mstechtalk.com/check-column-internal-name-sharepoint-list/ or https://sharepoint.stackexchange.com/questions/787/finding-the-internal-name-and-display-name-for-a-list-column
3- Use a C# reader and read your sharepoint list
The rest of operations (update/delete):
https://learn.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee539976(v%3Doffice.14)

Categories