Commit 20e8ee1c authored by Tobias Jeger's avatar Tobias Jeger

ESSENTIALS-461: Add support for provisioning the value lists in Spring.

parent 311de467
......@@ -743,6 +743,7 @@ dashboard/src/main/webapp/images/screenshots/robots02.png -text
dashboard/src/main/webapp/images/screenshots/search01.png -text
dashboard/src/main/webapp/images/screenshots/search02.png -text
dashboard/src/main/webapp/images/screenshots/selections01.png -text
dashboard/src/main/webapp/images/screenshots/selections02.png -text svneol=unset#image/png
dashboard/src/main/webapp/images/screenshots/seo01.png -text
dashboard/src/main/webapp/images/screenshots/simplecontent01.png -text
dashboard/src/main/webapp/images/screenshots/tagging01.png -text
......@@ -1359,6 +1360,7 @@ plugins/search/src/main/resources/instructions/xml/hst/search-page.xml -text
plugins/search/src/main/resources/instructions/xml/hst/search-sitemap.xml -text
plugins/selection/README.md -text
plugins/selection/pom.xml -text
plugins/selection/src/main/java/org/onehippo/cms7/essentials/plugins/selection/ProvisionedValueList.java -text
plugins/selection/src/main/java/org/onehippo/cms7/essentials/plugins/selection/SelectionFieldRestful.java -text
plugins/selection/src/main/java/org/onehippo/cms7/essentials/plugins/selection/SelectionResource.java -text
plugins/selection/src/main/resources/META-INF/resources/tool/selectionPlugin/selectionPlugin.html -text
......
......@@ -221,5 +221,16 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -163,7 +163,8 @@
"introduction": "With the Selections tool, you can add several kinds of dropdowns, checkboxes and radio groups to your document types.",
"description": "This tool helps you adding selection fields to your document types.",
"imageUrls": [
"/essentials/images/screenshots/selections01.png"
"/essentials/images/screenshots/selections01.png",
"/essentials/images/screenshots/selections02.png"
],
"documentationLink": "http://www.onehippo.org/library/concepts/plugins/selections/about.html",
"restClasses": ["org.onehippo.cms7.essentials.plugins.selection.SelectionResource"],
......
......@@ -37,6 +37,10 @@
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-essentials-plugin-sdk-implementation</artifactId>
</dependency>
<dependency>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-plugin-selections-hst-client</artifactId>
</dependency>
</dependencies>
<build>
......
/*
* Copyright 2015 Hippo B.V. (http://www.onehippo.com)
*
* 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.
*/
package org.onehippo.cms7.essentials.plugins.selection;
import org.onehippo.cms7.essentials.dashboard.model.Restful;
public class ProvisionedValueList implements Restful {
private String id;
private String path;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(final String path) {
this.path = path;
}
}
/*
* Copyright 2014 Hippo B.V. (http://www.onehippo.com)
* Copyright 2014-2015 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -16,13 +16,11 @@
package org.onehippo.cms7.essentials.plugins.selection;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;
import javax.inject.Inject;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
......@@ -38,15 +36,25 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import com.google.common.base.Strings;
import com.google.common.eventbus.EventBus;
import org.apache.commons.io.IOUtils;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.hippoecm.repository.api.NodeNameCodec;
import org.onehippo.cms7.essentials.dashboard.ctx.PluginContext;
import org.onehippo.cms7.essentials.dashboard.ctx.PluginContextFactory;
import org.onehippo.cms7.essentials.dashboard.event.RebuildEvent;
import org.onehippo.cms7.essentials.dashboard.rest.BaseResource;
import org.onehippo.cms7.essentials.dashboard.rest.MessageRestful;
import org.onehippo.cms7.essentials.dashboard.rest.PostPayloadRestful;
import org.onehippo.cms7.essentials.dashboard.utils.DocumentTemplateUtils;
import org.onehippo.cms7.essentials.dashboard.utils.GlobalUtils;
import org.onehippo.cms7.essentials.dashboard.utils.ProjectUtils;
import org.onehippo.cms7.essentials.dashboard.utils.TemplateUtils;
import org.onehippo.forge.selection.hst.manager.ValueListManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -57,6 +65,12 @@ public class SelectionResource extends BaseResource {
private static Logger log = LoggerFactory.getLogger(SelectionResource.class);
public static final String MULTISELECT_PLUGIN_CLASS = "org.onehippo.forge.selection.frontend.plugin.DynamicMultiSelectPlugin";
public static final String VALUELIST_MANAGER_ID = ValueListManager.class.getName();
private static final String VALUELIST_XPATH = "/beans/beans:bean[@id=\""
+ VALUELIST_MANAGER_ID + "\"]/beans:constructor-arg/beans:map";
@Inject
private EventBus eventBus;
@POST
@Path("/addfield")
......@@ -90,6 +104,74 @@ public class SelectionResource extends BaseResource {
return fields;
}
@GET
@Path("spring")
public List<ProvisionedValueList> loadProvisionedValueLists() {
List<ProvisionedValueList> pvlList = new ArrayList<>();
final Document document = readSpringConfiguration();
String xPath = VALUELIST_XPATH + "/beans:entry";
List valueLists = document.selectNodes(xPath);
Iterator iter = valueLists.iterator();
while (iter.hasNext()) {
Element valueList = (Element)iter.next();
ProvisionedValueList pvl = new ProvisionedValueList();
pvl.setId(valueList.attributeValue("key"));
pvl.setPath(valueList.attributeValue("value"));
pvlList.add(pvl);
}
return pvlList;
}
@POST
@Path("spring")
public MessageRestful storeProvisionedValueLists(final List<ProvisionedValueList> provisionedValueLists,
@Context HttpServletResponse response) {
final Document document = readSpringConfiguration();
if (document == null) {
return createErrorMessage("Failure parsing the Spring configuration.", response);
}
Element map = (Element)document.selectSingleNode(VALUELIST_XPATH);
if (map == null) {
return createErrorMessage("Failure locating the relevant piece of Spring configuration.", response);
}
// remove the old value lists
List<Element> oldValueLists = (List<Element>)map.elements();
for (Element e : oldValueLists) {
e.detach();
}
// add the new value lists
for (ProvisionedValueList pvl : provisionedValueLists) {
Element entry = map.addElement("entry");
entry.addAttribute("key", pvl.getId());
entry.addAttribute("value", pvl.getPath());
}
try {
final File springFile = getSpringFile();
springFile.getParentFile().mkdirs();
springFile.createNewFile();
FileOutputStream fos = new FileOutputStream(springFile);
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(fos, format);
writer.write(document);
writer.flush();
} catch (IOException ex) {
log.error("Problem writing the Spring configuration", ex);
return createErrorMessage("Failure storing the Spring configuration.", response);
}
final String message = "Spring configuration updated, project rebuild needed";
eventBus.post(new RebuildEvent("selectionPlugin", message));
return new MessageRestful("Successfully updated the Spring configuration");
}
/**
* Add all selection fields of a document type to a list.
*
......@@ -301,4 +383,65 @@ public class SelectionResource extends BaseResource {
destination.getSession().importXML(destination.getPath(), IOUtils.toInputStream(processedXml),
ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
}
private Document readSpringConfiguration() {
final File springFile = getSpringFile();
InputStream is = null;
if (springFile.exists() && springFile.isFile()) {
try {
is = new FileInputStream(springFile);
} catch (FileNotFoundException ex) {
log.error("Problem reading Spring configuration file.", ex);
}
} else {
// no Spring configuration present yet, use template.
final String path = "/xml/valuelistmanager.xml";
is = getClass().getResourceAsStream(path);
}
if (is != null) {
try {
Map<String, String> namespaceUris = new HashMap<>();
namespaceUris.put("beans", "http://www.springframework.org/schema/beans");
DocumentFactory factory = new DocumentFactory();
factory.setXPathNamespaceURIs(namespaceUris);
SAXReader reader = new SAXReader();
reader.setDocumentFactory(factory);
return reader.read(is);
} catch (DocumentException ex) {
log.error("Problem parsing Spring configuration file.", ex);
}
}
return null;
}
private File getSpringFile() {
final String baseDir = GlobalUtils.decodeUrl(ProjectUtils.getBaseProjectDirectory());
if (Strings.isNullOrEmpty(baseDir)) {
return null;
}
PluginContext context = PluginContextFactory.getContext();
String springFilePath = new StringBuilder()
.append(baseDir)
.append(File.separator)
.append(context.getProjectSettings().getSiteModule())
.append(File.separator)
.append("src")
.append(File.separator)
.append("main")
.append(File.separator)
.append("resources")
.append(File.separator)
.append("META-INF")
.append(File.separator)
.append("hst-assembly")
.append(File.separator)
.append("overrides")
.append(File.separator)
.append("valueListManager.xml").toString();
return new File(springFilePath);
}
}
\ No newline at end of file
......@@ -30,25 +30,29 @@
{ id: 'palette', label: 'Palette' }
];
// Since the tabs use transclusive scopes, we need to put our two-way bound variables into data structure.
$scope.data = {};
$scope.initializing = true;
$scope.addField = function() {
var payload = {
values: {
namespace: $scope.selectedDocumentType.prefix,
documentType: $scope.selectedDocumentType.name,
fieldName: $scope.fieldName,
selectionType: $scope.selectionType,
valueList: $scope.selectedValueList.value,
presentation: $scope.presentation.id,
orientation: $scope.orientation,
maxRows: $scope.maxRows,
allowOrdering: $scope.allowOrdering
namespace: $scope.data.selectedDocumentType.prefix,
documentType: $scope.data.selectedDocumentType.name,
fieldName: $scope.data.fieldName,
selectionType: $scope.data.selectionType,
valueList: $scope.data.selectedValueList.value,
presentation: $scope.data.presentation.id,
orientation: $scope.data.orientation,
maxRows: $scope.data.maxRows,
allowOrdering: $scope.data.allowOrdering
}
};
$http.post(restEndpoint + 'addfield/', payload).success(function () {
resetAddFieldForm();
reloadSelectionFields($scope.selectedDocumentType);
reloadSelectionFields($scope.data.selectedDocumentType);
$scope.fieldAdded = true;
$scope.modifiedType = $scope.selectedDocumentType;
$scope.modifiedType = $scope.data.selectedDocumentType;
});
};
$scope.showDocument = function(documentType) { // don't show the basedocument option
......@@ -58,18 +62,32 @@
$scope.valueListAsOption = function(valueList) {
return valueList.key + ' (' + valueList.value + ')';
};
$scope.saveProvisioning = function() {
var provisionedValueLists = [];
angular.forEach($scope.provisionedValueLists, function(valueList) {
if (valueList.included) {
provisionedValueLists.push({
id: valueList.id,
path: valueList.path
});
}
});
$http.post(restEndpoint + 'spring', provisionedValueLists).success(function() {
loadProvisionedValueLists();
});
};
$scope.selectionTypes = [ 'single', 'multiple' ];
$scope.$watch('selectionType', function(newValue) {
console.log("Selection type: ", newValue);
$scope.$watch('data.selectionType', function(newValue) {
if (newValue === 'single') {
$scope.typePresentations = singlePresentations;
} else if (newValue === 'multiple') {
$scope.typePresentations = multiplePresentations;
}
$scope.presentation = $scope.typePresentations[0]; // set default
$scope.data.presentation = $scope.typePresentations[0]; // set default
});
$scope.orientations = [ 'vertical', 'horizontal' ];
$scope.orientation = $scope.orientations[0]; // set default
$scope.data.orientation = $scope.orientations[0]; // set default
$scope.valueListNameByPath = function(path) {
var name = '';
angular.forEach($scope.valueLists, function(entry) {
......@@ -85,6 +103,7 @@
$scope.documentTypes = [];
$http.get($rootScope.REST.documents).success(function (data){
$scope.documentTypes = data;
$scope.initializing = false;
// if there's only one selectable type, preselect it.
var selectable = [];
......@@ -94,12 +113,12 @@
}
});
if (selectable.length == 1) {
$scope.selectedDocumentType = selectable[0];
$scope.data.selectedDocumentType = selectable[0];
}
});
// when changing the document type, set the default position and retrieve a fresh list of fields
$scope.$watch('selectedDocumentType', function (newDocType) {
$scope.$watch('data.selectedDocumentType', function (newDocType) {
if (newDocType) {
reloadSelectionFields(newDocType);
} else {
......@@ -111,15 +130,41 @@
function loadValueLists() {
$http.get($rootScope.REST.documents + "selection:valuelist").success(function (data) {
$scope.valueLists = data;
loadProvisionedValueLists();
});
}
function loadProvisionedValueLists() {
if ($scope.valueLists.length > 0) {
$http.get(restEndpoint + 'spring').success(function (oldProvisionedValueLists) {
var provisionedValueLists = [];
angular.forEach($scope.valueLists, function(valueList) {
var oldItem, newItem;
angular.forEach(oldProvisionedValueLists, function(oldValueList) {
if (oldValueList.path === valueList.value) {
oldItem = oldValueList;
}
});
if (oldItem) {
oldItem.included = true;
newItem = oldItem;
} else {
newItem = {
path: valueList.value
};
}
provisionedValueLists.push(newItem);
});
$scope.provisionedValueLists = provisionedValueLists;
});
}
}
function resetAddFieldForm() {
$scope.fieldName = '';
console.log("Setting selection type");
$scope.selectionType = 'single';
$scope.selectedValueList = undefined;
$scope.allowOrdering = false;
$scope.maxRows = undefined;
$scope.data.fieldName = '';
$scope.data.selectionType = 'single';
$scope.data.selectedValueList = undefined;
$scope.data.allowOrdering = false;
$scope.data.maxRows = undefined;
}
function reloadSelectionFields(documentType) {
$scope.selectionFields = [];
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2014 Hippo B.V. (http://www.onehippo.com)
Copyright 2014-2015 Hippo B.V. (http://www.onehippo.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -22,8 +22,7 @@
class="org.onehippo.forge.selection.hst.manager.DefaultValueListManager">
<constructor-arg>
<map>
<!--<entry key="relativeValuelist1" value="valuelists/valuelist1"/>-->
<!--<entry key="absoluteValuelist2" value="/content/documents/valuelists/valuelist2"/>-->
<!--<entry key="name" value="/absolute/repository/path"/>-->
</map>
</constructor-arg>
</bean>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment