Upload
tobias-mattsson
View
5.327
Download
1
Embed Size (px)
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.
Citation preview
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