Blossom Q&A Webinar

Post on 29-Nov-2014

5.325 views 1 download

description

Webinar with Tobias Mattsson, Lead developer of Blossom, the Spring integration for Magnolia CMS. Tobias answers submitted questions and gives a short introduction to the module and outlines what's new in the 3.0 update.

Transcript of Blossom Q&A Webinar

Blossom Q&A Webinar

1Monday, September 30, 13

2

Zak GreantCommunity Manager, Magnolia

Monday, September 30, 13

Questions?Use the questions toolbuilt into the GoToWebinarclient.

3Monday, September 30, 13

4

forums.magnolia-cms.com

Monday, September 30, 13

#blossomqa

5Monday, September 30, 13

Sr. Software Engineer, MagnoliaLead developer of Magnolia’s Spring integration

Spring Framework user since 2005

Tobias Mattsson

6Monday, September 30, 13

Blossom 3.0

7

Update for Magnolia 5 seriesRequires Magnolia 5.1Release candidate availableFinal release next week

Monday, September 30, 13

Magnolia + Spring = Blossom 8Monday, September 30, 13

@Template

9Monday, September 30, 13

TEMPLATEREQUEST CONTENT

CMS

10Monday, September 30, 13

TEMPLATEREQUEST CONTENT

CMS + Blossom

11

CONTROLLER

MODEL

VIEW

Monday, September 30, 13

Page Template

@Controller

@Template(id="myModule:pages/main", title="Main")public class MainTemplate {

   @RequestMapping("/main")

   public String render(ModelMap model) {

       return "pages/main";

   }

}

12Monday, September 30, 13

13

PAGE

Monday, September 30, 13

PAGES CONTAIN 0:n AREAS

14

PAGE

AREA

AREA

AREA

Monday, September 30, 13

PAGES CONTAIN 0:n AREAS

15

PAGE

AREA

AREA

AREA

AREAS HAVE 0:n COMPONENTS

COMPONENT

COMPONENTEtiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.

COMPONENTEtiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed posuere consectetur est at lobortis.

COMPONENT

Monday, September 30, 13

Area Template

@Controller

@Template(id="myModule:pages/main",title="Main Template")

public class MainTemplate {

   @Controller

   @Area("main")    public static class MainArea {

       @RequestMapping("/main/mainArea")

       public String render() {

           return "areas/main";

       }

   ...

16Monday, September 30, 13

Component Template

@Controller

@Template(id="myModule:components/shoppingCart",          title="Shopping Cart")@TemplateDescription("Shopping cart")

public class ShoppingCartComponent {

   @RequestMapping("/shoppingCart")

   public String handleRequest() {

       ...

       return "components/shoppingCart";

   }

...

17Monday, September 30, 13

SERVLETCONTAINER

RENDERINGFILTER

RENDERINGENGINE

BLOSSOM DISPATCHERSERVLET CONTROLLER

18

How Blossom Works

Monday, September 30, 13

Blossom 3.0

19

What’s new?

Monday, September 30, 13

20

Dialogs

Monday, September 30, 13

Fluent Builder-style API

@TabFactory("Content")public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields( cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body"), cfg.fields.websiteLink("categoryLink").label("Link"), cfg.fields.basicUpload("image").label("Image"), cfg.fields.checkbox("inlineImage").label("Inline Image")  );}

21Monday, September 30, 13

Blossom 2 Style Dialog

@TabFactory("Content")

public void contentTab(TabBuilder tab) {

DialogEdit dialogEdit = tab.addEdit("title", "Title", "");

dialogEdit.setRequired(true);

dialogEdit.setConfig("i18n", "true");

dialogEdit.setConfig("rows", 5);

}

22Monday, September 30, 13

Page Template with Dialog

@Controller

@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

   @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {

       tab.fields(cfg.fields.text("title").label("Title"));    }

}

23Monday, September 30, 13

Page Template with Dialog

@Controller

@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

   @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {

       tab.fields(cfg.fields.text("title").label("Title"));    }

}

23Monday, September 30, 13

Page Template with Dialog

@Controller

@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

