Mostrando entradas con la etiqueta magnolia. Mostrar todas las entradas
Mostrando entradas con la etiqueta magnolia. Mostrar todas las entradas

viernes, 11 de marzo de 2016

JCR-SQL2 optimize queries samples

Queries run on Magnolia 5.x


1. Search for specific Item type 

i.e. [mgnl:page] instead of using the default one [nt:base]
Example: Search for pages with stkNews template

select * from [nt:base] as t where isdescendantnode(t, '/demo-project') and  t.[mgnl:template]='standard-templating-kit:pages/stkNews'

This query takes 18ms to return.

select * from [mgnl:page] as t where isdescendantnode(t, '/demo-project') and  t.[mgnl:template]='standard-templating-kit:pages/stkNews'

This query takes 3ms to run.


2. Avoid the use of isdescendantnode if not necessary.

Example: Searching in data module, two different types of data with same property

SELECT * from [nt:base] AS t WHERE  ((ISDESCENDANTNODE(t, '/folder1type1')   AND [jcr:primaryType] = 'type1') or (ISDESCENDANTNODE(t, '/folder2type2')   AND [jcr:primaryType] = 'type2')) and t.categories = 'c6f40746-5be4-44c5-9eb1-0f5e59133350'

This query takes more than 6000ms to run

SELECT * from [nt:base] AS t WHERE   [jcr:primaryType] = 'type1' or [jcr:primaryType] = 'type2' and t.categories = 'c6f40746-5be4-44c5-9eb1-0f5e59133350'

This query takes 13ms to run, in this case we are looking for type1 that is just under folder1type1 and type2 that is just in folder2type2, so the isdescendantnode is redundant.


3. Performance

In order to check the queries performance and see if you need to optimize them, see previous post on http://tmiyar.blogspot.com.es/2012/06/queries-logging.html


4. query samples

* get latest modified pages in demo-project, here you need to know that any time you modify any component in the page, that page gets its modification date updated. That is why we only need to ask for mgnl:page
select * from [mgnl:page] as t where isdescendantnode(t, '/demo-project') order by t.[mgnl:lastModified]
the way to limit the result set is by code query.setLimit(3); before calling query.execute();

* find where a control is used in the configuration
select * from [mgnl:contentNode] as t where t.class like '%SelectFieldDefinition'
In this case you can decide to get the result type mgnl:content, you can do it in the jcr query tool.

* for queries that rely on a path with wildcards there is no other way that to use isdescendantnode and inschildnode joins that makes queries a bit more complex, in that cases I would recommend to use xpath as is not deprecated, it is faster and I dont think they will remove it.


Note: Nodes get cached by Jackrabbit, second time you run the query in our tool will be faster, keep that in mind when doing your tests.

viernes, 22 de enero de 2016

How to see the status of timed activations

This article is for Magnolia 5.x versions.

The Need

It happens that when you decide to send a page for publication, you don't know what is going on until you see the green status dot, things could happen like you may forget you already sent that page for publication and you send it again.

In order to keep track at all times of the tasks in progress there is a little development you could implement, and I will show you how.

The aim is to add a new column to the pages app that will show the information like in the image below.

As you can see there is a new column named "Publication Date", in that column we will see either the date of publication, or if is an immediate publication that someone is reviewing we will see "InProgress". We could add the other status like "rejected" but I will just display this two.

The Code

There is only one class that we need to create, that will extend from AbstractColumnFormatter, we will query the "tasks" workspace and display the status. Right now there is API method to get a task directly from the identifier of the node, that is why you will see 2 queries instead of one.

The Configuration

Once you have the class in your project the only thing left is to add it to the treeView of the pages app


The last thing to do is to add read permissions on tasks workspace to the users/groups you want to be able to use this functionality.

The code is available here.

To Improve

Right now it works just for the website workspace, it can be improved to read the workspace name in the configuration. 
It could be good the show if the activation is recursive.
In case there are more than one activation scheduled for one item it should be displayed.
Supper cool addition would be a button to stop an scheduled activation. 

viernes, 18 de octubre de 2013

JCR search text ignoring umlauts, accents

Problem.

Default Magnolia installation demo-project site, comes with a search box on the top right corner


If you try to search for something like résumé you would expect to get:

But instead you get:




Solution.

1. Configure tomcat encoding.


