Page tree
Skip to end of metadata
Go to start of metadata

Out of date

This tutorial is for Blossom version 1. For later versions see the getting started guides for Blossom 2 or Blossom 3 in the official documentation.

 

 

This tutorial explains how to develop a module created from scratch using blossom to manage the templates and paragraphs.

It has been developed/tested with JDK6.0, Magnolia v4.1+, Blossom v1.1.1, Spring v3.0.4, Eclipse 3.6 (Helios)

This is an overview of the simple site we are going to create.

The complete project can be downloaded here.

Pre-requisites

Installation / Configuration

Installation of dependencies

Copy the following dependencies to the WEB-INF/lib/ folders of your Author and Public instances.

  • magnolia-module-blossom-1.1.1.jar
  • org.springframework.aop-3.0.4.RELEASE
  • org.springframework.asm-3.0.4.RELEASE.jar
  • org.springframework.beans-3.0.4.RELEASE.jar
  • org.springframework.context.support-3.0.4.RELEASE.jar
  • org.springframework.context-3.0.4.RELEASE.jar
  • org.springframework.core-3.0.4.RELEASE.jar
  • org.springframework.expression-3.0.4.RELEASE.jar
  • org.springframework.web.servlet-3.0.4.RELEASE.jar
  • org.springframework.web-3.0.4.RELEASE.jar

Download Links:

Configuration of web.xml

Edit the WEB-INF/web.xml file of both your Public and Author instances adding the following snippet. Make sure you put it before Magnolias context listener.

<listener>
  <listener-class>info.magnolia.module.blossom.support.ServletContextExposingContextListener</listener-class>
</listener>

New Project Creation

You can download the empty project here or follow the steps below to create it.

Maven Archetype

For more details, check this page

Module Name

In this example, the module name is set to hyro-magnolia-blossom. It can be replaced with another name.

Execute the following command to create the project skeleton:

mvn archetype:generate -DarchetypeCatalog=https://nexus.magnolia-cms.com/content/groups/public/

Select the magnolia-module-archetype archetype, and enter your groupId, artifactId and other properties as prompted.

Import your new project in Eclipse: File > Import... > Maven > Existing Maven Projects

Clean project skeleton

