Magnolia blossom-webinar
-
Upload
boris-kraft -
Category
Technology
-
view
1.703 -
download
0
description
Transcript of Magnolia blossom-webinar
US NAVY INTEGRATES SPRING APPSWITH FLYING COLORS THANKS TO WEB CMS
+
WEBINAR
CAMPBELL EWALD
ABOUT THE WEBINARPosting Questions
Viewing the recorded webinar and related materials
Contacting Us
Tweeting - use hash tags: #magnolia_cms #campbellewald #springframework #springmvc #USNavy
2
WEBINAR
CAMPBELL EWALD
ABOUT US
MATT DERTINGER SOLUTIONS ARCHITECT, INTERACTIVE R&D, CE DETROITCAMPBELL EWALDTwi9er: @ma9nolia
SEAN MCMAINS SALES ENGINEERMAGNOLIA CMSsean.mcmains@magnolia-‐CMS.com
TOBIAS MATTSSON SENIOR SOFTWARE ENGINEER & BLOSSOM DEVELOPERMAGNOLIA CMStobias.ma9sson@magnolia-‐cms.com
3
WEBINAR
CAMPBELL EWALD
ABOUT CAMPBELL EWALD AND MAGNOLIACAMPBELL EWALDDigital AgencyMagnolia PartnerGeneral Motors, Kaiser Permanente, US Postal Service
MagnoliaJava-based Content Management SystemCommunity and Enterprise EditionsSony, Deloitte, Airbus and JBoss
4
WEBINAR
CAMPBELL EWALD
WHAT WE’LL TALK ABOUT
Overview of Magnolia Blossom module
Navy Use Cases
Step-by-Step Walkthrough
Questions
5
WEBINAR
CAMPBELL EWALD
BLOSSOM OVERVIEW
6
WEBINAR
CAMPBELL EWALD
A LITTLE PHILOSOPHY
7
The idea that sparked me writing the Blossom Module for Magnolia CMS was to bring the CMS and especially the content into Spring Web MVC not the other way around. Controllers should be the building blocks when composing pages.
– Tobias Mattsson http://tobias-mattsson-magnolia.blogspot.com/2011/03/spring-web-mvc-with-content.html
WEBINAR
CAMPBELL EWALD
WHAT’S BLOSSOM
Spring integration module for Magnolia CMS
Spring Web MVC with Content
Key features:
Annotation based API
Spring controllers exposed as template components
Pre-execution of template components
8
WEBINAR
CAMPBELL EWALD
WHY USE BLOSSOMLeverages proven Spring Framework
Easily integrate or migrate existing Spring-based applications
Spring developers will feel right at home
Dynamic dialogs, Virtual URI Mappings, Pre-execution
Easily access content in controllers
Best practice web app design patterns (MVC, IoC, AOP)
Open Source and Free to download and use
9
WEBINAR
CAMPBELL EWALD
NAVY USE CASES
10
TM
WEBINAR
CAMPBELL EWALD
ABOUT NAVY.COM
Official recruitment Website for the U.S. Navy
Redesigned in 2010, moved to Magnolia CMS
Application integration for subscription services, CRUD functionality and user-space applications
11
WEBINAR
CAMPBELL EWALD
BLOSSOM + RESTEASY
RESTEasy provided nice JAX-RS client
Aligned with Magnolia CMS roadmap
Allowed business components to be loosely-coupled to CMS
12
Navy Web Service Registry
Business Component 1
Business Component 2
Business Component 3
Business Component n
Navy.comNavy Custom Module
Blossom
WEBINAR
CAMPBELL EWALD
BLOSSOM EXAMPLESRequest More Information (Lead) Forms
Find a Recruiter
Subscriber
Life Ops
Reserve Pay Calculator
Training Locations Map
Poll
13
WEBINAR
CAMPBELL EWALD
FIND A RECRUITER
Finds the nearest Enlisted and Officer Recruiter Locations based on the postal code entered by the site visitor
Collect and validate input, call Navy Web Service, display results.
14
WEBINAR
CAMPBELL EWALD
LIFE OPS
Personality Profile Test to help potential recruits plan their future
Wizard type interface
Collect and validate selected options from site visitor, populate model, then calculate and display results
15
WEBINAR
CAMPBELL EWALD
STANDARD TEMPLATING KITThe STK is a production-
ready website construction framework.
• Best Practices• Rapid Prototyping•Modular Architecture• Extensible• Standards-Compliant
16
WEBINAR
CAMPBELL EWALD
BUILDING LIFE OPS: STEP-BY-STEP
17
WEBINAR
CAMPBELL EWALD
GETTING STARTEDStarting Spring - Add the Blossom Servlet Context Listener to your web.xml<listener> <listener-class>info.magnolia.module.blossom.support.ServletContextExposingContextListener</listener-class></listener>
Create a module for your custom Spring application components, quickest way:$ mvn archetype:generate -DarchetypeCatalog=http://nexus.magnolia-cms.com/content/groups/public/
Have your module class extend BlossomModuleSupport and implement start and stop methods:public class BlossomSampleModule extends BlossomModuleSupport implements ModuleLifecycle { public void start(ModuleLifecycleContext moduleLifecycleContext) { initRootWebApplicationContext("classpath:/applicationContext.xml"); initBlossomDispatcherServlet("blossom", "classpath:/blossom-servlet.xml"); } public void stop(ModuleLifecycleContext moduleLifecycleContext) { destroyDispatcherServlets(); closeRootWebApplicationContext(); }}
18
WEBINAR
CAMPBELL EWALD
SPRING BEAN CONFIGURATION
Create a Spring bean config file called 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-3.0.xsd http://www.magnolia-cms.com/schema/blossom http://www.magnolia-cms.com/schema/blossom-1.2.xsd"> <blossom:configuration /> </beans>
19
WEBINAR
CAMPBELL EWALD
SPRING BEAN CONFIGURATION
Create a Spring bean config file called 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="..."> <context:annotation-config /> <context:component-scan base-package="com.c_e.webinar.magnolia.module.blossomsample" use-default-filters="false"> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.Paragraph" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.Template" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.DialogFactory" /> <context:include-filter type="annotation" expression="info.magnolia.module.blossom.annotation.VirtualURIMapper" /> <context:include-filter type="assignable" expression="info.magnolia.cms.beans.config.VirtualURIMapping" /> </context:component-scan> ...</beans>
20
WEBINAR
CAMPBELL EWALD
SPRING BEAN CONFIGURATIONAdd Spring Handler Adapters and Mappings to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans ...> ... <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="customArgumentResolver"> <bean class="info.magnolia.module.blossom.web.BlossomWebArgumentResolver" /> </property> </bean> <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> ...</beans>
21
WEBINAR
CAMPBELL EWALD
SPRING BEAN CONFIGURATIONAdd Spring View Resolvers to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans...> ... <bean class="info.magnolia.module.blossom.view.UuidRedirectViewResolver"> <property name="order" name="1" /> </bean> <bean class="info.magnolia.module.blossom.view.TemplateViewResolver"> <property name="order" name="2" /> <property name="prefix" name="/ce-webinar-templating-kit/templates/blossomsamples" /> <property name="suffix" name=".ftl" /> <property name="viewRenderer"> <bean class="info.magnolia.module.blossom.view.FreemarkerTemplateViewRenderer" /> </property> </bean> <bean class="info.magnolia.module.blossom.view.ParagraphViewResolver"> <property name="order" name="3" /> <property name="prefix" name="/ce-webinar-templating-kit/paragraphs/blossomsamples" /> <property name="suffix" name=".ftl" /> <property name="viewRenderer"> <bean class="info.magnolia.module.blossom.view.FreemarkerParagraphViewRenderer" /> </property> </bean></beans>
22
WEBINAR
CAMPBELL EWALD
LIFE OPS MODEL
Uses Bean Validation API (JSR 303)
Excerpt:public class LifeOps implements Serializable { ... @NotNull @Min(0) @Max(18) private Integer advisor; ... @NotNull @Min(0) @Max(18) private Integer doer; ...}
23
WEBINAR
CAMPBELL EWALD
LIFE OPS MODEL
Add in bean validation support to blossom-servlet.xml<?xml version="1.0" encoding="UTF-8"?><beans ...> ... <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> ... <property name="webBindingInitializer"> <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"> <property name="validator" ref="validator" /> </bean> </property> </bean> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> ...</beans>
24
WEBINAR
CAMPBELL EWALD
LIFE OPS MODELConstructorpublic LifeOps(Integer advisor, Integer doer, Integer innovator, Integer persuader, Integer planner, Integer problemSolver) { this.advisor = advisor; this.doer = doer; this.innovator = innovator; this.persuader = persuader; this.planner = planner; this.problemSolver = problemSolver;}
Constructor using transients:public LifeOps(String[] activities, String[] interests, String[] careers) { this.activities = activities; this.interests = interests; this.careers = careers; this.resetScores(); this.calculateScores(activities); this.calculateScores(interests); this.calculateScores(careers);}
25
WEBINAR
CAMPBELL EWALD
LIFE OPS SPRING VALIDATORpublic class LifeOpsValidator implements Validator {
@Override public boolean supports(Class<?> clazz) { return LifeOps.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { LifeOps lifeOps = (LifeOps) target; if (lifeOps.getActivities() == null || lifeOps.getActivities().length == 0) { errors.rejectValue("activities", "activities.required", "Please choose at least one activity before proceeding."); } if (lifeOps.getInterests() == null || lifeOps.getInterests().length == 0) { errors.rejectValue("interests", "interests.required", "Please choose at least one interest before proceeding."); } if (lifeOps.getCareers() == null || lifeOps.getCareers().length == 0) { errors.rejectValue("careers", "careers.required", "Please choose at least one career before proceeding."); } }}
26
WEBINAR
CAMPBELL EWALD
LIFE OPS CONTROLLER@Controller@RequestMapping("/life-ops")@Paragraph("Life Ops form")@ParagraphDescription("Adds a Life Ops form and displays Life Ops Results")@I18nBasename("com.c_e.webinar.magnolia.module.blossomsample.messages")public class LifeOpsController {
private static final Logger log = LoggerFactory.getLogger(LifeOpsController.class); private final String LIFE_OPS_FORM_PATH = "life-ops/form"; private final String LIFE_OPS_SHOW_PATH = "life-ops/show"; @Autowired private Validator validator; @RequestMapping(method = RequestMethod.GET) public String form(LifeOps lifeOps) { return LIFE_OPS_FORM_PATH; } ...}
27
WEBINAR
CAMPBELL EWALD
LIFE OPS CONTROLLER@RequestMapping(method = RequestMethod.POST)public String processSubmit(@Valid LifeOps lifeOps, BindingResult result, Model model) { new LifeOpsValidator().validate(lifeOps, result); if (result.hasErrors()) { List<FieldError> fieldErrors = result.getFieldErrors(); Map<String, String> errorsMap = new HashMap<String, String>(); for (int i = 0; i < fieldErrors.size(); i++) { FieldError thisError = fieldErrors.get(i); errorsMap.put(thisError.getField(), thisError.getDefaultMessage() ); } lifeOps.setErrors(errorsMap); model.addAttribute("lifeOps", lifeOps); return LIFE_OPS_FORM_PATH; } else { lifeOps = new LifeOps(lifeOps.getActivities(), lifeOps.getInterests(), lifeOps.getCareers() ); model.addAttribute("lifeOps", lifeOps); return "redirect:/" + STKUtil.getSite().getName() + "/life-ops/results" + "/" + lifeOps.getAdvisor() + "/" + lifeOps.getDoer() + "/" + lifeOps.getInnovator() + "/" + lifeOps.getPersuader() + "/" + lifeOps.getPlanner() + "/" + lifeOps.getProblemSolver(); }}
28
WEBINAR
CAMPBELL EWALD
LIFE OPS CONTROLLER@RequestMapping(method = RequestMethod.GET, params = "advisor")public String show( @RequestParam Integer advisor, @RequestParam Integer doer, @RequestParam Integer innovator, @RequestParam Integer persuader, @RequestParam Integer planner, @RequestParam Integer problemSolver, Model model) { try { LifeOps lifeOps = new LifeOps(advisor, doer, innovator, persuader, planner, problemSolver); model.addAttribute("lifeOps", lifeOps); } catch (Exception e) { log.error("LifeOpsController.show() Exception:" + e); } return LIFE_OPS_SHOW_PATH;}
29
WEBINAR
CAMPBELL EWALD
LIFE OPS CONTROLLER@TabFactory("Settings")public void addTab(TabBuilder builder) { builder.addEdit("activitiesText", "Activities Text", "A short description for the activities fieldset"); builder.addEdit("interestsText", "Interests Text", "A short description for the interests fieldset"); builder.addEdit("careersText", "Careers Text", "A short description for the interests fieldset"); builder.addFckEditor("resultsText", "Results Text", "A short intro for the results page");}
30
WEBINAR
CAMPBELL EWALD
LIFE OPS VIRTUAL URI MAPPING@VirtualURIMapperpublic class LifeOpsURIMapper {
private Pattern pattern; public LifeOpsURIMapper() { this.pattern = Pattern.compile("^/life-ops/results/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/?"); }
public String mapping(String uri, HttpServletRequest request) { this.pattern = Pattern.compile("^/(" + STKUtil.getSite().getName() + "/)?life-ops/results/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/([0-1]?[0-8])/?"); Matcher matcher = pattern.matcher(uri); if (matcher.matches()) { return matcher.replaceAll("forward:/" + STKUtil.getSite().getName() + "/life-ops/results/?advisor=$2&doer=$3&innovator=$4&persuader=$5&planner=$6&problemSolver=$7"); } return null; }}
31
WEBINAR
CAMPBELL EWALD
LIFE OPS FORM VIEW[#assign cms=JspTaglibs["cms-taglib"]][#assign form=JspTaglibs["http://www.springframework.org/tags/form"]]
<div> [@cms.editBar /] [@form.form id="life-ops-form" modelAttribute="lifeOps"] ... <fieldset> <legend>Activities</legend> ... <dl class="prop"> <dt><span>What do you like to do?</span></dt> <dd class="value"> <ul> <li> <input id="activities1" name="activities" value="D" type="checkbox" /> <label for="activities1"><strong>Set up a home computer network</strong></label> </li> <li> <input id="activities2" name="activities" value="S" type="checkbox" /> <label for="activities2"><strong>Track the path of a hurricane</strong></label> </li> ... </ul> </dd> </dl> </fieldset>
32
WEBINAR
CAMPBELL EWALD
LIFE OPS RESULTS VIEW[#assign results = [ {"name": "problemSolver", "label": "Problem Solver", "score": lifeOps.problemSolver}, {"name": "planner", "label": "Planner", "score": lifeOps.planner}, {"name": "persuader", "label": "Persuader", "score": lifeOps.persuader}, {"name": "innovator", "label": "Innovator", "score": lifeOps.innovator}, {"name": "doer", "label": "Do-er", "score": lifeOps.doer}, {"name": "advisor", "label": "Advisor", "score": lifeOps.advisor}]]...
33
WEBINAR
CAMPBELL EWALD
LIFE OPS RESULTS VIEW<div class="super-list"> [@cms.editBar /] <ul> [#list results?sort_by("score")?reverse as result] [#assign profileDivId = result.label?lower_case?trim?replace(' ', '-')] [#assign scoreWidth = (result.score/2)*11+"%"] [#assign contentCollectionText = result.name + "Text"] [#assign contentCollectionExtras = result.name + "Extras"] <li> <h3> <a rel="bookmark" href="#${profileDivId}" title="${result.label}"> <dfn style="width:${scoreWidth};"><strong>${result.label}</strong></dfn> </a> </h3> ... [#if contentCollectionText?exists] [@cms.contentNodeIterator contentNodeCollectionName=contentCollectionText] [@cms.includeTemplate/] [/@cms.contentNodeIterator] [/#if] [@cms.newBar contentNodeCollectionName=contentCollectionText paragraph="stkTextImage, stkTeaserGroup" /] ... </li> [/#list] </ul></div><!-- end super-list -->
34
WEBINAR
CAMPBELL EWALD
35
QUESTIONS
WEBINAR
CAMPBELL EWALD
CONTACT INFO
MATT DERTINGER SOLUTIONS ARCHITECT, INTERACTIVE R&D, CE DETROITCAMPBELL EWALDTwi9er: @ma9nolia
SEAN MCMAINS SALES ENGINEERMAGNOLIA CMSsean.mcmains@magnolia-‐cms.com
TOBIAS MATTSSON SENIOR SOFTWARE ENGINEER & BLOSSOM DEVELOPERMAGNOLIA CMStobias.ma9sson@magnolia-‐cms.com
36
WEBINAR
CAMPBELL EWALD
FURTHER READINGVisit the Magnolia documentation site to download Magnolia and find tutorials and guideshttp://documentation.magnolia-cms.com/index.html
The Blossom reference documentationhttp://documentation.magnolia-cms.com/modules/blossom.html
Blossom Sample Webapphttp://documentation.magnolia-cms.com/modules/blossom.html#GettingStarted
Tobias Mattsson's bloghttp://tobias-mattsson-magnolia.blogspot.com/
Ask your questions and see what others are doing at the Magnolia community forumhttp://forum.magnolia-cms.com/forum.html
37