CAML query that includes folders in result set - sharepoint

I'm trying to write a CAML query that executes against a specific SPList, scoped to a specific folder, recursive from that point, and returns all ListItems (which meet a criteria) and Folders.
Here's the code for the query which seems like it should work (formatted for readability):
SPQuery query = new SPQuery();
query.Query = "
<Where>
<Or>
<Contains>
<FieldRef Name=\"FileRef\" />
<Value Type=\"Text\">foo</Value>
</Contains>
<Eq>
<FieldRef Name=\"FSObjType\" />
<Value Type=\"Lookup\">1</Value>
</Eq>
</Or>
</Where>";
query.ViewFields = "
<FieldRef Name=\"CustomField1\" Nullable=\"TRUE\" />
<FieldRef Name=\"CustomField2\" Nullable=\"TRUE\" />
<FieldRef Name=\"CustomField3\" Nullable=\"TRUE\" />
";
query.RowLimit = 500;
query.ViewAttributes = "Scope=\"RecursiveAll\"";
query.Folder = startingFolder;
DataTable dt = myList.GetItems(query).GetDataTable();
So - this only returns the ListItems - no folders.
If I remove the other conditions from the query, only leaving the FSObjType=1, I get a COM exception "Cannot complete this action. Please try again."
If I then remove the ViewFields, leaving only the Scope=RecursiveAll and FSObjType=1, I get an empty result set back.

Everyone is close, but not quite right.
using (SPSite site = new SPSite("http://server/site"))
{
SPWeb web = site.RootWeb; // See disposal guidance http://blogs.msdn.com/b/rogerla/archive/2008/10/04/updated-spsite-rootweb-dispose-guidance.aspx
SPQuery query = new SPQuery();
query.Query = #"
<Where>
<BeginsWith>
<FieldRef Name='ContentTypeId' />
<Value Type='ContentTypeId'>0x0120</Value>
</BeginsWith>
</Where>";
query.ViewAttributes = "Scope='RecursiveAll'";
SPList list = web.Lists[listId];
SPListItemCollection items = list.GetItems(query);
// Do stuff with your folders
}
First of all, using this FieldRef is wrong:
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
because the folder content type can be inherited. Therefore, you need to compare against the content type ID, like this:
<Where>
<BeginsWith>
<FieldRef Name='ContentTypeId' />
<Value Type='ContentTypeId'>0x0120</Value>
</BeginsWith>
</Where>
And then, set the view attribute Scope to RecursiveAll
<View Scope='RecursiveAll'>...</View>
That should return any item whose content type inherits from Folder (0x0120)

I don't have my dev image to test against, so I might need to revise this later; but I think you could try
query.ViewAttributes = "Scope=\"Recursive\"";
Retrieving the items will allow you to use SPUtility.GetUrlDirectory(url) to get the folder path for a given item, and parse the folder hierarchy from there.

You could try basing your caml query on the Folder Content Type instead,
<FieldRef Name='ContentType' /><Value Type='Text'>Folder</Value>
whilst keeping the
Query.ViewAttributes = "Scope=\"RecursiveAll\"";

I've solved this putting:
<QueryOptions>
<IncludeAttachmentUrls>True</IncludeAttachmentUrls>
<Folder/> </QueryOptions>
As query option
I found my question about it on stack overflow:
How can I iterate recursively though a sharepoint list using webservices?

If I remove the other conditions from the query, only leaving the FSObjType=1, I get a COM exception "Cannot complete this action. Please try again."
Did you remove the <Or> tags when you did this? If not it will not run correctly.
Regardless, that does not solve your problem. Have you tried leaving the query empty? Does it return anything?
I have been working on something similar and ran into an issue as well, perhaps it's somewhat related.

This still seems to an issue in SP 2010. Here's workaround code that will work for 2007 or 2010, based on this MSDN Forums post that uses the web services:
private static SPListItem RecurseIntoFolders(SPList list, SPFolder parentFolder, string fileReference)
{
var query = new SPQuery
{
Query = "<Where>" +
"<Eq><FieldRef Name='FSObjType'/><Value Type='Lookup'>1</Value></Eq>" +
"</Where>",
ViewFields = String.Format("<FieldRef Name='{0}' />", FileReferenceInternalFieldName),
ViewAttributes = "Scope='RecursiveAll'",
Folder = parentFolder
};
var items = list.GetItems(query);
if (items.Count == 0)
return null;
foreach (SPListItem item in items)
{
parentFolder = item.Folder;
// TODO: Any other checking that this is the item we want
return item;
}
return RecurseIntoFolders(list, parentFolder, fileReference);
}