Edit the module descriptor to match this one (src/main/resources/META-INF/magnolia/hyro-magnolia-blossom.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module SYSTEM "module.dtd" >
<module>
	<name>${project.artifactId}</name>
	<displayName>${project.name}</displayName>
	<description>${project.description}</description>
	<class>com.hyro.magnolia.blossom.core.ModuleCore</class>
	<versionHandler>com.hyro.magnolia.blossom.core.ModuleVersionHandler</versionHandler>
	<version>${project.version}</version>

	<dependencies>
		<dependency>
			<name>core</name>
			<version>4.1/*</version>
		</dependency>
	</dependencies>

</module>

Maven properties

Thanks to the maven properties plugin, we can reuse here the properties defined in the pom.xml file instead of hardcoding them.

2 - Module Bootstrap:

Rename the folder src/main/resources/mgnl-bootstrap/mymodule to hyro-magnolia-blossom and remove the two xml files located there.

3 - Module Bootstrap Samples:

Rename the folder src/main/resources/mgnl-bootstrap-samples/mymodule to hyro-magnolia-blossom and remove the xml files located there.

4 - Module Resource:

Clean the folder src/main/resources/mgnl-resources removing its content

5 - Templates:

Rename the folder src/main/resources/mgnl-files/templates/mymodule to hyro-magnolia-blossom. Remove the existing folders in there and create two folders templates and paragraphs.

Update project POM

Replace the content of the pom.xml file with this one.

Module Core and Version handler

Add the two following classes to the sources of your project

ModuleCore.java
package com.hyro.magnolia.blossom.core;

import info.magnolia.module.ModuleLifecycle;
import info.magnolia.module.ModuleLifecycleContext;
import info.magnolia.module.blossom.module.BlossomModuleSupport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Core class of the Magnolia and Blossom Module.<br/>
 * Starts the Spring Context using Blossom.
 *
 * @author bruno.chauvet
 *
 */
public class ModuleCore extends BlossomModuleSupport implements ModuleLifecycle {
    private static final Logger LOGGER = LoggerFactory.getLogger(ModuleCore.class);
    private static ModuleCore instance;

    /**
     * Initialise the instance variable
     */
    public ModuleCore() {
        ModuleCore.instance = this;
    }

    /**
     * Return the singleton instance
     *
     * @return
     */
    public static ModuleCore getInstance() {
        return ModuleCore.instance;
    }

    /**
     * Starts the Spring/Blossom context.
     */
    public void start(ModuleLifecycleContext moduleLifecycleContext) {
        ModuleCore.LOGGER.info(this.getClass().getSimpleName() + " is starting");
        super.initRootWebApplicationContext("classpath:/applicationContext.xml");
        super.initBlossomDispatcherServlet("blossom", "classpath:/blossom-servlet.xml");
    }

    /**
     * Stops the Spring/Blossom context.
     */
    public void stop(ModuleLifecycleContext moduleLifecycleContext) {
        ModuleCore.LOGGER.info(this.getClass().getSimpleName() + " is stopping");
        super.destroyDispatcherServlets();
        super.closeRootWebApplicationContext();
    }
}

Spring configuration files

The Spring configuration files to use are defined here: applicationContext.xml and blossom-servlet.xml.
The content of these files is detailed below.

ModuleVersionHandler.java
package com.hyro.magnolia.blossom.core;

import info.magnolia.module.DefaultModuleVersionHandler;
import info.magnolia.module.InstallContext;
import info.magnolia.module.delta.DeltaBuilder;
import info.magnolia.module.delta.ModuleBootstrapTask;
import info.magnolia.module.delta.ModuleFilesExtraction;
import info.magnolia.module.delta.Task;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class instructs Magnolia on what to do when this module is installed or
 * updated.
 *
 * @author bruno.chauvet
 *
 */
public class ModuleVersionHandler extends DefaultModuleVersionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ModuleVersionHandler.class);
    private static final String PROPERTY_FILE = "application.properties";

    /**
     * ModuleVersionHandler Constructor.
     *
     * Register any tasks in here that need to happen as part of an update. All
     * though the constructor here will always be invoked, it will only ever
     * have an effect for updates (not new installs) because the DeltaBuilder is
     * adding "update" tasks. By default new installs are setup correctly
     * through inherited behaviour. However, if you need to perform additional
     * non default tasks as part of a new install the way this needs to be done
     * is by overriding the getExtraInstallTasks() method and adding additional
     * non-default tasks in here.
     *
     * @see <a
     *      href="[WIKI|Handling module versions]">
     *          http://wiki.magnolia-cms.com/display/WIKI/Handling+module+versions
     *      </a>
     *      for more info on module versioning
     *
     */
    public ModuleVersionHandler() {
        // Get the module version from the properties
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTY_FILE);
        Properties properties = new Properties();
        try {
            properties.load(is);
        } catch (IOException e) {
            ModuleVersionHandler.LOGGER.error("Cannot load property file: " + PROPERTY_FILE, e);
        }

        // Register the module using version specified in the properties.
        super.register(DeltaBuilder.update(properties.getProperty("Application.version"), "Common tasks").addTasks(
                getCommonTasks()));
    }

    /**
     * Overwrites the list of tasks to perform during module installation.
     */
    @Override
    protected List<Task> getExtraInstallTasks(InstallContext installContext) {
        return this.getCommonTasks();
    }

    /**
     * Return the common task for every module update.
     *
     * @return
     */
    private List<Task> getCommonTasks() {
        final List<Task> commonTasks = new ArrayList<Task>();

        // Installs all files from
        // /src/main/resources/mgnl-bootstrap
        commonTasks.add(new ModuleBootstrapTask());

        // Extracts all files from
        // /src/main/resources/mgnl-files
        commonTasks.add(new ModuleFilesExtraction());

        return commonTasks;
    }
}

Application Version

The module version is dynamically retrieved from the application properties files

Properties file and Context configuration

Copy the following file to src/main/resources

application.properties
Application.name=${project.artifactId}
Application.version=${project.version}

This file is used by Maven to copy the project properties during building phase and set the properties during installation by the VersionHandler class.

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:blossom="http://www.magnolia-cms.com/schema/blossom"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.magnolia-cms.com/schema/blossom http://www.magnolia-cms.com/schema/blossom-1.1.xsd">

	<blossom:configuration />
