Orchard CMS - How to access the items in a container? - orchardcms

It is necessary to get access to the members of the container element, how to implement it, tell me where to look. Zaklyuchaetsya essence in the following: there is a container in which to contain certain elements necessary to realize the possibility of getting the property values of these elements.

To copy a previous answer I've made:
The actual content items aren't stored on ContainerPart. Instead, each contained item's CommonPart has a Container value that links to the container. You need to use LINQ to locate all ContentItems that have CommonPart.Container equal to your container.
int containerId = containerItem.Id;
var containedList = _contentManager
.Query<CommonPart>()
.Join<CommonPartRecord>()
.Where(x => x.Container.Id == containerId)
.List();

Related

In Orchard CMS, how can I get all items of a particular content type based in the values of an attached part's properties

I'm using Orchard 1.7.2.
I have created a new content type called PropertyImage of stereotype Media. I also created a part called PropertyPart and attached that part to my PropertyImage content type. This allows a user to pick a product when uploading a PropertyImage (ie to say 'This image is of this property').
So far so good.
Now what I'd like to do is query for all PropertyImages that have a PropertyPart attached to them where the associated property is x, y, or z.
This is what I have so far:
var images = _orchardServices.ContentManager
.Query<PropertyPart, PropertyRecord>()
.Where(p => p.PropertyId == id)
.ForType(new[] { "PropertyImage" });
This however will only return a collection of PropertyParts, which is not what I want, because I want the whole PropertyImage Content Item. How can I do this?
I should point out that properties come from an external source, and are therefore not content items.
Edit
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!

How to get only share point list columns that are displayed in the list properties?

I'm using Sharepoint 2010's web services interface to try to get the columns for a given list. I've got not problem with getting all of the columns using a GetList() call, but the issue is that I need to only get the columns that the user can see in the List Settings view of the Sharepoint UI.
The code that I'm currently using is as follows:
rootNode = serviceReference.GetList(List_id.ToString());
Element element = XElement.Parse(rootNode.OuterXml);
var fields = from e in element.Descendants()
where e.Name.LocalName == "Field" && e.Attribute("ID") != null &&
!(e.Attribute("Name").Value.StartsWith("_") && e.Attribute("SourceID").Value == "http://schemas.microsoft.com/sharepoint/v3")
select e;
Where serviceReference is an instance of the Sharepoint Lists Service and List_id is the GUID representing the list internally to Sharepoint.
This does filter out some of the columns that I don't want, but it doesn't get rid of everything.
Does anybody know what attributes I'm looking for to narrow it down just to the ones that the user can select to be added to a view? Or am I going about this entirely the wrong way?
Many thanks for any help.
The answer to this was that I was indeed looking in the wrong place for the information I needed. As user823959 pointed out, I needed to get the content type definition and use the fields in there rather than the list itself.
To do this was a two stage process, firstly we need to get the list of content types using the Lists.GetListContentTypes method (although this takes a content type id parameter, it doesn't actually seem to matter what we put here)
XmlNode rootNode = serviceReference.GetListContentTypes(List_id.ToString(), "0×01");
The CAML returned contains the definitions for each of the content types that are available in the list - with the first content type returned being the default one (in my case, the one I was after)
String contentType = rootNode.ChildNodes[0].Attributes["ID"].Value;
Once we've got the content type that we're after we can make a call to GetListContentType with the appropriate list content type id to get the full definition of the content type:
XmlNode contentTypeNode = serviceReference.GetListContentType(List_id.ToString(), contentType);
The CAML returned from this call will contain a list of field elements that correctly show the fields that are available in the SharePoint UI's view configuration. They can be selected in a LINQ query like this:
XElement contentTypesElement = XElement.Parse(contentTypeNode.OuterXml);
var fields = from e in contentTypesElement.Descendants()
where e.Name.LocalName == "Field"
select e;
At this point, we've got a list of Field XML elements that contain information about display names, static names, content types and a whole lot more. See Microsoft's documentation on the Lists.GetListContentType page for more information on the range of information returned about each field.
Many Thanks to user823959 for pointing me in the right direction.

Customized Tridion Search Index Handler: Custom vs Standard field for page url?

I was playing around with custom Search Indexing Handlers for SDL Tridion 2011 (GA). I got something working, using the very helpful information provided by Arjen, however I am not sure if my execution is the best option.
The requirement is to be able to search for pages in the CMS by url (eg www.example.com/news/index.html). In order to do this I have the created a class using the ISearchIndexingHandler interface (code below). I am indexing the url in the ContentText field of the item, however I am not sure if this would normally contain something else for a page (I think a page only has metadata so this should be OK). The advantage of using this over a custom field is that I can simply type the url in the search box without having to use <url> IN <fieldname> or something like that.
So my question is, is there any reason not to use ContentText for Pages, and is there any advantage in using a custom field? Also bonus marks go to anyone with good ideas on how to handle BluePrinting (if I create a page in a parent publication, I want the local urls also to be indexed in the child publications), and the case where a Structure group path is altered (I guess I can somehow trigger a re-index of child page items from within my indexing handler).
The code:
using System;
using Tridion.ContentManager.Search;
using Tridion.ContentManager.Search.Indexing.Handling;
using Tridion.ContentManager.Search.Indexing.Service;
using Tridion.ContentManager.Search.Indexing;
using Tridion.ContentManager.Search.Fields;
namespace ExampleSearchIndexHandler
{
public class PageUrlHandler : ISearchIndexingHandler
{
public void Configure(SearchIndexingHandlerSettings settings)
{
}
public void ExtractIndexFields(IdentifiableObjectData subjectData, Item item, CoreServiceProxy serviceProxy)
{
PageData data = subjectData as PageData;
if (data != null)
{
PublishLocationInfo info = data.LocationInfo as PublishLocationInfo;
string url = GetUrlPrefix(data) + info.PublishLocationUrl;
item.ContentText = url;
}
}
private string GetUrlPrefix(PageData page)
{
//hardcoded for now, but will be read from publication metadata
return "www.example.com";
}
}
}
You can store the url in the ContextText Property. Thies field is used to index Template content data.
Tridion does not index shared item(s) of child publication.
Indexing is triggered on Item modification(create, update, delete, localize and unlocalize).
Or you can use reindexing tool to reindex ur item. but there is no way to index shared items in child publication.
I don't think you can include the URL prefix in neither your search query as the indexed item. Because shared items are not indexed, you will probably index the Page from the Website Structure layer, which is never published.
When a Structure Group is moved you would have to make an event handler that triggers re-indexing all child pages using a protected method of the TOM.NET API. This method is not part of the public API, so posting the code for that solution would probably declare me a persona non grata with R&D :)
Before you re-index anything you should store the original publish location url of the Structure Group in the TcmEventArgs.ContextVariables property, so you can verify whether or not a re-indexing action is necessary.

