Find item in SharePoint list on a HyperLink field - sharepoint-online

I've looked at as many examples as possible and tried different combinations to what looks like a simple problem - I want to check if an O365 modern list item exists based on a URL (Hyperlink) field. My CAML is as below but returns no results - the list item URL field is a full url and the url variable is a text field containing the full URL also. I've also tried Eq and Contains.
"<View>
<Query>
<Where>
<BeginsWith>
<FieldRef Name='URL'/><Value Type='URL'>" + url + "
</Value>
</BeginsWith>
</Where>
</Query>
</View>"
Thanks,
John.
Full code
var OnePlaceURL = TenantBaseURL + "/sites/" + "oneplacesolutions";
var clientSiteRelativePath = "/sites/" + alias; //Even though the list has the full URL the CAML query has to give a relative path - oddity of SP URL field
var registerList = "DMS Clients";
try
{
using (var context = new ClientContext(OnePlaceURL))
{
context.Credentials = SPOCredentials;
Web web = context.Web;
context.Load(context.Web, w => w.Url);
context.ExecuteQueryRetry();
AddLogMessage(MessageLevel.Info, "AddSiteToOnePlaceSolutionsCentralRegister - loaded web" + context.Web.Url);
var targetList = context.Web.Lists.GetByTitle(registerList);
context.Load(targetList);
context.Load(targetList.Fields);
context.Load(targetList.GetItemById(1));
context.ExecuteQueryRetry();
AddLogMessage(MessageLevel.Info, "AddSiteToOnePlaceSolutionsCentralRegister - loaded list and fields, itemCount:" + targetList.ItemCount);
CamlQuery query = new CamlQuery();
query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='URL'/><Value Type='URL'>" + url + "</Value></Eq></Where></Query></View>";
AddLogMessage(MessageLevel.Info, "ViewXML:" + query.ViewXml);
var itemList = targetList.GetItems(query);
context.Load(itemList); // loading all the fields
context.ExecuteQueryRetry();

As I test,if we add external web site url in list,we could use caml like this:
<View><Query><Where><Eq><FieldRef Name='link' /><Value Type='URL'>http://www.google.com</Value></Eq></Where></Query></View>
If we add some internal site url in list,we need to create the caml like this:
<View><Query><Where><Eq><FieldRef Name='link' /><Value Type='URL'>/sites/dev/subsite</Value></Eq></Where></Query></View>
We do not need to add our tenant url.
More reference:
https://social.msdn.microsoft.com/Forums/office/en-US/2dc874ed-3a3d-4f26-b932-afb25c0142c8/caml-query-on-url-field?forum=sharepointdevelopmentlegacy
Updated:
<script type="text/javascript">
SP.SOD.executeOrDelayUntilScriptLoaded(ValidateForm,"sp.js");
function ValidateForm(){
var clientContext = new SP.ClientContext();
var list = clientContext.get_web().get_lists().getByTitle("test3");
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(
"<View><Query> <Where><Eq><FieldRef Name='link' /><Value Type='Url'>/sites/dev/subsite</Value></Eq></Where></Query></View>");
var items = list.getItems(camlQuery);
clientContext.load(items);
clientContext.executeQueryAsync(Function.createDelegate(this,onSuccess),Function.createDelegate(this, onFail));
function onSuccess(){
var ListEnumerator = items.getEnumerator();
while (ListEnumerator.moveNext()) {
var currentItem = ListEnumerator.get_current();
var link= currentItem.get_item("link");
console.log(link);
}
}
function onFail(sender,args){
console.log(args.get_message());
}
}
</script>

Related

Unable to get all the folders and files (under all level) inside a main folder using CAML

I have the following CAML query inside my sharepoint online remote event receiver where i am trying to get all the files and folders that are directly and indirectly added to the folder named FolderA:-
camlQuery6.ViewXml = "<View Scope=\"RecursiveAll\"><Query><Where><Eq><FieldRef Name=\"FileDirRef\" /><Value Type=\"Text\">" + context.Web.ServerRelativeUrl + "/ArchDocs/FolderA"</Value></Eq></Where></Query></View>";
ListItemCollection collListItem6 = context.Web.GetList(context.Web.ServerRelativeUrl + "/ArchDocs").GetItems(camlQuery6);
context.Load(collListItem6, items => items.Include(
item => item.Id,
item=>item["FileDirRef"],
item => item["Title"],
item => item["DealStage"]));
the above CAML will only return the main folder under /sites/projects/ArchDocs/FolderA, but will not return any of the sub-folders and files.. so can anyone advice how i need to modify the CAML, or CAML does not support this?
Use below solution:
var _List = context.Web.Lists.GetByTitle("MyDoc3");
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = #"<View Scope='RecursiveAll'>
<Query>
</Query>
</View>";
Folder folder = context.Web.GetFolderByServerRelativeUrl("/sites/lee/MyDoc3/ParentFolder");
context.Load(folder);
context.ExecuteQuery();
camlQuery.FolderServerRelativeUrl = folder.ServerRelativeUrl;
ListItemCollection listItems = _List.GetItems(camlQuery);
context.Load(listItems, items => items.Include(
item => item.Id,
item => item["FileDirRef"],
item => item["FileRef"],
item => item["Title"]));
context.ExecuteQuery();
The query is only returning items directly in that folder because your CAML is set to only return items where the FileDirRef (or Folder Path) is equal to /sites/projects/ArchDocs/FolderA, anything that is in a subfolder would have a different FileDirRef, such as /sites/projects/ArchDocs/FolderA/SubFolder1, which would not be equal to the path you specified. What you want to do, is instead of the <Eq> operator element, you want to use <BeginsWith>.
Note, you also appear to have syntax error with your string concatenation context.Web.ServerRelativeUrl + "/ArchDocs/FolderA"</Value>
Give this a try:
camlQuery6.ViewXml = "<View Scope=\"RecursiveAll\"><Query><Where><BeginsWith><FieldRef Name=\"FileDirRef\" /><Value Type=\"Text\">" + context.Web.ServerRelativeUrl + "/ArchDocs/FolderA" + "</Value></BeginsWith></Where></Query></View>";
ListItemCollection collListItem6 = context.Web.GetList(context.Web.ServerRelativeUrl + "/ArchDocs").GetItems(camlQuery6);
context.Load(collListItem6, items => items.Include(
item => item.Id,
item=>item["FileDirRef"],
item => item["Title"],
item => item["DealStage"]));

(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;
}

caml Query Returning Last Item in List

I have a CamlQuery which I set as follows:
function getPartID(partName) {
var clientContext = new SP.ClientContext('/sites/HepatoChemKitCustomizationForms/');
var oList = clientContext.get_web().get_lists().getByTitle('PartsTable');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name="Part_x0020_Name" /><Value Type="Text">' + partName + '</Value></Eq></Where></Query></View>');
this.collListItem = oList.getItems(camlQuery);
clientContext.load(collListItem);
clientContext.executeQueryAsync(Function.createDelegate(this, this.onPartQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
function onPartQuerySucceeded(sender, args) {
var listItemEnumerator = collListItem.getEnumerator();
while (listItemEnumerator.moveNext()) {
var oListItem = listItemEnumerator.get_current();
partID = (oListItem.get_item('Part_x0020_ID'));
}
}
'Part Name' is an existing field in the table on which I'm running the query, it is a text field, and the variable partName contains a string which is included in this field in the table.
However, the query returns the last item in the table instead of this one (one of the first items, not that it matters).
I've tried deleting the last item to see if there was something specific about that one but it happened again with the new last item.
I'm new to caml so I don't know what it going wrong with this.
Any help would be appreciated.
UPDATE:
I think I found the problem. When I ran alert (partName + " " + oListItem.get_item('Part_x0020_Name')); it shows this:
I don't know why the string is being built with a carriage return though. I use the following code to build the string:
(catalystArray[i] + "/" + baseArray[j]);
If the object from catalystArray does have a carriage return tacked onto it, how would I remove it?
UPDATE:
Solved it. Dumb mistake, but it took me all day to find.
catalystArray[i].replace(/[\n\r]+/g, '');
baseArray[i].replace(/[\n\r]+/g, '');
(catalystArray[i] + "/" + baseArray[j]);
When setting the View XML of a CAML Query object, you should include the outer <View> and <Query> elements in your XML.
If the XML is not in a format recognized by SharePoint, it'll perform a default query that will (usually) return all items. That is likely what is happening in your case, with the code enumerating through all the items in the results. As a consequence, only the last item's values are stored to the variables you expected.

SharePoint JSOM Parsing values in a hyperlink field

I have a value that looks like "mailto:a#b.com, mailto:a#b.com'. This is basically a hyperlink field and I want to parse this properly using SharePoint JSOM. I tried SP.FieldUrlValue, but it does not seem to have a method that lets you parse.
You can use the .get_url() function on the actual item value to get the hyperlink URL, or the .get_description() function to get the hyperlink's display text.
var linkField = "internalColumnName";
var listName = "List Title";
var clientContext = new SP.ClientContext();
var list = clientContext.get_web().get_lists().getByTitle(listName);
var camlQuery = new SP.CamlQuery();
var items = list.getItems(camlQuery);
clientContext.load(items);
clientContext.executeQueryAsync(Function.createDelegate(this,function(){
var itemEnumerator = items.getEnumerator();
while(itemEnumerator.moveNext()){
var item = itemEnumerator.get_current();
var url = item.get_item(linkField).get_url(); // <-- Get URL
var text = item.get_item(linkField).get_description(); // <-- Get Text
alert(url + ", " + text);
}
}),Function.createDelegate(this,function(sender, args){alert(args.get_message());}));

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

Resources