</beans>
blossom-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
	   	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<context:annotation-config />

	<context:component-scan base-package="com.hyro.magnolia.blossom.controller" />

	<bean class="info.magnolia.module.blossom.preexecution.BlossomHandlerMapping">
		<property name="targetHandlerMappings">
			<list>
				<bean
					class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
					<property name="useDefaultSuffixPattern" value="false" />
				</bean>
				<bean
					class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
			</list>
		</property>
	</bean>

	<bean class="info.magnolia.module.blossom.view.TemplateViewResolver">
		<property name="prefix" value="/templates/${project.artifactId}/templates/" />
		<property name="suffix" value=".jsp" />
		<property name="viewRenderer">
			<bean class="info.magnolia.module.blossom.view.JspTemplateViewRenderer" />
		</property>
	</bean>

	<bean class="info.magnolia.module.blossom.view.ParagraphViewResolver">
		<property name="prefix" value="/templates/${project.artifactId}/paragraphs/" />
		<property name="suffix" value=".jsp" />
		<property name="viewRenderer">
			<bean class="info.magnolia.module.blossom.view.JspParagraphViewRenderer" />
		</property>
	</bean>
</beans>

JSP Paths

The Templates and Paragraphs JSP paths are defined here in Spring Configuration view resolvers.

The project structure should look like this:

At this stage, the project can be built using maven and deployed in your magnolia instance.

Styling resources

Copy the static resources to the folder src/main/resources/mgnl-files. This contains the CSS and images used to display nicely the web site.

Template Creation using Blossom

JSP Template

Copy the templates contained in this archive in the folder src/main/resources/mgnl-files/templates/hyro-magnolia-blossom/templates.

Basically, the main template jsp named mainTemplate.jsp includes a header, content and footer and some content managed properties:

  • Header: Content managed list of header paragraphs
  • Content: Content managed list of simple and nested paragraphs
  • Footer: Static footer

Blossom Template class

Copy the following class to the sources of the project

MainTemplate.java
package com.hyro.magnolia.blossom.controller.template;

import info.magnolia.module.blossom.annotation.DialogFactory;
import info.magnolia.module.blossom.annotation.Template;
import info.magnolia.module.blossom.dialog.DialogBuilder;
import info.magnolia.module.blossom.dialog.TabBuilder;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller defining the Main Template.
 *
 * @author bruno.chauvet
 *
 */
@Template(name = "main-template", value = "Main Template", description = "Main Template example with Blossom", visible = true)
@Controller
public class MainTemplate {
    /**
     * Process the requests on URL /mainTemplate
     *
     * @return
     */
    @RequestMapping("/mainTemplate")
    public ModelAndView homeTemplate() {
        return new ModelAndView();
    }

    /**
     * Dialog factory associated to the Main page settings.
     *
     * @param dialog
     */
    @DialogFactory("main-dialog")
    public void homeDialog(DialogBuilder dialog) {
        TabBuilder settings = dialog.addTab("Main page settings");
        settings.addEdit("title", "Title", "The HTML page title");
        settings.addEdit("metaDescription", "Meta Description", "HTML Meta Description of the web site");
        settings.addEdit("metaKeywords", "Meta Keywords", "HTML Meta Keywords of the web site");
    }
}

Template Details

  • @Controller: Spring annotation to automatically deploy the class as a Controller
  • @Template: Blossom annotation to automatically register the template in Magnolia
  • @RequestMapping("/mainTemplate"): Spring annotation to map the URL /mainTemplate to src/main/resources/mgnl-files/templates/hyro-magnolia-blossom/templates/mainTemplate.jsp
  • @DialogFactory("main-dialog"): Blossom annotation to associate a dialog box to manage the properties of the template. Refered in the main template with:

    <cms:mainBar dialog="main-dialog" label="Main page properties"
    		adminButtonVisible="true" />
    

Paragraph Creation using Blossom

JSP Paragraphs

Copy the templates containes in this archive in the folder src/main/resources/mgnl-files/templates/hyro-magnolia-blossom/paragraphs.

These are the JSP pages used to display the paragraphs that we are going to create using Blossom

Blossom Paragraph classes

Header Paragraph

Copy the following class to your sources:

HeaderParagraph.java
package com.hyro.magnolia.blossom.controller.paragraph;
import info.magnolia.module.blossom.annotation.DialogFactory;
import info.magnolia.module.blossom.annotation.Paragraph;
import info.magnolia.module.blossom.annotation.ParagraphDescription;
import info.magnolia.module.blossom.annotation.TabFactory;
import info.magnolia.module.blossom.dialog.TabBuilder;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller defining the Header paragraph and dialog.
 *
 * @author bruno.chauvet
 *
 */
@Paragraph(name = "headerParagraph", value = "Header Paragraph", dialog = "header-dialog")
@ParagraphDescription("Header Paragraph")
@DialogFactory("header-dialog")
@Controller
public class HeaderParagraph {
    /**
     * Process the requests on URL /headerParagraph
     *
     * @return
     */
    @RequestMapping("/headerParagraph")
    public ModelAndView handleRequest() {
        return new ModelAndView();
    }

    /**
     * Tabs of the Header Paragraph.
     *
     * @param tab
     */
    @TabFactory("Header")
    public void content(TabBuilder tab) {
        tab.addStatic("Header paragraph");
        // Logo image
        tab.addFile("logo", "Logo", "Logo Image (about 120 x 100 px)");
        // Header text
        tab.addEdit("text", "Header Text", "Header Text (about 60 characters)");
    }
}

Paragraph Details

  • @Controller: Spring annotation to automatically deploy the class as a Controller
  • @Paragraph: Blossom annotation to automatically register the paragraph in Magnolia
    • name = "headerParagraph": Reference used in the <cms:newBar/> tags
    • dialog = "header-dialog": Reference to link the dialog box to use when creating/editing a paragraph
  • @RequestMapping("/headerParagraph"): Spring annotation to map the URL /headerParagraph to src/main/resources/mgnl-files/templates/hyro-magnolia-blossom/paragraphs/headerParagraph.jsp
  • @DialogFactory("header-dialog"): Blossom annotation to associate a dialog box to manage the content of the paragraph.

In the Header Template (/templates/common/header.jsp):

<!-- Display Headers paragraphs -->
<cms:contentNodeIterator contentNodeCollectionName="HeaderParagraphs">
	<cms:includeTemplate />
</cms:contentNodeIterator>

<!-- Admin bar to add a new Header paragraph -->
<cms:newBar contentNodeCollectionName="HeaderParagraphs"
	paragraph="headerParagraph" newLabel="New header paragraph" />

In the Header Paragraph (/paragraphs/headerParagraph.jsp):

<!-- Edit Header paragraph -->
<cms:editBar editLabel="Edit Header" />

<!-- Header paragraph content -->
<div class="header">
	<div class="headerLeft">
		<img id="logo" width="120px"
			height="100px" src="${pageContext.request.contextPath}${content.logo}" />
	</div>
	<div class="headerCenter">
		<span class="header">${content.text}</span>
	</div>
</div>
Simple Content Paragraph

Copy the following class to your sources:

SimpleParagraph.java
package com.hyro.magnolia.blossom.controller.paragraph;

import info.magnolia.cms.gui.dialog.DialogEdit;
import info.magnolia.cms.gui.dialog.DialogTab;
import info.magnolia.cms.util.AlertUtil;
import info.magnolia.module.blossom.annotation.DialogFactory;
import info.magnolia.module.blossom.annotation.Paragraph;
import info.magnolia.module.blossom.annotation.ParagraphDescription;
import info.magnolia.module.blossom.annotation.TabFactory;
import info.magnolia.module.blossom.annotation.TabOrder;
import info.magnolia.module.blossom.annotation.TabValidator;
import info.magnolia.module.blossom.dialog.TabBuilder;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller defining a Simple Paragraph.
 *
 * @author bruno.chauvet
 *
 */
@Paragraph(name = "simpleParagraph", value = "Simple Paragraph", dialog = "simple-dialog")
@ParagraphDescription("Simple Paragraph with title, text, description, image and link")
@DialogFactory("simple-dialog")
@TabOrder({ "Content", "Description" })
@Controller
public class SimpleParagraph {
    /**
     * Process the requests on URL /simpleParagraph
     *
     * @return
     */
    @RequestMapping("/simpleParagraph")
    public ModelAndView handleRequest() {
        return new ModelAndView();
    }

