/*
* #%L
* BroadleafCommerce Open Admin Platform
* %%
* Copyright (C) 2009 - 2013 Broadleaf Commerce
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.broadleafcommerce.openadmin.web.controller;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.broadleafcommerce.common.exception.ServiceException;
import org.broadleafcommerce.common.extension.ExtensionResultHolder;
import org.broadleafcommerce.common.persistence.EntityConfiguration;
import org.broadleafcommerce.common.util.BLCMapUtils;
import org.broadleafcommerce.common.util.TypedClosure;
import org.broadleafcommerce.common.web.controller.BroadleafAbstractController;
import org.broadleafcommerce.openadmin.dto.*;
import org.broadleafcommerce.openadmin.server.domain.PersistencePackageRequest;
import org.broadleafcommerce.openadmin.server.security.domain.AdminSection;
import org.broadleafcommerce.openadmin.server.security.remote.SecurityVerifier;
import org.broadleafcommerce.openadmin.server.security.service.navigation.AdminNavigationService;
import org.broadleafcommerce.openadmin.server.service.AdminEntityService;
import org.broadleafcommerce.openadmin.server.service.persistence.PersistenceResponse;
import org.broadleafcommerce.openadmin.web.form.component.ListGrid;
import org.broadleafcommerce.openadmin.web.form.entity.DynamicEntityFormInfo;
import org.broadleafcommerce.openadmin.web.form.entity.EntityForm;
import org.broadleafcommerce.openadmin.web.form.entity.EntityFormValidator;
import org.broadleafcommerce.openadmin.web.form.entity.Field;
import org.broadleafcommerce.openadmin.web.form.entity.FieldGroup;
import org.broadleafcommerce.openadmin.web.form.entity.Tab;
import org.broadleafcommerce.openadmin.web.service.FormBuilderService;
import org.springframework.ui.Model;
import org.springframework.util.MultiValueMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* An abstract controller that provides convenience methods and resource declarations for the Admin
*
* Operations that are shared between all admin controllers belong here.
*
* @see org.broadleafcommerce.openadmin.web.handler.AdminNavigationHandlerMapping
* @author elbertbautista
* @author apazzolini
*/
public abstract class AdminAbstractController extends BroadleafAbstractController {
public static final String FILTER_VALUE_SEPARATOR = "|";
public static final String FILTER_VALUE_SEPARATOR_REGEX = "\\|";
public static final String CURRENT_ADMIN_MODULE_ATTRIBUTE_NAME = "currentAdminModule";
public static final String CURRENT_ADMIN_SECTION_ATTRIBUTE_NAME = "currentAdminSection";
// ***********************
// RESOURCE DECLARATIONS *
// ***********************
@Resource(name = "blAdminEntityService")
protected AdminEntityService service;
@Resource(name = "blFormBuilderService")
protected FormBuilderService formService;
@Resource(name = "blAdminNavigationService")
protected AdminNavigationService adminNavigationService;
@Resource(name = "blEntityConfiguration")
protected EntityConfiguration entityConfiguration;
@Resource(name = "blEntityFormValidator")
protected EntityFormValidator entityFormValidator;
@Resource(name="blAdminSecurityRemoteService")
protected SecurityVerifier adminRemoteSecurityService;
/**
* Deprecated in favor of {@link org.broadleafcommerce.openadmin.web.controller.AdminAbstractControllerExtensionManager}
*/
@Deprecated
@Resource(name = "blMainEntityActionsExtensionManager")
protected MainEntityActionsExtensionManager mainEntityActionsExtensionManager;
@Resource(name = "blAdminAbstractControllerExtensionManager")
protected AdminAbstractControllerExtensionManager extensionManager;
// *********************************************************
// UNBOUND CONTROLLER METHODS (USED BY DIFFERENT SECTIONS) *
// *********************************************************
/**
* Convenience method for obtaining a fully built EntityForm for the given sectionKey, sectionClassName, and id.
*
* @param sectionKey
* @param sectionClassName
* @param id
* @return a fully composed EntityForm
* @throws ServiceException
*/
protected EntityForm getEntityForm(String sectionKey, String sectionClassName, String id) throws ServiceException {
SectionCrumb sc = new SectionCrumb();
sc.setSectionId(id);
sc.setSectionIdentifier("structured-content/all");
List<SectionCrumb> crumbs = new ArrayList<SectionCrumb>(1);
crumbs.add(sc);
PersistencePackageRequest ppr = getSectionPersistencePackageRequest(sectionClassName, crumbs, null);
ClassMetadata cmd = service.getClassMetadata(ppr).getDynamicResultSet().getClassMetaData();
Entity entity = service.getRecord(ppr, id, cmd, false).getDynamicResultSet().getRecords()[0];
Map<String, DynamicResultSet> subRecordsMap = service.getRecordsForAllSubCollections(ppr, entity, crumbs);
EntityForm entityForm = formService.createEntityForm(cmd, entity, subRecordsMap, crumbs);
return entityForm;
}
/**
* Returns a partial representing a dynamic form. An example of this is the dynamic fields that render
* on structured content, which are determined by the currently selected structured content type. This
* method is typically only invoked through Javascript and used to replace the current dynamic form with
* the one for the newly selected type.
*
* @param request
* @param response
* @param model
* @param pathVars
* @param info
* @return the return view path
* @throws Exception
*/
protected String getDynamicForm(HttpServletRequest request, HttpServletResponse response, Model model,
Map<String, String> pathVars,
DynamicEntityFormInfo info) throws Exception {
String sectionKey = getSectionKey(pathVars);
EntityForm blankFormContainer = new EntityForm();
EntityForm dynamicForm = getBlankDynamicFieldTemplateForm(info);
blankFormContainer.putDynamicForm(info.getPropertyName(), dynamicForm);
model.addAttribute("dynamicForm", dynamicForm);
model.addAttribute("entityForm", blankFormContainer);
model.addAttribute("dynamicPropertyName", info.getPropertyName());
String reqUrl = request.getRequestURL().toString();
reqUrl = reqUrl.substring(0, reqUrl.indexOf("/dynamicForm"));
model.addAttribute("currentUrl", reqUrl);
setModelAttributes(model, sectionKey);
return "views/dynamicFormPartial";
}
// **********************************
// HELPER METHODS FOR BUILDING DTOS *
// **********************************
/**
* Convenience method for obtaining a ListGrid DTO object for a collection. Note that if no <b>criteria</b> is
* available, then this should be null (or empty)
*
* @param mainMetadata class metadata for the root entity that this <b>collectionProperty</b> relates to
* @param id foreign key from the root entity for <b>collectionProperty</b>
* @param collectionProperty property that this collection should be based on from the root entity
* @param form the criteria form model attribute
* @param sectionKey the current main section key
* @return the list grid
* @throws ServiceException
*/
protected ListGrid getCollectionListGrid(ClassMetadata mainMetadata, Entity entity, Property collectionProperty,
MultiValueMap<String, String> requestParams, String sectionKey, PersistenceResponse persistenceResponse, List<SectionCrumb> sectionCrumbs)
throws ServiceException {
String idProperty = service.getIdProperty(mainMetadata);
if (persistenceResponse != null && persistenceResponse.getAdditionalData().containsKey(PersistenceResponse.AdditionalData.CLONEID)) {
entity.findProperty(idProperty).setValue((String) persistenceResponse.getAdditionalData().get(PersistenceResponse.AdditionalData.CLONEID));
}
DynamicResultSet drs = service.getRecordsForCollection(mainMetadata, entity, collectionProperty,
getCriteria(requestParams), getStartIndex(requestParams), getMaxIndex(requestParams), sectionCrumbs).getDynamicResultSet();
ListGrid listGrid = formService.buildCollectionListGrid(entity.findProperty(idProperty).getValue(), drs,
collectionProperty, sectionKey, sectionCrumbs);
listGrid.setListGridType(ListGrid.Type.INLINE);
return listGrid;
}
/**
* Convenience method for obtaining a ListGrid DTO object for a collection. Note that if no <b>criteria</b> is
* available, then this should be null (or empty)
*
* @param mainMetadata class metadata for the root entity that this <b>collectionProperty</b> relates to
* @param id foreign key from the root entity for <b>collectionProperty</b>
* @param collectionProperty property that this collection should be based on from the root entity
* @param form the criteria form model attribute
* @param sectionKey the current main section key
* @return the list grid
* @throws ServiceException
*/
protected ListGrid getCollectionListGrid(ClassMetadata mainMetadata, Entity entity, Property collectionProperty,
MultiValueMap<String, String> requestParams, String sectionKey, List<SectionCrumb> sectionCrumbs)
throws ServiceException {
return getCollectionListGrid(mainMetadata, entity, collectionProperty, requestParams, sectionKey, null, sectionCrumbs);
}
/**
* @see #getBlankDynamicFieldTemplateForm(DynamicEntityFormInfo, EntityForm)
* @param info
* @throws ServiceException
*/
protected EntityForm getBlankDynamicFieldTemplateForm(DynamicEntityFormInfo info) throws ServiceException {
return getBlankDynamicFieldTemplateForm(info, null);
}
/**
* @see #getBlankDynamicMasterFieldGroupTemplateForm(DynamicEntityFormInfo, EntityForm)
* @param info
* @throws ServiceException
*/
protected EntityForm getBlankDynamicMasterFieldGroupTemplateForm(DynamicEntityFormInfo info) throws ServiceException {
return getBlankDynamicMasterFieldGroupTemplateForm(info, null);
}
/**
* Convenience method to build a blank form for a Master Field Group.
*
* @param info
* @throws ServiceException
*/
protected EntityForm getBlankDynamicMasterFieldGroupTemplateForm(DynamicEntityFormInfo info, EntityForm dynamicFormOverride) throws ServiceException {
EntityForm dynamicForm = getEntityForm(info, dynamicFormOverride);
// Set the specialized name for these fields - we need to handle them separately
dynamicForm.clearFieldsMap();
for (Tab tab : dynamicForm.getTabs()) {
for (FieldGroup group : tab.getFieldGroups()) {
for (Field field : group.getFields()) {
if (field.isMasterField()) {
field.setName(info.getPropertyName() + DynamicEntityFormInfo.FIELD_SEPARATOR + field.getName());
} else {
group.removeField(field);
}
}
}
}
return dynamicForm;
}
/**
* Convenience method for obtaining a blank dynamic field template form. For example, if the main entity form should
* render different fields depending on the value of a specific field in that main form itself, the "dynamic" fields
* are generated by this method. Because this is invoked when a new value is chosen, the form generated by this method
* will never have values set.
*
* We only add fields that are not considered Master fields
*
* @param info
* @return the entity form
* @throws ServiceException
*/
protected EntityForm getBlankDynamicFieldTemplateForm(DynamicEntityFormInfo info, EntityForm dynamicFormOverride)
throws ServiceException {
EntityForm dynamicForm = getEntityForm(info, dynamicFormOverride);
// Set the specialized name for these fields - we need to handle them separately
dynamicForm.clearFieldsMap();
for (Tab tab : dynamicForm.getTabs()) {
for (FieldGroup group : tab.getFieldGroups()) {
for (Field field : group.getFields()) {
if (!field.isMasterField()) {
field.setName(info.getPropertyName() + DynamicEntityFormInfo.FIELD_SEPARATOR + field.getName());
} else {
group.removeField(field);
}
}
}
}
//extensionManager.getProxy().modifyDynamicForm(dynamicForm, );
return dynamicForm;
}
protected EntityForm getEntityForm(DynamicEntityFormInfo info, EntityForm dynamicFormOverride) throws ServiceException {
// We need to inspect with the second custom criteria set to the id of
// the desired structured content type
PersistencePackageRequest ppr = PersistencePackageRequest.standard()
.withCeilingEntityClassname(info.getCeilingClassName())
.withSecurityCeilingEntityClassname(info.getSecurityCeilingClassName())
.withCustomCriteria(new String[] { info.getCriteriaName(), null, info.getPropertyName(), info.getPropertyValue() });
ClassMetadata cmd = service.getClassMetadata(ppr).getDynamicResultSet().getClassMetaData();
EntityForm dynamicForm = formService.createEntityForm(cmd, null);
dynamicForm.clearFieldsMap();
if (dynamicFormOverride != null) {
dynamicFormOverride.clearFieldsMap();
Map<String, Field> fieldOverrides = dynamicFormOverride.getFields();
for (Entry<String, Field> override : fieldOverrides.entrySet()) {
if (dynamicForm.getFields().containsKey(override.getKey())) {
dynamicForm.getFields().get(override.getKey()).setValue(override.getValue().getValue());
}
}
}
return dynamicForm;
}
/**
* Convenience method for obtaining a dynamic field template form for a particular entity. This method differs from
* {@link #getBlankDynamicFieldTemplateForm(DynamicEntityFormInfo)} in that it will fill out the current values for
* the fields in this dynamic form from the database. This method is invoked when the initial view of a page containing
* a dynamic form is triggered.
*
* Optionally, you can pass in a pre-existing dynamic form to this method that already has updated values. Example usage
* would be for after validation has failed and you do not want to lookup old values from the database again.
*
* @param info
* @param entityId
* @param dynamicForm optional dynamic form that already has values to fill out
* @return the entity form
* @throws ServiceException
*/
protected EntityForm getDynamicFieldTemplateForm(DynamicEntityFormInfo info, String entityId, EntityForm dynamicFormOverride)
throws ServiceException {
// We need to inspect with the second custom criteria set to the id of
// the desired structured content type
PersistencePackageRequest ppr = PersistencePackageRequest.standard()
.withCeilingEntityClassname(info.getCeilingClassName())
.withSecurityCeilingEntityClassname(info.getSecurityCeilingClassName())
.withCustomCriteria(new String[] { info.getCriteriaName(), entityId, info.getPropertyName(), info.getPropertyValue() });
ClassMetadata cmd = service.getClassMetadata(ppr).getDynamicResultSet().getClassMetaData();
// However, when we fetch, the second custom criteria needs to be the id
// of this particular structured content entity
ppr.setCustomCriteria(new String[] { info.getCriteriaName(), entityId });
Entity entity = service.getRecord(ppr, info.getPropertyValue(), cmd, true).getDynamicResultSet().getRecords()[0];
List<Field> fieldsToMove = new ArrayList<Field>();
// override the results of the entity with the dynamic form passed in
if (dynamicFormOverride != null) {
dynamicFormOverride.clearFieldsMap();
Map<String, Field> fieldOverrides = dynamicFormOverride.getFields();
for (Entry<String, Field> override : fieldOverrides.entrySet()) {
if (entity.getPMap().containsKey(override.getKey())) {
entity.getPMap().get(override.getKey()).setValue(override.getValue().getValue());
} else {
fieldsToMove.add(override.getValue());
}
}
}
// Assemble the dynamic form for structured content type
EntityForm dynamicForm = formService.createEntityForm(cmd, entity, null, null);
for (Field field : fieldsToMove) {
FieldMetadata fmd = cmd.getPMap().get(field.getName()).getMetadata();
if (fmd instanceof BasicFieldMetadata) {
BasicFieldMetadata bfmd = (BasicFieldMetadata) fmd;
field.setFieldType(bfmd.getFieldType().toString());
field.setFriendlyName(bfmd.getFriendlyName());
field.setRequired(bfmd.getRequired());
}
dynamicForm.addField(field);
}
// Set the specialized name for these fields - we need to handle them separately
dynamicForm.clearFieldsMap();
for (Tab tab : dynamicForm.getTabs()) {
for (FieldGroup group : tab.getFieldGroups()) {
for (Field field : group.getFields()) {
field.setName(info.getPropertyName() + DynamicEntityFormInfo.FIELD_SEPARATOR + field.getName());
}
}
}
extensionManager.getProxy().modifyDynamicForm(dynamicForm, entityId);
return dynamicForm;
}
/**
* This method will scan the entityForm for all dynamic form fields and pull them out
* as appropriate.
*
* @param entityForm
*/
protected void extractDynamicFormFields(EntityForm entityForm) {
Map<String, Field> dynamicFields = new HashMap<String, Field>();
// Find all of the dynamic form fields
for (Entry<String, Field> entry : entityForm.getFields().entrySet()) {
if (entry.getKey().contains(DynamicEntityFormInfo.FIELD_SEPARATOR)) {
dynamicFields.put(entry.getKey(), entry.getValue());
}
}
// Remove the dynamic form fields from the main entity - they are persisted separately
for (Entry<String, Field> entry : dynamicFields.entrySet()) {
entityForm.removeField(entry.getKey());
}
// Create the entity form for the dynamic form, as it needs to be persisted separately
for (Entry<String, Field> entry : dynamicFields.entrySet()) {
String[] fieldName = entry.getKey().split("\\" + DynamicEntityFormInfo.FIELD_SEPARATOR);
DynamicEntityFormInfo info = entityForm.getDynamicFormInfo(fieldName[0]);
EntityForm dynamicForm = entityForm.getDynamicForm(fieldName[0]);
if (dynamicForm == null) {
dynamicForm = new EntityForm();
dynamicForm.setCeilingEntityClassname(info.getCeilingClassName());
entityForm.putDynamicForm(fieldName[0], dynamicForm);
}
entry.getValue().setName(fieldName[1]);
dynamicForm.addField(entry.getValue());
}
}
// ***********************************************
// HELPER METHODS FOR SECTION-SPECIFIC OVERRIDES *
// ***********************************************
/**
* This method is used to determine the current section key. For this default implementation, the sectionKey is pulled
* from the pathVariable, {sectionKey}, as defined by the request mapping on this controller. To support controller
* inheritance and allow more specialized controllers to delegate some methods to this basic controller, overridden
* implementations of this method could return a hardcoded value instead of reading the map
*
* @param pathVars - the map of all currently bound path variables for this request
* @return the sectionKey for this request
*/
protected String getSectionKey(Map<String, String> pathVars) {
return pathVars.get("sectionKey");
}
/**
* <p>Helper method to return an array of {@link org.broadleafcommerce.openadmin.dto.FilterAndSortCriteria} based on a map of propertyName -> list of criteria
* value. This will also grab the sorts off of the request parameters, if any.</p>
*
* <p>The multi-valued map allows users to specify multiple criteria values per property, as well as multiple sort
* properties and sort directions. For multiple sort properties and sort directions, these would usually come in as
* request parameters like:
* <br />
* <br />
* ....?sortProperty=defaultSku.name&sortProperty=manufacturer&sortDirection=ASCENDING&sortDirection=DESCENDING
* <br />
* <br />
* This would attach criteria such that defaultSku.name was sorted ascending, and manufacturer was sorted descending</p>
*
* @param requestParams usually a {@link MultiValueMap} that has been bound by a controller to receive all of the
* request parameters that are not explicitly named
* @return the final array of {@link org.broadleafcommerce.openadmin.dto.FilterAndSortCriteria} to pass to the fetch
*
* @see {@link #getSortPropertyNames(Map)}
* @see {@link #getSortDirections(Map)}
*/
protected FilterAndSortCriteria[] getCriteria(Map<String, List<String>> requestParams) {
if (requestParams == null || requestParams.isEmpty()) {
return null;
}
List<FilterAndSortCriteria> result = new ArrayList<FilterAndSortCriteria>();
for (Entry<String, List<String>> entry : requestParams.entrySet()) {
if (!entry.getKey().equals(FilterAndSortCriteria.SORT_PROPERTY_PARAMETER)
&& !entry.getKey().equals(FilterAndSortCriteria.SORT_DIRECTION_PARAMETER)
&& !entry.getKey().equals(FilterAndSortCriteria.MAX_INDEX_PARAMETER)
&& !entry.getKey().equals(FilterAndSortCriteria.START_INDEX_PARAMETER)) {
List<String> values = entry.getValue();
List<String> collapsedValues = new ArrayList<String>();
for (String value : values) {
if (value.contains(FILTER_VALUE_SEPARATOR)) {
String[] vs = value.split(FILTER_VALUE_SEPARATOR_REGEX);
for (String v : vs) {
collapsedValues.add(v);
}
} else {
collapsedValues.add(value);
}
}
FilterAndSortCriteria fasCriteria = new FilterAndSortCriteria(entry.getKey(), collapsedValues);
result.add(fasCriteria);
}
}
List<String> sortProperties = getSortPropertyNames(requestParams);
List<String> sortDirections = getSortDirections(requestParams);
if (CollectionUtils.isNotEmpty(sortProperties)) {
//set up a map to determine if there is already some criteria set for the sort property
Map<String, FilterAndSortCriteria> fasMap = BLCMapUtils.keyedMap(result, new TypedClosure<String, FilterAndSortCriteria>() {
@Override
public String getKey(FilterAndSortCriteria value) {
return value.getPropertyId();
}
});
for (int i = 0; i < sortProperties.size(); i++) {
boolean sortAscending = SortDirection.ASCENDING.toString().equals(sortDirections.get(i));
FilterAndSortCriteria propertyCriteria = fasMap.get(sortProperties.get(i));
//If there is already criteria for this property, attach the sort to that. Otherwise, create some new
//FilterAndSortCriteria for the sort
if (propertyCriteria != null) {
propertyCriteria.setSortAscending(sortAscending);
} else {
FilterAndSortCriteria fasc = new FilterAndSortCriteria(sortProperties.get(i));
fasc.setSortAscending(sortAscending);
result.add(fasc);
}
}
}
return result.toArray(new FilterAndSortCriteria[result.size()]);
}
/**
* Obtains the list of sort directions from the bound request parameters. Note that these should appear in the same
* relative order as {@link #getSortPropertyNames(Map)}
*
* @param requestParams
* @return
*/
protected List<String> getSortDirections(Map<String, List<String>> requestParams) {
List<String> sortTypes = requestParams.get(FilterAndSortCriteria.SORT_DIRECTION_PARAMETER);
return sortTypes;
}
/**
* Obtains the list of property names to sort on from the bound request parameters. Note that these should appear in the
* same relative order as {@link #getSortDirections(Map)}.
*
* @param requestParams
* @return
*/
protected List<String> getSortPropertyNames(Map<String, List<String>> requestParams) {
return requestParams.get(FilterAndSortCriteria.SORT_PROPERTY_PARAMETER);
}
/**
* Gets the fully qualified ceiling entity classname for this section. If this section is not explicitly defined in
* the database, will return the value passed into this function. For example, if there is a mapping from "/myentity" to
* "com.mycompany.myentity", both "http://localhost/myentity" and "http://localhost/com.mycompany.myentity" are valid
* request paths.
*
* @param sectionKey
* @return the className for this sectionKey if found in the database or the sectionKey if not
*/
protected String getClassNameForSection(String sectionKey) {
AdminSection section = adminNavigationService.findAdminSectionByURI("/" + sectionKey);
ExtensionResultHolder erh = new ExtensionResultHolder();
extensionManager.getProxy().overrideClassNameForSection(erh, sectionKey, section);
if (erh.getContextMap().get(AbstractAdminAbstractControllerExtensionHandler.NEW_CLASS_NAME) != null) {
return (String) erh.getContextMap().get(AbstractAdminAbstractControllerExtensionHandler.NEW_CLASS_NAME);
}
return (section == null) ? sectionKey : section.getCeilingEntity();
}
/**
* If there are certain types of entities that should not be allowed to be created, an override of this method would be
* able to specify that. It could also add additional types if desired.
*
* @param classTree
* @return a List<ClassTree> representing all potentially avaialble entity types to create
*/
protected List<ClassTree> getAddEntityTypes(ClassTree classTree) {
return classTree.getCollapsedClassTrees();
}
/**
* This method is called when attempting to add new entities that have a polymorphic tree.
*
* If this method returns null, there is no default type set for this particular entity type, and the user will be
* presented with a selection of possible types to utilize.
*
* If it returns a non-null value, the returned fullyQualifiedClassname will be used and will bypass the selection step.
*
* @return null if there is no default type, otherwise the default type
*/
protected String getDefaultEntityType() {
return null;
}
/**
* This method is invoked for every request for this controller. By default, we do not want to specify a custom
* criteria, but specialized controllers may want to.
*
* @return the custom criteria for this section for all requests, if any
*/
protected String[] getSectionCustomCriteria() {
return null;
}
/**
* @deprecated in favor of {@link #attachSectionSpecificInfo(PersistencePackageRequest, Map)}
*/
@Deprecated
protected void attachSectionSpecificInfo(PersistencePackageRequest ppr) {
}
/**
* This method is invoked whenever an assembled EntityForm is rendered. This typically occurs when viewing an entity
* in the admin or viewing an error state on a POST for that entity.
*
* @param entityForm
*/
protected void modifyEntityForm(EntityForm entityForm, Map<String, String> pathVars) {
}
/**
* This method is invoked whenever an assembled EntityForm is rendered for the add entity screen.
*
* @param entityForm
*/
protected void modifyAddEntityForm(EntityForm entityForm, Map<String, String> pathVars) {
}
/**
* A hook method that is invoked every time the {@link #getSectionPersistencePackageRequest(String)} method is invoked.
* This allows specialized controllers to hook into every request and manipulate the persistence package request as
* desired.
*
* @param ppr
*/
protected void attachSectionSpecificInfo(PersistencePackageRequest ppr, Map<String, String> pathVars) {
attachSectionSpecificInfo(ppr);
}
/**
* Obtains the requested start index parameter
*
* @param requestParams
* @return
*/
protected Integer getStartIndex(Map<String, List<String>> requestParams) {
if (requestParams == null || requestParams.isEmpty()) {
return null;
}
List<String> startIndex = requestParams.get(FilterAndSortCriteria.START_INDEX_PARAMETER);
return CollectionUtils.isEmpty(startIndex) ? null : Integer.parseInt(startIndex.get(0));
}
/**
* Obtains the requested max index parameter
*
* @param requestParams
* @return
*/
protected Integer getMaxIndex(Map<String, List<String>> requestParams) {
if (requestParams == null || requestParams.isEmpty()) {
return null;
}
List<String> maxIndex = requestParams.get(FilterAndSortCriteria.MAX_INDEX_PARAMETER);
return CollectionUtils.isEmpty(maxIndex) ? null : Integer.parseInt(maxIndex.get(0));
}
// ************************
// GENERIC HELPER METHODS *
// ************************
/**
* Attributes to add to the model on every request
*
* @param model
* @param sectionKey
*/
protected void setModelAttributes(Model model, String sectionKey) {
AdminSection section = adminNavigationService.findAdminSectionByURI("/" + sectionKey);
if (section != null) {
model.addAttribute("sectionKey", sectionKey);
model.addAttribute(CURRENT_ADMIN_MODULE_ATTRIBUTE_NAME, section.getModule());
model.addAttribute(CURRENT_ADMIN_SECTION_ATTRIBUTE_NAME, section);
}
extensionManager.getProxy().setAdditionalModelAttributes(model, sectionKey);
}
/**
* @deprecated in favor of {@link #getSectionPersistencePackageRequest(String, List, Map)}
*/
@Deprecated
protected PersistencePackageRequest getSectionPersistencePackageRequest(String sectionClassName, List<SectionCrumb> sectionCrumbs) {
return getSectionPersistencePackageRequest(sectionClassName, sectionCrumbs, null);
}
/**
* Returns a PersistencePackageRequest for the given sectionClassName. Will also invoke the
* {@link #getSectionCustomCriteria()} and {@link #attachSectionSpecificInfo(PersistencePackageRequest)} to allow
* specialized controllers to manipulate the request for every action in this controller.
*
* @param sectionClassName
* @param sectionCrumbs
* @param pathVars
* @return
*/
protected PersistencePackageRequest getSectionPersistencePackageRequest(String sectionClassName,
List<SectionCrumb> sectionCrumbs, Map<String, String> pathVars) {
PersistencePackageRequest ppr = PersistencePackageRequest.standard()
.withCeilingEntityClassname(sectionClassName)
.withCustomCriteria(getSectionCustomCriteria())
.withSectionCrumbs(sectionCrumbs);
attachSectionSpecificInfo(ppr, pathVars);
return ppr;
}
/**
* @deprecated in favor of {@link #getSectionPersistencePackageRequest(String, MultiValueMap, List, Map)}
*/
@Deprecated
protected PersistencePackageRequest getSectionPersistencePackageRequest(String sectionClassName,
MultiValueMap<String, String> requestParams, List<SectionCrumb> sectionCrumbs) {
return getSectionPersistencePackageRequest(sectionClassName, requestParams, sectionCrumbs, null);
}
/**
* Returns the result of a call to {@link #getSectionPersistencePackageRequest(String)} with the additional filter
* and sort criteria attached.
*
* @param sectionClassName
* @param filterAndSortCriteria
* @return the PersistencePacakageRequest
*/
protected PersistencePackageRequest getSectionPersistencePackageRequest(String sectionClassName,
MultiValueMap<String, String> requestParams, List<SectionCrumb> sectionCrumbs, Map<String, String> pathVars) {
FilterAndSortCriteria[] fascs = getCriteria(requestParams);
PersistencePackageRequest ppr = PersistencePackageRequest.standard()
.withCeilingEntityClassname(sectionClassName)
.withCustomCriteria(getSectionCustomCriteria())
.withFilterAndSortCriteria(fascs)
.withStartIndex(getStartIndex(requestParams))
.withMaxIndex(getMaxIndex(requestParams))
.withSectionCrumbs(sectionCrumbs);
attachSectionSpecificInfo(ppr, pathVars);
return ppr;
}
protected List<SectionCrumb> getSectionCrumbs(HttpServletRequest request, String currentSection, String currentSectionId) {
String crumbs = request.getParameter("sectionCrumbs");
List<SectionCrumb> myCrumbs = new ArrayList<SectionCrumb>();
if (!StringUtils.isEmpty(crumbs)) {
String[] crumbParts = crumbs.split(",");
for (String part : crumbParts) {
SectionCrumb crumb = new SectionCrumb();
String[] crumbPieces = part.split("--");
crumb.setSectionIdentifier(crumbPieces[0]);
crumb.setSectionId(crumbPieces[1]);
if (!myCrumbs.contains(crumb)) {
myCrumbs.add(crumb);
}
}
}
if (currentSection != null && currentSectionId != null) {
SectionCrumb crumb = new SectionCrumb();
if (currentSection.startsWith("/")) {
currentSection = currentSection.substring(1, currentSection.length());
}
crumb.setSectionIdentifier(currentSection);
crumb.setSectionId(currentSectionId);
if (!myCrumbs.contains(crumb)) {
myCrumbs.add(crumb);
}
}
return myCrumbs;
}
}