View
821
Download
0
Category
Tags:
Preview:
DESCRIPTION
This presentation was given at Amplify Miami 2014 by Jan Haderka, Senior Developer at Magnolia International, and Casey Dement, VP of Architecture at Sharecare, Inc. Casey showed how Sharecare integrated Magnolia and Blossom with Spring MVC and Akamai to build a highly flexible and scalable website. Jan gives an introduction and an update on the new features in Blossom 3.
Citation preview
!1
Blossom in the Real World
Sr. Software Engineer, Magnolia Lead developer of Blossom Module Spring Framework user since 2005
Tobias Mattsson
2@sigget
3
Jan HaderkaHead of Support, Magnolia
@rah003
4
Casey DementVP of Architecture, Sharecare
@casey_dement
#Mplify
5
Magnolia + Spring = Blossom 6
@Template
7
TEMPLATEREQUEST CONTENT
CMS
8
TEMPLATEREQUEST CONTENT
CMS + Blossom
9
CONTROLLER
MODEL
VIEW
Page Template
@Controller @Template(id="myModule:pages/main", title="Main") public class MainTemplate { !
@RequestMapping("/main") public String render(ModelMap model) { return "pages/main"; } }
10
11
PAGE
PAGES CONTAIN 0:n AREAS
12
PAGE
AREA
A R E A
AREA
PAGES CONTAIN 0:n AREAS
13
PAGE
AREA
A R E A
AREA
AREAS HAVE 0:n COMPONENTS
COMPONENT
COMPONENT Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
COMPONENT Etiam 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.
C O M P O N E N T
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"; } ...
14
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"; } ...
15
SERVLET CONTAINER
RENDERING FILTER
RENDERING ENGINE
BLOSSOM DISPATCHER SERVLET CONTROLLER
16
How Blossom Works
Blossom 3.0
17
What’s new?
Blossom 3.0
18
Update for Magnolia 5 series Requires Magnolia 5.1 new ways of building dialogs availability annotation
19
Dialogs
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") ); }
20
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") ); } }
21
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()) ); } ...
22
Dynamic Dialog
!!@Template(id="myModule:components/bookCategory", title="Book category") @TemplateDescription("A list of books in a given category.") @Controller public 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) ); } }
23
Dynamic Dialog
!@Template(id="myModule:components/bookCategory", title="Book category") @TemplateDescription("A list of books in a given category.") @Controller public 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) ); } }
24
@AvailableComponentClasses
25
Components Availability
! @ComponentCategory @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Banner { } ! @Area("banners") @AvailableComponentClasses({Banner.class}) @Controller public class BannersArea { … } ! @Banner @Template(id="myModule:components/largePromoBanner"), title="Large banner”) @Controller("banner") public class LargePromoBannerComponent { … }
26
@More
27
Content, content, more content
!@Template(id="myModule:components/bookCategory", title="Book category") @TemplateDescription("A list of books in a given category.") @Controller public 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) ); } }
28
Now The Real World 29
Thank You!
30
32
How do I get started on my project?
33
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
How would I build a REST interface to my CMS content using Blossom?
35
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>
36
REST Controller
@Controller public class ProductsRestController { !
@RequestMapping("/products/{productId}") public Product findProduct(@PathVariable String productId) { // implementation omitted } }
37
Can I leverage Magnolia's caching functionality to improve the performance of my Spring app?
38
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"; } }
How can I present dialogs in different languages using Blossom?
40
!@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 Dialog
!!/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 Dialog
How do I migrate my existing site to Magnolia/Blossom?
43
How can I integrate Spring Security?
44
45
How can I dependency inject spring beans into RenderingModels?
46
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); }
47
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); } !!!!
48
Views 49
Rendering an Area
FreeMarker [@cms.area name="main" /] !
JSP <cms:area name="main" />
50
Rendering Components in an Area
FreeMarker [#list components as component] [@cms.component content=component /] [/#list] !
JSP <c:forEach items="${components}" var="component"> <cms:component content="${component}" /> </c:forEach>
51
Page Template Availability
@Controller @Template(title="Article", id="myModule:/pages/article") public class ArticleTemplate { ... @Available public boolean isAvailable(Node node) { return node.getPath().startsWith("/articles/"); } }
52
Available Components
@Controller @Area("promos") @AvailableComponentClasses({TextComponent.class, ShoppingCartComponent.class}) public static class PromosArea { @RequestMapping("/main/promos") public String render() { return "areas/promos"; } }
53
Area Inheritance
@Controller @Area("promos") @Inherits @AvailableComponentClasses({TextComponent.class, ShoppingCartComponent.class}) public static class PromosArea { @RequestMapping("/main/promos") public String render() { return "areas/promos"; } }
54
Form Submission
/* Standard annotations omitted */ public class ContactFormComponent { @RequestMapping(value="/contact", method=RequestMethod.GET) public String viewForm(@ModelAttribute ContactForm contactForm) { return "components/contactForm";
} @RequestMapping(value="/contact", method=RequestMethod.POST) public String handleSubmit(@ModelAttribute ContactForm contactForm, ⏎ BindingResult result) { new ContactFormValidator().validate(contactForm, result); if (result.hasErrors()) { return "components/contactForm"; } return "redirect:/home/contact/thankyou.html";
}
55
But wait a minute …
56
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at info.magnolia.module.blossom.render.BlossomDispatcherServlet.forward(BlossomDispatcherServlet.java:123) at info.magnolia.module.blossom.render.BlossomTemplateRenderer.render(BlossomTemplateRenderer.java:78) ... 92 more Caused by: java.lang.IllegalStateException at org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:435)
… the response has been sent.
Controller Pre-execution
57
C M V
PAGE
C M V
Area
C M V
Component
C M V
Component
C M V
Component
C
CPE Implementation 58
Code in View <form> <blossom:pecid-input /> <input type=”text” name=”email” /> ... !
HTML Output <form> <input type=”hidden” name=”_pecid” value=”ff6cefa6-d958-47b1-af70-c82a414f17e1” /> <input type=”text” name=”email” /> ...
Spring Web MVC +
Content
59
Spring Web Flow
60
REQUEST LANDING PAGE
SPRING WEB FLOW
Spring Web Flow
61
REQUEST LANDING PAGE
FORM SUBMIT?
SPRING WEB FLOW
62
Trademarks
Other trademarks are the property of their respective owners.
MagnoliaThe Pulse are trademarks of
Magnolia International Limited.}
Recommended