0

The requirements is easy. Someone publish a JournalArticle with some Tags (TagA, TagB). On the other pages (Layouts) we have AssetPublisher portles that show all JournalArticles with those Tags (e.g. TagA or TagB). The question is, how to get this layouts programmaticaly?

Mark
  • 17,887
  • 13
  • 66
  • 93
  • I think you might need to go the `custom-sql` way with joining/sub-querying tables like PortletPreferences, Layout, AssetEntry, AssetTags, JournalArticle. This to do it at one go and not looping multiple times. – Prakash K Aug 17 '12 at 09:21

3 Answers3

2

I solve it with recursive DynamicQuery, enjoy:

public static Set<Layout> getLayoutsWithThisTags(SortedSet<String> tags) throws SystemException, PortalException {
    Set<Layout> layouts = new HashSet<Layout>();

    //build DynamicQuery that contains "assetTags" as "queryName0", see configuration of AssetPublisher
    DynamicQuery query = DynamicQueryFactoryUtil.forClass(com.liferay.portal.model.PortletPreferences.class, PortalClassLoaderUtil.getClassLoader())
            .add(PropertyFactoryUtil.forName("preferences").like("%<preference><name>queryName0</name><value>assetTags</value></preference>%"))
            .add(getTagConditions(tags));

    Set<PortletPreferences> preferences = new HashSet<PortletPreferences>(PortletPreferencesLocalServiceUtil.dynamicQuery(query));
    for (PortletPreferences portletPreferences : preferences) {
        long plid = portletPreferences.getPlid();
        layouts.add(LayoutLocalServiceUtil.getLayout(plid));
    }

    return layouts;
}

private static Criterion getTagConditions(SortedSet<String> tags) {
    //create recursive OR-Criterion that contains any of the tags
    Criterion criterion = RestrictionsFactoryUtil.or(
            PropertyFactoryUtil.forName("preferences").like("%<preference><name>queryValues0</name>%<value>" + tags.first() +"</value>%"),
            (tags.size() > 2) ? getTagConditions(tail(tags)) :
                PropertyFactoryUtil.forName("preferences").like("%<preference><name>queryValues0</name>%<value>" + tags.last() +"</value>%"));
    return criterion;
}

private static SortedSet<String> tail(SortedSet<String> tags) {
    tags.remove(tags.first());
    return tags; 
}

for Portal with 250 Pages (Layouts) this code need 12ms.

  • 1
    WOW! Innovative use of recursion. – Prakash K Aug 20 '12 at 15:13
  • 1
    This code `layouts.add(LayoutLocalServiceUtil.getLayout(plid));` can be optimized by collecting all the `plid`s and then firing just one query to get all the layouts. But with Oracle it would only accept a maximum of 1000 entries in the `in-clause`. This could have been done using a `for` loop also I think instead of recursion. But nice answer. Thanks! – Prakash K Aug 20 '12 at 15:19
0

Suddenly this came to my mind :-)


        List assetPublisherLayouts;
        List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
        for (Layout layout : layouts)
        {
            if(layout.getTypeSettings().contains("101_INSTANCE")) {
                assetPublisherLayouts.add(layout);
            }
        }

While 101 being the protlet ID for Asset publisher and it is instantiable..

Sharana
  • 749
  • 3
  • 8
  • I'm looking for only Layouts (AssetPublishers) that have configured specific Tag, like TagA or TagB. – Mark Aug 16 '12 at 14:13
  • I'm not entirely sure this is possible as there is no direct API access to the PortletProperties table in the database which stores information such as which tags are displayed for a AssetPublisher. You'd have to look at the Liferay Source code to see how they implement access to this table internally. – Jonny Aug 16 '12 at 19:21
0

I can think of two ways:

  1. Using DynamicQuery to fetch Layouts that contain Asset Publisher portlets and then processing the Layout list retrieved for those specific layouts which have Asset Publisher with TagA & TagB.
    The code might be something like this (disclaimer: it is just pseudo code :-)):

    layoutDynamicQuery.add(RestrictionFactoryUtil.ilike("typeSettings","%101_INSTANCE%"));
    
    List<Layout> layoutList = LayoutLocalServiceUtil.dynamicQuery(layoutDynamicQuery);
    
    List<Layout> finalLayoutList = new ArrayList<Layout>();
    
    for (Layout layout : layoutList) {
        // 1) fetch portletIds for this layout
        // 2) fetch relevant PortletPreferences for the instance-id for the AssetPublisher portlet, can use PortletPreferencesLocalServiceUtil
        // 3) Check if the tags (TagA & TagB) are present in the preference retrieved.
        // 4) if point-3 is true then: finalLayoutList.add(layout);
    }
    
  2. Using custom-sql to fetch the Layouts in a single complex sql query, by joining/subquerying required tables like AssetTags, Layout, PortletPreferences etc.

This is not a general requirement scenario in liferay, so it is obvious that there won't be a direct way of doing this.

Hope this proves to be of some help.

Prakash K
  • 11,669
  • 6
  • 51
  • 109