static string GetParentFolder(SPListItem itemToFind, SPFolder folder)
{
SPQuery query = new SPQuery();
// query.Query = "<OrderBy><FieldRef Name='Title'/></OrderBy>";
query.Query = "<Where><Eq><FieldRef Name=\"ID\"/><Value Type=\"Integer\">"+ itemToFind.ID +"</Value></Eq></Where>";
query.Folder = folder;
query.ViewAttributes = "Scope=\"Recursive\"";
SPListItemCollection items = itemToFind.ParentList.GetItems(query);
int intpartentFolderID=0 ;
if (items.Count > 0)
{
foreach (SPListItem item in items)
{
SPFile f = item.Web.GetFile(item.Url);
string test11 = f.ParentFolder.Name;
intpartentFolderID = f.ParentFolder.Item.ID;
//string test1 = item.File.ParentFolder.Name;
return (intpartentFolderID.ToString());
}
}
return (intpartentFolderID.ToString());
}

Related

CAML to search for pages returns all files and folders in a site

I am trying to write a simple web part for Sharepoint 2013 that searches for all pages of a certain content type and outputs a list of their titles. I am using a CAML query to search for that. But no matter what query, the result I have is just a list of all files and folders in this Sharepoint site.
Finally, I reduced the CAML query to a simple "find everything that starts with the letter T", but still the result is outputting all files and folders in the root level of the site.
What am I doing wrong?
protected override void CreateChildControls()
{
Label label1 = new Label();
try
{
SPQuery query = new SPQuery();
query.Query = #"<Query>
<Where>
<BeginsWith>
<FieldRef Name='Title'></FieldRef>
<Value Type='Text'>T</Value>
</BeginsWith>
</Where>
</Query>";
using (SPSite site = new SPSite("https://xxxxxx/sites/xxxxx/en/xxxx/"))
{
using (SPWeb web = site.OpenWeb())
{
PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(web);
PublishingPageCollection collection = pubweb.GetPublishingPages(query);
//now output the results of the query
label1.Text = "Items: " + collection.Count.ToString();
for (int i = 0; i < collection.Count; i++)
{
label1.Text += collection[i].Title + "<br>";
}
}
}
}
catch (Exception ex)
{
label1.Text = ex.Message;
}
Controls.Add(label1);
}
Weirdly, you need to do two things:
1) Remove the opening and closing tag;
2) Make sure the query does not start or end with a blank line.
Then the results will change the the correct ones.

Filtering SharePoint ListItems based on Modified By

I am writing custom coding for retrieving SharePoint list items based on modified by field. Can any one tell me how to get on object modeling.The code is like
SPSite objsite = new SPSite("sitename");
SPWeb objweb = objsite.OpenWeb();
SPDocumentLibrary docs = objweb.Lists["Shared Documents"] as SPDocumentLibrary;
Console.WriteLine(docs.ItemCount);`enter code here`
SPQuery query = new SPQuery();
query.Query = #"<where><eq><FieldRef Name='peoplenames' LookupId='TRUE'/><Value Type='User'>1;#1</Value></eq></where>";
query.RowLimit = 5;`enter code here`
SPListItemCollection items = docs.GetItems(query);
if (items.Count == 0)
return;
else
{
foreach (SPListItem item in items)
{
Console.WriteLine(item["peoplenames"]);
}
}
When i am trying to print values it shows exception.
The modified by field internal name is Editor. Your CAML query should be something like this
<Where>
<Contains>
<FieldRef Name='Editor' LookupValue='TRUE' />
<Value Type='Text'>User Display Name</Value>
</Contains>
</Where>

Baseviewid when using List.Views.Add in sharepoint 2013

