Commit 1973026e by Lyudmila Nepomnyashaya

Merge branch '2.6.x' into 'master'

Port play-cms to play framework 2.6.x See merge request !271
parents 94492571 76d214bd
......@@ -16,8 +16,10 @@
package ch.insign.cms;
import ch.insign.cms.attributeset.model.Attribute;
import ch.insign.cms.blocks.jotformpageblock.service.JotFormService;
import ch.insign.cms.email.EmailService;
import ch.insign.cms.managers.AttributeManager;
import ch.insign.cms.models.BlockManager;
import ch.insign.cms.models.CmsConfiguration;
import ch.insign.cms.models.Sites;
......@@ -44,6 +46,8 @@ public interface CMSApi {
BlockManager getBlockManager();
AttributeManager getAttributeManager();
FilterManager getFilterManager();
SearchManager getSearchManager();
......
......@@ -18,6 +18,7 @@ package ch.insign.cms;
import ch.insign.cms.blocks.jotformpageblock.service.JotFormService;
import ch.insign.cms.email.EmailService;
import ch.insign.cms.managers.AttributeManager;
import ch.insign.cms.models.*;
import ch.insign.commons.filter.FilterManager;
import ch.insign.commons.search.SearchManager;
......@@ -37,6 +38,7 @@ public class CMSApiImpl implements CMSApi {
private CmsConfiguration configuration;
private Sites sites = new Sites();
private BlockManager blockManager;
private AttributeManager attributeManager;
private FilterManager filterManager;
private SearchManager searchManager;
private UncachedManager uncachedManager;
......@@ -45,15 +47,16 @@ public class CMSApiImpl implements CMSApi {
private JotFormService jotFormService;
@Inject
public CMSApiImpl(CMSApiLifecycle cmsApiLifecycle, ApplicationLifecycle lifecycle, EmailService emailService) {
public CMSApiImpl(CMSApiLifecycle cmsApiLifecycle, ApplicationLifecycle lifecycle, EmailService emailService, JotFormService jotFormService, AttributeManager attributeManager) {
configuration = new CmsConfiguration();
sites = new Sites();
jotFormService = new JotFormService();
jotFormService = jotFormService;
blockManager = new BlockManager();
filterManager = new FilterManager();
searchManager = new SearchManager();
uncachedManager = new UncachedManager();
this.emailService = emailService;
this.attributeManager = attributeManager;
cmsApiLifecycle.onStart(this);
......@@ -107,6 +110,11 @@ public class CMSApiImpl implements CMSApi {
}
@Override
public AttributeManager getAttributeManager() {
return attributeManager;
}
@Override
public FilterManager getFilterManager() {
return filterManager;
}
......
......@@ -17,6 +17,10 @@
package ch.insign.cms;
import akka.actor.ActorSystem;
import ch.insign.cms.attributeset.controller.OptionAttributeController;
import ch.insign.cms.attributeset.controller.TextAttributeController;
import ch.insign.cms.attributeset.model.OptionAttribute;
import ch.insign.cms.attributeset.model.TextAttribute;
import ch.insign.cms.blocks.backendlinkblock.BackendLinkBlock;
import ch.insign.cms.blocks.errorblock.ErrorPage;
import ch.insign.cms.blocks.groupingblock.GroupingBlock;
......@@ -26,13 +30,16 @@ import ch.insign.cms.blocks.jotformpageblock.service.JotFormService;
import ch.insign.cms.blocks.linkblock.LinkBlock;
import ch.insign.cms.blocks.searchresultblock.SearchResultBlock;
import ch.insign.cms.blocks.sliderblock.SliderCollectionBlock;
import ch.insign.cms.managers.AttributeManager;
import ch.insign.cms.models.*;
import ch.insign.commons.db.MString;
import ch.insign.commons.filter.TagContentFilter;
import ch.insign.commons.util.Configuration;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Status;
import org.apache.commons.lang3.tuple.Pair;
import play.db.jpa.JPAApi;
import play.i18n.MessagesApi;
import javax.inject.Inject;
import javax.inject.Singleton;
......@@ -47,16 +54,21 @@ public class CMSApiLifecycleImpl implements CMSApiLifecycle {
private final ActorSystem actorSystem;
private final MessagesApi messagesApi;
@Inject
public CMSApiLifecycleImpl(JPAApi jpaApi, ActorSystem actorSystem) {
public CMSApiLifecycleImpl(JPAApi jpaApi, ActorSystem actorSystem, MessagesApi messagesApi) {
this.jpaApi = jpaApi;
this.actorSystem = actorSystem;
this.messagesApi = messagesApi;
}
@Override
public void onStart(CMSApi cmsApi) {
clearStaticState(cmsApi);
registerCmsAttributres(cmsApi);
// Register the cms blocks
registerCmsBlocks(cmsApi);
......@@ -82,6 +94,13 @@ public class CMSApiLifecycleImpl implements CMSApiLifecycle {
registerDataBinders();
}
private void registerCmsAttributres(CMSApi cmsApi) {
cmsApi.getAttributeManager().register(
Pair.of(OptionAttribute.class, new AttributeManager.Metadata(OptionAttributeController.class)),
Pair.of(TextAttribute.class, new AttributeManager.Metadata(TextAttributeController.class))
);
}
@Override
public void onStop(CMSApi cmsApi) {
BlockCache.cacheManagers().stream()
......@@ -102,7 +121,7 @@ public class CMSApiLifecycleImpl implements CMSApiLifecycle {
}
protected void registerJotFormService(CMSApi cmsApi) {
cmsApi.setJetFormSetup(new JotFormService());
cmsApi.setJetFormSetup(new JotFormService(jpaApi, messagesApi, actorSystem));
}
protected void registerUncachedPartials(CMSApi cmsApi) {
......
......@@ -16,14 +16,19 @@
package ch.insign.cms.attributeset;
import ch.insign.cms.attributeset.controller.AbstractAttributeController;
import ch.insign.cms.attributeset.model.*;
import ch.insign.commons.db.Model;
import play.data.DynamicForm;
import play.data.Form;
import play.twirl.api.Html;
import javax.inject.Inject;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
......@@ -40,6 +45,9 @@ import java.util.Set;
// so this is the lesser evil. -vz
public class AttributeService {
@Inject
private static ch.insign.cms.services.AttributeService attributeService;
/**
* Use this method to render an additional Html in your backend form, containing the attribute set
* belonging to an entity.
......@@ -50,58 +58,15 @@ public class AttributeService {
*/
public static Html renderForm(Form form, String attributeSet, String entityKey){
AttributeSet set = AttributeSet.find.byKey(attributeSet);
if (set != null)
return ch.insign.cms.attributeset.views.html.backend.attributeSet.render(form, set, entityKey);
else
return new Html("");
}
/**
* This method saves an {@link AttributeSetInstance}. Since attributes may belong to multiple attribute sets
* the method requires not only the key of the owning entity, but also of the corresponding attribute set. The
* attribute set is passed as a hidden field in the form and is read from the currently executed request.
* The hidden field is appended to your form automatically, if you use the
* {@link #renderForm(Form, String, String)} to render the attribute set in the backend form.
* @param entityKey The key of the entity, to which the instance belongs.
*/
public static void save(String entityKey) {
// FIXME: Binding form in save method is an ugly solution
// It relies on Http Context which may not exists here
if (play.mvc.Http.Context.current.get() == null) {
return;
}
DynamicForm form = DynamicForm.form().bindFromRequest();
String key = form.get("_attributeSet.key");
if (key == null){
return;
}
AttributeSet set = AttributeSet.find.byKey(key);
if (set == null){
return;
}
AttributeSetInstance instance = set.getAttributeSetInstance(entityKey);
if (instance == null) {
instance = new AttributeSetInstance();
instance.setOwnerObject(entityKey);
}
if (set != null) {
List<Html> attributeForms = set.getAttributesWithValues(entityKey).entrySet().stream()
.map(e -> attributeService.resolveController(e.getKey().getClass()).editForm(e.getKey(), form, e.getValue()))
.collect(Collectors.toList());
for (Attribute attr: set.getAttributes()){
Value val = attr.restoreValueFromRequest(instance.get(attr));
if (val == null) {
instance.remove(attr);
continue;
}
val.setInstance(instance);
instance.put(attr, val);
return ch.insign.cms.attributeset.views.html.backend.attributeSet.render(set, attributeForms);
}
set.getSelections().put(entityKey, instance);
instance.setSet(set);
instance.save();
return new Html("");
}
/**
......@@ -120,11 +85,13 @@ public class AttributeService {
return null;
}
Attribute attribute = Attribute.find.byKey(attrKey);
if (attribute == null) {
Optional<Attribute> optionalAttribute = attributeService.findOneByKey(attrKey);
if (!optionalAttribute.isPresent()) {
return null;
}
Attribute attribute = optionalAttribute.get();
if (!set.getAttributes().contains(attribute)) {
return null;
}
......
package ch.insign.cms.attributeset;
import ch.insign.cms.attributeset.model.Attribute;
import ch.insign.cms.attributeset.model.AttributeSet;
import ch.insign.cms.attributeset.model.AttributeSetInstance;
import ch.insign.cms.attributeset.model.Value;
import ch.insign.cms.models.AbstractBlock;
import ch.insign.cms.services.AttributeService;
import play.api.Play;
import play.data.DynamicForm;
import play.data.Form;
import play.data.FormFactory;
import play.db.jpa.JPAApi;
import javax.inject.Inject;
import javax.persistence.PostPersist;
import javax.persistence.PostUpdate;
public class AttributesListener {
private final JPAApi jpaApi;
private final ch.insign.cms.services.AttributeService attributeService;
private final FormFactory formFactory;
public AttributesListener() {
this(
Play.current().injector().instanceOf(JPAApi.class),
Play.current().injector().instanceOf(AttributeService.class),
Play.current().injector().instanceOf(FormFactory.class)
);
}
@Inject
public AttributesListener(JPAApi jpaApi, AttributeService attributeService, FormFactory formFactory) {
this.jpaApi = jpaApi;
this.attributeService = attributeService;
this.formFactory = formFactory;
}
/**
* This method saves an {@link AttributeSetInstance}. Since attributes may belong to multiple attribute sets
* the method requires not only the key of the owning entity, but also of the corresponding attribute set. The
* attribute set is passed as a hidden field in the form and is read from the currently executed request.
* The hidden field is appended to your form automatically, if you use the
* {@link ch.insign.cms.attributeset.AttributeService#renderForm(Form, String, String)} to render the attribute set in the backend form.
* @param abstractBlock entity, to which the instance belongs.
*/
@PostPersist
@PostUpdate
public void postUpdate(AbstractBlock abstractBlock) {
// FIXME: Binding attributes in save method relies on HTTP Context which may not exists.
if (play.mvc.Http.Context.current.get() == null
|| play.mvc.Http.Context.current.get().request().body() == null) {
return;
}
DynamicForm form = formFactory.form().bindFromRequest();
String key = form.get("_attributeSet.key");
if (key == null){
return;
}
AttributeSet set = AttributeSet.find.byKey(key);
if (set == null) {
return;
}
AttributeSetInstance instance = set.getAttributeSetInstance(abstractBlock.getId());
if (instance == null) {
instance = new AttributeSetInstance();
instance.setOwnerObject(abstractBlock.getId());
}
for (Attribute attr: set.getAttributes()){
Value val = attributeService.resolveController(attr.getClass()).restoreValueFromRequest(attr, instance.get(attr));
if (val == null) {
instance.remove(attr);
continue;
}
val.setInstance(instance);
instance.put(attr, val);
}
set.getSelections().put(abstractBlock.getId(), instance);
instance.setSet(set);
jpaApi.em().merge(instance);
}
}
package ch.insign.cms.attributeset.controller;
import ch.insign.cms.attributeset.model.Attribute;
import ch.insign.cms.attributeset.model.Value;
import ch.insign.cms.attributeset.views.html.backend.attributeAddEdit;
import ch.insign.cms.services.AttributeService;
import ch.insign.commons.db.SmartForm;
import play.data.Form;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Results;
import play.twirl.api.Html;
import javax.inject.Inject;
public abstract class AbstractAttributeController<T extends Attribute> extends Controller {
protected final AttributeService attributeService;
@Inject
public AbstractAttributeController(AttributeService attributeService) {
this.attributeService = attributeService;
}
public abstract Class<T> getAttributeClass();
/**
* This method is called in the backend when an attribute is added/edited. It should return only
* the ADDITIONAL fields and/or markup that are specific for your subclass. The basic form elements
* are already added and handled automatically.
* @param form The add/edit form for the attribute.
* @return The additional Html that you want to inject into the already prepared form.
*/
public Html editAttributeForm(T attribute, Form<T> form) {
return attributeAddEdit.render(form, attribute, new Html(""));
}
/**
* This method is called in the backend when the user edits a Block. This method will be called
* for each attribute in the Block's assigned AttributeSet. It must return Html containing a field
* corresponding to the attribute.
* @param attribute
* @param form The full add/edit form for the attribute
* @param value The selected value of the attribute for the current Block
* @return The additional Html that should be added to the form, corresponding to the current attribute.
*/
public abstract Html editForm(T attribute, Form form, Value value);
/**
* This method will be called when a Block is added/edited in the backend after the user submits
* the form. The method will be called for each Attribute in the AttributeSet belonging to the Block.
* The method should return a fully populated instance of a Value, corresponding to what the user
* has entered in the form for this attribute. You can use for example DynamicForm's bindFromRequest
* to parse the current request.
* @return A fully populated Value, corresponding to the request.
*/
public abstract Value restoreValueFromRequest(T attribute, Value oldValue);
/**
* The AttributeController delegates to this method when the user submits the form for creation
* of a new Attribute. The default implementation of the method binds the request to a Form<> of
* the specific Attribute. You can override this implementation if you need to handle the form
* submission request in any specific or custom way.
* @return A redirect to the page that should be visible after the user creates a new attribute.
*/
public Result doAdd() throws IllegalAccessException, InstantiationException {
Form form = SmartForm.form(getAttributeClass()).bindFromRequest();
if (form.hasErrors()) {
return Results.badRequest(editAttributeForm(getAttributeClass().newInstance(), form));
}
attributeService.save((Attribute) form.get());
return Results.redirect(ch.insign.cms.attributeset.controller.routes.AttributeController.index());
}
/**
* The AttributeController delegates to this method when the user submits the form for editing
* of an existing Attribute. The default implementation of the method binds the request to a Form<>
* of the specific Attribute. You can override this implementation if you need to handle the form
* submission request in any specific or custom way.
* @return A redirect to the page that should be visible after the user edits an existing attribute.
*/
public Result doEdit(T attribute) {
Form form = SmartForm.form(getAttributeClass()).fill(attribute).bindFromRequest();
if (form.hasErrors()) {
return Results.badRequest(editAttributeForm(attribute, form));
}
attributeService.save((Attribute) form.get());
return Results.redirect(ch.insign.cms.attributeset.controller.routes.AttributeController.index());
}
}
......@@ -17,93 +17,88 @@
package ch.insign.cms.attributeset.controller;
import ch.insign.cms.attributeset.model.Attribute;
import ch.insign.cms.attributeset.model.AttributeSet;
import ch.insign.cms.attributeset.model.OptionAttribute;
import ch.insign.cms.attributeset.model.OptionValue;
import ch.insign.cms.attributeset.views.html.backend.attributeAddEdit;
import ch.insign.cms.attributeset.model.TextAttribute;
import ch.insign.cms.attributeset.views.html.backend.attributeList;
import ch.insign.cms.permissions.aop.RequiresBackendAccess;
import ch.insign.cms.services.AttributeService;
import ch.insign.commons.db.SmartForm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.data.Form;
import play.db.jpa.Transactional;
import play.mvc.Controller;
import play.mvc.Result;
import java.util.ArrayList;
import javax.inject.Inject;
import java.util.Optional;
@RequiresBackendAccess
@Transactional
public class AttributeController extends Controller {
private final static Logger logger = LoggerFactory.getLogger(AttributeController.class);
private final AttributeService attributeService;
@Inject
public AttributeController(AttributeService attributeService) {
this.attributeService = attributeService;
}
public Result index() {
return ok(attributeList.render(Attribute.find.all()));
return ok(attributeList.render(attributeService.findAll()));
}
public Result showAdd(String klass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Attribute attribute = (Attribute)Class.forName(klass).newInstance();
Form form = SmartForm.form(attribute.getClass());
return ok(SmartForm.signForms(attributeAddEdit.render(form, attribute)));
return ok(SmartForm.signForms(attributeService.resolveController(klass).editAttributeForm(attribute, form)));
}
public Result doAdd(String klass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return ((Attribute)Class.forName(klass).newInstance()).doAdd();
public Result doAdd(String klass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
return attributeService.resolveController(klass).doAdd();
}
public Result delete(String id){
Attribute attr = Attribute.find.byId(id);
if (attr != null){
deleteAttributeRelations(attr);
attr.delete();
Optional<? extends Attribute> attr = attributeService.findOne(id);
if (attr.isPresent()) {
attributeService.delete(attr.get());
}
return redirect(ch.insign.cms.attributeset.controller.routes.AttributeController.index());
}
/**
* Delete attribute relations
*
* Before delete attribute we need delete all relations:
* - mapped values
* - attribute set
*
* See https://jira.insign.ch/browse/HTS-327
*/
private void deleteAttributeRelations(Attribute attribute) {
// Delete all mapped values
Optional.of(attribute)
.filter(attr -> attr instanceof OptionAttribute)
.map(attr -> (OptionAttribute) attr)
.map(OptionAttribute::getOptions)
.orElse(new ArrayList<>()).stream()
.flatMap(option -> option.getMappedValues().stream())
.forEach(OptionValue::delete);
// Delete attribute from attribute set
AttributeSet.find.byAttribute(attribute)
.forEach(attributeSet -> attributeSet.removeAttribute(attribute));
return redirect(ch.insign.cms.attributeset.controller.routes.AttributeController.index());
}
public Result showEdit(String id){
Attribute attr = Attribute.find.byId(id);
if (attr == null){
Optional<? extends Attribute> attr = attributeService.findOne(id);
if (!attr.isPresent()) {
// TODO: handle missing ids
throw new IllegalArgumentException("Attribute with id "+id+" not found!");
throw new IllegalArgumentException("Attribute with id " + id + " not found!");
}
Attribute attribute = attr.get();
Form<? extends Attribute> form;
if (attribute instanceof TextAttribute) {
form = SmartForm.form(TextAttribute.class).fill((TextAttribute) attribute);
} else if (attribute instanceof OptionAttribute) {
form = SmartForm.form(OptionAttribute.class).fill((OptionAttribute) attribute);
} else {
throw new RuntimeException("Unsupported attribute type.");
}
Form form = SmartForm.form((Class<Attribute>)attr.getClass()).fill(attr);
return ok(SmartForm.signForms(attributeAddEdit.render(form, attr)));
return ok(SmartForm.signForms(attributeService.resolveController(attribute.getClass()).editAttributeForm(attribute, form)));
}
public Result doEdit(String id){
Attribute attr = Attribute.find.byId(id);
if (attr == null){
Optional<? extends Attribute> optionalAttribute = attributeService.findOne(id);
if (!optionalAttribute.isPresent()){
// TODO: Handle exception
throw new IllegalArgumentException("Attribute with id "+id+" not found!");
throw new IllegalArgumentException("Attribute with id " + id + " not found!");
}
return attr.doEdit();
Attribute attribute = optionalAttribute.get();
return attributeService.resolveController(attribute.getClass()).doEdit(attribute);
}
}
......@@ -20,18 +20,26 @@ import ch.insign.cms.attributeset.model.AttributeSet;
import ch.insign.cms.attributeset.views.html.backend.attributeSetAddEdit;
import ch.insign.cms.attributeset.views.html.backend.attributeSetList;
import ch.insign.cms.permissions.aop.RequiresBackendAccess;
import ch.insign.cms.services.AttributeService;
import ch.insign.commons.db.SmartForm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.data.Form;
import play.db.jpa.Transactional;
import play.mvc.Controller;
import play.mvc.Result;
import javax.inject.Inject;
import java.util.Optional;
@RequiresBackendAccess
@Transactional
public class AttributeSetController extends Controller {
private final static Logger logger = LoggerFactory.getLogger(AttributeSetController.class);
private final AttributeService attributeService;
@Inject
public AttributeSetController(AttributeService attributeService) {
this.attributeService = attributeService;
}
public Result index() {
return ok(attributeSetList.render(AttributeSet.find.all()));
......@@ -39,14 +47,15 @@ public class AttributeSetController extends Controller {
public Result showAdd() {
Form<AttributeSet> form = SmartForm.form(AttributeSet.class);
return ok(attributeSetAddEdit.render(form));
return ok(attributeSetAddEdit.render(form, attributeService.findAll()));
}
public Result doAdd(){
public Result doAdd() {
Form<AttributeSet> form = SmartForm.form(AttributeSet.class).bindFromRequest("name", "attributes*", "key");
if (form.hasErrors()) {
return badRequest(attributeSetAddEdit.render(form));
return badRequest(attributeSetAddEdit.render(form, attributeService.findAll()));
}
form.get().save();
return redirect(ch.insign.cms.attributeset.controller.routes.AttributeSetController.index());
}
......@@ -63,22 +72,23 @@ public class AttributeSetController extends Controller {
AttributeSet set = AttributeSet.find.byId(id);
if (set == null){
// TODO: handle missing ids
throw new IllegalArgumentException("Attribute Set with id "+id+" not found!");
throw new IllegalArgumentException("Attribute Set with id " + id + " not found!");
}
Form<AttributeSet> form = SmartForm.form(AttributeSet.class).fill(set);
return ok(attributeSetAddEdit.render(form));
return ok(attributeSetAddEdit.render(form, attributeService.findAll()));
}
public Result doEdit(String id){
AttributeSet set = AttributeSet.find.byId(id);
if (set == null){
// TODO: Handle exception
throw new IllegalArgumentException("Attribute Set with id "+id+" not found!");
throw new IllegalArgumentException("Attribute Set with id " + id + " not found!");
}
Form<AttributeSet> form = SmartForm.form(AttributeSet.class).fill(set).bindFromRequest("name", "attributes*", "key");
if (form.hasErrors()) {
return badRequest(attributeSetAddEdit.render(form));
return badRequest(attributeSetAddEdit.render(form, attributeService.findAll()));
}