Programatically add Append-Only Comments to Sharepoint List Item - c#

I'm hoping to be able to append to the V3Comments (Append-Only Comments) column on a Sharepoint list item programmatically using Microsoft.SharePoint.Client.
I'm using a sequence similar to:
using (ClientContext context = new ClientContext("path_to_site"))
{
List list = context.Web.Lists.GetByTitle("list_name");
CamlQuery query = new CamlQuery();
ListItemCollection list_items = list.GetItems(query);
context.Load(list_items);
context.ExecuteQuery();
ListItem list_item = list_items[0];
list_item["V3Comments"] = "New comment.";
list_item.Update();
context.ExecuteQuery();
}
When I attempt to do this, the following InnerException is thrown:
Field or property "AttachmentFiles" does not exist.
Any pointers on what I need to get this working would be useful.

The list I am using had attachments enabled (despite not actually being used). After disabling the attachments, and leaving it for some time, it works as intended.
I wish I could explain the reason for this, but it would be pure speculation.
I've no idea how this would be done for a list with attachments.

Related

Retrieving all items from a SharePoint list and putting each field into an object

I am trying to retrieve all of the items in a list from a SharePoint site. The fields are titled "Review Level Title", "Reviewer IDs", and "Review Level Priority". What I'm trying to do is to get the information from all three fields seperately, put them into the object I created, and then return the list with all of the objects I have created for each SharePoint item.
I have researched a lot on how to access this information from the SharePoint site, but I can not get it to work. Here is what I have created so far:
public List<OperationsReviewLevel> Get()
{
var operationsReviewLevels = new List<OperationsReviewLevel>();
ClientContext context = new ClientContext(ConfigurationManager.AppSettings["SharePointEngineeringChangeRequest"]);
var SPList = context.Web.Lists.GetByTitle("Review Levels");
CamlQuery query = new CamlQuery();
ListItemCollection entries = SPList.GetItems(query);
context.Load(entries);
context.ExecuteQuery();
foreach(ListItem currentEntry in entries)
{
operationsReviewLevels.Add(new OperationsReviewLevel(currentEntry["Review Level Title"].ToString(), currentEntry["Reviewer IDs"].ToString(), (int)currentEntry["Review Level Priority"]));
}
return operationsReviewLevels;
}
Whenever I try this code, I receive an error saying:
Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException: The property or field has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.
I can not find any solutions to this error (in my scenario) online, and was wondering if anyone could see what I am doing wrong in this scenario.
Thanks everyone!
After reading the comment from Alessandra Amosso under my question, I ended up debugging entries. It took a lot of digging in the debugger, but I was able to find what the field names were being retrieved as. Debugging your ListItemCollection, if you go into Data, then any entry there, and then into FieldValues, you can see what each field value should be retrieved as.
In my case, all spaces were replaces with _x0020_ and the word priority was cut to just priorit due to length of the field name.
With this, I was able to change my foreach loop to:
foreach (ListItem currentEntry in entries)
{
operationsReviewLevels.Add(new OperationsReviewLevel(currentEntry["Review_x0020_Level_x0020_Title"].ToString(), currentEntry["Reviewer_x0020_IDs"].ToString(), Convert.ToInt32(currentEntry["Review_x0020_Level_x0020_Priorit"].ToString())));
}
And it now works properly.
Hope this helps anyone in the future!
Guess you're using SharePoint online, SharePoint online will remove the field special characters as staticname when creating fields, for example: Review Level Title will be ReviewLevelTitle.
Here is my test code.
foreach (ListItem currentEntry in entries)
{
Console.WriteLine(currentEntry["ReviewLevelTitle"].ToString()+'-'+ currentEntry["ReviewerIDs"].ToString()+'-'+ currentEntry["ReviewLevelPriority"]);
//operationsReviewLevels.Add(new OperationsReviewLevel(currentEntry["Review Level Title"].ToString(), currentEntry["Reviewer IDs"].ToString(), (int)currentEntry["Review Level Priority"]));
}
If you're not using SharePoint online,make sure the fields match also.

How to find SharePoint documents by custom column value using Microsoft Graph API