In order to be able to look for text with special characters, first thing you need to do is configure tomcat encoding. The default Tomcat encoding is ISO-8859-1, this encoding does not support some special characters, that means when rendering the text the accents, umlauts won't be displayed.

This link explains how to add the UTF-8 encoding into your Tomcat configuration:
http://struts.apache.org/release/2.0.x/docs/how-to-support-utf-8-uriencoding-with-tomcat.html

2. Create a new analyzer (optional).


You would probably like that when people makes a spelling mistake by missing the accents for example, that they get the results nevertheless, search for "camion" should return results with either "camion" or "camión".

Have a look at the following link, it explains step by step how to do it: http://docs.jboss.org/exojcr/1.12.13-GA/developer/en-US/html/ch-jcr-query-usecases.html#JCR.IgnoreAccentSymbols






martes, 19 de junio de 2012

How To Export Website Content To Excel

Here is the thing, I have a big report on my site that grew quite big, now is not easy to review it without scrolling up and down.

Well, how about exporting just that content to an Excel file where we can get a graphic of the data, or maybe sort it by specific column or do complex calculations? Here is a very fast way to produce such file.

First we need to give a distinct id to our table, and any other data we will need to produce the output we desire, i.e. title of the report, on our template file:

<h1 id="toExcelReportTitle">Sample Report Title</h1> <table id="toExcelTable"</table> ....

Then we will need a button that when on clicked it will send a request to a servlet that will do the export.



The JQuery is used to load the HTML of the report table and the data of the report title into the parameters in the form. This way when we click on the save button, it will send this parameters to the servlet we have previously created.

The servlet will the read this parameters and write them to the response. This response will automatically be rendered into Excel form by setting the Content Type to application/vnd.ms-excel.



And this is a sample of the resulting excel file:




By exporting the content this way, we don't need to request the table page again, we will just get the already rendered data and sent it to the servlet that will send the data back as an excel file.

martes, 12 de junio de 2012

Queries Logging

Want to know which queries are run and how log did they take individually?
What about enabling the log for jackrabbit? If we enalbe it, it will tell us something like this.

org.apache.jackrabbit.core.query.QueryImpl: executed in 0.00 s. (select * from mgnl:user where jcr:path = '/system/superuser' or jcr:path like '/system/%/superuser')

There are two ways, easiest and does not need restart of the server:
You have to go to menu Tools, logging and there set the value for the query class to DEBUG


Second way is to extend the log4j.xml file so it logs the queries, good thing about this is that you can make it write the queries in a new file, also, the change wont be lost when you restart the server, but, you will need to restart the server in order to see the new logging output.



Note that my log4j.xml is in webapp/WEB-INF/config/default/log4j.xml

martes, 29 de mayo de 2012

Creating a PDF from Website Content

I recently had a requirement to convert website content to a PDF file. First I thought it was going to be a lot of coding. I would have to make every page component look the way it should on a printable document and generate a nice PDF file from the whole page.

I did not want to spend too much time. There must be an easier way. How else could the Save as PDF button in Safari do such a good job printing a page without knowing anything about my Web application?

Searching for a shortcut I came across the Flying Saucer Project:
Flying Saucer is an XML/CSS renderer, which means it takes XML files as input, applies formatting and styling using CSS, and generates a rendered representation of that XML as output. The output may go to a PDF file. -- Flying Saucer User's Guide
Very impressive work! Especially considering how much it can accomplish with very little code and a print style sheet.

Using the Flying Saucer library in a servlet to access the website anonymously worked OK. But when I tried authenticated access Magnolia CMS resources, everything stopped working. Fortunately, this was easily resolved with the callback class that the Flying Saucer Project offers. I must confess that I overlooked it first. The callback class not only allows you to decide how to access the website resources but also lets you decide how to render the images and styles.

The rest of the work such as which elements to display, where to display them, the page size and page breaks are specified in the style sheet using the W3C Paged Media syntax.



See linked document on how would the document look like by printing the About section of the demo project with all pages in the same PDF document.

For this example, I used the standard print.css that comes with the demo-project theme-pop, adding just a style for the images so it does not make a page break in the middle of the image:

img { page-break-inside: avoid; }

Many thanks to the Flying Saucer Project developers for providing this amazing utility!

lunes, 19 de diciembre de 2011

Rendering Paragraphs with AJAX, Part III: Autocomplete example

In Rendering Paragraphs with AJAX Part 1 and Part 2 I showed basic examples of using AJAX in paragraphs in Magnolia CMS. In this last post, I demonstrate how to create a predictive search for contacts and how to render a contact's details when it is selected.