Updating already-deployed SharePoint content types to handle additional item events

I have a site content type that was used for a handful of lists throughout my site collection. In that content type, I describe an event receiver to handle the ItemAdding event. This works fine. Now I need to update the content type so that ItemUpdating is also handled. Off the top of my head, I tried simply modifying the xml for my content type, since this seemed to allow for easy version tracking. This worked in the sense that my updates were applied to the site content type, but not to my lists that had been using this content type. This much was expected. Then I noticed that the SharePoint SDK takes a grim view of that:
Under no circumstances should you
update the content type definition
file for a content type after you have
installed and activated that content
type. Windows SharePoint Services does
not track changes made to the content
type definition file. Therefore, you
have no method for pushing down
changes made to site content types to
the child content types.
The SDK then points to a couple sections which describe how to use the UI or code to push changes. Since the UI offers no hook into event receivers, I guess I will be choosing the code path.
I thought I'd be able to do something like this and just add a new event receiver to the list's copy of the content type:
SPList list = web.Lists["My list"];
SPContentType ctype = list.ContentTypes["My content type"];
// Doesn't work -- EventReceivers is null below.
ctype.EventReceivers.Add(SPEventReceiverType.ItemUpdating,
"My assembly name", "My class name");
But the catch is that ctype.EventReceivers is null here, even though I have ItemAdding already hooked up to this list. It appears that it was moved to the list itself. So, the list has a valid EventReceivers collection.
SPList list = web.Lists["My list"];
list.EventReceivers.Add(SPEventReceiverType.ItemUpdating,
"My assembly name", "My class name");
So, I have a couple questions:
Is the correct way to do this just to add any new event receivers directly to the list and just forget about my content type altogether?
To accomplish this change, what's the best way to handle this in terms of configuration management? Should I create a simple console app to find all the appropriate lists and modify each of them? Or is somehow creating a Feature a better option? Either way, it seems like this change is going to be off on its own and difficult to discover by future devs who might need to work with this content type.
Did you call ctype.Update(true) after adding the EventReceiver? If you don't it won't be persisted .
And don't use the List content type, use SPWeb.ContentTypes instead.
This code works for me:
var docCt = web.ContentTypes[new SPContentTypeId("0x0101003A3AF5E5C6B4479191B58E78A333B28D")];
//while(docCt.EventReceivers.Count > 0)
// docCt.EventReceivers[docCt.EventReceivers.Count - 1].Delete();
docCt.EventReceivers.Add(SPEventReceiverType.ItemUpdated, "ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c5b857a999fb347e", "CLASSNAME");
docCt.Update(true);
The true parameter means it gets pushed down to all child ContentTypes as well. (i.e. to all lists using the content type).
To answer the second part of your question, this a tricky thing because of the fact that changes to contenttypes on the sitecollection won´t be pushed down to the lists where it´s used. A "copy" is essentially made of the fields in the sitecollection and there is no more link between them after you add a contenttype to the list. I think this is due to the fact that you are supposed to make changes to lists without it affecting the sitecollection. Anyhow my contribution to this "problem", and how I have solved it, involves making the xml the "master" and in a featurereceiver I pull up the xml and find all places where the contenttype is used and from there update the contenttypes (really the fieldrefs) on list level to match the one in the xml. The code goes something like:
var elementdefinitions = properties.Feature.Definition.GetElementDefinitions();
foreach (SPElementDefinition elementDefinition in elementdefinitions)
{
if (elementDefinition.ElementType == "ContentType")
{
XmlNode ElementXML = elementDefinition.XmlDefinition;
// get all fieldrefs nodes in xml
XmlNodeList FieldRefs = ElementXML.ChildNodes[0].ChildNodes;
// get reference to contenttype
string ContentTypeID = ElementXML.Attributes["ID"].Value.ToString();
SPContentType ContentType =
site.ContentTypes[new SPContentTypeId(ContentTypeID)];
// Get all all places where the content type beeing used
IList<SPContentTypeUsage> ContentTypeUsages =
SPContentTypeUsage.GetUsages(ContentType);
}
}
The next thing is to compare the fieldrefs in xml xml with the fields on the list (done by the ID attribute) and making sure that they are equal. Unfortunately we can´t update all things on the SPFieldLink class (the fieldref) and (yes I know it´s not supported) here I have actually used reflection to update those values (f.e. ShowInEditForm ).
As far as the second part of your question, I wanted to pass along what we've done for similar situations in the past. In our situation, we needed a couple of different scrips: One that would allow us to propagate content type updates down to all of the lists in all webs and another that would reset Master Pages/Page Layouts to the site definition (un-customized form).
So, we created some custom stsadm commands for each of these actions. Doing it this way is nice because the scripts can be placed into source control and it implements the already-existing stsadm interface.
Custom SharePoint stsadm Commands