In SharePoint Online site via my Office 365 account, I've added a column - "CustomerId" to my documents. I want to find all documents with CustomerId of 102 in C# (not in JavaScript).
So far, I'm able to get all files under a folder
var files = graphClient.Sites[siteId].Drive.Items[driveItemId]
.Children.Request().GetAsync().Result;
Or see the same result in Graph Explorer
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{driveItemId}/children
but I haven't figured out the correct syntax to get all documents (driveIems) using the custom column filter condition in C# or in Graph Explorer. Examples of things I've tried:
In C#
var files = graphClient.Sites[siteId].Drive.Items[driveItemId]
.Search("fields/CustomerId eq 102").Request().GetAsync().Result;
In Graph Explorer
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{driveItemId}/search(q='CustomerId eq 102')
Hope someone can help me out on this.
Update:
Previously I got the driveItemId from
var customerFolder = graphClient.Sites[siteId].Drive.Root
.ItemWithPath("CustomerGroup/IndustryGroup").Request().GetAsync().Result;
string driveItemId = customerFolder.Id;
I see I can get a ListItem
var customerFolder = graphClient.Sites[siteId].Drive.Root
.ItemWithPath("CustomerGroup/IndustryGroup").Request()
.Expand(d => d.ListItem).GetAsync().Result;
but I only found a list ID of "4" from customerFolder.ListItem.Id
How shall I get a list ID so that I can use it in graphClient.Sites[siteId].Lists[listId]?
I would suggest to utilize the following query:
https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items?filter=fields/CustomerId eq 123&expand=driveItem
Explanation:
filter items in a list via filter query option
return associated drive items for a list item via expand query option
Here is an example for msgraph-sdk-dotnet:
var request = await graphClient.Sites[siteId].Lists[listId].Items.Request().Filter("fields/CustomerId eq 123").Expand(item => item.DriveItem).GetAsync();
foreach (var item in request)
{
Console.WriteLine(item.DriveItem.WebUrl);
}
Update
The underlying document library list (along with its properties) for a drive could be retrieved like this:
var list = await graphClient.Sites[siteId].Drive.List.Request().GetAsync();
Console.WriteLine(list.Id); //gives SharePoint List Id
Note: https://graph.microsoft.com/beta/sites/{site-id}/drive
endpoint returns the default drive (document library) for this site
Reference
Working with SharePoint sites in Microsoft Graph

C# Outlook AddIn - Determine EWS Item ID for the selected Email