I'm using C# to modify a view within a photo library across my sharepoint 2013 farm where there are around 600 sites at the moment.
I'm using the method
olist.Views.Add("AllPictures", strColl, sQuery, 40, true, true,Microsoft.SharePoint.SPViewCollection.SPViewType.Html, false)
and basically I'm trying to recreate the main "All Pictures" view as running a routine to switch this view to a grid has caused this view to no longer display anything. My problem is that once I've deleted the view and run the code above it will create a new All Pictures view that has a baseviewid of 6 so the photos appear as thumbnails rather than a table with the details of the photo. I need to be able to specify the baseviewid as 1 but I've not yet found a way to do this.
Solution:
Use code which I found here http://stefan-stanev-sharepoint-blog.blogspot.co.uk/2010/02/listviewwebpart-spview-two-sides-of.html in the comments section. I hope Stefan doesn't mind me copying some of it here.
Important to note is that the wp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper(); line is commented out because otherwise it will use the baseviewid of the current default view which is 6
which is exactly what we don't want.
The code gets the Photos and Videos list, then gets all the web parts that the list uses, when it gets the AllPictures web part it then saves it for later deletion, adds a new listviewwebpart (but at this stage we can
only add the baseviewid), then deletes the current web part (it seems to only work well in this order) and then gets the hidden view that is created, see stefan's blog for more details.
The second call to Program.UseList then resets some of the view back to how it should look if there is a baseviewid of 1, for this I look at another site with a baseviewid of 1 and checked in sharepoint designer at the XmlDefinition
to see exactly what the xml should look like.
Hope this helps someone else out.
public void LibraryCode(string url)
{
Guid hiddenViewGuid = Guid.Empty;
SPList library = null;
string listUrl = "Photos1/Forms/AllPictures.aspx";
Program.UseList(url, listUrl, list =>
{
SPFile file = list.ParentWeb.GetFile(listUrl);
Guid listID = file.ParentFolder.ParentListId;
if (!listID.Equals(Guid.Empty))
{
library = list.ParentWeb.Lists[listID];
if (library.ForceCheckout && file.Level != SPFileLevel.Checkout) file.CheckOut();
}
SPLimitedWebPartManager mngr = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
SPLimitedWebPartCollection webparts = mngr.WebParts;
System.Web.UI.WebControls.WebParts.WebPart op = null;
for (int k = 0; k < webparts.Count; k++)
{
//get reference to webpart
op = webparts[k];
//check webpart Title to find webpart whose value is to be changed
if (op.Title.Equals("AllPictures"))
{
break;
}
}
XsltListViewWebPart wp = new XsltListViewWebPart();
wp.ListName = list.ID.ToString("B").ToUpper();
//comment out the line below or the new view gets the same baseviewid as
//wp.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();
wp.XmlDefinition = "<View BaseViewID='1'/>";
mngr.AddWebPart(wp, "Left", 1);
//remove existing web part otherwise there will be two tables of the data on the same page
mngr.DeleteWebPart(op);
hiddenViewGuid = new Guid(wp.ViewGuid);
});
Program.UseList(url, listUrl, list =>
{
SPView view = list.Views[hiddenViewGuid];
view.Title = "All Pictures";
view.Update();
// load the passed viewSchema in an XmlDocument
XmlDocument doc = new XmlDocument();
doc.LoadXml(view.HtmlSchemaXml);
// set the Name attribute with the ID of the hidden LVP's SPView - we use a simple trick to update the full schema of the hidden view
XmlElement root = doc.DocumentElement;
root.SetAttribute("Name", view.ID.ToString("B").ToUpper());
root.SetAttribute("BaseViewID", "1");
root.SetAttribute("Type", "HTML");
root.SetAttribute("Hidden", "False");
// do some changes to the view schema
XmlElement viewFieldsEl = doc.SelectSingleNode("//ViewFields") as XmlElement;
if (viewFieldsEl != null) { viewFieldsEl.InnerXml = #"<FieldRef Name=""DocIcon""/>
<FieldRef Name=""LinkFilename""/>
<FieldRef Name=""ImageSize""/>
<FieldRef Name=""FileSizeDisplay""/>
<FieldRef Name=""Modified""/>
<FieldRef Name=""RequiredField"" Explicit=""TRUE""/>
<FieldRef Name=""PreviewOnForm"" Explicit=""TRUE""/>
<FieldRef Name=""ImageCreateDate""/>
<FieldRef Name=""Project_x0020_Tags""/>
<FieldRef Name=""_Comments""/>
<FieldRef Name=""Originator""/>"; }
XmlElement javascriptEl = doc.SelectSingleNode("//JSLink") as XmlElement;
if (javascriptEl != null) { javascriptEl.InnerXml = "clienttemplates.js|callout.js"; }
// create a dummy view to hold the new schema
SPView cloneView = new SPView(list, doc);
// a small trick with reflection so that we can update the dummy view
typeof(SPView).GetField("m_bExistsInDatabase", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(cloneView, true);
// this will actually update the hidden SPView associated with the LVP
cloneView.Update();
if (library != null)
{
SPFile file = list.ParentWeb.GetFile(listUrl);
if (library.ForceCheckout) file.CheckIn("");
if (library.EnableMinorVersions) file.Publish("");
if (library.EnableModeration) file.Approve("");
}
});
}

Get Item based on index from Sharepoint CAML Query to SPListItemCollection

I am trying to query list items using caml (first time doing this) and sort the items based on modified date so I can get access to most recent documents. However when trying to get the SPListItem based on the ID I am failing.
Code:
SPQuery query = new SPQuery();
String camlquery = "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>";
query.ViewAttributes = "Scope = 'Recursive'";
query.ViewFields = "<FieldRef Name='Modified' /><FieldRef Name='Title' /><FieldRef Name='Name' />";
query.Query = camlquery;
query.IncludeMandatoryColumns = true;
SPListItemCollection col = list.GetItems(query);
//failing here... I think
SPListItem item = col.GetItemById(0);
Is there a way to get an SPListItem based on its index from within the SPListItemCollection?
I have tried
SPListItem item = col[0];
Also with no luck.
Here is the error I am getting:
Value does not fall within the expected range.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentException: Value does not fall within the expected range.
If you want to get a SPListItem using Id then you do not need to go for caml, try this,
SPSite oSite = new SPSite("http:/MyTestSite/");
SPWeb oWeb = oSite.OpenWeb();
SPList oList = oWeb.Lists["MyCustomList"];
SPListItem oListItem = oList.Items.GetItemById(1);
But If you insist on getting it by using caml, make sure you include Id in your ViewFields caml query.
And get the items using Id as :
foreach (ListItem item in col)
{
if(item["Id"] == 1) //Only an example
{
var itemFromId = item;
}
}
Also to note, The value of the ID property is not the same as the index of the item in the collection of list items. This property contains the item's 1-based integer ID, which is one greater than the ID of the item that was previously added. If the item is deleted, its ID is not reused.[MoreInfo]
The issue was with the collection that I was returning. I had to make sure there were results prior to using the item:
if (col.Count > 0)
{
SPListItem item = col[0];
}
Silly mistake.

How to edit and read data in a SharePoint List

I want to query a SharePoint list and want to use the data to be displayed in a usercontrol that I have created.
Earlier I had the data coming from a database.
Now I need to modify it to work with a SharePoint list.
Can you please guide me.
Grace.
SPList list = null;
SPListItemCollection LIC = null;
SPListItem listItem = null;
using (SPSite mainSite = new SPSite("http://sitewhereyourlistis"))
{
using (SPWeb mainWeb = mainSite.OpenWeb())
{
list = mainWeb.Lists["ListNameHere"];
//**You will most likely want to limit your return to a single record so I have created the caml to do so with the List Item ID
String caml = String.Format("<Where><Eq><FieldRef Name=\"ID\" /><Value Type=\"Counter\">{0}</Value></Eq></Where>", ListItemID);
SPQuery qry = new SPQuery();
qry.Query = caml;
LIC = list.GetItems(qry);
listItem = LIC[0];
//**Here is where you will fill your textboxes
txtTextBoxName.Text = listItem["ColumnNameHere"].toString();
//**The above statement likes to error out if the value is null so I like to use a custom Application Helper Function to prevent things like this.
//**My actual call to get the data would look like this.
txtTextBoxName.Text = EnsureTextValue(listItem, "ColumnNameHere");
// I added the Function Definition Below
}
}
public String EnsureTextValue(SPListItem item, String param)
{
String tmp = String.Empty;
try
{
tmp = item[param].ToString();
}
catch { "Run Debug Method or Whatever Here" }
return tmp;
}
I am not sure if I missed anything but this should get you close.
Hope it helps!
To get Distinct Values for the column Click Here
To edit the values in the list there is a nice post here
I have used this to Query the SharePoint List
using (SPWeb web = SPContext.Current.Site.RootWeb)
{
SPList mylist = web.Lists["listName"];
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name='columnName' /><Value Type='Text'>" + strXYZ+ "</Value></Eq></Where>";
SPListItemCollection items = mylist.GetItems(query);
tempDT = items.GetDataTable();
return tempDT;
}

Resources