Skip to content

Commit

Permalink
Add feature to export configuration which closes #663 (#779)
Browse files Browse the repository at this point in the history
* Add API to export configuration from UI which closes #663

* Add export button which closes #663
  • Loading branch information
ocadaruma authored and takahi-i committed Apr 18, 2017
1 parent e41a39b commit 6cc8218
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import cc.redpen.RedPen;
import cc.redpen.RedPenException;
import cc.redpen.config.ConfigurationExporter;
import cc.redpen.config.Symbol;
import cc.redpen.config.SymbolType;
import cc.redpen.parser.DocumentParser;
Expand All @@ -34,6 +35,8 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

/**
Expand Down Expand Up @@ -118,4 +121,27 @@ public Response getRedPens(@QueryParam("lang") @DefaultValue("") String lang) th

return Response.ok().entity(response).build();
}

@Path("/export")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_XML)
@WinkAPIDescriber.Description("Returns the configuration XML corresponds to the UI")
public Response exportConfiguration(JSONObject requestJSON) {

LOG.info("Exporting configuration using JSON request");

RedPen redPen = new RedPenService(context).getRedPenFromJSON(requestJSON);

String result = null;

try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
new ConfigurationExporter().export(redPen.getConfiguration(), baos);
result = new String(baos.toByteArray());
} catch (IOException e) {
LOG.error("Exception when exporting configuration", e);
}

return Response.ok(result, RedPenResource.MIME_TYPE_XML).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import cc.redpen.RedPen;
import cc.redpen.RedPenException;
import cc.redpen.config.ConfigurationLoader;
import cc.redpen.config.Symbol;
import cc.redpen.config.SymbolType;
import cc.redpen.formatter.Formatter;
import cc.redpen.model.Document;
import cc.redpen.parser.DocumentParser;
Expand All @@ -43,10 +41,9 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static cc.redpen.server.api.RedPenService.getOrDefault;

/**
* Resource to validate documents.
Expand Down Expand Up @@ -160,68 +157,8 @@ public Response validateDocumentJSON(JSONObject requestJSON) throws RedPenExcept
String documentText = getOrDefault(requestJSON, "document", "");
String format = getOrDefault(requestJSON, "format", DEFAULT_FORMAT);

String lang = DEFAULT_LANG;

Map<String, Map<String, String>> properties = new HashMap<>();
JSONObject config = null;
if (requestJSON.has("config")) {
try {
config = requestJSON.getJSONObject("config");
lang = getOrDefault(config, "lang", DEFAULT_LANG);
if (config.has("validators")) {
JSONObject validators = config.getJSONObject("validators");
Iterator keyIter = validators.keys();
while (keyIter.hasNext()) {
String validator = String.valueOf(keyIter.next());
Map<String, String> props = new HashMap<>();
properties.put(validator, props);
JSONObject validatorConfig = validators.getJSONObject(validator);
if ((validatorConfig != null) && validatorConfig.has("properties")) {
JSONObject validatorProps = validatorConfig.getJSONObject("properties");
Iterator propsIter = validatorProps.keys();
while (propsIter.hasNext()) {
String propname = String.valueOf(propsIter.next());
props.put(propname, validatorProps.getString(propname));
}
}
}
}
} catch (Exception e) {
LOG.error("Exception when processing JSON properties", e);
}
}

RedPen redPen = new RedPenService(context).getRedPen(lang, properties);

// override any symbols
if ((config != null) && config.has("symbols")) {
try {
JSONObject symbols = config.getJSONObject("symbols");
Iterator keyIter = symbols.keys();
while (keyIter.hasNext()) {
String symbolName = String.valueOf(keyIter.next());
try {
SymbolType symbolType = SymbolType.valueOf(symbolName);
JSONObject symbolConfig = symbols.getJSONObject(symbolName);
Symbol originalSymbol = redPen.getConfiguration().getSymbolTable().getSymbol(symbolType);
if ((originalSymbol != null) && (symbolConfig != null) && symbolConfig.has("value")) {
String value = symbolConfig.has("value") ? symbolConfig.getString("value") : String.valueOf(originalSymbol.getValue());
boolean spaceBefore = symbolConfig.has("before_space") ? symbolConfig.getBoolean("before_space") : originalSymbol.isNeedBeforeSpace();
boolean spaceAfter = symbolConfig.has("after_space") ? symbolConfig.getBoolean("after_space") : originalSymbol.isNeedAfterSpace();
String invalidChars = symbolConfig.has("invalid_chars") ? symbolConfig.getString("invalid_chars") : String.valueOf(originalSymbol.getInvalidChars());
if ((value != null) && !value.isEmpty()) {
redPen.getConfiguration().getSymbolTable().overrideSymbol(new Symbol(symbolType, value.charAt(0), invalidChars, spaceBefore, spaceAfter));
}
}
RedPen redPen = new RedPenService(context).getRedPenFromJSON(requestJSON);

} catch (IllegalArgumentException iae) {
LOG.error("Ignoring unknown SymbolType " + symbolName);
}
}
} catch (Exception e) {
LOG.error("Exception when processing JSON symbol overrides", e);
}
}
Document parsedDocument = redPen.parse(DocumentParser.of(documentParser), documentText);

List<ValidationError> errors = redPen.validate(parsedDocument);
Expand Down Expand Up @@ -258,16 +195,4 @@ public JSONObject tokenize(@FormParam("document") @DefaultValue("") String docum

return new JSONObject().put("tokens", tokenizer.tokenize(document == null ? "" : document));
}

private String getOrDefault(JSONObject json, String property, String defaultValue) {
try {
String value = json.getString(property);
if (value != null) {
return value;
}
} catch (Exception e) {
// intentionally empty
}
return defaultValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,15 @@

import cc.redpen.RedPen;
import cc.redpen.RedPenException;
import cc.redpen.config.Configuration;
import cc.redpen.config.ConfigurationLoader;
import cc.redpen.config.ValidatorConfiguration;
import cc.redpen.config.*;
import cc.redpen.model.Document;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContext;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

/**
* Helper class to access RedPen instances for use within the webapp
Expand Down Expand Up @@ -90,6 +86,78 @@ public RedPen getRedPen(String lang) {
return redPens.getOrDefault(lang, redPens.get(DEFAULT_LANGUAGE));
}

/**
* Create a new redpen for the JSON object.
* @param requestJSON the JSON contains configurations
* @return a configured redpen instance
*/
public RedPen getRedPenFromJSON(JSONObject requestJSON) {
String lang = "en";

Map<String, Map<String, String>> properties = new HashMap<>();
JSONObject config = null;
if (requestJSON.has("config")) {
try {
config = requestJSON.getJSONObject("config");
lang = getOrDefault(config, "lang", "en");
if (config.has("validators")) {
JSONObject validators = config.getJSONObject("validators");
Iterator keyIter = validators.keys();
while (keyIter.hasNext()) {
String validator = String.valueOf(keyIter.next());
Map<String, String> props = new HashMap<>();
properties.put(validator, props);
JSONObject validatorConfig = validators.getJSONObject(validator);
if ((validatorConfig != null) && validatorConfig.has("properties")) {
JSONObject validatorProps = validatorConfig.getJSONObject("properties");
Iterator propsIter = validatorProps.keys();
while (propsIter.hasNext()) {
String propname = String.valueOf(propsIter.next());
props.put(propname, validatorProps.getString(propname));
}
}
}
}
} catch (Exception e) {
LOG.error("Exception when processing JSON properties", e);
}
}

RedPen redPen = this.getRedPen(lang, properties);

// override any symbols
if ((config != null) && config.has("symbols")) {
try {
JSONObject symbols = config.getJSONObject("symbols");
Iterator keyIter = symbols.keys();
while (keyIter.hasNext()) {
String symbolName = String.valueOf(keyIter.next());
try {
SymbolType symbolType = SymbolType.valueOf(symbolName);
JSONObject symbolConfig = symbols.getJSONObject(symbolName);
Symbol originalSymbol = redPen.getConfiguration().getSymbolTable().getSymbol(symbolType);
if ((originalSymbol != null) && (symbolConfig != null) && symbolConfig.has("value")) {
String value = symbolConfig.has("value") ? symbolConfig.getString("value") : String.valueOf(originalSymbol.getValue());
boolean spaceBefore = symbolConfig.has("before_space") ? symbolConfig.getBoolean("before_space") : originalSymbol.isNeedBeforeSpace();
boolean spaceAfter = symbolConfig.has("after_space") ? symbolConfig.getBoolean("after_space") : originalSymbol.isNeedAfterSpace();
String invalidChars = symbolConfig.has("invalid_chars") ? symbolConfig.getString("invalid_chars") : String.valueOf(originalSymbol.getInvalidChars());
if ((value != null) && !value.isEmpty()) {
redPen.getConfiguration().getSymbolTable().overrideSymbol(new Symbol(symbolType, value.charAt(0), invalidChars, spaceBefore, spaceAfter));
}
}

} catch (IllegalArgumentException iae) {
LOG.error("Ignoring unknown SymbolType " + symbolName);
}
}
} catch (Exception e) {
LOG.error("Exception when processing JSON symbol overrides", e);
}
}

return redPen;
}

