Sharepoint Online Workflow restart error (Console Application) - c#

I frequently get requests to restart a workflow for some of my list items. I have written an application which takes library name and list item name as an input. when i try to start a workflow (StartWorkflow method) i get this error Exception from HRESULT: 0x8102009B and no inner exception. Below is my code.
Sometimes the workflow may have already been started. In that case i need to cancel the workflow and then restart.
I did lot of search on this issue. Most of the resolution is for SP 2010. We are using SP Online
clientContext.Credentials = new SharePointOnlineCredentials(Userid, Password);
var web = clientContext.Web;
List list = web.Lists.GetByTitle("listname");
clientContext.Load(list);
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = "<View><Query><OrderBy><FieldRef Name='FileLeafRef' Descending='True'/></OrderBy><Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>" + ListItemName + ".xml</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>";
ListItemCollection items = list.GetItems(camlQuery);
clientContext.Load(items);
clientContext.ExecuteQuery();
var id = items.Where(a => a.FieldValues["FileLeafRef"].ToString() == ListItemName + ".xml").Select(a => a.Id).FirstOrDefault();
Guid listItemID = (Guid)items.Where(a => a.FieldValues["FileLeafRef"].ToString() == ListItemName + ".xml").Select(a => a.FieldValues["GUID"]).FirstOrDefault();
WorkflowAssociationCollection wfaCollection = items.Where(a => a.FieldValues["FileLeafRef"].ToString() == ListItemName + ".xml")
.Select(a => a.ParentList.WorkflowAssociations).FirstOrDefault();
clientContext.Load(wfaCollection);
var workflowServiceManager = new WorkflowServicesManager(clientContext, web);
clientContext.Load(workflowServiceManager);
clientContext.ExecuteQuery();
InteropService workflowInteropService = workflowServiceManager.GetWorkflowInteropService();
clientContext.Load(workflowInteropService);
var wfaName = wfaCollection.Where(a => a.Enabled == true).Select(a => a.Name).FirstOrDefault();
Guid wfaId = wfaCollection.Where(a => a.Enabled == true).Select(a => a.Id).FirstOrDefault();
var initiationData = new Dictionary<string, object>();
ClientResult<Guid> resultGuid = workflowInteropService.StartWorkflow(wfaName, new Guid(), list.Id, listItemID, initiationData);
clientContext.ExecuteQuery();

Related

Unable to update "Last_x0020_Modified" in a SharePoint document

I would like to update "Last_x0020_Modified" in the SharePoint documentation.
I am unable to update with the error.
"Invalid data is being used to update the list item. The field you are trying to update may be read-only."
Is there a way to update "Last_x0020_Modified"?
using (var context = new ClientContext("URL"))
{
var passWord = new SecureString();
foreach (char c in "pass".ToCharArray())
{
passWord.AppendChar(c);
}
context.Credentials = new SharePointOnlineCredentials("account", passWord);
var documents = context.Web.Lists.GetByTitle("Document");
documents.Fields.GetByInternalNameOrTitle("Last_x0020_Modified").ReadOnlyField = false;
documents.Update();
context.ExecuteQuery();
var query = CamlQuery.CreateAllItemsQuery();
var collListItem = documents.GetItems(query);
context.Load(collListItem,
items => items.Include(
item => item.Id,
item => item.DisplayName,
item => item.ContentType,
item => item["Modified"],
item => item["Last_x0020_Modified"]
));
context.ExecuteQuery();
foreach (var listItem in collListItem.Where(item => item.ContentType.Name == "folder"))
{
listItem["Last_x0020_Modified"] = "2020/09/01T09:00:00";
}
context.ExecuteQuery();
documents.Fields.GetByInternalNameOrTitle("Last_x0020_Modified").ReadOnlyField = true;
documents.Update();
context.ExecuteQuery();
}
Best Regard.
Per my test, I got the same result as yours on my end. Looks like we cannot change the property "Last_x0020_Modified".
Besides, I found the field "Last_x0020_Modified" is a lookup field.

Microsoft.SharePoint.Client.ServerException - User cannot be found