SharePoint: Content Type ID differs form content type id attached to a list instance

I got a question regarding content types and their IDs and how to use them with the object model.
First of all, I defined some site columns, a content type using these columns and a list definition using this content type via CAML. Each of the three components is realized as a feature. Another feature creates a list instance of this list definition.
So when I try to add a new item to the list using the my content type I use the following code.
SPListItem listItem = list.Items.Add();
SPContentType type = list.ContentTypes[new ContentTypeId("0x010044fb4458c2eb4800825910845a35554c")];
listItem["ContentTypeId"] = type.Id;
listItem["Title"] = "Titel";
listItem.Update();
When I execute this piece of code the type object is still null, also I'm sure the content type is attached to the list. Debugging the code and inspecting the list's content types shows me that the content type attached to the list doesn't have the id I defined in the content type definition (CAML). The id within the list instance is different but starts with the one I defined in the content type definition.
0x010044FB4458C2EB4800825910845A35554C0077D683BDD9969F4E920A27C334463321
So is this behavior normal? I expected the content type attached to the list to have the same id as in the definition.
My main goal is avoiding to use the name of the content type to retrieve it from the list's content types, but to use the unique id.
bye
Flo
When you create a list based on a content type, each instance of the list is actually a new content type that inherits from the parent.
Take a look at this...
http://soerennielsen.wordpress.com/2007/09/11/propagate-site-content-types-to-list-content-types/
Update-Regarding finding the instance of the List without using the name, take a look at the SPContentTypeUsage class also!
Don't stress too much about coding the Content Type's name as a constant within your code. While at first glance it may seem that it is possible to change the name of the content type, it should be regarded as a constant because the implications of changing the content type's name are non-trivial enough to demand a full build and re-release of your solution, allowing a change of the coded constant.
The ContentTypeID on the list is the ContentTypeID + the list ID.
The following code should do the trick:
//i try and keep these in a string constants class to avoid
//having to manage "magic strings" throughout my solution
SPContentTypeID ctid = new ContentTypeId("0x010044fb4458c2eb4800825910845a35554c");
//here's the magic
SPContentTypeID listCTID = list.ContentTypes.BestMatch(ctid);
//retrieve the ContentType with the appropriate ID
SPContentType type = list.ContentTypes[listCTID];
//do your thing
SPListItem listItem = list.Items.Add();
listItem["ContentTypeId"] = type.Id;
listItem["Title"] = "Titel";
listItem.Update();

Resources