    /**
     * First Dialog Tab.
     *
     * @param tab
     */
    @TabFactory("Content")
    public void content(TabBuilder tab) {
        tab.addStatic("Title, image and link of the paragraph");
        tab.addEdit("title", "Title", "");
        tab.addFile("image", "Image", "Image 100 x 70 px");
        tab.addEdit("linkText", "Link Text", "");
        tab.addLink("link", "Link", "");
    }

    /**
     * Second Dialog Tab.
     *
     * @param tab
     */
    @TabFactory("Description")
    public void description(TabBuilder tab) {
        tab.addStatic("Description of the paragraph");
        tab.addFckEditor("description", "Description", "Rich text editor");
    }

    /**
     * First Tab Validator.
     *
     * @param dialogTab
     */
    @TabValidator("Content")
    public void validate(DialogTab dialogTab) {
        DialogEdit title = (DialogEdit) dialogTab.getSub("title");
        if (StringUtils.isEmpty(title.getValue())) {
            AlertUtil.setMessage("You need to enter a title !");
        }
    }
}

Paragraph Details

  • @Controller: Spring annotation to automatically deploy the class as a Controller
  • @Paragraph: Blossom annotation to automatically register the paragraph in Magnolia
    • name = "simpleParagraph": Reference used in the <cms:newBar/> tags
    • dialog = "simple-dialog": Reference to link the dialog box to use when creating/editing a paragraph
  • @RequestMapping("/simpleParagraph"): Spring annotation to map the URL /simpleParagraph to src/main/resources/mgnl-files/templates/hyro-magnolia-blossom/paragraphs/simpleParagraph.jsp
  • @DialogFactory(simple-dialog"): Blossom annotation to associate a dialog box to manage the content of the paragraph.
  • @TabOrder({"Content", "Description"}): Blossom annotation to define the order of the dialog box tabs
  • @TabFactory("Content") and @TabFactory("Description"): Blossom annotation to create a tab and its associated properties in a dialog box

In the Content Template (/templates/common/content.jsp):

<!-- Content Paragraphs-->
<cms:contentNodeIterator contentNodeCollectionName="ContentParagraphs">
	<cms:includeTemplate />
</cms:contentNodeIterator>

<cms:newBar contentNodeCollectionName="ContentParagraphs"
	paragraph="simpleParagraph"
	newLabel="New Content Paragraph" />

In the Simple Content Paragraph (/paragraphs/simpleParagraph.jsp):

<div class="simple">
	<div style="float:right;">
		<img width="76px" src="${pageContext.request.contextPath}${content.image}" />
	</div>
	<h2>${content.title}</h2>
	<span class="description">${content.description}</span>
	<a href="${content.link}">${content.linkText}</a>
	<cms:editBar editLabel="Edit Paragraph" />
</div>
Nested Content Paragraph

Copy the following classes to your sources:

NestedParagraph.java
package com.hyro.magnolia.blossom.controller.paragraph;

import info.magnolia.cms.gui.dialog.DialogEdit;
import info.magnolia.cms.gui.dialog.DialogTab;
import info.magnolia.cms.util.AlertUtil;
import info.magnolia.module.blossom.annotation.DialogFactory;
import info.magnolia.module.blossom.annotation.Paragraph;
import info.magnolia.module.blossom.annotation.ParagraphDescription;
import info.magnolia.module.blossom.annotation.TabFactory;
import info.magnolia.module.blossom.annotation.TabValidator;
import info.magnolia.module.blossom.dialog.TabBuilder;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller defining a Nested Paragraph.
 *
 * @author bruno.chauvet
 *
 */
@Paragraph(name = "nestedParagraph", value = "Nested Paragraph", dialog = "nested-dialog")
@ParagraphDescription("Nested Paragraph with a title and a list of links")
@DialogFactory("nested-dialog")
@Controller
public class NestedParagraph {
    @RequestMapping("/nestedParagraph")
    public ModelAndView handleRequest() {
        return new ModelAndView();
    }

    @TabFactory("Content")
    public void content(TabBuilder tab) {
        tab.addStatic("Title, Title of the paragraph");
        tab.addEdit("title", "Title", "Main title");
    }

    @TabValidator("Content")
    public void validate(DialogTab dialogTab) {
        DialogEdit title = (DialogEdit) dialogTab.getSub("title");
        if (StringUtils.isEmpty(title.getValue())) {
            AlertUtil.setMessage("You need to enter a title !");
        }
    }
}
NestedSubParagraph.java
package com.hyro.magnolia.blossom.controller.paragraph;

import info.magnolia.cms.gui.dialog.DialogEdit;
import info.magnolia.cms.gui.dialog.DialogTab;
import info.magnolia.cms.util.AlertUtil;
import info.magnolia.module.blossom.annotation.DialogFactory;
import info.magnolia.module.blossom.annotation.Paragraph;
import info.magnolia.module.blossom.annotation.ParagraphDescription;
import info.magnolia.module.blossom.annotation.TabFactory;
import info.magnolia.module.blossom.annotation.TabValidator;
import info.magnolia.module.blossom.dialog.TabBuilder;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller defining a Nested SubParagraph.
 *
 * @author bruno.chauvet
 *
 */
@Paragraph(name = "nestedSubParagraph", value = "Nested SubParagraph", dialog = "nested-sub-dialog")
@ParagraphDescription("Nested SubParagraph with title and links")
@DialogFactory("nested-sub-dialog")
@Controller
public class NestedSubParagraph {
    @RequestMapping("/nestedSubParagraph")
    public ModelAndView handleRequest() {
        return new ModelAndView();
    }

    @TabFactory("Content")
    public void content(TabBuilder tab) {
        tab.addStatic("Title, Title of the paragraph");
        tab.addEdit("text", "Text", "Text");
        tab.addLink("link", "Link", "Link");
    }

    @TabValidator("Content")
    public void validate(DialogTab dialogTab) {
        DialogEdit text = (DialogEdit) dialogTab.getSub("text");
        if (StringUtils.isEmpty(text.getValue())) {
            AlertUtil.setMessage("You need to enter a text !");
        }
    }
}

You can find here the usual Spring and Blossom annotations.

Regarding the paragraphs JSP:

nestedParagraph.jsp
<div class="nested">
<h2>${content.title}</h2>

<!-- Counter to display subparagraphs on two columns -->
<c:set var="index" value="1" scope="request" />

<ul class="nested">
	<cms:contentNodeIterator
		contentNodeCollectionName="NestedSubParagraphs">
		<cms:includeTemplate />
	</cms:contentNodeIterator>
</ul>

<cms:newBar contentNodeCollectionName="NestedSubParagraphs"
	paragraph="nestedSubParagraph" newLabel="New Link" />

<cms:editBar editLabel="Edit Paragraph" />
</div>
nestedSubParagraph.jsp
<!-- Start a new list when half of the subparagraphs have been displayed -->
<c:if test="${index > (size/2)}">
	<c:set var="index" value="1" scope="request" />
	</ul>
	<ul class="nested">
</c:if>

<li><a href="${content.link}">${content.text}</a> <cms:editBar
	editLabel="Edit Link" /></li>

<c:set var="index" value="${index + 1}" scope="request" />

At this point, you can package and deploy your module in magnolia. You will then be able to create a new page using the Main Template we created and start adding content in it.

6 Comments

  1. Any comments are welcome

    1. Thanks for this contribution, nice work!

      I have a few notes and suggestions,

      • I see you used the archetype, its sadly a bit outdated. The new maven repository is http://nexus.magnolia-cms.com/content/repositories/magnolia.public.releases/.
      • Also, you're annotating your paragraphs both by setting dialog="" and adding @DialogFactory, this isnt necessary. The paragraph is in itself a dialog factory. Think of it as having an anonymous(unnamed) dialog created by its tab factories.
      • The last code block should probably be named nestedSubParagraph.jsp.
      • You don't need to download and add the spring and blossom jars if you're using maven.
      • The version handler might be a bit too complicated for a tutorial type article.

      Again, nice work! (smile)

      1. The archetype has now been updated - see Module QuickStart for instructions

      2. Please use https to access nexus from now on since http access will shortly be disabled.

  2. How about we create a blossom-specific archetype ? Would remove a lot of manual work that seems to be needed when following this article. Ping me if you need help to do this !

  3. That would be awesome if you can post an updated fully working example with blossom 2.0 and magnolia 4.5.x.