Get a Web-content article with a specific Structure in Liferay - liferay

I have started developing portlets with Liferay and I would like to show one (or more) Web-content article(s) with a specified structure.
For example, suppose I've a structure "A" so how can I get the last web-content article which is created using this structure?
This article explains how to get articles with a tag but not with a structure.
Thank you

The Liferay API Docs (this is for 6.1, as I don't know what version you're using) are your friend as is the Liferay source code.
In short you'll want to use one of the following API methods:
JournalArticleLocalServiceUtil.getStructureArticles(long groupId, String structureId);
JournalArticleLocalServiceUtil.getStructureArticles(long groupId, String structureId, int start, int end, OrderByComparator obc)
These rely on knowing the ID of the structure from which your content was generated, if you don't know what it is then you can use the following API method to get a list of all of them for your current Community:
JournalStructureLocalServiceUtil.getStructures(long groupId)
You can also use similar methods to find Journal Articles by the JournalTemplate that they use:
JournalTemplateLocalServiceUtil.getStructureTemplates(long groupId, String structureId);
JournalArticleLocalServiceUtil.getTemplateArticles(long groupId, String templateId);
JournalArticleLocalServiceUtil.getTemplateArticles(long groupId, String templateId, int start, int end, OrderByComparator obc)
Comment back if you have any questions, or if this answers your question please hit the "Accept answer" button tick! Thanks!

Related

Exception when using Projection with DynamicQuery in liferay 6.1?

Since when edit a web content, liferay automatically creates new version of an article, I want to get the latest version of a specific article. I used the dynamic query as follows:
DynamicQuery query = DynamicQueryFactoryUtil.forClass(JournalArticle.class, PortletClassLoaderUtil.getClassLoader());
query.setProjection(ProjectionFactoryUtil.max("version"));
List<JournalArticle> jList = (List<JournalArticle>)JournalArticleLocalServiceUtil.dynamicQuery(query);
I searched on google and notice ProjectionFactoryUtil.max("version") was used a lot. But in my case, exceptions have thrown:
"java.lang.Double cannot be cast to com.liferay.portlet.journal.model.JournalArticle"
Am I missing something?
Use of ProjectionFactoryUtil.max("version") in DynamicQuery will return double value which will be maximum of field 'version'. You are trying to cast double type value to JournalArticle thats why facing this exception.
HTH
Sent from mobile.
As for the intent of getting the latest version of a JournalArticle: JournalArticleLocalServiceUtil has an API method fetchLatestArticle (with a few varying parameter sets, for example to prefer published articles over non-published).
In Liferay 5.x it's called getLatestArticle.

Get user-specific web-content

I would like to ask if there is any Java API call in liferay which returns the web contents, which were uploaded by a specific user.
For example, I have one user who has upload some content and I want to show in a portlet this content, how can I do this via java?
If you are specifically talking about web-content which is displayed inside Web-content Display portlet, then you can use the JournalArticleService and JournalArticleLocalService API to fetch the content depending upon the User.
Prior to Liferay 6.0 Web-content was known as JournalArticle and hence the API name has not changed.
So for example:
You can use DynamicQuery API, as follows:
long userId = 10987L; // ofcourse you need to find this
DynamicQuery dynamicQuery = JournalArticleLocalServiceUtil.dynamicQuery();
dynamicQuery.add(RestrictionsFactoryUtil.eq("userId", userId));
int startOfList = 0;
int endOfList = 1000;
// if you want all the JournalArticle retrieved then use:
// int endOfList = QueryUtil.ALL_POS;
// this will retrieve the list of webcontents
List<JournalArticle> articles = (List<JournalArticle>) JournalArticleLocalServiceUtil.dynamicQuery(dynamicQuery, startOfList, endOfList);
The above code will retrieve all the JournalArticles so you would get all the versions of a single web-content since all these versions are stored in the same JournalArticle table. So for this you can add conditions to the dynamicQuery for the fields like version, id, resourcePrimKey, articleId, groupId, companyId etc.
Or if you have more complex needs than you can create a custom-sql-finder in liferay to fetch the desired data from any combination of Liferay DB tables.
If you are talking about contents as in Blogs, Wikis, Files, Webcontents etc then either use their respective *LocalServiceUtil or you can use AssetEntryLocalServiceUtil to fetch the assets for a particular User.
So with AssetEntryLocalServiceUtil also you can use the DynamicQuery API as shown above. The code may not be same but will be along the same lines.
You can know more about DynamicQuery API from this blog.

DDD: should "Comment" in an "Article" be an aggregate root?