Is it possible to determine the Exchange Server ItemID for a MailItem (the selected Item in the active explorer)? The solution I am working on has an Outlook AddIn component and another component that accesses mail items through EWS.
I have code similar to the below in my Outlook addin:
Outlook.Explorer ActiveExplorer = Globals.ThisAddIn.Application.ActiveExplorer();
object selectedItem = ActiveExplorer.Selection[1];
Outlook.MailItem selectedEmail = selectedItem as Outlook.MailItem;
In this way I can access certain properties of the email but it is important to the workings of the overall solution that the property values are exactly the same as those returned by EWS. For example, if the property returned a time, it would be important that the time matched down to the millisecond.
If I had the ItemID I could bind to and work with the Item (from within the addin) using something like the below.
Item myItem = Item.Bind(MyExchangeService, MyItemID);
On a whim I have tried binding to MailItem.EntryID but I got a malformed ID error (which didn't surprise me). I have been trying to determine if the Exchange ID was available through MailItem.PropertyAccessor.GetProperty but I am not really familiar with accessing properties in this way and haven't had any luck so far.
Thoughts?
I came across the following Stack Overflow post which didn't exactly answer my question but changed my focus to converting the EntryID into the EWS ID rather than finding the EWS ID.
Exchange ItemID differs from GlobalAppointmentID for Outlook AddIn
With this new angle I was able to find the following site which directly addressed my issue.
https://bernhardelbl.wordpress.com/2013/04/15/converting-entryid-to-ewsid-using-exchange-web-services-ews/
I have posted the code here in full in case the link gets broken.
string ConvertHexEntryIdToEwsId(ExchangeService esb, string sID, string strSMTPAdd)
{
AlternateId objAltID = new AlternateId();
objAltID.Format = IdFormat.HexEntryId;
objAltID.Mailbox = strSMTPAdd;
objAltID.UniqueId = sID;
AlternateIdBase objAltIDBase = esb.ConvertId(objAltID, IdFormat.EwsId);
AlternateId objAltIDResp = (AlternateId)objAltIDBase;
return objAltIDResp.UniqueId;
}

Sharepoints 2013: Getting ItemCollection via List.GetItems

I have the following code, which successfully gets the list, but the spListItems variable never seems to never yield any results, even though the spList reports having listitems.
Any Ideas???
var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
ListItemCollection spListItems = null;
List spList = null;
using (var clientContext = spContext.CreateUserClientContextForSPHost())
{
if (clientContext != null)
{
CamlQuery caml = new CamlQuery();
// initilize our collections and vars
spList = clientContext.Web.Lists.GetByTitle("EclipseAppPages");
clientContext.Load<List>(spList);
clientContext.ExecuteQuery();
// do it again
spListItems = spList.GetItems(caml);
clientContext.Load<ListItemCollection>(spListItems);
clientContext.ExecuteQuery();
// loop items and populate model
foreach (ListItem l in spListItems)
{
model.Add(l);
}
}
}
Edit 18-09: A little bit more info into this, this code is hosted inside a 'autohosted' mvc app for sharepoint. I am accessing the HostWeb from my AppWeb, my first conclusion would be permissions, however the fact that im getting the list back, and interestingly when i debug the variable the spListItems has an item count of > 0 but for some reason the context ISNT lazy loading the values.
Turns out it was app permissions, I found the solution to this, trying to find a solution to a query string filter issue.
If you get the same problem, go into your AppParts AppManifest.xml and add the following node:
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web"
Right="FullControl" />
</AppPermissionRequests>
Alternatively you can do this via the visual studio wizard, double click your AppManifest file, goto permisions tab, Scope is 'Web', Permission 'Choose relavent permission'.
For more info on app permissions goto http://msdn.microsoft.com/en-us/library/fp179892(office.15).aspx
The reason that the result of your query is empty is that there CamlQuery object does not have ViewXml or Query property value.
Read the following article on how to query SharePoint Lists. http://msdn.microsoft.com/en-us/library/ff798388.aspx
Also, Use RowLimit & ViewFields properties to limit the results, for performance considerations.
alternatively you can use Linq to SharePoint if you are comfortable with that.

Use Exchange Recipient Update Service via c# without PowerShell

I'm searching to get the current members of a dynamic distribution group by exchange servers. Dynamic distribution groups are based on a specified filter. The "Recipient Update Service" (RUS) find each contact by runtime, based on this filter.
I've found a lot of information to solve the problem by using a wrapper class of exchange powershell in interaction of classic commandline arguments. But this is not my intended way.
I thought there should be a special command of "Exchange Web Services" (EWS) to get the dynamic members by runtime or by interop. I was unable to find some information about this.
Does anybody have an idea or some information to solve this problem via c#?
DirectoryServices seems to do the trick for me. Create a DirectoryEntry pointing to the dynamic distribution list (schema class name = "msExchDynamicDistributionList"), and then use the "msExchDynamicDLBaseDN" and "msExchDynamicDLFilter" properties to search for the members:
using (var group = new DirectoryEntry("LDAP://CN=MyGroup,OU=MyOU,DC=company,DC=com"))
{
string baseDN = (string)group.Properties["msExchDynamicDLBaseDN"].Value;
string filter = (string)group.Properties["msExchDynamicDLFilter"].Value;
using (var searchRoot = new DirectoryEntry("LDAP://" + baseDN))
using (var searcher = new DirectorySearcher(searchRoot, filter, propertiesToLoad))
using (var results = searcher.FindAll())
{
foreach (SearchResult result in results)
{
// Use the result
}
}
}
Remember that the members of the group could be regular groups or other dynamic distribution groups as well as users, contacts and public folders.

Categories

Resources