Orchard - Query Custom Fields in Content Item - orchardcms

I have a Content Type "News" with custom field "NewsDate" (DateTimeField) and "Active" (BooleanField)
Now I'm need to get 3 active atimes order desc by NewsDate
Get all news, make them toList() and from there manipulate the data is not a solution.
P.S. I need to do something like:
var items = contentManager
.Query(Entities[PageType.Press])
.OrderByDescending<CommonPartRecord, DateTime?>(record => record.PublishedUtc)
.Slice(0, 3);
but instead of PublishedUTC use my custom field "NewsDate" and add Active == true, However it is not possible due to Orchard architecture of storing custom data in a separate field as XML data.
UPDATED:
In a nutshell I want to generate from code behind the following Query:
DECLARE #temp as TABLE(id int, xmldata xml)
INSERT #temp VALUES(1,'<Data><News><NewsDate>07/14/2011 11:42:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link></News></Data>')
INSERT #temp VALUES(2,'<Data><News><NewsDate>07/11/2011 12:11:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link></News></Data>')
INSERT #temp VALUES(3,'<Data><News><NewsDate>02/21/2012 16:56:00</NewsDate><Link Title="" DisplayText="" Link="www.example.com" OpenInNewTab="True">www.example.com</Link><NewsLink></NewsLink></News></Data>')
SELECT
TOP 3 [id],
[xmldata].value('(Data/News/NewsDate)[1]', 'datetime') as NewsDate
FROM #temp
ORDER BY NewsDate DESC
P.S. I looked through the code for DynamicContentQueryTests, however all the examples uses the Part, and in my case Fields are just in the ContentItem:
E.g. News content type contains NewsDate field (datetime field) and some parts as well
Any suggestions are greatly appreciated.

Querying fields is possible since 1.4 through Projector and underlying index tables and new APIs on Content Manager. Your simplest bet actually may be to create a projection.

To get the values of fields that have been attached directly to a Content Item, you need to first look for the Content Part with the same name as the item, which is created by Orchard. So in your case in the Parts list for each News Content Item you'll find a part called "NewsPart", and inside this the Fields property will have your NewsDate and Active fields.
However, like you say Orchard serializes the field values into XML for storage to prevent it having to change the database structure every time you add/remove a field to/from a content type. For this reason, querying and ordering by fields is not recommended because all the serialized data needs to be de-serialized for each Content Item. If you want to be able to do this, the best way is to make your own Content Part with a Content Part Record and use your own table for storage, then you can do this:
contentManager.Query<NewsPart, NewsPartRecord>()...
...and query/sort on whatever values you like.

Bertrand is correct. Check this link : http://www.davidhayden.me/blog/projector-module-in-orchard-cms. You can implement your requirements easily with Projection Module. The link will tell you the steps to do that. If you don't want a widget, you can create a page with your query too. Once the module is enabled, you will see the option to create a Projection Page in the left hand side navigation.
EDIT :
Can't you simply parse the XML? You can query your content type 'News'. Something like -
var contentItems = contentManager.Query<ContentPart>("News").Join<TitlePartRecord>().Join<AutoroutePartRecord>().Join<CommonPartRecord>().List();
Once you have that, you can access the XML data by :
foreach (var item in contentItems)
{
var contentItem = (ContentItem)item.ContentItem;
ContentItemVersionRecord contentItemRecord = contentItem.VersionRecord;
string data = contentItemRecord.Data;
//Call some function here to parse 'data' and store the object in the list.
}
'data' has the information in XML format that you need.
You can parse it and then store the objects in your list that you can order by your custom field.

Related

How to select only needed fields of objects?

I am using the Pimcore API to fetch objects.
$myObjects = new Object\MyObject\Listing();
$myObjects->load();
$myObjects->getObjects();
Works as expected. Now I want to select only a specific field of my objects, e.g. the name field.
How can I tell Pimcore to select only fields that I want? Is it even possibile through the API or do I need to use custom SQL? If so, how can I do that?
Best regards
The pimcore listing is always returning the complete set of objects matching your listing condition...
If you want a fast and easy way to only select one field of your object, I recommend to use the pimcore db class:
$db = \Pimcore\Db::get();
$fieldsArray = $db->fetchCol("SELECT `YOUR_FIELD` FROM `object_query_CLASS-ID`");
This will return you an array width all 'YOUR_FIELD' values from the object query table of your class.
To get the class ID for your query dynamically your should use:
$classId = \Pimcore\Model\Object\MyObject::classId();
Edit:
To get more than one field column, you need to use 'fetchAll' instead of 'fetchCol':
$fieldsArray = $db->fetchAll("SELECT `YOUR_FIELD`, `YOUR_FIELD_2` FROM `object_query_CLASS-ID`");

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.

how to render custom layout of the projection of known content items

I have defined my own projection by a query which returns a set of content items of known content type. I would like to take pick up certain content parts of these content items and display them in the list. Using shape tracing tool I have found the view template where to write my custom layout:
/Views/Parts.ProjectionPart.cshtml
but from the Model variable in the template I can not get the data I want because it is way too high above from the content parts data.
a good example of what I want: let's say I want to render product catalog as defined in this tutorial:
http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-part-5
but I want only to render a list which consists from items:
name of the owner who created the product
name of the product.
publish date of the product
and I need to render it at one place, i.e., not separately in their own part views.
Have you tried adding a layout in the projector module? There is a properties mode option that lets you select which fields/data to show. If the data you want is not there, you should be able to implement an IPropertyProvider. There are examples of this in the Projections module code.