The end result looks like this, hoping it motivates you to read the whole post :-). It is a new paragraph that contains a text box. You type a contact name in the box and the paragraph starts listing matching contacts from the directory.



When you select one of the contacts, a contact paragraph is rendered, displaying full contact information from the directory.


What is needed to achieve this? A paragraph, a model class and of course a nice jQuery Autocomplete widget. The last bit is what makes this example so simple to create.

Paragraph script

The paragraph script looks like this.
 [#assign cms=JspTaglibs["cms-taglib"]]  
 [#-- TODO This link and script MUST BE MOVED to a file into the theme --]    
 <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css"   
    rel="stylesheet" type="text/css"/>  
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js">
    </script>
 <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js">
    </script>
 [#-- End TODO --]
 [#assign contentLink= mgnl.createLink(content)!]  
 <script>  
 jQuery(document).ready(function() {  
    jQuery("input#autocomplete").autocomplete({  
       source: function( request, response ) {  
          jQuery.getJSON('${contentLink}', {term : request.term}, function(data) {  
             response(data);  
             });  
          },  
       select: function( event, ui ) {  
          jQuery.get('${contentLink}', { uuid: ui.item.uuid},function(data) {  
          jQuery('#ajax').html(data);  
          });  
          return false;  
          }  
       });  
    });  
 </script>  
 [@cms.editBar /]  
 <div>  
    Find Contacts: <input id="autocomplete" />  
    <div id="ajax">  
    </div>  
 </div>  
Note that the links to jQuery UI cannot be placed directly in the script as I have done here. The code above is just a demonstration so you can see what files are needed. See jQuery Autocomplete documentation for details.

The paragraph script renders an input field that has an ID autocomplete. The source for this field is a JSON object provided by the paragraph model via an AJAX call. The JSON object provides contact names starting with the string you type in the input field. When a contact is selected, a second AJAX call asks the paragraph model to render a contact paragraph using the UUID of the selected item.

Autocomplete paragraph model class

The paragraph model class for autocomplete looks like this.
 public String execute() {  
    String uuid = MgnlContext.getParameter("uuid");  
    //first it will run the autocomplete  
    if(StringUtils.isEmpty(uuid)) {  
       return renderAutocomplete();  
    } else {  
       return renderParagraph(uuid);  
    }  
 }  
There are two method calls in the execute method:
  • renderAutocomplete returns a JSON object of contact names and UUIDs.
  • renderParagraph renders a contact paragraph for the selected UUID.

renderAutocomplete method

The renderAutocomplete method looks for the contact information by givenName and then constructs a JSON object with two fields, label and uuid, and sends it to the response.
 private String renderAutocomplete() {  
    String autocomplete = "";  
    String repositoryId = "data";  
    String dataType = "Contact";  
    String propertyName = "givenName";  
    String term = MgnlContext.getParameter("term");  
    if(StringUtils.isNotEmpty(term)){  
       String statement = "//element(*, "+ dataType +")[jcr:like(fn:upper-case(@"    
          + propertyName + "),'%" + term.toUpperCase() + "%')]";  
       Collection<Content> nodes = QueryUtil.query(repositoryId, statement , "xpath");    
       autocomplete +="[";  
       Iterator<Content> it = nodes.iterator();  
       while (it.hasNext()) {  
          Content node = it.next();  
          autocomplete += "{ \"label\":\"" + NodeDataUtil.getString(node, propertyName)   
          + " " + NodeDataUtil.getString(node, "familyName") + "\", \"uuid\": \"" 
          + node.getUUID() +"\"}";  
          if(it.hasNext()) {  
             autocomplete += ",";  
          }   
       }  
       autocomplete +="]";  
       try {  
          MgnlContext.getWebContext().getResponse().getOutputStream().print(autocomplete);  
          } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
       }  
       return RenderingModel.SKIP_RENDERING;  
    }  
 return super.execute();  
 }  

renderParagraph method

The renderParagraph method is bit more complex because what we want to render is of type Contact. Instead of using the renderParagraph method from MagnoliaTemplatingUtilities, we have to render it using ParagraphRendererManager.
 private String renderParagraph(String uuid) {  
    String repositoryId = "data";  
    String paragraphName = "contact";  
    Content ajaxContent = ContentUtil.getContentByUUID(repositoryId, uuid);  
    if(ajaxContent != null) {  
       try {  
          if (ajaxContent != null && ajaxContent.isNodeType(ItemType.CONTENTNODE.getSystemName())) {  
             MagnoliaTemplatingUtilities.getInstance().renderParagraph(ajaxContent,   
                MgnlContext.getWebContext().getResponse().getWriter(), paragraphName);  
          } else if(ajaxContent != null && !ajaxContent.isNodeType(ItemType.CONTENTNODE.getSystemName())) {  
             //this bit of code is needed as content is not a contentnode, is a datatype from data repo.  
             Paragraph paragraph = ParagraphManager.getInstance().getParagraphDefinition(paragraphName);  
             ParagraphRenderer renderer = ParagraphRendererManager.getInstance().getRenderer(paragraph.getType());  
             renderer.render(ajaxContent, (Paragraph) paragraph, MgnlContext.getWebContext().getResponse().getWriter());  
          }  
       } catch (RenderException e) {  
          // TODO Auto-generated catch block  
          e.printStackTrace();  
       } catch (IOException e) {  
          // TODO Auto-generated catch block  
          e.printStackTrace();  
       }  
    }  
    return RenderingModel.SKIP_RENDERING;  
 }  

Contact paragraph model class

For the Contact paragraph, I just used the one provided by STK and changed the model to get the content from the UUID provided.
 public String execute() {  
    String uuid = MgnlContext.getParameter("uuid");  
    if(StringUtils.isEmpty(uuid)) {  
       uuid = getContent().getNodeData("contact").getString();  
    }  
    HierarchyManager hm = MgnlContext.getHierarchyManager(DataModule.getRepository());  
    if (!StringUtils.isEmpty(uuid)) {  
       try {  
          contact = hm.getContentByUUID(uuid);  
          } catch (RepositoryException e) {  
             throw new RuntimeException(e);  
       }  
    }   
    return super.execute();  
 }  
Download the code for this example from the Magnolia SVN. It is just a working sample to demonstrate this functionality and might be removed at some point so better grab it quick!

martes, 29 de noviembre de 2011

Rendering Paragraphs with AJAX, Part II

In Rendering Paragraphs with AJAX Part 1, I showed a basic example of an AJAX paragraph in Magnolia CMS. We loaded an paragraph via an AJAX call and rendered it on the page.

Now we will see how to send parameters on the AJAX call and display changes by using the model class.

For a use case, suppose you have to display a large amount of content. To make the content easier for the user to browse, you add pagination. You only want to update the paginated content when the user clicks a Previous or Next button.

You need a way to tell the server what page the user is currently on. The server can then provide the previous page or the next page. In order to do this you need a model class, a paragraph definition and a Freemarker template script (.ftl file).

Template script

Add the following code to your Freemarker template script:

<div id="ajax">
[#assign pageIndex = model.pageIndex]
<script>
function next() {
    jQuery.get('${ctx.contextPath}${content.@handle}.html', 
{index: '${pageIndex}'}, 
 function(data) {
         jQuery('#ajax').html(data);
     });  
}
</script>
<div>
   Page: <span id="text">${pageIndex}</span>
</div>
<a href="javascript:next();">NEXT</a>
</div>
The content paragraph to be rendered is inside the div element that has an ID ajax. The script assigns the value that comes from the model method call to a variable pageIndex. In the script, an AJAX call reloads the current paragraph, passing a new parameter index with the value we assigned earlier.

In a nested div element, we write the current value of the variable on the page.

On the last line, we create a NEXT link that the user can click. The click will execute the AJAX call, run the paragraph model, and return the new pageIndex value.

Model class

Now let's see what the model class looks like.
public String execute() {
        
    String index = MgnlContext.getParameter("index");
    if(StringUtils.isNotEmpty(index)) {
        pageIndex = Integer.parseInt(index) +1;
    }
    return super.execute();
}

public int getPageIndex() {
    return pageIndex;
}
The class has two methods:
  • Overridden execute method that reads the parameter we sent by clicking NEXT and adds 1 to pageIndex.
  • getPageIndex method to retrieve the value of the member variable pageIndex.

Paragraph definition



The rendered NEXT  button looks like this on the page.

jueves, 27 de octubre de 2011

Rendering Paragraphs with AJAX, Part I

Ever wondered how to reload just some paragraphs on your page? Imagine you have pagination and you want to reload just the paginated paragraph when clicking previous/next? Is Magnolia CMS capable of this without complex coding? Yes, and on this post you will find some ways of achieving this.

First thing to know is, since Magnolia CMS version 4.x paragraphs are autorenderable. What does this mean? It means you can copy the path of a paragraph, paste it in the address bar of your browser and you will see the paragraph rendered. Try the following URL from our demo site, it will render the teaser of the Article page.
http://demopublic.magnolia-cms.com/demo-project/content/00.html

What can you do with this? It means you don't need to create a servlet or anything to render a paragraph dynamically on a page. Just a call to the appropriate script will let you load any paragraph on our page.

Here is a sample using JQuery. Add this code into a .ftl file. You also need to create a paragraph definition and add it to be used in a template.
<script type="text/javascript">
function loadArticleTeaser() {
  jQuery.get('${contextPath}/demo-project/main/00.html',function(data) {
    jQuery('#ajax').html(data);
  });
}
</script>
<div id="ajax"> </div>
<a href="javascript:loadArticleTeaser()">Load Teaser via AJAX call </a>
The JQuery call (http://api.jquery.com/jQuery.get/) will get the specified URL and, on success, will run the method that loads the result (paragraph) from the AJAX call into a div with ID "ajax". To run the whole thing you just need to click the "Load Teaser via AJAX call" link.

This is a basic sample of an AJAX paragraph. In the next post you will see how to send parameters and process them in the paragraph model class.

martes, 27 de septiembre de 2011

Tips & Tricks: Make activation comment mandatory

With this tip, you can make everybody to write a comment when they send an activation request, though I cannot guarantee that comment would be meaningful!

Step 1. Set property required=true in activation dialog.



Step 2. Add the following code to the saveOnClick property of the dialog.

if(document.getElementById('comment').value != '')  {window.close();opener.mgnl.workflow.WorkflowWebsiteTree.submitActivation($('mgnlFormMain'));} else {alert('Must write a comment for activation.');}

lunes, 29 de agosto de 2011

Creating site navigation from data nodes

This solution has been implemented for Magnolia Shop Module (contributed by fastforward).

The aim is to create menu navigation based on nodes in the data repository. The navigation should be displayed on an existing site as section navigation. This way, when you navigate to the section, product categories defined in data repository will be used as items in the vertical menu.

Note that the breadcrumb is also updated with the product categories.

Assuming we know how the default navigation works in STK, we do the following:
  1. Create customized navigation classes. One class will provide the navigation model and the second will define the navigation item that provides us with the item title and URL.

    See ProductCategoryNavigationModel and ProductCategoryNavigationItem as examples. The getItems method returns the relevant nodes from the data repository. Constructor parameters for both classes depend on what you need for your implementation. In this particular case the parameter is specific to the shop, the shop name, so that we can create a correct link from the data repository.
  1. Create a template model class that instantiates the customized navigation and assigns the model class to our template. Doing it this way, you keep the default navigation for all site content that is not part of the shop.

    See ShopSingletonParagraphTemplateModel where the navigation is instantiated.

    public ProductCategoryNavigationModel getProductCategoryNavigation() {return new ProductCategoryNavigationModel(getCurrentShop());}
    As we are also customizing the breadcrumb, look at the getBreadcrumb method as well where items from the data repository are appended.

    Once we have the model class ready, we add it to our template configuration

  1. Freemarker templates.

    We optionally need a new verticalNavigation.ftl.
    The only change in vertical navigation is to call our custom navigation that we created with a new name:
    [@renderNavigation navigation=model.productCategoryNavigation /]
    And we set the template file in the site configuration of our custom template definition:

martes, 12 de julio de 2011

Adding extra styles files to an existing theme

The question raised when creating a new module: how can I add the styles files of my module to the pop theme?

This guide shows how to add the installation task and how to bootstrap the content, but if you don't want to code anything, at the end of the blog there is an explanation on how to do it directly on the author instance:

Step 1, Create the new styles file i.e. myextrastyles.css

Step 2, Put the file in the module resources folder following STK theme structure, src/main/resources and the path shown in the following screenshoot:




Step 3, Add an installation task on your module versionhandler.

protected List getExtraInstallTasks(InstallContext installContext) {
final List installTasks = new ArrayList();
installTasks.addAll(super.getExtraInstallTasks(installContext));
installTasks.add(new InstallResourcesTask("/templating-kit/themes/pop/css/mystyles.css", 
   "processedCss", STKResourceModel.class.getName()));


Step 4, Update the theme configuration. In order for the theme to find the new style file, the file has to be configured in the theme configuration.




Step 5, Export the configuration of our file shown in Step 4. Once exported you can place the file (config.modules.standard-templating-kit.config.themes.pop.cssFiles.myextrastyles.xml)
in the bootstrap folder of your module.

NOTE: All this can be achieved without any code, you just need to set the file in the theme configuration (Step 4) and upload this new styles file into the resources workspace.

jueves, 10 de marzo de 2011

Adding Flickr images to Magnolia CMS

This is a step-by-step guide on how to create your custom DAM and plug it into Magnolia CMS. Will allow you to add images from external services like Flickr, as well as adding images from other servers using their url (It comes with a forge module!)

This is a sample handle for external image/video urls, very basic as it does not add the imaging support for now.

* Requirements - Standard Templating Kit Module

1. The Asset

We need to create our own Asset class that will implement the interface Asset

If we want to have exact same behavior as in STK/ETK we will need to find a way to store 'meta data' (caption, description) of our assets, we could use the data module for that matter.

In this sample we wont store any metadata for the images, nor use the imaging support.

That leaves us with just one method to implement 'getLink' and this will be the value stored when we open the dialog to select an image.

public String getLink() throws DAMException {
return link;
}

Creation of link is on the class constructor, as easy as this

link = nodeData.getString();



2. The handler.

Should extend class AbstractHandler . We will extend from AbstractHandler that has the methods to read the configuration, that leaves us with two methods to implement as needed: getAsset and getAssetByKey. In this case we implement getAsset to return an instance of our Asset.

final String name = getNodeDataName(node, nodeDataPrefix);

Important thing to note here is that we need to get the name of the nodeData that has the url of our image. When you set an image using the dam controls, you name your node as i.e. 'myimage' and on saving you will get a nodeData with the name you gave plus the name of the dam control used. To make it simpler a call to the method getNodeDataName will give you the real name with which your node has been saved. In this sample it will contain the exact url of our image, no need of further processing.

final NodeData nodeData = node.getNodeData(name);

This line above gets the node data, now with the right name.

return new UrlAsset(nodeData);

Here is where we return our asset.

3. The controls (configuration).

Need to add the configuration for this external dam. For that we create a new node under our site configuration. There we put the handler we are going to use and the control/s in which we will store the image information. See image:




And to finish this post there is a module in Magnolia forge magnolia-module-urldam with the code to add an external dam handler.

jueves, 25 de febrero de 2010

From HTML to XML and beyond!

What happens if you need to deliver same content but in different format? Sure you don't want to duplicate content. Well Magnolia CMS provides a feature that with little work lets you deliver same content in any format you need, it could be xml, or even html but optimized for newsletters.

How to do this? You need to select a template definition and add sub templates like in the image below.



This sample is using Magnolia CMS version 3.6 (feature also available for 4.x) with just one sub template defined that will render as xml when I request the page as url.xml and rendered as html when requested as url.html.

Because at the moment, sub templates do not work for paragraphs, in the template file that generates the xml content we will have to iterate through the paragraphs.

In my sample I used jsp, so I iterated main collection within a scriplet, by first getting the active page and then its children:

Content actPage = Resource.getActivePage(request);
if(actPage.hasContent("mainColumnParagraphs")) {
//some code
}


When using freemarker there is no need to use java:

[#list content.main?children as para]
[#assign paragraphContent=para]
[#if para.metaData.template='mgnlTextBox']
[#include "textBox.ftl" ]
[/#if]
[/#list]


One of my use cases was to generate a newsletter template for the web and a simpler one that would be supported by the mail clients.

Auto generate paragraphs

By default Magnolia CMS STK comes with a mechanism to auto generate paragraphs in main and extras areas. This is very useful when you have the need of a paragraph to be present in all pages using some template. That paragraph could be modifiable or not and could be initialized with some default parameters or not.

If we have a look at the class SingletonParagraphTemplateModel, we can see that in the execute method has two method calls, one to generate a single paragraph in the main area (see stkSiteMap template) and the other one to generate any number of paragraphs in the extras area.

See below the template configuration for the main area auto generated sitemap paragraph



Nice thing is that you can extend this class or implement your own to generate any paragraph in any area of the page.

Below is the template configuration for the extras area auto generated paragraphs.