   @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {

       tab.fields(cfg.fields.text("title").label("Title"));    }

} <title>${content.title}</title><!-- In the view -->

23Monday, September 30, 13

@Controller@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

@Area("main") @Controller public static class MainArea {

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.text("border").label("Border width") );

Area Template with Dialog 24Monday, September 30, 13

@Controller@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

@Area("main") @Controller public static class MainArea {

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.text("border").label("Border width") );

Area Template with Dialog 24Monday, September 30, 13

@Controller@Template(title="Article", id="myModule:pages/article")public class ArticleTemplate {

@Area("main") @Controller public static class MainArea {

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.text("border").label("Border width") );

Area Template with Dialog 24

<div id="main" style="border:${content.border}px solid #000"> <h2>${content.heading}</h2> <c:forEach items="${components}" var="component"> <cms:component content="${component}" /> </c:forEach></div> <!-- In the view -->

Monday, September 30, 13

Component Template with Dialog

@Controller

@Template(title="Text", id="myModule:components/text")

public class TextComponent {

@RequestMapping("/text")

public String render() {

return "components/text";

}

@TabFactory("Content")

public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body")

25Monday, September 30, 13

Component Template with Dialog

@Controller

@Template(title="Text", id="myModule:components/text")

public class TextComponent {

@RequestMapping("/text")

public String render() {

return "components/text";

}

@TabFactory("Content")

public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body")

25Monday, September 30, 13

Component Template with Dialog

@Controller

@Template(title="Text", id="myModule:components/text")

public class TextComponent {

@RequestMapping("/text")

public String render() {

return "components/text";

}

@TabFactory("Content")

public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("Heading"), cfg.fields.richText("body").label("Text body")

25

<h1>${content.heading}</h1><p>${cmsfn:decode(content).body}</p><!-- In the view -->

Monday, September 30, 13

YouTube Video Component

@Controller@Template(title="YouTube Video", id="myModule:components/youtube")@TemplateDescription("Embed a YouTube video")

public class YoutubeComponent {

@RequestMapping("/youtube") public String render(Node node, ModelMap model) throws RepositoryException { model.put("videoId", node.getProperty("videoId").getString()); return "components/youtube"; }

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("videoId").label("Video ID") ); }}

26Monday, September 30, 13

YouTube Video Component

@Controller@Template(title="YouTube Video", id="myModule:components/youtube")@TemplateDescription("Embed a YouTube video")

public class YoutubeComponent {

@RequestMapping("/youtube") public String render(Node node, ModelMap model) throws RepositoryException { model.put("videoId", node.getProperty("videoId").getString()); return "components/youtube"; }

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("videoId").label("Video ID") ); }}

26Monday, September 30, 13

YouTube Video Component

@Controller@Template(title="YouTube Video", id="myModule:components/youtube")@TemplateDescription("Embed a YouTube video")

public class YoutubeComponent {

@RequestMapping("/youtube") public String render(Node node, ModelMap model) throws RepositoryException { model.put("videoId", node.getProperty("videoId").getString()); return "components/youtube"; }

@TabFactory("Content") public void contentTab(UiConfig cfg, TabBuilder tab) { tab.fields( cfg.fields.text("videoId").label("Video ID") ); }}

26

<iframe width="100%" height="400" src="//www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen></iframe><!-- In the view -->

Monday, September 30, 13

Dialogs and the Class Hierarchy

public abstract class BasePageTemplate { @TabFactory("Meta") public void metaTab(UiConfig cfg, TabBuilder tab) { tab.fields(

cfg.fields.text("metaAuthor").label("Author"),     cfg.fields.text("metaKeywords").label("Keywords"),     cfg.fields.text("metaDescription").label("Description")    );  }}

27Monday, September 30, 13

Input Validation

...

public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("name").label("Name").required(), cfg.fields.text("email").label("Email")⏎ .validator(cfg.validators.email()) );

}

...

28Monday, September 30, 13

Dynamic Dialog

@Template(id="myModule:components/bookCategory", title="Book category")

@TemplateDescription("A list of books in a given category.")@Controllerpublic class BookCategoryComponent {    @Autowired    private SalesApplicationWebService service;

   @RequestMapping("/bookcategory")    public String render(Node content, ModelMap model) throws RepositoryException {        String category = content.getProperty("category").getString();        model.put("books", service.getBooksInCategory(category));        return "components/bookCategory";    } @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        Collection<String> categories = service.getBookCategories();        tab.fields( cfg.fields.select("category").label("Category").options(categories)        );    }}

29Monday, September 30, 13

Dynamic Dialog

@Template(id="myModule:components/bookCategory", title="Book category")@TemplateDescription("A list of books in a given category.")

@Controllerpublic class BookCategoryComponent {    @Autowired    private SalesApplicationWebService service;

   @RequestMapping("/bookcategory")    public String render(Node content, ModelMap model) throws RepositoryException {        String category = content.getProperty("category").getString();        model.put("books", service.getBooksInCategory(category));        return "components/bookCategory";    } @TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        Collection<String> categories = service.getBookCategories();        tab.fields( cfg.fields.select("category").label("Category").options(categories)        );    }}

30Monday, September 30, 13

Dynamic Dialog

@Template(id="myModule:components/bookCategory", title="Book category")