Orchard CMS - How to manage new fields for custom module widget with database?

I've been working with a custom module that has a widget with some fields stored in the DB. I added a new non-required field to the widget and a corresponding field in the database is not being added. What's the best way to handle this type of change?
I can manually add a field in the dev database to match it, but this is unrealistic for 50 some odd production tenant sites.
To expand on #Bertand's answer a little, you don’t need to create database columns when you add fields (Boolean Field, Text Field, Media Picker Field etc) to content parts because the database structure for storing fields is already in place in the database.
Fields are stored as serialized XML against the content part. All of my records are stored in the Orchard_Framework_ContentItemVersionRecord table (there’s a similar table Orchard_Framework_ContentItemRecord, but that doesn’t appear to be used.
The XML is essentially of the format:
<Data>
<SomePart>
<SomeFieldName SomeFieldProperty=”value”
SomeOtherProperty=”value”>FieldValue</SomeFieldName>
</SomePart>
<SomeOtherPart>
<SomeOtherFieldName>FieldValue</SomeOtherFieldName>
</SomeOtherPart>
</Data>
So, for example you might have something like this:
<Data>
<PicturePart>
<picture AlternateText="" Class="" Style="" Alignment="" Width="1219"
Height="709">~/Media/Default/images/greatPicture.jpg</picture>
</PicturePart>
<PictureType>
<ExtraDescription>It’s a great picture!</ExtraDescription>
</Pictureype>
</Data>
Orchard uses the information from the Settings_XXXRecord tables to determine the types of the fields when it’s deserializing them back from the database.
All of this is why, as Bertand says here, fields are easier to create, but harder to query.
Looking at the solution you've posted, it looks like you're adding custom columns, rather than Orchard Fields. If this is what you're trying to do, then it seems like something you should be doing in a datamigration for your module by using AddColumn, as described here. This would look something like:
public int UpdateFrom1() {
SchemaBuilder.AlterTable("TABLE_NAME", table => table
.AddColumn<int>("mynewcolumn")
.AddColumn<string>("mynewcolumn2")
);
return 2;
}
I'm not sure if it's the ideal solution, but running an sql script that manually adds the columns to the module table for all tenants sites seems to do the trick okay. Luckily I setup all my sites to exist in the same database!
SELECT 'Alter table ' + TABLE_NAME + ' Add mynewcolumn int null,
mynewcolumn2 nvarchar(15) null' FROM INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME LIKE '%the_suffix_my_module_puts_for_a_tenant'
A field is not supposed to add a column in the database. By design.

How to create a lookup column that targets a Doc Lib and uses the 'Name' of the document?

How do you create a lookup column to a Document Library that uses the 'Name' of the document as the lookup value?
I found a blog post that recommends adding another custom field like "FileName" and then using a item reciever to populate the custom field with the value from the Name field but that seems cheesy.
Link to the blog in case people are interested:
http://blogs.msdn.com/pranab/archive/2008/01/08/sharepoint-2007-moss-wss-issue-with-lookup-column-to-doc-lib-name-field.aspx
I've got a bunch of custom document content types that I dont want to clutter with a work around that should really work anyway.
I created a one step workflow to set the title from the name, fired on modify and created. Seems to work and took seconds to create.
One way you can do this (although not the easiest way) is by creating a custom field type that extends the SPFieldLookup class. SharePoint's field editor for Lookup fields purposefully hides any columns types that aren't supported by Lookup fields, but you can create a field editor for your custom field type that shows them.
However, I have created a Lookup column that points to a Name column in a Document Library before, and it probably doesn't work like you'd expect. While the value stored in the lookup column is valid, it doesn't show up right in List view or on the View Properties form.
The solution you posted may actually be the best way to handle this. Lookup fields require some kludges if you want to handle more complex scenarios, but that's because they're not meant to provide the same functionality as a foreign key relationship in a database.
Coding in any form always scares me. So Here's what I did: I simply renamed the Stupid "Title" Field to something else, say "Keywords", since you cant do anything with that field: cant even make it mandatory.
Then I created another Single line field called "Title" and used this field for the Lookups
Well there is a simple solution to that and in might work in some case.
In the nutshell if you make the Title field Mandatory, this will force the user to enter a title. In that manner we can use title field as a lookup field.
Now How to do that?
One you are done create a document library go to the library setting. Select Advance Setting and Select Yes for the option "Allow management of content types?".
Then go back to the Library setting and Under content types select the "Document" Content type. THen Select Title Column and then Select "Required (Must contain information)" and say OK.
Now try uploading a document to this document library. You will see Title field in the form.
Hope this helps
Cheers
Vaqar
You have to add the field as XML with the ShowField as 'FileLeafRef'
var XmlFieldDefinition = "<Field DisplayName='myLookupColumn' Type='LookupMulti' StaticName='myLookupColumn' Name='myLookupColumn' Required='FALSE' List='THE LOOKUP ID HERE' WebId='THE WEB ID HERE' UnlimitedLengthInDocumentLibrary='TRUE' Mult='TRUE' Sortable='FALSE' ShowField='FileLeafRef' />"
Field fld = fieldCollection.AddFieldAsXml(XmlFieldDefinition, true, AddFieldOptions.DefaultValue);
ClientContext.Load(fld);
ClientContext.ExecuteQuery();

Resources