├── .gitignore ├── LICENSE.md ├── README.md ├── liferay-spring-mvc-thymeleaf-angular-core ├── pom.xml └── src │ └── main │ ├── java │ └── ab │ │ └── liferay │ │ └── spring │ │ └── mvc │ │ └── thymeleaf │ │ └── angular │ │ └── core │ │ ├── base │ │ ├── adapter │ │ │ ├── PortletRequestBodyImpl.java │ │ │ ├── PortletRequestBodyWebArgumentResolver.java │ │ │ └── PortletResponseBodyImpl.java │ │ ├── annotation │ │ │ ├── AllController.java │ │ │ ├── EditController.java │ │ │ ├── HelpController.java │ │ │ ├── PortletRequestBody.java │ │ │ ├── PortletResponseBody.java │ │ │ └── ViewController.java │ │ ├── component │ │ │ └── request │ │ │ │ ├── MessageStore.java │ │ │ │ └── RequestBodyCache.java │ │ ├── config │ │ │ ├── CoreConfig.java │ │ │ └── ThymeleafConfig.java │ │ ├── context │ │ │ └── AnnotationConfigurationPortletContext.java │ │ ├── dialect │ │ │ ├── CoreDialect.java │ │ │ └── CoreDialectImpl.java │ │ ├── handler │ │ │ └── MappingExceptionResolver.java │ │ ├── model │ │ │ ├── MessageList.java │ │ │ ├── MessageType.java │ │ │ └── MessageTypeList.java │ │ ├── service │ │ │ ├── I18nMessageConstants.java │ │ │ ├── I18nMessageSourceImpl.java │ │ │ ├── MessageService.java │ │ │ ├── MessageServiceImpl.java │ │ │ ├── PortletService.java │ │ │ ├── PortletServiceImpl.java │ │ │ ├── RenderService.java │ │ │ └── RenderServiceImpl.java │ │ └── util │ │ │ └── Integration.java │ │ └── portlet │ │ └── config │ │ └── PortletCoreConfig.java │ └── resources │ └── jsp │ └── portlet_i18n_configuration.jsp ├── liferay-spring-mvc-thymeleaf-angular-parent └── pom.xml └── liferay-spring-mvc-thymeleaf-angular-portlet ├── pom.xml └── src ├── main ├── java │ └── ab │ │ └── liferay │ │ └── spring │ │ └── mvc │ │ └── thymeleaf │ │ └── angular │ │ ├── base │ │ └── config │ │ │ └── BaseConfig.java │ │ ├── delegate │ │ ├── config │ │ │ └── DelegateConfig.java │ │ └── controller │ │ │ └── DelegateRestController.java │ │ └── portlet │ │ ├── config │ │ └── PortletConfig.java │ │ ├── controller │ │ ├── PersonRestController.java │ │ └── PersonViewController.java │ │ ├── model │ │ └── Person.java │ │ └── service │ │ ├── PersonService.java │ │ └── PersonServiceImpl.java ├── resources │ ├── content │ │ └── language_template.properties │ ├── friendly-url-router.xml │ ├── portlet.properties │ └── resource-actions │ │ └── default.xml └── webapp │ ├── WEB-INF │ ├── liferay-display.xml │ ├── liferay-plugin-package.properties │ ├── liferay-portlet.xml │ ├── portlet.xml │ ├── thymeleaf │ │ └── templates │ │ │ ├── error │ │ │ └── error.xhtml │ │ │ └── index │ │ │ ├── index.xhtml │ │ │ └── render.xhtml │ └── web.xml │ ├── jsp │ └── configuration.jsp │ └── static │ ├── css │ └── main.css │ ├── images │ └── image.jpg │ └── js │ ├── angular │ ├── angular-1.3.14.js │ ├── app-bootstrap.js │ ├── app-init.js │ ├── controller.js │ ├── factory.js │ └── service.js │ └── jquery │ ├── app.js │ └── jquery-1.11.1.min.js └── test └── java └── ab └── liferay └── spring └── mvc └── thymeleaf └── angular └── portlet └── test ├── PersonRestControllerTest.java ├── PersonViewControllerTest.java ├── TestWrapperPersonRestController.java └── TestWrapperPersonViewController.java /.gitignore: -------------------------------------------------------------------------------- 1 | **/build 2 | **/.settings 3 | **/.classpath 4 | **/bin 5 | **/lib 6 | **/target 7 | **/plugin.xml 8 | *.augmented.* 9 | **/*.ipr 10 | **/*.iws 11 | **/*.iml 12 | **/.gradle 13 | **/cert 14 | **/out 15 | **/.dev/ 16 | **/.tmp/ 17 | **/.DS_Store 18 | **/*.sublime-project 19 | **/*.sublime-workspace 20 | **/bower_components/ 21 | **/node_modules/ 22 | **/test/coverage/ 23 | **/dist 24 | **/npm-debug.log 25 | **/pdf-out 26 | data/ 27 | .project 28 | *-ui.js 29 | *-ui.tpl.js 30 | **/classes 31 | 32 | docs 33 | 34 | **/overlays 35 | 36 | .idea/* -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Anton Bayer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Liferay - Spring MVC - Thymeleaf - Angular - Portlet 2 | 3 | After playing around with several tutorials which have not fully filled my demands I decided to implement my Liferay Spring MVC Portlet from scratch with all the features I was used to have in a standalone Spring MVC application: 4 | 5 | * RestController for AngularJS. 6 | * ViewController with Thymeleaf and partial fragment ajax updates via jQuery. 7 | * @ResponseBody and @RequestBody which are not supported by spring for portlets out of the box. 8 | * Full support for integration tests with MockMVC. 9 | * 100% Java Config and no xml-file at all. 10 | * Custom Thymeleaf src-attribute for easy use in Portlets to get static resources: <img sc:src="images/image.jpg" /> 11 | * Service to render Thymeleaf templates on server into strings to return as json object for multi fragment updates via ajax. 12 | * Added friendly url mapping and service methods to generate them - also to other pages and other portlets. 13 | * Add FlashMessages for holding messages over a redirect from a ActionRequest to a RenderRequest. 14 | 15 | The project consists of 3 Maven dependencies: 16 | 17 | * -parent: Does what a parent Maven dependency has to do. 18 | * -core: The basic/generic functionality and configuration you probably need in every portlet. 19 | * -portlet: A demo portlet that renders a list of persons with Thymeleaf on server-side and AngularJS via Rest on client-side. 20 | 21 | I solved the support of @ResponseBody and @RequestBody for portlets by implementing my own @PortletResponseBody and @PortletRequestBody. 22 | 23 | To be able to do integration tests with MockMVC I wrapped the portlet controller into a new controller and override the RequestMapping. That works like a charm. 24 | 25 | ### Build, run tests and install the Portlet 26 | 27 | Run the maven command to install the portlet to your liferay installation. 28 | 29 | ``` 30 | mvn clean install liferay:deploy -P liferay 31 | ``` 32 | 33 | Do not forget to configure the liferay params in Maven. In my case I set the profile 'liferay' for executing the tasks which is defined in my user_home\\.m2\settings.xml: 34 | 35 | ```xml 36 | 37 | liferay 38 | 39 | 6.2.10.6 40 | your_path\liferay-portal 41 | ${liferay.home}\deploy 42 | ${liferay.home}\tomcat-7.0.42\webapps 43 | ${liferay.home}\tomcat-7.0.42\lib\ext 44 | ${liferay.home}\tomcat-7.0.42\webapps\ROOT 45 | 46 | 47 | ``` 48 | 49 | Notes: 50 | 51 | * The portlet is tested with Java 1.7 and liferay-portal-6.2-ee-sp5. 52 | * Developed with IntellijIDEA Ultimate 14. 53 | * All used Maven dependencies are in the latest version (03/17/15). 54 | 55 | ### Improvements? 56 | 57 | If you have any idea for improvments send me an [Email](mailto:wwa2007@nurfuerspam.de) or send me a merge request. 58 | 59 | If you like it, feel free to give me a star and watch out for further updates. ;) 60 | 61 | Cheers from Vienna 62 | 63 | Toni :) 64 | 65 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ab.liferay.spring.mvc.thymeleaf.angular 7 | liferay-spring-mvc-thymeleaf-angular-parent 8 | 1.0-SNAPSHOT 9 | ../liferay-spring-mvc-thymeleaf-angular-parent/pom.xml 10 | 11 | liferay-spring-mvc-thymeleaf-angular-core 12 | Spring MVC Thymeleaf Angular Core 13 | 14 | 15 | com.liferay.portal 16 | portal-service 17 | 18 | 19 | javax.portlet 20 | portlet-api 21 | 22 | 23 | javax.servlet 24 | javax.servlet-api 25 | 26 | 27 | org.springframework 28 | spring-webmvc-portlet 29 | 30 | 31 | org.thymeleaf 32 | thymeleaf-spring4 33 | 34 | 35 | com.fasterxml.jackson.core 36 | jackson-databind 37 | 38 | 39 | joda-time 40 | joda-time 41 | 42 | 43 | com.liferay.portal 44 | util-taglib 45 | 46 | 47 | javax.servlet 48 | jstl 49 | 50 | 51 | javax.servlet.jsp 52 | jsp-api 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/adapter/PortletRequestBodyImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.PortletRequestBody; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.component.request.RequestBodyCache; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.core.MethodParameter; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.support.WebArgumentResolver; 10 | import org.springframework.web.context.request.NativeWebRequest; 11 | 12 | import javax.portlet.ClientDataRequest; 13 | import java.io.IOException; 14 | import java.text.MessageFormat; 15 | 16 | public class PortletRequestBodyImpl implements PortletRequestBodyWebArgumentResolver { 17 | 18 | private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); 19 | private final RequestBodyCache requestBodyCache; 20 | 21 | @Autowired 22 | public PortletRequestBodyImpl(RequestBodyCache requestBodyCache) { 23 | this.requestBodyCache = requestBodyCache; 24 | } 25 | 26 | @Override 27 | public Object resolveArgument(MethodParameter param, NativeWebRequest request) throws Exception { 28 | 29 | if (request.getNativeRequest() instanceof ClientDataRequest && param.hasParameterAnnotation(PortletRequestBody.class)) { 30 | String value = param.getParameterAnnotation(PortletRequestBody.class).value(); 31 | ClientDataRequest clientDataRequest = request.getNativeRequest(ClientDataRequest.class); 32 | if (!PortletRequestBody.DEFAULT.equals(value)) { 33 | if (isMethod(clientDataRequest, RequestMethod.POST)) { 34 | String json = JSON_MAPPER.readTree(getRequestBody(clientDataRequest)).get(value).toString(); 35 | return JSON_MAPPER.readValue(json, param.getParameterType()); 36 | } else if (isMethod(clientDataRequest, RequestMethod.GET)) { 37 | return JSON_MAPPER.readValue(request.getParameter(value), param.getParameterType()); 38 | } 39 | throw new RuntimeException(MessageFormat.format("REST Method {0} for values not supported.", clientDataRequest.getMethod())); 40 | } 41 | if (isMethod(clientDataRequest, RequestMethod.POST)) { 42 | return JSON_MAPPER.readValue(clientDataRequest.getReader(), param.getParameterType()); 43 | } 44 | throw new RuntimeException(MessageFormat.format("REST Method {0} for body not supported.", clientDataRequest.getMethod())); 45 | } 46 | return WebArgumentResolver.UNRESOLVED; 47 | } 48 | 49 | private boolean isMethod(ClientDataRequest clientDataRequest, RequestMethod requestMethod) { 50 | return requestMethod.name().equalsIgnoreCase(clientDataRequest.getMethod()); 51 | } 52 | 53 | private String getRequestBody(ClientDataRequest clientDataRequest) { 54 | if (requestBodyCache.getBody() == null) { 55 | StringBuilder builder = new StringBuilder(); 56 | String aux = ""; 57 | try { 58 | while ((aux = clientDataRequest.getReader().readLine()) != null) { 59 | builder.append(aux); 60 | } 61 | requestBodyCache.setBody(builder.toString()); 62 | } catch (IOException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } 66 | return requestBodyCache.getBody(); 67 | 68 | } 69 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/adapter/PortletRequestBodyWebArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter; 2 | 3 | import org.springframework.web.bind.support.WebArgumentResolver; 4 | 5 | public interface PortletRequestBodyWebArgumentResolver extends WebArgumentResolver { 6 | } 7 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/adapter/PortletResponseBodyImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.PortletResponseBody; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.util.Integration; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.ui.ExtendedModelMap; 7 | import org.springframework.web.context.request.NativeWebRequest; 8 | import org.springframework.web.servlet.ModelAndView; 9 | import org.springframework.web.servlet.View; 10 | import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; 11 | 12 | import javax.portlet.ResourceResponse; 13 | import java.lang.reflect.Method; 14 | import java.util.Map; 15 | 16 | public class PortletResponseBodyImpl implements ModelAndViewResolver { 17 | 18 | private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); 19 | 20 | @Override 21 | public ModelAndView resolveModelAndView(Method method, Class clazz, final Object returnValue, ExtendedModelMap modelMap, NativeWebRequest request) { 22 | 23 | if (request.getNativeResponse() instanceof ResourceResponse && method.getAnnotation(PortletResponseBody.class) != null) { 24 | return new ModelAndView() { 25 | @Override 26 | public View getView() { 27 | return new View() { 28 | @Override 29 | public String getContentType() { 30 | return Integration.JSON_UTF_8_CONTENT_TYPE.toString(); 31 | } 32 | 33 | @Override 34 | public void render(Map model, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception { 35 | JSON_MAPPER.writeValue(response.getWriter(), returnValue); 36 | } 37 | }; 38 | } 39 | }; 40 | } 41 | return UNRESOLVED; 42 | } 43 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/AllController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | import java.lang.annotation.*; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | @Controller 12 | @RequestMapping({"view", "edit", "help"}) 13 | public @interface AllController { 14 | String value() default ""; 15 | } 16 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/EditController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | import java.lang.annotation.*; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | @Controller 12 | @RequestMapping("edit") 13 | public @interface EditController { 14 | String value() default ""; 15 | } 16 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/HelpController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | import java.lang.annotation.*; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | @Controller 12 | @RequestMapping("help") 13 | public @interface HelpController { 14 | String value() default ""; 15 | } 16 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/PortletRequestBody.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target(ElementType.PARAMETER) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface PortletRequestBody { 9 | String DEFAULT = ""; 10 | 11 | String value() default DEFAULT; 12 | } 13 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/PortletResponseBody.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target(ElementType.METHOD) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface PortletResponseBody { 9 | } 10 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/annotation/ViewController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | import java.lang.annotation.*; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | @Controller 12 | @RequestMapping("view") 13 | public @interface ViewController { 14 | String value() default ""; 15 | } 16 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/component/request/MessageStore.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.component.request; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageList; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageType; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageTypeList; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.annotation.Scope; 9 | import org.springframework.context.annotation.ScopedProxyMode; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.context.WebApplicationContext; 12 | 13 | import javax.annotation.PostConstruct; 14 | import javax.portlet.PortletSession; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | 18 | @Component 19 | @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) 20 | public class MessageStore extends HashMap { 21 | 22 | private final PortletService portletService; 23 | private String FLASH_SESSION_MESSAGE_STORE = "FLASH_SESSION_MESSAGE_STORE"; 24 | 25 | @Autowired 26 | public MessageStore(PortletService portletService) { 27 | this.portletService = portletService; 28 | } 29 | 30 | @PostConstruct 31 | public void postContruct() { 32 | Object obj = this.portletService.getPortletSession().getAttribute(FLASH_SESSION_MESSAGE_STORE, PortletSession.PORTLET_SCOPE); 33 | if (obj != null && obj instanceof MessageStore) { 34 | MessageStore flashMessageStore = (MessageStore) obj; 35 | for (String key : flashMessageStore.keySet()) { 36 | put(key, flashMessageStore.get(key)); 37 | } 38 | } 39 | this.portletService.getPortletSession().removeAttribute(FLASH_SESSION_MESSAGE_STORE, PortletSession.PORTLET_SCOPE); 40 | } 41 | 42 | public void addRequestMessage(String message, MessageType messageType, String group) { 43 | addMessage(this, message, messageType, group); 44 | } 45 | 46 | public void addFlashMessage(String message, MessageType messageType, String group) { 47 | addMessage(getFlashMessageStore(), message, messageType, group); 48 | } 49 | 50 | public MessageList getMessages(MessageType messageType, String group) { 51 | MessageTypeList groupList = get(group); 52 | if (groupList == null) { 53 | return null; 54 | } 55 | return groupList.get(messageType); 56 | } 57 | 58 | private void addMessage(MessageStore messageStore, String message, MessageType messageType, String group) { 59 | 60 | MessageTypeList groupList = messageStore.get(group); 61 | if (groupList == null) { 62 | messageStore.put(group, new MessageTypeList()); 63 | groupList = messageStore.get(group); 64 | } 65 | 66 | List messageTypeList = groupList.get(messageType); 67 | if (messageTypeList == null) { 68 | groupList.put(messageType, new MessageList()); 69 | messageTypeList = groupList.get(messageType); 70 | } 71 | messageTypeList.add(message); 72 | } 73 | 74 | private MessageStore getFlashMessageStore() { 75 | Object obj = this.portletService.getPortletSession().getAttribute(FLASH_SESSION_MESSAGE_STORE, PortletSession.PORTLET_SCOPE); 76 | if (obj != null && obj instanceof MessageStore) { 77 | return (MessageStore) obj; 78 | } 79 | this.portletService.getPortletSession().setAttribute(FLASH_SESSION_MESSAGE_STORE, new MessageStore(portletService), PortletSession.PORTLET_SCOPE); 80 | return getFlashMessageStore(); 81 | } 82 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/component/request/RequestBodyCache.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.component.request; 2 | 3 | import org.springframework.context.annotation.Scope; 4 | import org.springframework.context.annotation.ScopedProxyMode; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.context.WebApplicationContext; 7 | 8 | @Component 9 | @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) 10 | public class RequestBodyCache { 11 | 12 | private String body; 13 | 14 | public RequestBodyCache() { 15 | } 16 | 17 | public String getBody() { 18 | return body; 19 | } 20 | 21 | public void setBody(String body) { 22 | this.body = body; 23 | } 24 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/config/CoreConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.config; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter.PortletRequestBodyImpl; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter.PortletRequestBodyWebArgumentResolver; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.adapter.PortletResponseBodyImpl; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.component.request.RequestBodyCache; 7 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.handler.MappingExceptionResolver; 8 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.I18nMessageSourceImpl; 9 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 10 | import org.springframework.context.MessageSource; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.ComponentScan; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.context.annotation.Import; 15 | import org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter; 16 | 17 | @Configuration 18 | @Import(ThymeleafConfig.class) 19 | @ComponentScan(basePackages = {"ab.liferay.spring.mvc.thymeleaf.angular.core.base.component", "ab.liferay.spring.mvc.thymeleaf.angular.core.base.service", "ab.liferay.spring.mvc.thymeleaf.angular.core.controller"}) 20 | public class CoreConfig { 21 | 22 | private static final String ERROR_VIEW = "error/error"; 23 | 24 | @Bean 25 | public MappingExceptionResolver mappingExceptionResolver(PortletService portletService) { 26 | 27 | MappingExceptionResolver mappingExceptionResolver = new MappingExceptionResolver(portletService); 28 | mappingExceptionResolver.setDefaultErrorView(ERROR_VIEW); 29 | return mappingExceptionResolver; 30 | } 31 | 32 | @Bean 33 | public AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter(PortletRequestBodyWebArgumentResolver portletRequestBody) { 34 | 35 | AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter = new AnnotationMethodHandlerAdapter(); 36 | annotationMethodHandlerAdapter.setCustomArgumentResolver(portletRequestBody); 37 | annotationMethodHandlerAdapter.setCustomModelAndViewResolver(new PortletResponseBodyImpl()); 38 | annotationMethodHandlerAdapter.setOrder(0); 39 | 40 | return annotationMethodHandlerAdapter; 41 | } 42 | 43 | @Bean 44 | public PortletRequestBodyWebArgumentResolver portletRequestBody(RequestBodyCache requestBodyCache) { 45 | return new PortletRequestBodyImpl(requestBodyCache); 46 | } 47 | 48 | @Bean 49 | public MessageSource messageSource(final PortletService portletService) { 50 | return new I18nMessageSourceImpl(portletService); 51 | } 52 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/config/ThymeleafConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.config; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.dialect.CoreDialect; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.dialect.CoreDialectImpl; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.MessageService; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.web.servlet.ViewResolver; 10 | import org.thymeleaf.dialect.IDialect; 11 | import org.thymeleaf.spring4.SpringTemplateEngine; 12 | import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; 13 | import org.thymeleaf.spring4.view.ThymeleafViewResolver; 14 | 15 | import java.util.HashSet; 16 | import java.util.Set; 17 | 18 | @Configuration 19 | public class ThymeleafConfig { 20 | 21 | private final String SYSTEM_PROPERTY_THYMELEAF_CACHEABLE = "thymeleaf.cacheable"; 22 | 23 | @Bean 24 | public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { 25 | ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); 26 | viewResolver.setTemplateEngine(templateEngine); 27 | return viewResolver; 28 | } 29 | 30 | @Bean 31 | public SpringResourceTemplateResolver templateResolver() { 32 | SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); 33 | templateResolver.setTemplateMode("HTML5"); 34 | templateResolver.setPrefix("/WEB-INF/thymeleaf/templates/"); 35 | templateResolver.setSuffix(".xhtml"); 36 | templateResolver.setCharacterEncoding("UTF-8"); 37 | templateResolver.setCacheable(getThymeleafCacheable()); 38 | return templateResolver; 39 | } 40 | 41 | @Bean 42 | public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver, CoreDialect coreDialect) { 43 | SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 44 | templateEngine.setTemplateResolver(templateResolver); 45 | 46 | Set dialects = new HashSet(); 47 | dialects.add(coreDialect); 48 | templateEngine.setAdditionalDialects(dialects); 49 | 50 | return templateEngine; 51 | } 52 | 53 | @Bean 54 | public CoreDialect messageListDialect(final PortletService portletService, final MessageService messageService) { 55 | return new CoreDialectImpl(portletService, messageService); 56 | } 57 | 58 | private boolean getThymeleafCacheable() { 59 | String thymeleafCacheable = System.getProperty(SYSTEM_PROPERTY_THYMELEAF_CACHEABLE); 60 | return thymeleafCacheable == null ? true : !thymeleafCacheable.equalsIgnoreCase(Boolean.FALSE.toString()); // always cacheable (true), expect the string 'false' 61 | } 62 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/context/AnnotationConfigurationPortletContext.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.context; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.config.BeanDefinition; 5 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 6 | import org.springframework.beans.factory.support.GenericBeanDefinition; 7 | import org.springframework.context.annotation.AnnotationConfigUtils; 8 | import org.springframework.web.portlet.context.AbstractRefreshablePortletApplicationContext; 9 | 10 | import java.io.IOException; 11 | 12 | public class AnnotationConfigurationPortletContext extends AbstractRefreshablePortletApplicationContext { 13 | 14 | @Override 15 | protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 16 | AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory); 17 | loadConfigBeanDefinitions(beanFactory); 18 | } 19 | 20 | private void loadConfigBeanDefinitions(DefaultListableBeanFactory beanFactory) { 21 | for (String configurationClassName : getConfigLocations()) { 22 | BeanDefinition configBeanDefinition = new GenericBeanDefinition(); 23 | configBeanDefinition.setBeanClassName(configurationClassName); 24 | loadConfigBeanDefinition(configBeanDefinition, beanFactory); 25 | } 26 | } 27 | 28 | private void loadConfigBeanDefinition(BeanDefinition beanDefinition, DefaultListableBeanFactory beanFactory) { 29 | beanFactory.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/dialect/CoreDialect.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.dialect; 2 | 3 | import org.thymeleaf.dialect.IDialect; 4 | 5 | public interface CoreDialect extends IDialect { 6 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/dialect/CoreDialectImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.dialect; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageList; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageType; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.MessageService; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 7 | import org.thymeleaf.Arguments; 8 | import org.thymeleaf.dialect.AbstractDialect; 9 | import org.thymeleaf.dom.Element; 10 | import org.thymeleaf.dom.NestableNode; 11 | import org.thymeleaf.dom.Text; 12 | import org.thymeleaf.processor.*; 13 | import org.thymeleaf.processor.element.AbstractElementProcessor; 14 | import org.thymeleaf.standard.processor.attr.AbstractStandardSingleAttributeModifierAttrProcessor; 15 | 16 | import java.util.HashSet; 17 | import java.util.Set; 18 | 19 | public class CoreDialectImpl extends AbstractDialect implements CoreDialect { 20 | 21 | public static final String LAYOUT_NAMESPACE = "http://core"; 22 | final String DIALECT_PREFIX = "core"; 23 | final String PROCESSOR_MESSAGES_NAME = "messages"; 24 | final String PROCESSOR_SRC_NAME = "src"; 25 | private final MessageService messageService; 26 | private final PortletService portletService; 27 | 28 | public CoreDialectImpl(PortletService portletService, MessageService messageService) { 29 | this.portletService = portletService; 30 | this.messageService = messageService; 31 | } 32 | 33 | @Override 34 | public String getPrefix() { 35 | return DIALECT_PREFIX; 36 | } 37 | 38 | @Override 39 | public Set getProcessors() { 40 | final Set processors = new HashSet(); 41 | processors.add(new MessageListProcessor(new ElementNameProcessorMatcher(PROCESSOR_MESSAGES_NAME, true))); 42 | processors.add(new StandardContentProcessor(new AttributeNameProcessorMatcher(PROCESSOR_SRC_NAME))); 43 | return processors; 44 | } 45 | 46 | private class MessageListProcessor extends AbstractElementProcessor { 47 | 48 | protected MessageListProcessor(IElementNameProcessorMatcher matcher) { 49 | super(matcher); 50 | } 51 | 52 | @Override 53 | protected ProcessorResult processElement(Arguments arguments, Element element) { 54 | 55 | String group = element.getAttributeValue("group"); 56 | if (group == null) { 57 | group = MessageService.MESSAGE_DEFAULT_GROUP; 58 | } 59 | String type = element.getAttributeValue("type"); 60 | if (type == null) { 61 | type = MessageService.MESSAGE_DEFAULT_MESSAGE_TYPE.getName(); 62 | } 63 | MessageType messageType = MessageType.getMessageTypeByName(type); 64 | 65 | final NestableNode parent = element.getParent(); 66 | MessageList messages = messageService.getRequestNFlashMessages(messageType, group); 67 | if (messages != null) { 68 | Element container = new Element("ul"); 69 | element.addChild(container); 70 | for (String message : messages) { 71 | Element entry = new Element("li"); 72 | entry.addChild(new Text(message)); 73 | container.addChild(entry); 74 | } 75 | parent.insertBefore(element, container); 76 | } 77 | parent.removeChild(element); 78 | 79 | return ProcessorResult.OK; 80 | } 81 | 82 | @Override 83 | public int getPrecedence() { 84 | return 1000; 85 | } 86 | } 87 | 88 | private class StandardContentProcessor extends AbstractStandardSingleAttributeModifierAttrProcessor { 89 | 90 | protected StandardContentProcessor(IAttributeNameProcessorMatcher matcher) { 91 | super(matcher); 92 | } 93 | 94 | @Override 95 | public int getPrecedence() { 96 | return 1000; 97 | } 98 | 99 | @Override 100 | protected ModificationType getModificationType(Arguments arguments, Element element, String attributeName, String newAttributeName) { 101 | return ModificationType.SUBSTITUTION; 102 | } 103 | 104 | @Override 105 | protected boolean removeAttributeIfEmpty(Arguments arguments, Element element, String attributeName, String newAttributeName) { 106 | return false; 107 | } 108 | 109 | @Override 110 | protected String getTargetAttributeName(Arguments arguments, Element element, String attributeName) { 111 | return PROCESSOR_SRC_NAME; 112 | } 113 | 114 | @Override 115 | protected String getTargetAttributeValue( 116 | final Arguments arguments, final Element element, final String attributeName) { 117 | return portletService.getStaticContentUrl(element.getAttributeValue(attributeName)); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/handler/MappingExceptionResolver.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.handler; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.portlet.ModelAndView; 7 | import org.springframework.web.portlet.handler.SimpleMappingExceptionResolver; 8 | 9 | @Configuration 10 | public class MappingExceptionResolver extends SimpleMappingExceptionResolver { 11 | 12 | private static final String EXCEPTION_HEADER_FLAG = "internal-server-exception"; 13 | private final PortletService portletService; 14 | 15 | @Autowired 16 | public MappingExceptionResolver(PortletService portletService) { 17 | this.portletService = portletService; 18 | } 19 | 20 | protected ModelAndView getModelAndView(String viewName, Exception ex) { 21 | portletService.getPortletResponse().setProperty(EXCEPTION_HEADER_FLAG, Boolean.toString(true)); 22 | return super.getModelAndView(viewName, ex); 23 | } 24 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/model/MessageList.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.model; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class MessageList extends ArrayList { 6 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/model/MessageType.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.model; 2 | 3 | import java.text.MessageFormat; 4 | 5 | public enum MessageType { 6 | 7 | ERROR("error"), INFO("info"), WARNING("warning"); 8 | 9 | private final String name; 10 | 11 | MessageType(String name) { 12 | this.name = name; 13 | } 14 | 15 | public static MessageType getMessageTypeByName(String name) { 16 | for (MessageType messageType : MessageType.values()) { 17 | if (messageType.getName().equals(name)) { 18 | return messageType; 19 | } 20 | } 21 | throw new RuntimeException(MessageFormat.format("MessageType {0} not found.", name)); 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/model/MessageTypeList.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.model; 2 | 3 | import java.util.HashMap; 4 | 5 | public class MessageTypeList extends HashMap { 6 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/I18nMessageConstants.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | public class I18nMessageConstants { 4 | 5 | public static final String MISSING_PROPERTY_INDICATOR = "??##**!!__"; 6 | public static final String CONFIGURATION_LANGUAGE_TO_UPDATE = "language_toupdate"; 7 | public static final String TEMPLATE_RESOURCE_BUNDLE_BASENAME = "content/language_template"; 8 | public static final String CONFIGURATION_LANGUAGE_PREFIX = "language_"; 9 | public static final String BASENAME = "language"; 10 | public static final String RESOURCE_BUNDLE_FILENAME_SUFFIX = ".properties"; 11 | public static final String RESOURCE_DEFAULT_PATH = "/language-bundles"; 12 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/I18nMessageSourceImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import com.liferay.portal.kernel.language.LanguageUtil; 4 | import com.liferay.portal.kernel.util.StringPool; 5 | import com.liferay.portal.util.PortalUtil; 6 | import org.joda.time.DateTime; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.MessageSource; 9 | import org.springframework.context.support.AbstractMessageSource; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.web.portlet.util.PortletUtils; 12 | 13 | import javax.portlet.PortletPreferences; 14 | import javax.portlet.ReadOnlyException; 15 | import javax.portlet.ValidatorException; 16 | import java.io.*; 17 | import java.net.MalformedURLException; 18 | import java.net.URL; 19 | import java.net.URLClassLoader; 20 | import java.nio.charset.StandardCharsets; 21 | import java.text.MessageFormat; 22 | import java.util.*; 23 | 24 | @Service 25 | public class I18nMessageSourceImpl extends AbstractMessageSource implements MessageSource { 26 | 27 | private static final int MINUTES = 5; 28 | private final PortletService portletService; 29 | private final Map resourceBundles; 30 | private DateTime timestamp; 31 | private boolean toUpdate; 32 | 33 | @Autowired 34 | public I18nMessageSourceImpl(PortletService portletService) { 35 | this.portletService = portletService; 36 | resourceBundles = new HashMap(); 37 | timestamp = new DateTime().minusMinutes(MINUTES); // garantees file generation on first run 38 | toUpdate = true; 39 | } 40 | 41 | /** 42 | * get {@link MessageFormat} by code and locale 43 | * return . 44 | */ 45 | @Override 46 | protected MessageFormat resolveCode(String code, Locale locale) { 47 | 48 | return new MessageFormat(getText(locale, code)); 49 | } 50 | 51 | private String getText(Locale locale, String code) { 52 | 53 | ResourceBundle resourceBundle = getResourceBundle(locale); 54 | if (resourceBundle == null) { 55 | return getFallback(locale, code); 56 | } 57 | try { 58 | return resourceBundle.getString(code); 59 | } catch (MissingResourceException e) { 60 | return getFallback(locale, code); 61 | } 62 | } 63 | 64 | private String getFallback(Locale locale, String code) { 65 | try { // fallback to the portlet language bundle in resources/content 66 | return LanguageUtil.get(portletService.getPortletConfig(), locale, code); 67 | } catch (MissingResourceException e) { 68 | return I18nMessageConstants.MISSING_PROPERTY_INDICATOR + code + StringPool.UNDERLINE + locale; 69 | } 70 | } 71 | 72 | private ResourceBundle getResourceBundle(Locale locale) { 73 | 74 | if (!isResourceBundleUpToDate()) { 75 | regenerateBundleFiles(); 76 | } 77 | 78 | ResourceBundle resourceBundle = resourceBundles.get(locale); 79 | if (resourceBundle == null) { 80 | resourceBundle = loadResourceBundleFromFile(locale); 81 | } 82 | return resourceBundle; 83 | } 84 | 85 | private ResourceBundle loadResourceBundleFromFile(Locale locale) { 86 | 87 | // load ResourceBundle from Filesystem 88 | File dir = new File(getDefaultPath()); 89 | URL[] urls = new URL[0]; 90 | try { 91 | urls = new URL[]{dir.toURI().toURL()}; 92 | } catch (MalformedURLException e) { 93 | e.printStackTrace(); 94 | } 95 | ClassLoader loader = new URLClassLoader(urls); 96 | ResourceBundle resourceBundle = null; 97 | try { 98 | resourceBundle = ResourceBundle.getBundle(I18nMessageConstants.BASENAME, locale, loader); 99 | resourceBundles.put(locale, resourceBundle); 100 | } catch (MissingResourceException e) { 101 | } 102 | return resourceBundle; 103 | } 104 | 105 | private boolean isResourceBundleUpToDate() { 106 | // check resourcebundle not every time. only very x minutes 107 | DateTime newTimeStamp = new DateTime(); 108 | if (newTimeStamp.isAfter(timestamp)) { // never set before or timeout 109 | timestamp = newTimeStamp.plusMinutes(MINUTES); 110 | toUpdate = updateToUpdate(); 111 | return !toUpdate; // update happened in flag 112 | } 113 | return true; 114 | } 115 | 116 | private synchronized boolean updateToUpdate() { 117 | PortletPreferences portletPreferences = portletService.getPortletPreferences(); 118 | if (portletPreferences.getMap().size() == 0) { 119 | return false; 120 | } 121 | String pref = portletPreferences.getValue(I18nMessageConstants.CONFIGURATION_LANGUAGE_TO_UPDATE, StringPool.BLANK).toString(); 122 | String key = getInstanceKey(); 123 | if (!pref.contains(key)) { 124 | try { 125 | portletPreferences.setValue(I18nMessageConstants.CONFIGURATION_LANGUAGE_TO_UPDATE, pref + key); 126 | } catch (ReadOnlyException e) { 127 | e.printStackTrace(); 128 | } 129 | try { 130 | portletPreferences.store(); 131 | } catch (IOException e) { 132 | e.printStackTrace(); 133 | } catch (ValidatorException e) { 134 | e.printStackTrace(); 135 | } 136 | return true; 137 | } 138 | return false; 139 | } 140 | 141 | private String getInstanceKey() { 142 | return StringPool.PIPE + PortalUtil.getComputerAddress() + StringPool.EQUAL + PortalUtil.getComputerName() + StringPool.PIPE; 143 | } 144 | 145 | private synchronized void regenerateBundleFiles() { 146 | 147 | if (!toUpdate) { // method is synchronized - check again if necessary. probably it has already been happen 148 | return; 149 | } 150 | toUpdate = false; 151 | 152 | resourceBundles.clear(); 153 | 154 | PortletPreferences portletPreferences = portletService.getPortletPreferences(); 155 | if (portletPreferences == null) { 156 | return; 157 | } 158 | 159 | File dir = new File(getDefaultPath()); 160 | boolean dirExists = true; 161 | if (!dir.exists()) { 162 | dirExists = dir.mkdirs(); 163 | } 164 | if (dirExists) { 165 | for (Locale availableLocale : LanguageUtil.getAvailableLocales()) { 166 | String languageContent = portletPreferences.getValue(I18nMessageConstants.CONFIGURATION_LANGUAGE_PREFIX + availableLocale.toString(), StringPool.BLANK); 167 | String filename = I18nMessageConstants.BASENAME + StringPool.UNDERLINE + availableLocale.toString() + I18nMessageConstants.RESOURCE_BUNDLE_FILENAME_SUFFIX; 168 | try { 169 | PrintWriter writer = null; 170 | writer = new PrintWriter(dir.getAbsolutePath() + File.separator + filename, String.valueOf(StandardCharsets.ISO_8859_1)); 171 | writer.print(languageContent); 172 | writer.close(); 173 | } catch (FileNotFoundException e) { 174 | e.printStackTrace(); 175 | } catch (UnsupportedEncodingException e) { 176 | e.printStackTrace(); 177 | } 178 | } 179 | } 180 | } 181 | 182 | private String getDefaultPath() { 183 | return PortletUtils.getTempDir(portletService.getPortletContext()).getAbsolutePath() + I18nMessageConstants.RESOURCE_DEFAULT_PATH; 184 | } 185 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/MessageService.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageList; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageType; 5 | 6 | import java.util.Locale; 7 | 8 | public interface MessageService { 9 | 10 | String MESSAGE_DEFAULT_GROUP = "global"; 11 | MessageType MESSAGE_DEFAULT_MESSAGE_TYPE = MessageType.ERROR; 12 | 13 | void addRequestMessage(String message); 14 | 15 | void addRequestMessage(String message, String group); 16 | 17 | void addRequestMessage(String message, MessageType messageType); 18 | 19 | void addRequestMessage(String message, MessageType messageType, String group); 20 | 21 | void addRequestMessage(String message, Object[] arg); 22 | 23 | void addRequestMessage(String message, Object[] arg, String group); 24 | 25 | void addRequestMessage(String message, Object[] arg, MessageType messageType); 26 | 27 | void addRequestMessage(String message, Object[] arg, MessageType messageType, String group); 28 | 29 | MessageList getRequestNFlashMessages(MessageType messageType, String group); 30 | 31 | String getMessage(String message); 32 | 33 | String getMessage(String message, Object[] arg); 34 | 35 | String getMessage(String message, Object[] arg, Locale locale); 36 | 37 | void addFlashMessage(String message); 38 | 39 | void addFlashMessage(String message, String group); 40 | 41 | void addFlashMessage(String message, MessageType messageType); 42 | 43 | void addFlashMessage(String message, MessageType messageType, String group); 44 | 45 | void addFlashMessage(String message, Object[] arg); 46 | 47 | void addFlashMessage(String message, Object[] arg, String group); 48 | 49 | void addFlashMessage(String message, Object[] arg, MessageType messageType); 50 | 51 | void addFlashMessage(String message, Object[] arg, MessageType messageType, String group); 52 | } 53 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/MessageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.component.request.MessageStore; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageList; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageType; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.MessageSource; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.Locale; 11 | 12 | @Service 13 | public class MessageServiceImpl implements MessageService { 14 | 15 | private final PortletService portletService; 16 | private final MessageSource messageSource; 17 | private MessageStore messageStore; 18 | 19 | @Autowired 20 | public MessageServiceImpl(PortletService portletService, MessageSource messageSource, MessageStore messageStore) { 21 | this.portletService = portletService; 22 | this.messageSource = messageSource; 23 | this.messageStore = messageStore; 24 | } 25 | 26 | @Override 27 | public void addRequestMessage(String message) { 28 | addRequestMessage(message, null, MESSAGE_DEFAULT_MESSAGE_TYPE, MESSAGE_DEFAULT_GROUP); 29 | } 30 | 31 | @Override 32 | public void addRequestMessage(String message, String group) { 33 | addRequestMessage(message, null, MESSAGE_DEFAULT_MESSAGE_TYPE, group); 34 | } 35 | 36 | @Override 37 | public void addRequestMessage(String message, MessageType messageType) { 38 | addRequestMessage(message, null, messageType, MESSAGE_DEFAULT_GROUP); 39 | } 40 | 41 | @Override 42 | public void addRequestMessage(String message, MessageType messageType, String group) { 43 | addRequestMessage(message, null, messageType, group); 44 | } 45 | 46 | @Override 47 | public void addRequestMessage(String message, Object[] arg) { 48 | addRequestMessage(message, arg, MESSAGE_DEFAULT_MESSAGE_TYPE, MESSAGE_DEFAULT_GROUP); 49 | } 50 | 51 | @Override 52 | public void addRequestMessage(String message, Object[] arg, String group) { 53 | addRequestMessage(message, arg, MESSAGE_DEFAULT_MESSAGE_TYPE, group); 54 | } 55 | 56 | @Override 57 | public void addRequestMessage(String message, Object[] arg, MessageType messageType) { 58 | addRequestMessage(message, arg, messageType, MESSAGE_DEFAULT_GROUP); 59 | } 60 | 61 | @Override 62 | public void addRequestMessage(String message, Object[] arg, MessageType messageType, String group) { 63 | String fullMessage = messageSource.getMessage(message, arg, portletService.getLocale()); 64 | messageStore.addRequestMessage(fullMessage, messageType, group); 65 | } 66 | 67 | @Override 68 | public MessageList getRequestNFlashMessages(MessageType messageType, String group) { 69 | return messageStore.getMessages(messageType, group); 70 | } 71 | 72 | @Override 73 | public String getMessage(String message) { 74 | return getMessage(message, null); 75 | } 76 | 77 | @Override 78 | public String getMessage(String message, Object[] arg) { 79 | return getMessage(message, arg, portletService.getLocale()); 80 | } 81 | 82 | @Override 83 | public String getMessage(String message, Object[] arg, Locale locale) { 84 | return messageSource.getMessage(message, arg, locale); 85 | } 86 | 87 | @Override 88 | public void addFlashMessage(String message) { 89 | addFlashMessage(message, null, MESSAGE_DEFAULT_MESSAGE_TYPE, MESSAGE_DEFAULT_GROUP); 90 | } 91 | 92 | @Override 93 | public void addFlashMessage(String message, String group) { 94 | addFlashMessage(message, null, MESSAGE_DEFAULT_MESSAGE_TYPE, group); 95 | } 96 | 97 | @Override 98 | public void addFlashMessage(String message, MessageType messageType) { 99 | addFlashMessage(message, null, messageType, MESSAGE_DEFAULT_GROUP); 100 | } 101 | 102 | @Override 103 | public void addFlashMessage(String message, MessageType messageType, String group) { 104 | addFlashMessage(message, null, messageType, group); 105 | } 106 | 107 | @Override 108 | public void addFlashMessage(String message, Object[] arg) { 109 | addFlashMessage(message, arg, MESSAGE_DEFAULT_MESSAGE_TYPE, MESSAGE_DEFAULT_GROUP); 110 | } 111 | 112 | @Override 113 | public void addFlashMessage(String message, Object[] arg, String group) { 114 | addFlashMessage(message, arg, MESSAGE_DEFAULT_MESSAGE_TYPE, group); 115 | } 116 | 117 | @Override 118 | public void addFlashMessage(String message, Object[] arg, MessageType messageType) { 119 | addFlashMessage(message, arg, messageType, MESSAGE_DEFAULT_GROUP); 120 | } 121 | 122 | @Override 123 | public void addFlashMessage(String message, Object[] arg, MessageType messageType, String group) { 124 | String fullMessage = messageSource.getMessage(message, arg, portletService.getLocale()); 125 | messageStore.addFlashMessage(fullMessage, messageType, group); 126 | } 127 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/PortletService.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import com.liferay.portal.theme.ThemeDisplay; 4 | 5 | import javax.portlet.*; 6 | import java.util.Locale; 7 | import java.util.Map; 8 | 9 | public interface PortletService { 10 | 11 | String STATIC_CONTENT_RESOURCE_URL = "/static/"; 12 | 13 | PortletConfig getPortletConfig(); 14 | 15 | PortletRequest getPortletRequest(); 16 | 17 | ThemeDisplay getThemeDisplay(); 18 | 19 | PortletURL getPortletUrl(Map params); 20 | 21 | PortletURL getPortletUrl(Map params, String url, String portletId); 22 | 23 | String getPortletId(String portletName); 24 | 25 | RenderRequest getRenderRequest(); 26 | 27 | ResourceRequest getResourceRequest(); 28 | 29 | ActionRequest getActionRequest(); 30 | 31 | EventRequest getEventRequest(); 32 | 33 | PortletResponse getPortletResponse(); 34 | 35 | RenderResponse getRenderResponse(); 36 | 37 | ResourceResponse getResourceResponse(); 38 | 39 | ActionResponse getActionResponse(); 40 | 41 | EventResponse getEventResponse(); 42 | 43 | PortletSession getPortletSession(); 44 | 45 | Locale getLocale(); 46 | 47 | WindowState getWindowStateExclusive(); 48 | 49 | WindowState getWindowStatePopup(); 50 | 51 | WindowState getWindowStateMaximized(); 52 | 53 | WindowState getWindowStateMinimized(); 54 | 55 | WindowState getWindowStateNormal(); 56 | 57 | String getStaticContentUrl(String path); 58 | 59 | PortletPreferences getPortletPreferences(); 60 | 61 | PortletContext getPortletContext(); 62 | } 63 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/PortletServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import com.liferay.portal.kernel.exception.PortalException; 4 | import com.liferay.portal.kernel.exception.SystemException; 5 | import com.liferay.portal.kernel.portlet.LiferayWindowState; 6 | import com.liferay.portal.kernel.util.JavaConstants; 7 | import com.liferay.portal.model.Layout; 8 | import com.liferay.portal.service.LayoutLocalServiceUtil; 9 | import com.liferay.portal.service.PortletLocalServiceUtil; 10 | import com.liferay.portal.service.ServiceContextThreadLocal; 11 | import com.liferay.portal.theme.ThemeDisplay; 12 | import com.liferay.portal.util.PortalUtil; 13 | import com.liferay.portlet.PortletURLFactoryUtil; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.web.context.request.RequestAttributes; 16 | import org.springframework.web.context.request.RequestContextHolder; 17 | 18 | import javax.portlet.*; 19 | import javax.servlet.http.HttpServletRequest; 20 | import java.util.List; 21 | import java.util.Locale; 22 | import java.util.Map; 23 | 24 | @Service 25 | public class PortletServiceImpl implements PortletService { 26 | 27 | @Override 28 | public PortletRequest getPortletRequest() { 29 | Object o = RequestContextHolder.currentRequestAttributes().getAttribute(JavaConstants.JAVAX_PORTLET_REQUEST, RequestAttributes.SCOPE_REQUEST); 30 | if (o instanceof PortletRequest) { 31 | return ((PortletRequest) o); 32 | } 33 | throw new RuntimeException("no PortletRequest."); 34 | } 35 | 36 | @Override 37 | public PortletConfig getPortletConfig() { 38 | Object o = getPortletRequest().getAttribute(JavaConstants.JAVAX_PORTLET_CONFIG); 39 | if (o instanceof PortletConfig) { 40 | return ((PortletConfig) o); 41 | } 42 | throw new RuntimeException("no PortletConfig."); 43 | } 44 | 45 | @Override 46 | public PortletPreferences getPortletPreferences() { 47 | return getPortletRequest().getPreferences(); 48 | } 49 | 50 | @Override 51 | public PortletContext getPortletContext() { 52 | return getPortletSession().getPortletContext(); 53 | } 54 | 55 | @Override 56 | public ThemeDisplay getThemeDisplay() { 57 | return ServiceContextThreadLocal.getServiceContext().getThemeDisplay(); 58 | } 59 | 60 | @Override 61 | public PortletURL getPortletUrl(Map params) { 62 | return getPortletUrl(params, getThemeDisplay().getLayout().getPlid(), PortalUtil.getPortletId(getPortletRequest())); 63 | } 64 | 65 | @Override 66 | public PortletURL getPortletUrl(Map params, String url, String portletName) { 67 | Layout layout = null; 68 | try { 69 | layout = LayoutLocalServiceUtil.getFriendlyURLLayout(getThemeDisplay().getScopeGroupId(), false, url); 70 | } catch (PortalException e) { 71 | e.printStackTrace(); 72 | } catch (SystemException e) { 73 | e.printStackTrace(); 74 | } 75 | if (layout == null) { 76 | throw new RuntimeException("no Layout."); 77 | } 78 | return getPortletUrl(params, layout.getPlid(), getPortletId(portletName)); 79 | } 80 | 81 | private PortletURL getPortletUrl(Map params, long plid, String portletId) { 82 | PortletURL redirectURL = PortletURLFactoryUtil.create(getPortletRequest(), portletId, plid, PortletRequest.RENDER_PHASE); 83 | 84 | for (String key : params.keySet()) { 85 | redirectURL.setParameter(key, params.get(key)); 86 | } 87 | try { 88 | redirectURL.setWindowState(LiferayWindowState.NORMAL); 89 | } catch (WindowStateException e) { 90 | e.printStackTrace(); 91 | } 92 | try { 93 | redirectURL.setPortletMode(PortletMode.VIEW); 94 | } catch (PortletModeException e) { 95 | e.printStackTrace(); 96 | } 97 | return redirectURL; 98 | } 99 | 100 | @Override 101 | public RenderRequest getRenderRequest() { 102 | PortletRequest o = getPortletRequest(); 103 | if (o instanceof RenderRequest) { 104 | return ((RenderRequest) o); 105 | } 106 | throw new RuntimeException("no RenderRequest."); 107 | } 108 | 109 | @Override 110 | public ResourceRequest getResourceRequest() { 111 | PortletRequest o = getPortletRequest(); 112 | if (o instanceof ResourceRequest) { 113 | return ((ResourceRequest) o); 114 | } 115 | throw new RuntimeException("no ResourceRequest."); 116 | } 117 | 118 | @Override 119 | public ActionRequest getActionRequest() { 120 | PortletRequest o = getPortletRequest(); 121 | if (o instanceof ActionRequest) { 122 | return ((ActionRequest) o); 123 | } 124 | throw new RuntimeException("no ActionRequest."); 125 | } 126 | 127 | @Override 128 | public EventRequest getEventRequest() { 129 | PortletRequest o = getPortletRequest(); 130 | if (o instanceof EventRequest) { 131 | return ((EventRequest) o); 132 | } 133 | throw new RuntimeException("no EventRequest."); 134 | } 135 | 136 | @Override 137 | public PortletResponse getPortletResponse() { 138 | Object o = getPortletRequest().getAttribute(JavaConstants.JAVAX_PORTLET_RESPONSE); 139 | if (o instanceof PortletResponse) { 140 | return ((PortletResponse) o); 141 | } 142 | throw new RuntimeException("no PortletResponse."); 143 | } 144 | 145 | @Override 146 | public RenderResponse getRenderResponse() { 147 | PortletResponse o = getPortletResponse(); 148 | if (o instanceof RenderResponse) { 149 | return ((RenderResponse) o); 150 | } 151 | throw new RuntimeException("no RenderResponse."); 152 | } 153 | 154 | @Override 155 | public ResourceResponse getResourceResponse() { 156 | PortletResponse o = getPortletResponse(); 157 | if (o instanceof ResourceResponse) { 158 | return ((ResourceResponse) o); 159 | } 160 | throw new RuntimeException("no ResourceResponse."); 161 | } 162 | 163 | @Override 164 | public ActionResponse getActionResponse() { 165 | PortletResponse o = getPortletResponse(); 166 | if (o instanceof ActionResponse) { 167 | return ((ActionResponse) o); 168 | } 169 | throw new RuntimeException("no ActionResponse."); 170 | } 171 | 172 | @Override 173 | public EventResponse getEventResponse() { 174 | PortletResponse o = getPortletResponse(); 175 | if (o instanceof EventResponse) { 176 | return ((EventResponse) o); 177 | } 178 | throw new RuntimeException("no EventResponse."); 179 | } 180 | 181 | @Override 182 | public PortletSession getPortletSession() { 183 | return getPortletRequest().getPortletSession(); 184 | } 185 | 186 | @Override 187 | public Locale getLocale() { 188 | return getPortletRequest().getLocale(); 189 | } 190 | 191 | @Override 192 | public WindowState getWindowStateExclusive() { 193 | return LiferayWindowState.EXCLUSIVE; 194 | } 195 | 196 | @Override 197 | public WindowState getWindowStatePopup() { 198 | return LiferayWindowState.POP_UP; 199 | } 200 | 201 | @Override 202 | public WindowState getWindowStateMaximized() { 203 | return LiferayWindowState.MAXIMIZED; 204 | } 205 | 206 | @Override 207 | public WindowState getWindowStateMinimized() { 208 | return LiferayWindowState.MINIMIZED; 209 | } 210 | 211 | @Override 212 | public WindowState getWindowStateNormal() { 213 | return LiferayWindowState.NORMAL; 214 | } 215 | 216 | @Override 217 | public String getStaticContentUrl(String path) { 218 | PortletRequest portletRequest = getPortletRequest(); 219 | HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(portletRequest); 220 | return PortalUtil.getStaticResourceURL(httpServletRequest, portletRequest.getContextPath() + STATIC_CONTENT_RESOURCE_URL + path); 221 | } 222 | 223 | @Override 224 | public String getPortletId(String portletName) { 225 | List portletList = PortletLocalServiceUtil.getPortlets(); 226 | for ( 227 | com.liferay.portal.model.Portlet portlet : portletList) { 228 | if (portletName.equals(portlet.getPortletName())) { 229 | return portlet.getPortletId(); 230 | } 231 | } 232 | throw new RuntimeException("no valid PortletName:" + portletName); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/RenderService.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import org.springframework.ui.ModelMap; 4 | import org.springframework.web.servlet.ModelAndView; 5 | 6 | public interface RenderService { 7 | 8 | String renderTemplate(ModelAndView modelAndView); 9 | 10 | String renderTemplate(String viewName, ModelMap modelMap); 11 | 12 | String renderTemplate(String s); 13 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/service/RenderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.service; 2 | 3 | import com.liferay.portal.util.PortalUtil; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.ui.ModelMap; 7 | import org.springframework.web.servlet.ModelAndView; 8 | import org.thymeleaf.context.IContext; 9 | import org.thymeleaf.context.WebContext; 10 | import org.thymeleaf.spring4.SpringTemplateEngine; 11 | import org.thymeleaf.spring4.view.AjaxThymeleafView; 12 | import org.thymeleaf.spring4.view.ThymeleafView; 13 | import org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | 18 | @Service 19 | public class RenderServiceImpl implements RenderService { 20 | 21 | private static final java.lang.String FRAGMENT_SEPARATOR = "::"; 22 | private final PortletService portletService; 23 | private final SpringTemplateEngine templateEngine; 24 | 25 | @Autowired 26 | public RenderServiceImpl(PortletService portletService, SpringTemplateEngine templateEngine) { 27 | this.portletService = portletService; 28 | this.templateEngine = templateEngine; 29 | } 30 | 31 | @Override 32 | public String renderTemplate(ModelAndView modelAndView) { 33 | 34 | ThymeleafView view = new AjaxThymeleafView(); 35 | String[] views = modelAndView.getViewName().split(FRAGMENT_SEPARATOR); 36 | view.setTemplateName(views[0].trim()); 37 | if (views.length == 2) { 38 | view.setFragmentSpec(new StandardDOMSelectorFragmentSpec(views[1].trim())); 39 | } 40 | 41 | HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(portletService.getPortletRequest()); 42 | HttpServletResponse httpServletResponse = PortalUtil.getHttpServletResponse(portletService.getPortletResponse()); 43 | 44 | IContext iContext = new WebContext(httpServletRequest, httpServletResponse, httpServletRequest.getServletContext(), portletService.getLocale(), modelAndView.getModel()); 45 | return templateEngine.process(view.getTemplateName(), iContext, view.getFragmentSpec()); 46 | } 47 | 48 | @Override 49 | public String renderTemplate(String viewName, ModelMap modelMap) { 50 | return renderTemplate(new ModelAndView(viewName, modelMap)); 51 | } 52 | 53 | @Override 54 | public String renderTemplate(String viewName) { 55 | return renderTemplate(viewName, null); 56 | } 57 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/base/util/Integration.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.base.util; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.http.MediaType; 6 | 7 | import java.io.IOException; 8 | import java.nio.charset.Charset; 9 | 10 | public class Integration { 11 | 12 | public static final MediaType JSON_UTF_8_CONTENT_TYPE = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); 13 | 14 | public static byte[] convertObjectToJsonBytes(Object object) throws IOException { 15 | ObjectMapper mapper = new ObjectMapper(); 16 | mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 17 | return mapper.writeValueAsBytes(object); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/core/portlet/config/PortletCoreConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.core.portlet.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | 5 | @Configuration 6 | public class PortletCoreConfig { 7 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-core/src/main/resources/jsp/portlet_i18n_configuration.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.I18nMessageConstants" %> 2 | <%@ page import="com.liferay.portal.kernel.language.LanguageUtil" %> 3 | <%@ page import="com.liferay.portal.kernel.util.StringPool" %> 4 | <%@ page import="org.springframework.core.io.ClassPathResource" %> 5 | <%@ page import="java.nio.file.Files" %> 6 | <%@ page import="java.nio.file.Paths" %> 7 | <%@ page import="java.util.Locale" %> 8 | <%@ taglib prefix="portlet" uri="http://java.sun.com/portlet" %> 9 | <%@ taglib prefix="aui" uri="http://liferay.com/tld/aui" %> 10 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 11 | <%@ taglib uri="http://liferay.com/tld/theme" prefix="theme" %> 12 | <%@ taglib prefix="liferay-ui" uri="http://liferay.com/tld/ui" %> 13 | 14 | 15 | 16 | 18 | 19 | <% 20 | String template = new String(Files.readAllBytes(Paths.get(new ClassPathResource(I18nMessageConstants.TEMPLATE_RESOURCE_BUNDLE_BASENAME + ".properties").getFile().getAbsolutePath()))); 21 | %> 22 |
23 |
Language Template
24 |
26 |
27 | <% 28 | for (Locale availableLocale : LanguageUtil.getAvailableLocales()) { 29 | String key = I18nMessageConstants.CONFIGURATION_LANGUAGE_PREFIX + availableLocale.toString(); 30 | String preference = "preferences" + StringPool.DOUBLE_DASH + key + StringPool.DOUBLE_DASH; 31 | String language = portletPreferences.getValue(key, StringPool.BLANK); 32 | String label = "Language " + availableLocale.toString(); 33 | %> 34 |
35 |
37 |
38 |
39 | <% 40 | } 41 | String preference = "preferences" + StringPool.DOUBLE_DASH + I18nMessageConstants.CONFIGURATION_LANGUAGE_TO_UPDATE + StringPool.DOUBLE_DASH; 42 | %> 43 | 44 | 45 |
46 |
-------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-parent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | ab.liferay.spring.mvc.thymeleaf.angular 7 | liferay-spring-mvc-thymeleaf-angular-parent 8 | 1.0-SNAPSHOT 9 | pom 10 | Spring MVC Thymeleaf Angular Parent 11 | 12 | UTF-8 13 | 4.1.5.RELEASE 14 | 15 | 16 | 17 | 18 | src/main/java 19 | 20 | **/*.java 21 | 22 | 23 | 24 | src/main/resources 25 | 26 | 27 | 28 | 29 | src/test/java 30 | 31 | **/*.java 32 | 33 | 34 | 35 | src/test/resources 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 3.2 43 | 44 | true 45 | true 46 | 1.7 47 | 1.7 48 | 128m 49 | 256m 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-surefire-plugin 55 | 2.18.1 56 | 57 | 2 58 | false 59 | ab.liferay.spring.mvc.thymeleaf.angular.core.base.util.Integration 60 | 61 | 62 | 63 | org.apache.maven.surefire 64 | surefire-junit47 65 | 2.18.1 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | com.liferay.maven.plugins 74 | liferay-maven-plugin 75 | ${liferay.version} 76 | 77 | ${liferay.auto.deploy.dir} 78 | ${liferay.app.server.deploy.dir} 79 | ${liferay.app.server.lib.global.dir} 80 | ${liferay.app.server.portal.dir} 81 | ${liferay.version} 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | ab.liferay.spring.mvc.thymeleaf.angular 91 | liferay-spring-mvc-thymeleaf-angular-core 92 | 1.0-SNAPSHOT 93 | 94 | 95 | org.springframework 96 | spring-test 97 | ${org.springframework.version} 98 | test 99 | 100 | 101 | com.liferay.portal 102 | portal-service 103 | ${liferay.version} 104 | provided 105 | 106 | 107 | com.liferay.maven.plugins 108 | liferay-maven-plugin 109 | ${liferay.version} 110 | 111 | 112 | com.liferay.portal 113 | util-taglib 114 | ${liferay.version} 115 | provided 116 | 117 | 118 | javax.servlet 119 | jstl 120 | 1.2 121 | 122 | 123 | javax.portlet 124 | portlet-api 125 | 2.0 126 | provided 127 | 128 | 129 | javax.servlet 130 | javax.servlet-api 131 | 3.1.0 132 | provided 133 | 134 | 135 | junit 136 | junit 137 | 4.12 138 | test 139 | 140 | 141 | org.springframework 142 | spring-webmvc-portlet 143 | ${org.springframework.version} 144 | 145 | 146 | org.thymeleaf 147 | thymeleaf-spring4 148 | 2.1.4.RELEASE 149 | 150 | 151 | com.fasterxml.jackson.core 152 | jackson-databind 153 | 2.5.1 154 | 155 | 156 | org.mockito 157 | mockito-core 158 | 1.10.19 159 | test 160 | 161 | 162 | org.assertj 163 | assertj-core 164 | 2.0.0 165 | test 166 | 167 | 168 | org.hamcrest 169 | hamcrest-all 170 | 1.3 171 | test 172 | 173 | 174 | com.jayway.jsonpath 175 | json-path 176 | 1.2.0 177 | test 178 | 179 | 180 | org.jsoup 181 | jsoup 182 | 1.8.1 183 | test 184 | 185 | 186 | joda-time 187 | joda-time 188 | 2.2 189 | 190 | 191 | javax.servlet.jsp 192 | jsp-api 193 | 2.2 194 | provided 195 | 196 | 197 | 198 | 199 | ../liferay-spring-mvc-thymeleaf-angular-core 200 | ../liferay-spring-mvc-thymeleaf-angular-portlet 201 | 202 | 203 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | ab.liferay.spring.mvc.thymeleaf.angular 7 | liferay-spring-mvc-thymeleaf-angular-parent 8 | 1.0-SNAPSHOT 9 | ../liferay-spring-mvc-thymeleaf-angular-parent/pom.xml 10 | 11 | liferay-spring-mvc-thymeleaf-angular-portlet 12 | war 13 | Spring MVC Thymeleaf Angular Portlet 14 | 15 | 16 | 17 | com.liferay.maven.plugins 18 | liferay-maven-plugin 19 | 20 | portlet 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-war-plugin 26 | 2.6 27 | 28 | 29 | 30 | ab.liferay.spring.mvc.thymeleaf.angular 31 | liferay-spring-mvc-thymeleaf-angular-core 32 | jar 33 | 34 | **/*.jsp 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ab.liferay.spring.mvc.thymeleaf.angular 46 | liferay-spring-mvc-thymeleaf-angular-core 47 | 48 | 49 | javax.portlet 50 | portlet-api 51 | 52 | 53 | javax.servlet 54 | javax.servlet-api 55 | 56 | 57 | com.liferay.portal 58 | portal-service 59 | 60 | 61 | com.liferay.portal 62 | util-taglib 63 | 64 | 65 | javax.servlet 66 | jstl 67 | 68 | 69 | org.mockito 70 | mockito-core 71 | 72 | 73 | junit 74 | junit 75 | 76 | 77 | org.jsoup 78 | jsoup 79 | 80 | 81 | org.springframework 82 | spring-test 83 | 84 | 85 | org.assertj 86 | assertj-core 87 | 88 | 89 | org.hamcrest 90 | hamcrest-all 91 | 92 | 93 | com.jayway.jsonpath 94 | json-path 95 | 96 | 97 | com.fasterxml.jackson.core 98 | jackson-databind 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/base/config/BaseConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.base.config; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.config.CoreConfig; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @Configuration 8 | @Import(CoreConfig.class) 9 | public class BaseConfig { } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/delegate/config/DelegateConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.delegate.config; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.converter.HttpMessageConverter; 8 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 9 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 11 | 12 | import com.fasterxml.jackson.annotation.JsonInclude; 13 | import com.fasterxml.jackson.databind.ObjectMapper; 14 | 15 | @Configuration 16 | @EnableWebMvc 17 | @ComponentScan(basePackages = { "ab.liferay.spring.mvc.thymeleaf.angular.delegate.controller" }) 18 | public class DelegateConfig extends WebMvcConfigurerAdapter { 19 | 20 | @Override 21 | public void configureMessageConverters(List> converters) { 22 | final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); 23 | final ObjectMapper objectMapper = new ObjectMapper(); 24 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 25 | converter.setObjectMapper(objectMapper); 26 | converters.add(converter); 27 | super.configureMessageConverters(converters); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/delegate/controller/DelegateRestController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.delegate.controller; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import org.springframework.web.context.request.RequestContextHolder; 14 | import org.springframework.web.context.request.ServletRequestAttributes; 15 | 16 | import com.liferay.portal.kernel.exception.PortalException; 17 | import com.liferay.portal.kernel.exception.SystemException; 18 | import com.liferay.portal.model.User; 19 | import com.liferay.portal.util.PortalUtil; 20 | 21 | @RestController 22 | @RequestMapping("api/names") 23 | class DelegateRestController { 24 | 25 | private static final Logger LOG = LoggerFactory.getLogger(DelegateRestController.class); 26 | 27 | @RequestMapping(value = "/all", method = RequestMethod.GET) 28 | public List geNames() throws SystemException, PortalException { 29 | LOG.info("Got request for RestContactController getContacts"); 30 | ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 31 | HttpServletRequest req = sra.getRequest(); 32 | User user = PortalUtil.getUser(req); 33 | if (user != null) { 34 | return Arrays.asList(user.getScreenName(), user.getFullName()); 35 | } 36 | return Arrays.asList("name1", "name2"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/config/PortletConfig.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.config; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.portlet.config.PortletCoreConfig; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | 8 | @Configuration 9 | @Import(PortletCoreConfig.class) 10 | @ComponentScan(basePackages = {"ab.liferay.spring.mvc.thymeleaf.angular.portlet.service", "ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller"}) 11 | public class PortletConfig { } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/controller/PersonRestController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.PortletRequestBody; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.PortletResponseBody; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.ViewController; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.RenderService; 7 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 8 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 9 | import com.liferay.portal.kernel.log.Log; 10 | import com.liferay.portal.kernel.log.LogFactoryUtil; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.ui.ModelMap; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestMethod; 15 | import org.springframework.web.portlet.bind.annotation.ResourceMapping; 16 | 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | @ViewController 22 | public class PersonRestController { 23 | 24 | public static final String REST_RESOURCE = "rest"; 25 | public static final String HTML_JSON_RESOURCE = "htmljson"; 26 | private static Log _log = LogFactoryUtil.getLog(PersonRestController.class); 27 | private final RenderService renderService; 28 | private final PersonService personService; 29 | 30 | @Autowired 31 | public PersonRestController(PersonService personService, RenderService renderService) { 32 | this.personService = personService; 33 | this.renderService = renderService; 34 | } 35 | 36 | @ResourceMapping(value = REST_RESOURCE) 37 | @RequestMapping(method = RequestMethod.GET) 38 | @PortletResponseBody 39 | public List rest() { 40 | 41 | return personService.getPersons(); 42 | } 43 | 44 | @ResourceMapping(REST_RESOURCE) 45 | @RequestMapping(method = RequestMethod.POST) 46 | public void rest(@PortletRequestBody Person person) { 47 | 48 | personService.addPerson(person); 49 | } 50 | 51 | @ResourceMapping(HTML_JSON_RESOURCE) 52 | @RequestMapping(method = RequestMethod.GET) 53 | @PortletResponseBody 54 | public Map html() { 55 | 56 | Map jsonMap = new HashMap(); 57 | List persons = personService.getPersons(); 58 | 59 | ModelMap modelMap = new ModelMap(); 60 | modelMap.put("persons", persons); 61 | String html = renderService.renderTemplate("index/index :: persons", modelMap); 62 | jsonMap.put("persons", html); 63 | 64 | modelMap = new ModelMap(); 65 | modelMap.put("count", persons.size()); 66 | html = renderService.renderTemplate("index/index :: count", modelMap); 67 | jsonMap.put("count", html); 68 | 69 | return jsonMap; 70 | } 71 | 72 | // GET with querystring param: ...&person1={"firstname": "test-1-1"}&person2={"firstname": "test-1-2"} -> Working 73 | // @ResourceMapping("get-json-1") 74 | // @RequestMapping(method = RequestMethod.GET) 75 | // public void test1(@PortletRequestBody("person1") Person p1, @PortletRequestBody("person2") Person p2) { 76 | // System.out.println(p1.getFirstname() + " " + p2.getFirstname()); 77 | // return; 78 | // } 79 | 80 | // POST with body: {"person1": {"firstname": "test-2-1"}, "person2": {"firstname": "test-2-2"}} -> Working 81 | // @ResourceMapping("post-json-2") 82 | // @RequestMapping(method = RequestMethod.POST) 83 | // public void test2(@PortletRequestBody("person1") Person p1, @PortletRequestBody("person2") Person p2) { 84 | // System.out.println(p1.getFirstname() + " " + p2.getFirstname()); 85 | // return; 86 | // } 87 | 88 | // POST with body: {"firstname": "test3"} -> Working 89 | // @ResourceMapping("post-json-3") 90 | // @RequestMapping(method = RequestMethod.POST) 91 | // public void test3(@PortletRequestBody Person p) { 92 | // System.out.println(p.getFirstname()); 93 | // return; 94 | // } 95 | } 96 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/controller/PersonViewController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.annotation.ViewController; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.model.MessageType; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.MessageService; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 7 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 8 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 9 | import com.liferay.portal.kernel.log.Log; 10 | import com.liferay.portal.kernel.log.LogFactoryUtil; 11 | import com.liferay.portal.kernel.util.ParamUtil; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.ui.Model; 14 | import org.springframework.ui.ModelMap; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.portlet.bind.annotation.ActionMapping; 18 | import org.springframework.web.portlet.bind.annotation.RenderMapping; 19 | import org.springframework.web.servlet.ModelAndView; 20 | 21 | import javax.portlet.PortletURL; 22 | import javax.portlet.RenderResponse; 23 | import javax.portlet.ResourceURL; 24 | import javax.portlet.WindowStateException; 25 | import java.io.IOException; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | @ViewController 31 | public class PersonViewController { 32 | 33 | private static Log _log = LogFactoryUtil.getLog(PersonViewController.class); 34 | 35 | private final PersonService personService; 36 | private final PortletService portletService; 37 | private final MessageService messageService; 38 | 39 | @Autowired 40 | public PersonViewController(PersonService personService, PortletService portletService, MessageService messageService) { 41 | this.personService = personService; 42 | this.portletService = portletService; 43 | this.messageService = messageService; 44 | } 45 | 46 | @RenderMapping 47 | @RequestMapping 48 | public String view(ModelMap model) throws WindowStateException { 49 | 50 | _log.debug("handle view"); 51 | 52 | RenderResponse response = portletService.getRenderResponse(); 53 | 54 | List persons = personService.getPersons(); 55 | model.addAttribute("persons", persons); 56 | model.addAttribute("count", persons.size()); 57 | 58 | ResourceURL resourceURL = response.createResourceURL(); 59 | resourceURL.setResourceID(PersonRestController.REST_RESOURCE); 60 | model.addAttribute("resourceURL", resourceURL.toString()); 61 | 62 | PortletURL renderUrl = response.createRenderURL(); 63 | renderUrl.setParameter("personId", String.valueOf(personService.getPersons().get(0).getId())); 64 | model.addAttribute("renderUrl", renderUrl.toString()); 65 | 66 | PortletURL actionUrl = response.createActionURL(); 67 | actionUrl.setParameter("personId", String.valueOf(personService.getPersons().get(0).getId())); 68 | model.addAttribute("actionUrl", actionUrl.toString()); 69 | 70 | PortletURL ajaxUrl = response.createRenderURL(); 71 | ajaxUrl.setParameter("persons", ""); 72 | ajaxUrl.setWindowState(portletService.getWindowStateExclusive()); 73 | model.addAttribute("ajaxUrl", ajaxUrl.toString()); 74 | 75 | ResourceURL jsonHtmlUrl = response.createResourceURL(); 76 | jsonHtmlUrl.setResourceID(PersonRestController.HTML_JSON_RESOURCE); 77 | model.addAttribute("jsonHtmlUrl", jsonHtmlUrl.toString()); 78 | 79 | PortletURL friendlyActionRedirectUrl = response.createActionURL(); 80 | friendlyActionRedirectUrl.setParameter("action", "addRedirect"); 81 | friendlyActionRedirectUrl.setParameter("id", "5"); 82 | model.addAttribute("friendlyActionRedirectUrl", friendlyActionRedirectUrl.toString()); 83 | 84 | PortletURL friendlyActionForwardUrl = response.createActionURL(); 85 | friendlyActionForwardUrl.setParameter("action", "addForward"); 86 | friendlyActionForwardUrl.setParameter("id", "5"); 87 | model.addAttribute("friendlyActionForwardUrl", friendlyActionForwardUrl.toString()); 88 | 89 | PortletURL friendlyRenderUrl = response.createRenderURL(); 90 | friendlyRenderUrl.setParameter("render", "details"); 91 | friendlyRenderUrl.setParameter("id", "6"); 92 | model.addAttribute("friendlyRenderUrl", friendlyRenderUrl.toString()); 93 | 94 | messageService.addRequestMessage("index.info", MessageType.INFO); 95 | 96 | return "index/index"; 97 | } 98 | 99 | @RenderMapping(params = {"persons"}) 100 | public ModelAndView persons(Model model) { 101 | 102 | _log.debug("handle persons"); 103 | 104 | model.addAttribute("persons", personService.getPersons()); 105 | return new ModelAndView("index/index :: persons", model.asMap()); 106 | } 107 | 108 | @RenderMapping(params = {"personId"}) 109 | public String render(ModelMap model) { 110 | 111 | _log.debug("handle render"); 112 | 113 | long personId = ParamUtil.getLong(portletService.getPortletRequest(), "personId"); 114 | messageService.addRequestMessage("render.info.person", new Object[]{personId}, MessageType.INFO); 115 | return "index/render"; 116 | } 117 | 118 | @ActionMapping(params = {"personId"}) 119 | public void action() { 120 | 121 | _log.debug("handle action"); 122 | 123 | long personId = ParamUtil.getLong(portletService.getPortletRequest(), "personId"); 124 | messageService.addRequestMessage("action.warning.person", new Object[]{personId}, MessageType.WARNING); 125 | portletService.getActionResponse().setRenderParameter("personId", String.valueOf(personId)); 126 | } 127 | 128 | @ActionMapping(params = "action=addForward") 129 | public void friendlyActionForward(ModelMap model, @RequestParam("id") final String personId) throws IOException { 130 | 131 | _log.debug("handle friendly action forward"); 132 | 133 | messageService.addRequestMessage("action.forward.warning.person", new Object[]{personId}, MessageType.INFO); 134 | 135 | portletService.getActionResponse().setRenderParameter("id", String.valueOf(personId)); 136 | portletService.getActionResponse().setRenderParameter("render", "details"); 137 | } 138 | 139 | @ActionMapping(params = "action=addRedirect") 140 | public void friendlyActionRedirect(ModelMap model, @RequestParam("id") final String personId) throws IOException { 141 | 142 | _log.debug("handle friendly action redirect"); 143 | 144 | messageService.addFlashMessage("action.redirect.warning.person", new Object[]{personId}, MessageType.INFO); 145 | 146 | Map map = new HashMap(); 147 | map.put("render", "details"); 148 | map.put("id", String.valueOf(personId)); 149 | 150 | PortletURL internal = portletService.getPortletUrl(map); 151 | 152 | String url = "/test"; 153 | String portletName = "demo-portlet"; 154 | PortletURL external = portletService.getPortletUrl(map, url, portletName); 155 | 156 | _log.debug(internal); 157 | _log.debug(external); 158 | 159 | portletService.getActionResponse().sendRedirect(internal.toString()); 160 | } 161 | 162 | @RenderMapping(params = "render=details") 163 | public String friendlyRender(ModelMap model, @RequestParam("id") final String personId) { 164 | 165 | _log.debug("handle friendly render"); 166 | 167 | messageService.addRequestMessage("render.info.person", new Object[]{personId}, MessageType.INFO); 168 | return "index/render"; 169 | } 170 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/model/Person.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.model; 2 | 3 | public class Person { 4 | 5 | private long id; 6 | private String firstname; 7 | private String lastname; 8 | 9 | public Person() { } 10 | 11 | public Person(long id, String firstname, String lastname) { 12 | this.id = id; 13 | this.firstname = firstname; 14 | this.lastname = lastname; 15 | } 16 | 17 | public long getId() { 18 | return id; 19 | } 20 | 21 | public void setId(long id) { 22 | this.id = id; 23 | } 24 | 25 | public String getLastname() { 26 | return lastname; 27 | } 28 | 29 | public void setLastname(String lastname) { 30 | this.lastname = lastname; 31 | } 32 | 33 | public String getFirstname() { 34 | return firstname; 35 | } 36 | 37 | public void setFirstname(String firstname) { 38 | this.firstname = firstname; 39 | } 40 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/service/PersonService.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.service; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 4 | 5 | import java.util.List; 6 | 7 | public interface PersonService { 8 | 9 | List getPersons(); 10 | 11 | void addPerson(Person person); 12 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/service/PersonServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.service; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Service 10 | public class PersonServiceImpl implements PersonService { 11 | 12 | List persons; 13 | 14 | public PersonServiceImpl() { } 15 | 16 | @Override 17 | public List getPersons() { 18 | 19 | if (persons == null) { 20 | persons = new ArrayList(); 21 | 22 | addPerson(new Person(1, "DemoFirstname1", "DemoFirstname1")); 23 | addPerson(new Person(2, "DemoFirstname2", "DemoFirstname2")); 24 | addPerson(new Person(3, "DemoFirstname3", "DemoFirstname3")); 25 | } 26 | 27 | return persons; 28 | } 29 | 30 | @Override 31 | public void addPerson(Person person) { 32 | getPersons().add(person); 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/resources/content/language_template.properties: -------------------------------------------------------------------------------- 1 | welcome=Welcome 2 | description=Description 3 | index.info=Index Info 4 | # {0} ... personId 5 | render.info.person=Render Info Person {0} 6 | # {0} ... personId 7 | action.warning.person=Action Warning Person {0} 8 | # {0} ... personId 9 | action.forward.warning.person=Action Forward Warning Person {0} 10 | # {0} ... personId 11 | action.redirect.warning.person=Action Redirect Warning Person {0} 12 | error=Error -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/resources/friendly-url-router.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | /action/{action}/id/{id} 8 | {id} 9 | {action} 10 | 1 11 | 12 | 13 | 14 | 15 | 16 | /render/{render}/id/{id} 17 | {id} 18 | {render} 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/resources/portlet.properties: -------------------------------------------------------------------------------- 1 | include-and-override=${liferay.home}/liferay-spring-mvc-thymeleaf-angular-portlet-ext.properties 2 | resource.actions.configs=resource-actions/default.xml -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/resources/resource-actions/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | demo-portlet 8 | 9 | 10 | ADD_TO_PAGE 11 | VIEW 12 | CONFIGURATION 13 | HELP 14 | 15 | 16 | VIEW 17 | HELP 18 | 19 | 20 | VIEW 21 | HELP 22 | 23 | 24 | ADD_TO_PAGE 25 | CONFIGURATION 26 | 27 | 28 | VIEW 29 | HELP 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/liferay-display.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/liferay-plugin-package.properties: -------------------------------------------------------------------------------- 1 | name=Liferay Spring MVC Thymeleaf Angular Portlet 2 | module-group-id=ab.liferay.spring.mvc.thymeleaf.angular.portlet 3 | module-incremental-version=1 4 | tags=Liferay, Spring MVC, Thymeleaf, Angular 5 | short-description=Liferay Spring MVC Thymeleaf Angular Portlet 6 | page-url= 7 | author=Anton Bayer 8 | change-log= 9 | licenses= -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/liferay-portlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | demo-portlet 10 | com.liferay.portal.kernel.portlet.DefaultConfigurationAction 11 | 12 | 13 | com.liferay.portal.kernel.portlet.DefaultFriendlyURLMapper 14 | 15 | demo 16 | friendly-url-router.xml 17 | 18 | true 19 | false 20 | 21 | false 22 | 23 | /static/css/main.css 24 | 25 | /static/js/angular/angular-1.3.14.js 26 | /static/js/jquery/jquery-1.11.1.min.js 27 | /static/js/angular/app-init.js 28 | /static/js/angular/controller.js 29 | /static/js/angular/service.js 30 | /static/js/angular/factory.js 31 | /static/js/angular/app-bootstrap.js 32 | /static/js/jquery/app.js 33 | 34 | 35 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/portlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | demo-portlet 9 | Demo Portlet 10 | org.springframework.web.portlet.DispatcherPortlet 11 | 12 | 13 | 14 | contextClass 15 | ab.liferay.spring.mvc.thymeleaf.angular.core.base.context.AnnotationConfigurationPortletContext 16 | 17 | 18 | 19 | contextConfigLocation 20 | ab.liferay.spring.mvc.thymeleaf.angular.portlet.config.PortletConfig 21 | 22 | 23 | config-template 24 | /jsp/configuration.jsp 25 | 26 | 27 | 0 28 | 29 | 30 | text/html 31 | view 32 | 33 | 34 | content/language_template 35 | 36 | 37 | Demo Portlet 38 | Liferay Spring MVC Thymeleaf Angular Portlet 39 | Liferay Spring MVC Thymeleaf Angular Portlet 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/thymeleaf/templates/error/error.xhtml: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | 5 |
-------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/thymeleaf/templates/index/index.xhtml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 | 9 | 10 |

11 | 12 |

13 | 14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 |
26 |

Thymeleaf App

27 | 28 |
Update list with HTML 29 | ajax call 30 | (First add persons via angular. Count fragment will not get updated.) 31 |
32 |
Update list with Json ajax call 34 | (First add persons via angular.s) 35 |
36 |
37 | Count: 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 |
49 | 50 |
51 | 52 |
53 |
54 |

Angular App

55 | add Person 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
{{person.id}}{{person.firstname}}{{person.lastname}}
65 |
66 |
67 | 68 |
-------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/thymeleaf/templates/index/render.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 |

Render

10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | contextClass 9 | 10 | org.springframework.web.context.support.AnnotationConfigWebApplicationContext 11 | 12 | 13 | 14 | 15 | 16 | contextConfigLocation 17 | 18 | ab.liferay.spring.mvc.thymeleaf.angular.base.config.BaseConfig 19 | 20 | 21 | 22 | 23 | org.springframework.web.context.ContextLoaderListener 24 | 25 | 26 | 27 | ViewRendererServlet 28 | org.springframework.web.servlet.ViewRendererServlet 29 | 30 | 31 | 32 | ViewRendererServlet 33 | /WEB-INF/servlet/view 34 | 35 | 36 | 37 | PortalDelegateServlet 38 | com.liferay.portal.kernel.servlet.PortalDelegateServlet 39 | 40 | servlet-class 41 | org.springframework.web.servlet.DispatcherServlet 42 | 43 | 44 | sub-context 45 | api 46 | 47 | 48 | contextClass 49 | org.springframework.web.context.support.AnnotationConfigWebApplicationContext 50 | 51 | 52 | contextConfigLocation 53 | ab.liferay.spring.mvc.thymeleaf.angular.delegate.config.DelegateConfig 54 | 55 | 1 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/jsp/configuration.jsp: -------------------------------------------------------------------------------- 1 | 4 | <%@ taglib prefix="liferay-portlet" uri="http://liferay.com/tld/portlet" %> 5 | <%@ taglib prefix="aui" uri="http://liferay.com/tld/aui" %> 6 | <%@ page import="com.liferay.portal.kernel.util.Constants" %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <%@include file="portlet_i18n_configuration.jsp" %> 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonbayer/Liferay-Spring-MVC-Thymeleaf-Angular-Portlet/e8f3b33a47458295e6c1185962b2b63e12eb0129/liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/css/main.css -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/images/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonbayer/Liferay-Spring-MVC-Thymeleaf-Angular-Portlet/e8f3b33a47458295e6c1185962b2b63e12eb0129/liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/images/image.jpg -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/angular/app-bootstrap.js: -------------------------------------------------------------------------------- 1 | angular.bootstrap(document.getElementById(angularApp), [angularApp]); -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/angular/app-init.js: -------------------------------------------------------------------------------- 1 | var angularApp = 'angularApp'; 2 | 3 | angular.module(angularApp, [ 4 | angularApp + '.directives', 5 | angularApp + '.controllers', 6 | angularApp + '.services', 7 | angularApp + '.factories' 8 | ]).config(['$httpProvider', function ($httpProvider) { 9 | $httpProvider.interceptors.push('httpInterceptor'); 10 | }]); 11 | 12 | angular.module(angularApp + '.directives', []); 13 | angular.module(angularApp + '.controllers', []); 14 | angular.module(angularApp + '.services', []); 15 | angular.module(angularApp + '.factories', []); -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/angular/controller.js: -------------------------------------------------------------------------------- 1 | angular.module(angularApp + '.controllers') 2 | .controller('personController', ['personService', function (personService) { 3 | var thiz = this; 4 | thiz.persons = []; 5 | personService.getPersons().success(function (response) { 6 | thiz.persons = response; 7 | }); 8 | 9 | thiz.addPerson = function () { 10 | var person = {}; 11 | person.id = this.persons.length + 1; 12 | person.firstname = "DemoFirstname" + person.id; 13 | person.lastname = "DemoLastname" + person.id; 14 | person.lastname = "DemoLastname" + person.id; 15 | personService.addPerson(person).success(function (response) { 16 | thiz.persons.push(person); 17 | }); 18 | }; 19 | }]); -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/angular/factory.js: -------------------------------------------------------------------------------- 1 | angular.module(angularApp + '.controllers') 2 | .factory('httpInterceptor', ['$rootElement', '$q', function ($rootElement, $q) { 3 | return { 4 | response: function (response) { 5 | if (response.headers()['internal-server-exception']) { 6 | angular.element($rootElement).replaceWith(response.data); 7 | return $q.reject(response); 8 | } 9 | return response; 10 | } 11 | }; 12 | }]); -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/angular/service.js: -------------------------------------------------------------------------------- 1 | angular.module(angularApp + '.services') 2 | .service('personService', ['$http', function ($http) { 3 | this.getPersons = function () { 4 | return $http({ 5 | method: 'GET', 6 | url: resourceURL 7 | }); 8 | }; 9 | 10 | this.addPerson = function (person) { 11 | return $http({ 12 | method: 'POST', 13 | url: resourceURL, 14 | data: person 15 | }); 16 | }; 17 | }]); -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/main/webapp/static/js/jquery/app.js: -------------------------------------------------------------------------------- 1 | function updateFragment(thiz) { 2 | var $this = jQuery(thiz); 3 | jQuery.ajax({ 4 | url: $this.attr("href"), 5 | type: 'GET', 6 | success: function (response) { 7 | jQuery($this.data("update")).replaceWith(response); 8 | } 9 | }); 10 | return false; 11 | } 12 | 13 | function updateFragments(thiz) { 14 | var $this = jQuery(thiz); 15 | jQuery.ajax({ 16 | url: $this.attr("href"), 17 | type: 'GET', 18 | success: function (response) { 19 | var entries = parseFragments($this.data("update")); 20 | for (var prop in entries) { 21 | jQuery(entries[prop]).replaceWith(response[prop]); 22 | } 23 | } 24 | }); 25 | return false; 26 | 27 | function parseFragments(text) { 28 | var blocks = text.split("|"); 29 | var params = {}; 30 | for (var i = 0; i < blocks.length; i++) { 31 | var entry = blocks[i].split("="); 32 | params[entry[0]] = entry[1]; 33 | } 34 | return params; 35 | } 36 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/test/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/test/PersonRestControllerTest.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.test; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.RenderService; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.util.Integration; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller.PersonRestController; 7 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 8 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.experimental.categories.Category; 12 | import org.junit.runner.RunWith; 13 | import org.mockito.Mockito; 14 | import org.mockito.invocation.InvocationOnMock; 15 | import org.mockito.stubbing.Answer; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.mock.web.portlet.MockRenderResponse; 20 | import org.springframework.test.context.ContextConfiguration; 21 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 22 | import org.springframework.test.context.web.WebAppConfiguration; 23 | import org.springframework.test.web.servlet.MockMvc; 24 | import org.springframework.test.web.servlet.ResultActions; 25 | import org.springframework.web.context.WebApplicationContext; 26 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Locale; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | import static org.hamcrest.Matchers.hasSize; 34 | import static org.mockito.Matchers.anyObject; 35 | import static org.mockito.Mockito.doAnswer; 36 | import static org.mockito.Mockito.when; 37 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 38 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 39 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 40 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 41 | 42 | @RunWith(SpringJUnit4ClassRunner.class) 43 | @Category(Integration.class) 44 | @WebAppConfiguration 45 | @ContextConfiguration(classes = PersonRestControllerTest.Config.class) 46 | public class PersonRestControllerTest { 47 | 48 | @Autowired 49 | protected PortletService portletService; 50 | @Autowired 51 | private PersonService personService; 52 | @Autowired 53 | private WebApplicationContext ctx; 54 | 55 | private MockMvc mockMvc; 56 | 57 | private List persons; 58 | 59 | @Before 60 | public void setUp() { 61 | mockMvc = webAppContextSetup(ctx).build(); 62 | Locale.setDefault(Config.LOCALE); 63 | 64 | persons = new ArrayList(); 65 | persons.add(new Person(1, "TestFirstname1", "TestFirstname1")); 66 | persons.add(new Person(2, "TestFirstname3", "TestFirstname2")); 67 | persons.add(new Person(3, "TestFirstname3", "TestFirstname3")); 68 | when(personService.getPersons()).thenReturn(persons); 69 | } 70 | 71 | @Test 72 | public void restGet() throws Exception { 73 | 74 | ResultActions result = mockMvc.perform(get("/test/" + PersonRestController.REST_RESOURCE)); 75 | result.andExpect(status().isOk()) 76 | .andExpect(content().contentType(Integration.JSON_UTF_8_CONTENT_TYPE)) 77 | .andExpect(jsonPath("$", hasSize(personService.getPersons().size()))); 78 | 79 | assertThat(result.andReturn().getResponse().getContentAsString()).isNotEmpty(); 80 | } 81 | 82 | @Test 83 | public void restPost() throws Exception { 84 | 85 | final Person person = new Person(4, "TestFirstname4", "TestFirstname4"); 86 | 87 | doAnswer(new Answer() { 88 | @Override 89 | public Void answer(InvocationOnMock invocation) throws Throwable { 90 | persons.add((Person) invocation.getArguments()[0]); 91 | return null; 92 | } 93 | }).when(personService).addPerson((Person) anyObject()); 94 | 95 | 96 | ResultActions result = mockMvc.perform(post("/test/" + PersonRestController.REST_RESOURCE) 97 | .contentType(Integration.JSON_UTF_8_CONTENT_TYPE) 98 | .content(Integration.convertObjectToJsonBytes(person))); 99 | result.andExpect(status().isOk()) 100 | .andExpect(status().is(200)); 101 | 102 | restGet(); 103 | } 104 | 105 | @EnableWebMvc 106 | @Configuration 107 | public static class Config { 108 | 109 | public static Locale LOCALE = Locale.GERMAN; 110 | 111 | @Bean 112 | public PersonService personService() { 113 | 114 | PersonService mock = Mockito.mock(PersonService.class); 115 | return mock; 116 | } 117 | 118 | @Bean 119 | public PortletService portletService() { 120 | PortletService mock = Mockito.mock(PortletService.class); 121 | when(mock.getRenderResponse()).thenReturn(new MockRenderResponse()); 122 | when(mock.getLocale()).thenReturn(LOCALE); 123 | return mock; 124 | } 125 | 126 | @Bean 127 | public RenderService RenderService() { 128 | RenderService mock = Mockito.mock(RenderService.class); 129 | return mock; 130 | } 131 | 132 | @Bean 133 | public TestWrapperPersonRestController testPersonRestController(PersonService personService, RenderService renderService) { 134 | return new TestWrapperPersonRestController(personService, renderService); 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/test/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/test/PersonViewControllerTest.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.test; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.config.ThymeleafConfig; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.I18nMessageConstants; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.MessageService; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 7 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.util.Integration; 8 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 9 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 10 | import org.jsoup.Jsoup; 11 | import org.jsoup.nodes.Document; 12 | import org.jsoup.nodes.Element; 13 | import org.jsoup.select.Elements; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.junit.experimental.categories.Category; 17 | import org.junit.runner.RunWith; 18 | import org.mockito.Mockito; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.context.MessageSource; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | import org.springframework.context.annotation.Import; 24 | import org.springframework.context.support.ResourceBundleMessageSource; 25 | import org.springframework.mock.web.portlet.MockRenderResponse; 26 | import org.springframework.test.context.ContextConfiguration; 27 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 28 | import org.springframework.test.context.web.WebAppConfiguration; 29 | import org.springframework.test.web.servlet.MockMvc; 30 | import org.springframework.test.web.servlet.ResultActions; 31 | import org.springframework.web.context.WebApplicationContext; 32 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 33 | 34 | import javax.portlet.WindowState; 35 | import java.nio.charset.StandardCharsets; 36 | import java.util.ArrayList; 37 | import java.util.List; 38 | import java.util.Locale; 39 | 40 | import static org.assertj.core.api.Assertions.assertThat; 41 | import static org.junit.Assert.assertEquals; 42 | import static org.junit.Assert.assertFalse; 43 | import static org.mockito.Matchers.any; 44 | import static org.mockito.Mockito.when; 45 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 46 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 47 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 48 | 49 | @RunWith(SpringJUnit4ClassRunner.class) 50 | @Category(Integration.class) 51 | @WebAppConfiguration 52 | @ContextConfiguration(classes = PersonViewControllerTest.Config.class) 53 | public class PersonViewControllerTest { 54 | 55 | @Autowired 56 | private PersonService personService; 57 | 58 | @Autowired 59 | private PortletService portletService; 60 | 61 | @Autowired 62 | private WebApplicationContext ctx; 63 | 64 | private MockMvc mockMvc; 65 | 66 | private List persons; 67 | 68 | @Before 69 | public void setUp() { 70 | mockMvc = webAppContextSetup(ctx).build(); 71 | Locale.setDefault(Config.LOCALE); 72 | } 73 | 74 | @Test 75 | public void viewIndex() throws Exception { 76 | 77 | when(portletService.getWindowStateExclusive()).thenReturn(WindowState.NORMAL); 78 | 79 | persons = new ArrayList(); 80 | persons.add(new Person(1, "TestFirstname1", "TestLastname1")); 81 | persons.add(new Person(2, "TestFirstname2", "TestLastname2")); 82 | persons.add(new Person(3, "TestFirstname3", "TestLastname3")); 83 | when(personService.getPersons()).thenReturn(persons); 84 | 85 | ResultActions result = mockMvc.perform(get("/test/view").locale(Locale.getDefault())); 86 | result.andExpect(status().isOk()) 87 | .andExpect(view().name("index/index")) 88 | .andExpect(model().attributeExists("persons")) 89 | .andExpect(model().attributeExists("resourceURL")); 90 | 91 | String contentAsString = result.andReturn().getResponse().getContentAsString(); 92 | assertThat(contentAsString).isNotEmpty(); 93 | assertFalse(contentAsString.contains(I18nMessageConstants.MISSING_PROPERTY_INDICATOR)); 94 | 95 | Document parse = Jsoup.parse(contentAsString); 96 | Elements elements = parse.select("table#persons > tbody > tr"); 97 | List servicePersons = personService.getPersons(); 98 | assertEquals(elements.size(), servicePersons.size()); 99 | for (int i = 0; i < elements.size() || i < servicePersons.size(); i++) { 100 | Element tr = elements.get(i); 101 | Person person = servicePersons.get(i); 102 | assertEquals(tr.children().get(0).text(), String.valueOf(person.getId())); 103 | assertEquals(tr.children().get(1).text(), person.getFirstname()); 104 | assertEquals(tr.children().get(2).text(), person.getLastname()); 105 | } 106 | } 107 | 108 | @Import(ThymeleafConfig.class) 109 | @EnableWebMvc 110 | @Configuration 111 | public static class Config { 112 | 113 | private static final java.lang.String STATIC_URL = "/test/img.png"; 114 | public static Locale LOCALE = Locale.GERMAN; 115 | 116 | @Bean 117 | public PersonService personService() { 118 | PersonService mock = Mockito.mock(PersonService.class); 119 | return mock; 120 | } 121 | 122 | @Bean 123 | public PortletService portletService() { 124 | PortletService mock = Mockito.mock(PortletService.class); 125 | when(mock.getRenderResponse()).thenReturn(new MockRenderResponse()); 126 | when(mock.getStaticContentUrl(any(String.class))).thenReturn(STATIC_URL); 127 | when(mock.getLocale()).thenReturn(LOCALE); 128 | return mock; 129 | } 130 | 131 | @Bean 132 | public MessageService messageService() { 133 | MessageService mock = Mockito.mock(MessageService.class); 134 | return mock; 135 | } 136 | 137 | @Bean 138 | public MessageSource messageSource(final PortletService portletService) { 139 | 140 | ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 141 | messageSource.setBasename(I18nMessageConstants.TEMPLATE_RESOURCE_BUNDLE_BASENAME); 142 | messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString()); 143 | return messageSource; 144 | } 145 | 146 | @Bean 147 | public TestWrapperPersonViewController testPersonViewController(PersonService personService, PortletService portletService, MessageService messageService) { 148 | return new TestWrapperPersonViewController(personService, portletService, messageService); 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/test/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/test/TestWrapperPersonRestController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.test; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.RenderService; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller.PersonRestController; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.model.Person; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | 12 | import java.util.List; 13 | 14 | @RequestMapping("/test") 15 | public class TestWrapperPersonRestController extends PersonRestController { 16 | 17 | 18 | public TestWrapperPersonRestController(PersonService personService, RenderService renderService) { 19 | super(personService, renderService); 20 | } 21 | 22 | @RequestMapping(value = "/" + PersonRestController.REST_RESOURCE) 23 | @ResponseBody 24 | @Override 25 | public List rest() { 26 | return super.rest(); 27 | } 28 | 29 | @RequestMapping(value = "/" + PersonRestController.REST_RESOURCE, method = RequestMethod.POST) 30 | @Override 31 | public void rest(@RequestBody Person person) { 32 | super.rest(person); 33 | } 34 | } -------------------------------------------------------------------------------- /liferay-spring-mvc-thymeleaf-angular-portlet/src/test/java/ab/liferay/spring/mvc/thymeleaf/angular/portlet/test/TestWrapperPersonViewController.java: -------------------------------------------------------------------------------- 1 | package ab.liferay.spring.mvc.thymeleaf.angular.portlet.test; 2 | 3 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.MessageService; 4 | import ab.liferay.spring.mvc.thymeleaf.angular.core.base.service.PortletService; 5 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.controller.PersonViewController; 6 | import ab.liferay.spring.mvc.thymeleaf.angular.portlet.service.PersonService; 7 | import org.springframework.ui.ModelMap; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | 10 | import javax.portlet.WindowStateException; 11 | 12 | @RequestMapping("/test") 13 | public class TestWrapperPersonViewController extends PersonViewController { 14 | 15 | public TestWrapperPersonViewController(PersonService personService, PortletService portletService, MessageService messageService) { 16 | super(personService, portletService, messageService); 17 | } 18 | 19 | @RequestMapping(value = "/view") 20 | @Override 21 | public String view(ModelMap model) throws WindowStateException { 22 | return super.view(model); 23 | } 24 | 25 | @RequestMapping(value = "/render") 26 | @Override 27 | public String render(ModelMap model) { 28 | return super.render(model); 29 | } 30 | 31 | @RequestMapping(value = "/action") 32 | @Override 33 | public void action() { 34 | super.action(); 35 | } 36 | } --------------------------------------------------------------------------------