I am starting to design a first simple application DDD-style, and I am starting to see how the concepts work together.
If I design a classic blog application, Article class will be one of my aggregate roots. I want to retrieve articles, manage and delete all the related data (publication date, authors...). I am having difficulties with comments. At first, Comment seems to be an entity that is part of the Article aggregate: a comment is created in relation to an article, if I delete an Article, I will delete related comments.
Then I want to display a small box on the blog with the latest comments posted on the blog, for any article. So it looks like I want to retrieve comments from my data store (and only comments). From my understanding of DDD ideas, that makes it an aggregate root. But that does not seem totally right as Comment seems to depend strongly on Article.
How would you model it?
Thanks.
When you think about it you will probably find various reasons why a Comment should be an Aggregate itself:
You want to list the latest comments
You may want to list all comments by a particular user
You may want comments to be nested (a comment being a reply to another comment)
You may want to approve/reject comments through an admin interface
A user may want to edit or delete his/her comment
...
I take the following as a general rule of thumb: Keep your Aggregates as small as possible. When in doubt, err on the side of more Aggregates.
By modelling it this way, you can attach the comments to multiple objects, like Article and User
Comment
string Text
string UserName
bool IsApproved
Article
string Title
string Body
...
List<CommentIds> CommentIds
User
string UserName
...
List<CommentIds> CommentIds
ListOfTenLatestComments
List<CommentIds> CommentIds

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.

Azure Table Storage - Customising table entity saves for persisting collections

There's already a lot of blog posts out there about being able to hook into the WritingEntity event to customise the XML that gets submitted to the server, such as this.
Has anything changed with this process in the newer versions of the SDK? I ask because I have the following simple entity:
public class Label : TableServiceEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public string ContactInfo { get; set; }
public List<string> Urls { get; set; }
public Label()
{
Urls = new List<string>():
}
}
I want to be able to persist that collection of URLs, and I'm already aware that the only thing that's supported directly in terms of arrays/collections is binary arrays. So I thought, fine, I'll just hook into that WritingEntity event and serialize that list to JSON/XML, then add that to the properties list as per that blog post. Then deserialize back to the list during the handling of the ReadEntity event.
However, when I do that, on the call to SaveChanges on the TableServiceContext I get a DataServiceRequest exception that contains an inner NotSupported exception with the message "Only collections of entities are supported". Is this because the String class doesn't inherit from TableEntity? The thing that's confusing me is, when I check the XML that it's written out, it has actually been able to successfully write the custom XML with the additionally added property containing the serialized list, despite the exception.
When I try to retrieve the label via CreateQuery, I get the same exception thrown.
Can anybody tell me what I'm doing wrong here, and what the best practice is for dealing with this situation? I've already came across Lokad Cloud for doing the persistence, but it doesn't seem ideal to me as the querying options for getting data back out are too limited for what I'm wanting to do.
I did have a look at past questions but none seem to address this issue directly.
Any advice would be appreciated!
Based on the response:
I don't know if you got the impression that I'm serializing the entire entity manually? The partition key is just "LABELX", where X is the first letter of the Name property of the label, and the row key is just the string representation of the GUID (I know it's wasteful to store both of those, but I'm just trying to get up and running at the moment).
If you set a breakpoint on the first line of the WritingEntity event and you inspect the XML that's in the e.Data property, there is nothing to represent the URLs collection in the XML. It doesn't matter whether the URLs list is empty, null, or it has entries in it - it doesn't appear at all in the XML, so it doesn't matter what list I pass in. So I think that should answer all 4 questions.
Inside the writing entity event, there really isn't anything special: just code to serialize the list to XML, and then code to add a property to the XML, as per the blog post - it all runs without any exceptions.
OK, sorry, I had neglected to mention that fact that I'm only using the development storage at the moment. The problem seemed to be the fact that I had created some Label entities that didn't have any URLs, before I had created ones that did, and so the schema information in the TableContainer table didn't have the additional URL property. After I cleaned out the database and added a fully populated object before adding anything else, everything worked OK!
I've got some code working on this - and it definitely seems to work with 1.4 SDK
My code is more based on generic entities and the sources I used for inspiration were:
Jai's post on http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/481afa1b-03a9-42d9-ae79-9d5dc33b9297/
with modifications from Yi-Lun Luo on http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/f57cb566-cc7a-4b31-b1ab-47b6d16604af/
and also a good some credit due to ideas from http://azuretablequery.codeplex.com/
I'm guessing that something is wrong in your WritingEntity event handler. Can you post any more of your code - especially:
how are you serialising the RowKey and PartitionKey?
are you removing the raw Urls list from the serialisation?
if you're using XML for the inner serialisation, then are you fully escaping that XML?
One further debugging idea is to just try to get the code working step by step - i.e.
start with an entity with no list,
then try adding another simple test property using a WritingEntity hook,
then try adding a Urls list, and removing it during WritingEntity.
then try serialising and adding this new property in WritingEntity

Resources