Get list files from sharepoint library:
using (ClientContext cxt = new ClientContext("Site/Library"))
{
List doclib = cxt.Web.Lists.GetByTitle(listTitle);
cxt.Load(doclib, l => l.RootFolder, l => l.Fields);
cxt.ExecuteQuery();
do
{
CamlQuery query = new CamlQuery();
query.ListItemCollectionPosition = pos;
query.ViewXml = String.Format(#"<View Scope='RecursiveAll'><RowLimit>{0}</RowLimit></View>", PartCount);
ListItemCollection listItems = doclib.GetItems(query);
cxt.Load(
listItems,
a => a.ListItemCollectionPosition,
a => a.Include(
b => b["FileRef"],
b => b.File,
b => b.File.Author).Where(b => (string)b["FSObjType"] != "1"));
cxt.ExecuteQuery();
pos = listItems.ListItemCollectionPosition;
}
while (pos != null);
}
without the expression "b => b.File.Author" works well, on another library everything works with this expression too. Exception:
Microsoft.SharePoint.Client.ServerException
Data:
Message:
User cannot be found.
Stack:
at Microsoft.SharePoint.Client.ClientRequest.ProcessResponseStream(Stream responseStream)
at Microsoft.SharePoint.Client.ClientRequest.ProcessResponse()
at Microsoft.SharePoint.Client.ClientRequest.ExecuteQueryToServer(ChunkStringBuilder sb)
at Microsoft.SharePoint.Client.ClientRequest.ExecuteQuery()
at Microsoft.SharePoint.Client.ClientRuntimeContext.ExecuteQuery()
at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery()

Check if user has access to SharePoint Online site

I have the code below:
using (var context = new ClientContext(URL))
{
context.Credentials = new SharePointOnlineCredentials(username, password);
Web web = context.Web;
context.Load(web.Lists, lists => lists.Include(list => list.Title, list => list.Id));
context.ExecuteQuery();
List SupportList = context.Web.Lists.GetByTitle("Support");
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem newItem = SupportList.AddItem(itemCreateInfo);
foreach (var selector in f.rtxt_select.Text.Split(','))
{
var s = selector.Split('=');
newItem[s[0].Trim()] = m.GetType().GetProperty(s[1].Trim()).GetValue(m, null);
}
newItem.Update();
context.ExecuteQuery();
f.txtMessage.AppendText("Message has been added to Sharepoint List \n ");
f.lbl_successCount.Text = (Convert.ToInt32(f.lbl_successCount.Text) + 1).ToString();
}
Everything works just fine if the username and password is correct. But Is there is a way i can validate if the user has access to the site(SharePoint Online)?
Check Web.GetUserEffectivePermissions method.

Read site page contents in Sharepoint using CSOM

In my project I need to store the site pages locally. These pages may be of published or unpublished pages. I have tried with following code but unable to get contents of the page.
ClientContext clientcontext = new ClientContext(url);
var credentials = new SharePointOnlineCredentials(userid, SecurePass);
clientcontext.Credentials = credentials;
Web web = clientcontext.Web;
clientcontext.Load(web, website => website.Lists);
clientcontext.ExecuteQuery();
clientcontext.Load(web, website => website.Lists,
website => website.Lists.Where(
x => x.BaseTemplate == 119));
clientcontext.ExecuteQuery();
var _collection = web.Lists;
foreach (var pages in _collection)
{
clientcontext.Load(pages);
clientcontext.ExecuteQuery();
CamlQuery cq = new CamlQuery();
var pg = pages.GetItems(cq);
clientcontext.Load(pg);
clientcontext.ExecuteQuery();
foreach (var item in pg)
{
clientcontext.Load(item);
clientcontext.ExecuteQuery();
var f_webPage = clientcontext.Web.GetFileByServerRelativeUrl(item.File.ServerRelativeUrl);
clientcontext.Load(f_webPage);
clientcontext.ExecuteQuery();
}
}
How can I get site pages contents?
Thanks in advance.
In Site Pages library (list type of ListTemplateType.WebPageLibrary) the content for list item depending on the underling the content type could be extracted from:
WikiField field in case of Wiki Page
CanvasContent1 field in case of Site Page (aka "modern" page)
Example
var listTitle = "Site Pages";
var list = ctx.Web.Lists.GetByTitle(listTitle);
var items = list.GetItems(CamlQuery.CreateAllItemsQuery());
ctx.Load(items, icol => icol.Include( i => i["WikiField"], i => i["CanvasContent1"], i => i["FileRef"], i => i.ContentType));
ctx.ExecuteQuery();
foreach (var item in items)
{
Console.WriteLine(">>> {0}", item["FileRef"]);
switch (item.ContentType.Name)
{
case "Site Page":
Console.WriteLine(item["CanvasContent1"]);
break;
case "Wiki Page":
Console.WriteLine(item["WikiField"]);
break;
}
}

How to find all groups in ActiveDirectory where the current user has WriteProperty access?

Currently i'd like to find all groups within the Active Directory where the current user has the right WriteProperty.
The problem is that i can find all groups where the user directly is inserted, but when the user is within a group and that group has write access it won't show up. I thought that setting the booleans of GetAccessRules() would help here, but it doesn't.
So here is the code i already have:
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
//Apply some filter to focus on only some specfic objects
searcher.Filter = "(&(objectClass=group)(name=*part_of_group_name*))";
return searcher;
});
var itemsFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var itemsWithWriteAccess = itemsFound
.Where(entry => entry.ObjectSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier))
.Cast<ActiveDirectoryAccessRule>()
.Where(rule => rule.IdentityReference == identity)
.Where(rule => (rule.ActiveDirectoryRights & ActiveDirectoryRights.WriteProperty) == ActiveDirectoryRights.WriteProperty)
.Count() > 0);
foreach (var item in itemsWithWriteAccess)
{
Debug.Print(item.Name);
}
After a long time and the help of Harvey through this question i finally found a good working solution.
As already explained by Harvey it can be a little difficult to really further understand what you'll get back in entry.Properties["allowedAttributesEffective"].Value. But for normal purposes all you have to check for a write permission is that this field is simply not null.
Here is the sample code:
// (replace "part_of_group_name" with some partial group name existing in your AD)
var groupNameContains = "part_of_group_name";
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
// Apply some filter to focus on only some specfic objects
searcher.Filter = String.Format("(&(objectClass=group)(name=*{0}*))", groupNameContains);
return searcher;
});
var directoryEntriesFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var allowedTo = directoryEntriesFound.Select(entry =>
{
using (entry)
{
entry.RefreshCache(new string[] { "allowedAttributesEffective" });
var rights = entry.Properties["allowedAttributesEffective"].Value == null ? "read only" : "write";
return new { Name = entry.Name, AllowedTo = rights };
}
});
foreach (var item in allowedTo)
{
var message = String.Format("Name = {0}, AllowedTo = {1}", item.Name, item.AllowedTo);
Debug.Print(message);
}

Categories

Resources