@TemplateDescription("A list of books in a given category.")@Controllerpublic class BookCategoryComponent {    @Autowired    private SalesApplicationWebService service;

   @RequestMapping("/bookcategory")

   public String render(Node content, ModelMap model) throws RepositoryException {        String category = content.getProperty("category").getString();        model.put("books", service.getBooksInCategory(category));        return "components/bookCategory";    }

@TabFactory("Content")    public void contentTab(UiConfig cfg, TabBuilder tab) {        Collection<String> categories = service.getBookCategories();        tab.fields( cfg.fields.select("category").label("Category").options(categories)        );    }}

31Monday, September 30, 13

Questions

32Monday, September 30, 13

How do I get started on my project?

33Monday, September 30, 13

mvn archetype:generate -DarchetypeCatalog=http://nexus.magnolia-cms.com/

content/groups/public/

choose magnolia-blossom-module-archetype

Define value for property 'groupId': : com.acme

Define value for property 'artifactId': : acme-module

Define value for property 'version': 1.0-SNAPSHOT:

Define value for property 'package': com.acme:

Define value for property 'magnolia-version': : 4.5.11

Define value for property 'module-class-name': : AcmeModule

Define value for property 'module-name': acme-module: acmeModule

http://wiki.magnolia-cms.com/display/WIKI/Creating+a+new+Blossom+project+using+maven+archetypes

Monday, September 30, 13

How would I build a REST interface to my CMS content using Blossom?

35Monday, September 30, 13

REST servlet in module descriptor

<servlets>

  <servlet>

    <name>rest</name>

    <class>org.springframework.web.servlet.DispatcherServlet</class>

    <mappings>

      <mapping>/rest/*</mapping>

    </mappings>

    <params>

      <param>

        <name>contextConfigLocation</name>

        <value>classpath:/rest-servlet.xml</value>

      </param>

    </params>

  </servlet>

</servlets>

36Monday, September 30, 13

REST Controller

@Controller

public class ProductsRestController {

@RequestMapping("/products/{productId}")

public Product findProduct(@PathVariable String productId) {

// implementation omitted

}

}

37Monday, September 30, 13

Can I leverage Magnolia's caching functionality to improve the performance of my Spring app?

38Monday, September 30, 13

Influencing caching 39

@Controller

@Template(title="Text", id="myModule:components/text")

public class TextComponent {

@RequestMapping("/text")

public String render(HttpResponse response) {

response.setHeader("Cache-Control", "no-cache"); return "components/text.jsp";

}

}

Monday, September 30, 13

How can I present dialogs in different languages using Blossom?

40Monday, September 30, 13

@Controller

@Template(title = "Text", id = "blossomSampleModule:components/text")

@I18nBasename("info.magnolia.blossom.sample.messages")

public class TextComponent {

@TabFactory("textComponent.contentTab.label")

public void contentTab(UiConfig cfg, TabBuilder tab) {

tab.fields(

cfg.fields.text("heading").label("textComponent.contentTab.heading")

);

}

}

41I18n Blossom DialogMonday, September 30, 13

/info/magnolia/blossom/sample/messages_en.properties

textComponent.contentTab.label = Content

textComponent.contentTab.heading = Heading

/info/magnolia/blossom/sample/messages_sv.properties

textComponent.contentTab.label = Innehåll

textComponent.contentTab.heading = Rubrik

42I18n Blossom DialogMonday, September 30, 13

How do I migrate my existing site to Magnolia/Blossom?

43Monday, September 30, 13

How can I integrate Spring Security?

44Monday, September 30, 13

45Monday, September 30, 13

How can I dependency inject spring beans into RenderingModels?

46Monday, September 30, 13

Autowired RenderingModel

public class MyRenderingModel extends RenderingModelImpl<TemplateDefinition> {

@Autowired

private MyService service;

public MyRenderingModel(ServletContext servletContext, Node content, TemplateDefinition definition, RenderingModel<?> parent) {

super(content, definition, parent);

WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(servletContext);

AutowireCapableBeanFactory beanFactory = wac.getAutowireCapableBeanFactory();

beanFactory.autowireBean(this);

}

47Monday, September 30, 13

Autowired RenderingModel

public class MyRenderingModel extends AbstractAutowiredRenderingModel<TemplateDefinition> {

@Autowired

private MyService service;

public MyRenderingModel(ServletContext servletContext, Node content, TemplateDefinition definition, RenderingModel<?> parent) {

super(content, definition, parent, servletContext);

}

48Monday, September 30, 13

More questions?

49Monday, September 30, 13

Magnolia + Spring = Blossom 50Monday, September 30, 13

Thank You!

51Monday, September 30, 13

52

magnolia-cms.com/spring

Monday, September 30, 13