/**
* Create a new redpen for the specified language. The validator properties map is a map of validator names to their (optional) properties.
* Only validitors present in this map are added to the redpen configuration
Expand Down Expand Up @@ -123,4 +191,16 @@ public RedPen getRedPen(String lang, Map<String, Map<String, String>> validatorP
public Map<String, RedPen> getRedPens() {
return redPens;
}

public static String getOrDefault(JSONObject json, String property, String defaultValue) {
try {
String value = json.getString(property);
if (value != null) {
return value;
}
} catch (Exception e) {
// intentionally empty
}
return defaultValue;
}
}
4 changes: 4 additions & 0 deletions redpen-server/src/main/webapp/css/redpen.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ input[type="radio"], input[type="checkbox"] {
font-size: 11px;
}

.float-right {
float: right;
}

.control-label {
color: #838178;
font-size: 12px;
Expand Down
3 changes: 3 additions & 0 deletions redpen-server/src/main/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ <h1>Welcome to <span class="redpen-red">Red</span>Pen!</h1>
<label class="control-label">Language
<select id="redpen-language" class="form-control"></select>
</label>
<label class="control-label float-right"><br>
<button id="redpen-export" class="form-control">export</button>
</label>
<label class="control-label" style="display:none">Config
<select id="redpen-configuration" class="form-control"> </select>
</label>
Expand Down
36 changes: 36 additions & 0 deletions redpen-server/src/main/webapp/js/redpen-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,38 @@ RedPenUI.Utils.setLanguage = function (lang) {
RedPenUI.Utils.showConfigurationOptions(firstValidRedpen);
};

RedPenUI.Utils.exportConfiguration = function () {
var activeValidators = $("#redpen-active-validators").find("input:checked");
var params = {
config: RedPenUI.Utils.getConfiguration(
$("#redpen-configuration").val(),
$.map(activeValidators, function(element){return $(element).val()}))
};
redpen.export(params, function (data) {
var URL = window.URL || window.webkitURL;
var url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(data)], {type: 'application/xml'}));

var link = document.createElement('a');
if (typeof link.download === 'undefined') {
window.location = url;
// cleanup
setTimeout(function () {
URL.revokeObjectURL(url);
}, 100);
} else {
document.body.appendChild(link);
link.href = url;
link.download = 'redpen-conf.xml';
link.click();
// cleanup
setTimeout(function () {
URL.revokeObjectURL(url);
document.body.removeChild(link);
}, 100);
}
});
};


RedPenUI.showComponents = function(configuration) {
RedPenUI.currentConfiguration = configuration; // for non-inner methods
Expand Down Expand Up @@ -632,6 +664,10 @@ RedPenUI.showComponents = function(configuration) {
RedPenUI.Utils.validateDocument();
});

$("#redpen-export").click(function () {
RedPenUI.Utils.exportConfiguration();
});

$("#redpen-token-editor").on("change keyup", RedPenUI.Utils.updateTokens);
$("input[type='radio'][name='redpen-token-lang']").on("change", RedPenUI.Utils.updateTokens);

Expand Down
17 changes: 17 additions & 0 deletions redpen-server/src/main/webapp/js/redpen.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ var redpen = (function ($) {
});
};

this.export = function (parameters, callback) {
$.ajax({
type: "POST",
url: baseUrl + "rest/config/export",
data: JSON.stringify(parameters),
dataType: 'xml',
contentType: "application/json; charset=utf-8",
success: function (data) {
if (callback) {
callback(data);
}
}
}).fail(function (err) {
console.log(err);
});
};


return this;
})(jQuery);
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,12 @@ public void redPenFields() throws Exception {
assertTrue(!ja.getString("validators").isEmpty());
assertTrue(!ja.getString("symbols").isEmpty());
}

@Test
public void exportConfiguration() throws Exception {
String json = "{\"config\": {\"lang\": \"ru\"}}";
String response = (String)resource.exportConfiguration(new JSONObject(json)).getEntity();

assertTrue(response.contains("redpen-conf lang=\"ru\""));
}
}
Loading

0 comments on commit 6cc8218

Please sign in to comment.