From 54ac4ea70142ffb93c081773d14fa7ee84c69c6c Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Mon, 14 Sep 2015 11:50:47 +0200 Subject: [PATCH 001/119] layers-editor first commit --- demo/pom.xml | 5 +++ layers-editor/pom.xml | 30 +++++++++++++++++ .../unredd/layersEditor/LayersServlet.java | 33 +++++++++++++++++++ .../main/resources/META-INF/web-fragment.xml | 21 ++++++++++++ .../resources/nfms/modules/layers-editor.js | 3 ++ pom.xml | 3 +- 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 layers-editor/pom.xml create mode 100644 layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java create mode 100644 layers-editor/src/main/resources/META-INF/web-fragment.xml create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor.js diff --git a/demo/pom.xml b/demo/pom.xml index 2e41053..03f3f75 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -29,6 +29,11 @@ base 3.1-SNAPSHOT + + org.fao.unredd + layers-editor + 3.1-SNAPSHOT + org.fao.unredd footnote diff --git a/layers-editor/pom.xml b/layers-editor/pom.xml new file mode 100644 index 0000000..914b81d --- /dev/null +++ b/layers-editor/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.fao.unredd + portal-root + 3.1-SNAPSHOT + + layers-editor + layers-editor + http://maven.apache.org + + UTF-8 + + + + org.fao.unredd + core + 3.1-SNAPSHOT + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + diff --git a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java new file mode 100644 index 0000000..f06173b --- /dev/null +++ b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java @@ -0,0 +1,33 @@ +package org.fao.unredd.layersEditor; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.IOUtils; +import org.fao.unredd.portal.Config; + +public class LayersServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType("application/json"); + resp.setCharacterEncoding("utf8"); + + Config config = (Config) getServletContext().getAttribute("config"); + String layersTemplate = IOUtils.toString( + new File(config.getDir(), "layers.json").toURI(), "UTF-8"); + + PrintWriter writer = resp.getWriter(); + writer.write(layersTemplate); + + } + +} diff --git a/layers-editor/src/main/resources/META-INF/web-fragment.xml b/layers-editor/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 0000000..2ca21ac --- /dev/null +++ b/layers-editor/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,21 @@ + + + + + + core + + + + + layers + org.fao.unredd.layersEditor.LayersServlet + + + layers + /layers.json + + + \ No newline at end of file diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.js b/layers-editor/src/main/resources/nfms/modules/layers-editor.js new file mode 100644 index 0000000..674ba88 --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor.js @@ -0,0 +1,3 @@ +define([], function() { + alert("Hello editor"); +}); diff --git a/pom.xml b/pom.xml index d2b16ca..9aa030f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,8 @@ argentina tour footnote - + layers-editor + UTF-8 9.2 From 1c73513d6c972a70a527f03f96905086ced70171 Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Mon, 14 Sep 2015 18:17:38 +0200 Subject: [PATCH 002/119] Add edit action buttons to TOC --- .../nfms/modules/layer-list-selector.css | 2 +- .../main/resources/nfms/modules/layer-list.js | 2 +- .../resources/nfms/modules/images/pencil.png | Bin 0 -> 346 bytes .../nfms/modules/layer-edit-button.js | 24 ++++++++++++++++++ .../resources/nfms/modules/layers-editor.css | 8 ++++++ .../resources/nfms/modules/layers-editor.js | 2 +- 6 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 layers-editor/src/main/resources/nfms/modules/images/pencil.png create mode 100644 layers-editor/src/main/resources/nfms/modules/layer-edit-button.js create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor.css diff --git a/base/src/main/resources/nfms/modules/layer-list-selector.css b/base/src/main/resources/nfms/modules/layer-list-selector.css index 0c5c884..1ba4bac 100644 --- a/base/src/main/resources/nfms/modules/layer-list-selector.css +++ b/base/src/main/resources/nfms/modules/layer-list-selector.css @@ -2,7 +2,7 @@ left: 10px; position: absolute; top: 215px; - width: 250px; + min-width: 250px; z-index: 1100; } diff --git a/base/src/main/resources/nfms/modules/layer-list.js b/base/src/main/resources/nfms/modules/layer-list.js index d6cb24e..650175d 100644 --- a/base/src/main/resources/nfms/modules/layer-list.js +++ b/base/src/main/resources/nfms/modules/layer-list.js @@ -46,7 +46,7 @@ define([ "jquery", "message-bus", "layer-list-selector", "i18n", "moment", "jque event.stopPropagation() }); element.addClass("layer_info_button").addClass("group_info_button"); - divTitle.append(element); + divTitle.prepend(element); } } diff --git a/layers-editor/src/main/resources/nfms/modules/images/pencil.png b/layers-editor/src/main/resources/nfms/modules/images/pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..cc38964c1222ab4a93f8919f6cf749dcbf563974 GIT binary patch literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~Cm+8A_nl|rUw}f-JzX3_EKV=IWbMW5D8v3Bf0F5f z3HMq(xH=UwGILcUGT$n;>}?3=$ZU~uIR8NN4NEQ$hnEX0H+SLn@P|B0<{Vl1?Calu zXOvH5To!zh!2A5hVfI%OI;U^w%f4cmBYEF7?Wu3c;}Z7x3U~9A2cdT!%C+z4iOz~? z_4(7xcHUwJr(}zdOxh~DhaCDdM2a7j%(UI}P|Vkm-S4rT+{G!OcQ!DrJN+tO@ejK* zH>;W0!3$d2^KTqixmRS?_N`<|JGV?SyLQI&2PG|QZ`dE^lq)Lay3zZ7mXqJcxy``` mw2yr(|9bkAfVrha9pkjyaqKzgt!D$h%;4$j=d#Wzp$Pzvf`&){ literal 0 HcmV?d00001 diff --git a/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js b/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js new file mode 100644 index 0000000..48e543c --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js @@ -0,0 +1,24 @@ +define([ "message-bus" ], function(bus) { + + function buildLink(type, id) { + return $("") + .addClass("layer_edit_button") + .attr("id", "layer_edit_button_" + id) + .click(function(event) { + showForm(type, id); + }); + }; + + function showForm(type, id) { + console.log("Edit " + type + " " + id); + }; + + bus.listen("before-adding-layers", function() { + bus.send("register-layer-action", function(layer) { + return buildLink("layer", layer.id); + }); + bus.send("register-group-action", function(group) { + return buildLink("group", group.id); + }); + }); +}); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.css b/layers-editor/src/main/resources/nfms/modules/layers-editor.css new file mode 100644 index 0000000..390d9a7 --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor.css @@ -0,0 +1,8 @@ +.layer_edit_button { + background: url("images/pencil.png") no-repeat; + display: block; + height: 16px; + width: 16px; + padding: 0; + text-decoration: none; +} diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.js b/layers-editor/src/main/resources/nfms/modules/layers-editor.js index 674ba88..bf942ae 100644 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor.js +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor.js @@ -1,3 +1,3 @@ define([], function() { - alert("Hello editor"); + console.log("Hello Editor"); }); From 9c160e37f72380fa32c9814cd73490ac646bace0 Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Tue, 15 Sep 2015 15:18:37 +0200 Subject: [PATCH 003/119] Parse layers.json & find elements to be edited --- base/src/main/resources/nfms/base-conf.json | 3 +- base/src/main/resources/nfms/jslib/text.js | 391 ++++++++++++++++++ .../nfms/modules/layer-edit-button.js | 24 -- .../resources/nfms/modules/layers-editor.js | 35 +- .../resources/nfms/modules/layers-json.js | 31 ++ 5 files changed, 457 insertions(+), 27 deletions(-) create mode 100644 base/src/main/resources/nfms/jslib/text.js delete mode 100644 layers-editor/src/main/resources/nfms/modules/layer-edit-button.js create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-json.js diff --git a/base/src/main/resources/nfms/base-conf.json b/base/src/main/resources/nfms/base-conf.json index 30361fa..821a9c4 100644 --- a/base/src/main/resources/nfms/base-conf.json +++ b/base/src/main/resources/nfms/base-conf.json @@ -22,7 +22,8 @@ "highcharts-theme-sand" : "../jslib/sand-signika", "openlayers" : "../jslib/OpenLayers/OpenLayers.debug", "mustache" : "../jslib/jquery.mustache", - "moment" : "../jslib/moment" + "moment" : "../jslib/moment", + "text" : "../jslib/text" }, "shim" : { "fancy-box" : [ "jquery" ], diff --git a/base/src/main/resources/nfms/jslib/text.js b/base/src/main/resources/nfms/jslib/text.js new file mode 100644 index 0000000..4c311ed --- /dev/null +++ b/base/src/main/resources/nfms/jslib/text.js @@ -0,0 +1,391 @@ +/** + * @license RequireJS text 2.0.14 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/text for details + */ +/*jslint regexp: true */ +/*global require, XMLHttpRequest, ActiveXObject, + define, window, process, Packages, + java, location, Components, FileUtils */ + +define(['module'], function (module) { + 'use strict'; + + var text, fs, Cc, Ci, xpcIsWindows, + progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], + xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, + bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, + hasLocation = typeof location !== 'undefined' && location.href, + defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), + defaultHostName = hasLocation && location.hostname, + defaultPort = hasLocation && (location.port || undefined), + buildMap = {}, + masterConfig = (module.config && module.config()) || {}; + + text = { + version: '2.0.14', + + strip: function (content) { + //Strips declarations so that external SVG and XML + //documents can be added to a document without worry. Also, if the string + //is an HTML document, only the part inside the body tag is returned. + if (content) { + content = content.replace(xmlRegExp, ""); + var matches = content.match(bodyRegExp); + if (matches) { + content = matches[1]; + } + } else { + content = ""; + } + return content; + }, + + jsEscape: function (content) { + return content.replace(/(['\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029"); + }, + + createXhr: masterConfig.createXhr || function () { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else if (typeof ActiveXObject !== "undefined") { + for (i = 0; i < 3; i += 1) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + return xhr; + }, + + /** + * Parses a resource name into its component parts. Resource names + * look like: module/name.ext!strip, where the !strip part is + * optional. + * @param {String} name the resource name + * @returns {Object} with properties "moduleName", "ext" and "strip" + * where strip is a boolean. + */ + parseName: function (name) { + var modName, ext, temp, + strip = false, + index = name.lastIndexOf("."), + isRelative = name.indexOf('./') === 0 || + name.indexOf('../') === 0; + + if (index !== -1 && (!isRelative || index > 1)) { + modName = name.substring(0, index); + ext = name.substring(index + 1); + } else { + modName = name; + } + + temp = ext || modName; + index = temp.indexOf("!"); + if (index !== -1) { + //Pull off the strip arg. + strip = temp.substring(index + 1) === "strip"; + temp = temp.substring(0, index); + if (ext) { + ext = temp; + } else { + modName = temp; + } + } + + return { + moduleName: modName, + ext: ext, + strip: strip + }; + }, + + xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, + + /** + * Is an URL on another domain. Only works for browser use, returns + * false in non-browser environments. Only used to know if an + * optimized .js version of a text resource should be loaded + * instead. + * @param {String} url + * @returns Boolean + */ + useXhr: function (url, protocol, hostname, port) { + var uProtocol, uHostName, uPort, + match = text.xdRegExp.exec(url); + if (!match) { + return true; + } + uProtocol = match[2]; + uHostName = match[3]; + + uHostName = uHostName.split(':'); + uPort = uHostName[1]; + uHostName = uHostName[0]; + + return (!uProtocol || uProtocol === protocol) && + (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && + ((!uPort && !uHostName) || uPort === port); + }, + + finishLoad: function (name, strip, content, onLoad) { + content = strip ? text.strip(content) : content; + if (masterConfig.isBuild) { + buildMap[name] = content; + } + onLoad(content); + }, + + load: function (name, req, onLoad, config) { + //Name has format: some.module.filext!strip + //The strip part is optional. + //if strip is present, then that means only get the string contents + //inside a body tag in an HTML string. For XML/SVG content it means + //removing the declarations so the content can be inserted + //into the current doc without problems. + + // Do not bother with the work if a build and text will + // not be inlined. + if (config && config.isBuild && !config.inlineText) { + onLoad(); + return; + } + + masterConfig.isBuild = config && config.isBuild; + + var parsed = text.parseName(name), + nonStripName = parsed.moduleName + + (parsed.ext ? '.' + parsed.ext : ''), + url = req.toUrl(nonStripName), + useXhr = (masterConfig.useXhr) || + text.useXhr; + + // Do not load if it is an empty: url + if (url.indexOf('empty:') === 0) { + onLoad(); + return; + } + + //Load the text. Use XHR if possible and in a browser. + if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { + text.get(url, function (content) { + text.finishLoad(name, parsed.strip, content, onLoad); + }, function (err) { + if (onLoad.error) { + onLoad.error(err); + } + }); + } else { + //Need to fetch the resource across domains. Assume + //the resource has been optimized into a JS module. Fetch + //by the module name + extension, but do not include the + //!strip part to avoid file system issues. + req([nonStripName], function (content) { + text.finishLoad(parsed.moduleName + '.' + parsed.ext, + parsed.strip, content, onLoad); + }); + } + }, + + write: function (pluginName, moduleName, write, config) { + if (buildMap.hasOwnProperty(moduleName)) { + var content = text.jsEscape(buildMap[moduleName]); + write.asModule(pluginName + "!" + moduleName, + "define(function () { return '" + + content + + "';});\n"); + } + }, + + writeFile: function (pluginName, moduleName, req, write, config) { + var parsed = text.parseName(moduleName), + extPart = parsed.ext ? '.' + parsed.ext : '', + nonStripName = parsed.moduleName + extPart, + //Use a '.js' file name so that it indicates it is a + //script that can be loaded across domains. + fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; + + //Leverage own load() method to load plugin value, but only + //write out values that do not have the strip argument, + //to avoid any potential issues with ! in file names. + text.load(nonStripName, req, function (value) { + //Use own write() method to construct full module value. + //But need to create shell that translates writeFile's + //write() to the right interface. + var textWrite = function (contents) { + return write(fileName, contents); + }; + textWrite.asModule = function (moduleName, contents) { + return write.asModule(moduleName, fileName, contents); + }; + + text.write(pluginName, nonStripName, textWrite, config); + }, config); + } + }; + + if (masterConfig.env === 'node' || (!masterConfig.env && + typeof process !== "undefined" && + process.versions && + !!process.versions.node && + !process.versions['node-webkit'] && + !process.versions['atom-shell'])) { + //Using special require.nodeRequire, something added by r.js. + fs = require.nodeRequire('fs'); + + text.get = function (url, callback, errback) { + try { + var file = fs.readFileSync(url, 'utf8'); + //Remove BOM (Byte Mark Order) from utf8 files if it is there. + if (file[0] === '\uFEFF') { + file = file.substring(1); + } + callback(file); + } catch (e) { + if (errback) { + errback(e); + } + } + }; + } else if (masterConfig.env === 'xhr' || (!masterConfig.env && + text.createXhr())) { + text.get = function (url, callback, errback, headers) { + var xhr = text.createXhr(), header; + xhr.open('GET', url, true); + + //Allow plugins direct access to xhr headers + if (headers) { + for (header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header.toLowerCase(), headers[header]); + } + } + } + + //Allow overrides specified in config + if (masterConfig.onXhr) { + masterConfig.onXhr(xhr, url); + } + + xhr.onreadystatechange = function (evt) { + var status, err; + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + status = xhr.status || 0; + if (status > 399 && status < 600) { + //An http 4xx or 5xx error. Signal an error. + err = new Error(url + ' HTTP status: ' + status); + err.xhr = xhr; + if (errback) { + errback(err); + } + } else { + callback(xhr.responseText); + } + + if (masterConfig.onXhrComplete) { + masterConfig.onXhrComplete(xhr, url); + } + } + }; + xhr.send(null); + }; + } else if (masterConfig.env === 'rhino' || (!masterConfig.env && + typeof Packages !== 'undefined' && typeof java !== 'undefined')) { + //Why Java, why is this so awkward? + text.get = function (url, callback) { + var stringBuffer, line, + encoding = "utf-8", + file = new java.io.File(url), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), + content = ''; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + + if (line !== null) { + stringBuffer.append(line); + } + + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator); + stringBuffer.append(line); + } + //Make sure we return a JavaScript string and not a Java string. + content = String(stringBuffer.toString()); //String + } finally { + input.close(); + } + callback(content); + }; + } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && + typeof Components !== 'undefined' && Components.classes && + Components.interfaces)) { + //Avert your gaze! + Cc = Components.classes; + Ci = Components.interfaces; + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); + + text.get = function (url, callback) { + var inStream, convertStream, fileObj, + readData = {}; + + if (xpcIsWindows) { + url = url.replace(/\//g, '\\'); + } + + fileObj = new FileUtils.File(url); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, "utf-8", inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + convertStream.close(); + inStream.close(); + callback(readData.value); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } + }; + } + return text; +}); diff --git a/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js b/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js deleted file mode 100644 index 48e543c..0000000 --- a/layers-editor/src/main/resources/nfms/modules/layer-edit-button.js +++ /dev/null @@ -1,24 +0,0 @@ -define([ "message-bus" ], function(bus) { - - function buildLink(type, id) { - return $("") - .addClass("layer_edit_button") - .attr("id", "layer_edit_button_" + id) - .click(function(event) { - showForm(type, id); - }); - }; - - function showForm(type, id) { - console.log("Edit " + type + " " + id); - }; - - bus.listen("before-adding-layers", function() { - bus.send("register-layer-action", function(layer) { - return buildLink("layer", layer.id); - }); - bus.send("register-group-action", function(group) { - return buildLink("group", group.id); - }); - }); -}); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.js b/layers-editor/src/main/resources/nfms/modules/layers-editor.js index bf942ae..9536d01 100644 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor.js +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor.js @@ -1,3 +1,34 @@ -define([], function() { - console.log("Hello Editor"); +define(["message-bus", "layers-json"], function(bus, layers) { + + bus.listen("before-adding-layers", function() { + bus.send("register-layer-action", function(layer) { + return link(layer.id, editLayer); + }); + bus.send("register-group-action", function(group) { + return link(group.id, editGroup); + }); + }); + + function link(id, onClick) { + return $("") + .addClass("layer_edit_button") + .click(function() { + onClick.call(null, id); + }); + } + + function editLayer(id) { + var portalLayer = layers.getPortalLayer(id); + var wmsLayer = layers.getWmsLayer(portalLayer.layers[0]); + alert("Edit Layer: " + JSON.stringify({ + portalLayer: portalLayer, + wmsLayer: wmsLayer + }, null, 3)); + } + + function editGroup(id) { + var group = layers.getGroup(id); + alert("Edit Group: " + group.label); + } + }); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-json.js b/layers-editor/src/main/resources/nfms/modules/layers-json.js new file mode 100644 index 0000000..16527bc --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-json.js @@ -0,0 +1,31 @@ +define(["text!../layers.json"], function(layersjson) { + + var config = JSON.parse(layersjson); + + function find(arr, key, value, recurse) { + for (var i = 0; i < arr.length; i++) { + var el = arr[i]; + if (el.hasOwnProperty(key) && el[key] == value) { + return el; + } else if(recurse && el.hasOwnProperty(recurse)) { + el = find(el[recurse], key, value, recurse); + if (el) { + return el; + } + } + } + }; + + return { + getPortalLayer: function(id) { + return find(config.portalLayers, "id", id); + }, + getWmsLayer: function(id) { + return find(config.wmsLayers, "id", id); + }, + getGroup: function(id) { + return find(config.groups, "id", id, "items"); + } + }; + +}); From 49b994bbe9f5220e77f1c8591a711911c2aa1450 Mon Sep 17 00:00:00 2001 From: Micho Garcia Date: Wed, 7 Oct 2015 15:43:10 +0200 Subject: [PATCH 004/119] Servlet get layers.json from request body. Creates a backup of current layers.json --- layers-editor/pom.xml | 12 ++ .../unredd/layersEditor/LayersServlet.java | 58 +++++++-- .../unredd/layersEditor/LayersEditorTest.java | 113 ++++++++++++++++++ layers-editor/src/test/resources/layers.json | 37 ++++++ .../src/test/resources/log4j.properties | 8 ++ 5 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java create mode 100644 layers-editor/src/test/resources/layers.json create mode 100644 layers-editor/src/test/resources/log4j.properties diff --git a/layers-editor/pom.xml b/layers-editor/pom.xml index 914b81d..814d956 100644 --- a/layers-editor/pom.xml +++ b/layers-editor/pom.xml @@ -15,6 +15,18 @@ UTF-8 + + org.mockito + mockito-all + 1.9.5 + test + + + junit + junit + test + 4.8.2 + org.fao.unredd core diff --git a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java index f06173b..5d2f2be 100644 --- a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java +++ b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java @@ -1,33 +1,75 @@ package org.fao.unredd.layersEditor; +import java.io.BufferedReader; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; +import java.sql.Timestamp; +import java.util.Date; +import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.annotation.MultipartConfig; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.fao.unredd.portal.Config; +@MultipartConfig public class LayersServlet extends HttpServlet { - private static final long serialVersionUID = 1L; + private static final String APPLICATION_JSON = "application/json"; + private static final String CONFIG = "config"; + private static final String UTF_8 = "UTF-8"; + private static final String LAYERS_JSON = "layers.json"; + private static final String BACKUP_FOLDER = "backup"; + private static final long serialVersionUID = 1L; - @Override + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.setContentType("application/json"); - resp.setCharacterEncoding("utf8"); - - Config config = (Config) getServletContext().getAttribute("config"); + resp.setContentType(APPLICATION_JSON); + resp.setCharacterEncoding(UTF_8); + Config config = (Config) getServletContext().getAttribute(CONFIG); String layersTemplate = IOUtils.toString( - new File(config.getDir(), "layers.json").toURI(), "UTF-8"); + new File(config.getDir(), LAYERS_JSON).toURI(), UTF_8); PrintWriter writer = resp.getWriter(); writer.write(layersTemplate); - } + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + ServletContext ctx = getServletContext(); + Config config = (Config) ctx.getAttribute(CONFIG); + File backupDir = new File(config.getDir(), BACKUP_FOLDER); + if (backupDir.exists()) { + FileUtils.cleanDirectory(backupDir); + } + File layersJSON = new File(config.getDir(), LAYERS_JSON); + if (layersJSON.exists()) { + Date date = new Date(); + Timestamp now = new Timestamp(date.getTime()); + File layersJSONBack = new File(backupDir, now.toString().replaceAll("\\s","_").concat("-".concat(LAYERS_JSON))); + FileUtils.copyFile(layersJSON, layersJSONBack); + layersJSON.delete(); + } else { + // TODO Is this needed? + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Not found layers.json file in portal.properties folder!"); + } + layersJSON.createNewFile(); + BufferedReader reader = req.getReader(); + OutputStream out = new FileOutputStream(layersJSON); + IOUtils.copy(reader, out, UTF_8); + + out.close(); + reader.close(); + } + } diff --git a/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java b/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java new file mode 100644 index 0000000..0a76d50 --- /dev/null +++ b/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java @@ -0,0 +1,113 @@ +/** + * + */ +package org.fao.unredd.layersEditor; + +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.sql.Timestamp; +import java.util.Date; +import java.util.Iterator; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.fao.unredd.portal.Config; +import org.fao.unredd.portal.ConfigFolder; +import org.fao.unredd.portal.DefaultConfig; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author michogarcia + * + */ +public class LayersEditorTest { + + private static final boolean APPEND = true; + private static final String UTF_8 = "UTF-8"; + private LayersServlet servlet; + private Config defaultconfig; + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + servlet = new LayersServlet(); + ServletConfig config = mock(ServletConfig.class); + servlet.init(config); + defaultconfig = new DefaultConfig(mock(ConfigFolder.class), false); + ServletContext context = mock(ServletContext.class); + when(config.getServletContext()).thenReturn(context); + when(context.getAttribute("config")).thenReturn(defaultconfig); + } + + /** + * Test method for {@link org.fao.unredd.layersEditor.LayersServlet#doPut(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}. + * @throws IOException + * @throws ServletException + */ + @Test + public void testDoPutHttpServletRequestHttpServletResponse() throws ServletException, IOException { + + HttpServletRequest req = mock(HttpServletRequest.class); + HttpServletResponse resp = mock(HttpServletResponse.class); + + File tmpDir = testFolder.getRoot(); + + when(defaultconfig.getDir()).thenReturn(tmpDir); + + URL url = this.getClass().getResource("/layers.json"); + File layersJSON = new File(url.getFile()); + + Date date = new java.util.Date(); + Timestamp now = new Timestamp(date.getTime()); + + FileUtils.copyFileToDirectory(layersJSON, tmpDir); + + File layersJSONCopy = new File(tmpDir, "layers.json"); + FileUtils.writeStringToFile(layersJSONCopy, now.toString(), APPEND); + + BufferedReader readerLayers = new BufferedReader(new FileReader(layersJSON)); + when(req.getReader()).thenReturn(readerLayers); + + servlet.doPut(req, resp); + + File backupFolder = new File(tmpDir, "backup"); + InputStream afterPutBackupIs = null; + Iterator allFilesInBackup = FileUtils.iterateFiles(backupFolder, null, false); + while (allFilesInBackup.hasNext()) { + File aFileInFolder = allFilesInBackup.next(); + afterPutBackupIs = new FileInputStream(aFileInFolder); + } + InputStream originalLayerJSONIs = new FileInputStream(new File(url.getFile())); + InputStream afterPutLayersIs = new FileInputStream(new File(tmpDir, "layers.json")); + boolean equals = IOUtils.contentEquals(afterPutLayersIs, originalLayerJSONIs); + assertTrue(equals); + + InputStream againOriginalLayerJSONIs = new FileInputStream(new File(url.getFile())); + equals = IOUtils.contentEquals(afterPutBackupIs, againOriginalLayerJSONIs); + assertTrue(!equals); + } +} diff --git a/layers-editor/src/test/resources/layers.json b/layers-editor/src/test/resources/layers.json new file mode 100644 index 0000000..8b26ff5 --- /dev/null +++ b/layers-editor/src/test/resources/layers.json @@ -0,0 +1,37 @@ +{ + "wmsLayers": [ + { + "wmsName": "unredd:blue_marble", + "baseUrl": "http://rdc-snsf.org/diss_geoserver/wms", + "id": "blue-marble", + "visible": true + } + ], + "portalLayers": [ + { + "active": true, + "layers": [ + "blue-marble" + ], + "id": "blue-marble", + "infoFile": "blue_marble.html", + "label": "Mosaico im\u00e1genes MODIS" + } + ], + "groups": [ + { + "items": [ + { + "items": [ + "blue-marble" + ], + "id": "innerbase", + "label": "Mosaico MODIS" + } + ], + "id": "base", + "label": "Im\u00e1genes satelitales" + } + ], + "default-server": "http://geo2.ambiente.gob.ar/portal/gs" +} diff --git a/layers-editor/src/test/resources/log4j.properties b/layers-editor/src/test/resources/log4j.properties new file mode 100644 index 0000000..107db0b --- /dev/null +++ b/layers-editor/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +# Root logger option +log4j.rootLogger=DEBUG, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file From bf94b2583418d4d11173088132d72a0b654c558e Mon Sep 17 00:00:00 2001 From: Micho Garcia Date: Wed, 7 Oct 2015 15:51:26 +0200 Subject: [PATCH 005/119] Added OK response. PUT returns 200 if all it is ok. --- .../main/java/org/fao/unredd/layersEditor/LayersServlet.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java index 5d2f2be..86e8e83 100644 --- a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java +++ b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java @@ -70,6 +70,8 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) out.close(); reader.close(); + + resp.sendError(HttpServletResponse.SC_OK); } } From 678527a572001f65968d9ee635f3a13b1d235ca3 Mon Sep 17 00:00:00 2001 From: Micho Garcia Date: Wed, 7 Oct 2015 16:06:30 +0200 Subject: [PATCH 006/119] After 505 response do nothing --- .../unredd/layersEditor/LayersServlet.java | 104 ++++++++++-------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java index 86e8e83..7020033 100644 --- a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java +++ b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java @@ -18,60 +18,68 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.fao.unredd.portal.ClientContentServlet; import org.fao.unredd.portal.Config; @MultipartConfig public class LayersServlet extends HttpServlet { - private static final String APPLICATION_JSON = "application/json"; - private static final String CONFIG = "config"; - private static final String UTF_8 = "UTF-8"; - private static final String LAYERS_JSON = "layers.json"; - private static final String BACKUP_FOLDER = "backup"; - private static final long serialVersionUID = 1L; - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - resp.setContentType(APPLICATION_JSON); - resp.setCharacterEncoding(UTF_8); - Config config = (Config) getServletContext().getAttribute(CONFIG); - String layersTemplate = IOUtils.toString( + private static Logger logger = Logger.getLogger(ClientContentServlet.class); + + private static final String APPLICATION_JSON = "application/json"; + private static final String CONFIG = "config"; + private static final String UTF_8 = "UTF-8"; + private static final String LAYERS_JSON = "layers.json"; + private static final String BACKUP_FOLDER = "backup"; + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentType(APPLICATION_JSON); + resp.setCharacterEncoding(UTF_8); + Config config = (Config) getServletContext().getAttribute(CONFIG); + String layersTemplate = IOUtils.toString( new File(config.getDir(), LAYERS_JSON).toURI(), UTF_8); - - PrintWriter writer = resp.getWriter(); - writer.write(layersTemplate); - } - @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - - ServletContext ctx = getServletContext(); - Config config = (Config) ctx.getAttribute(CONFIG); - File backupDir = new File(config.getDir(), BACKUP_FOLDER); - if (backupDir.exists()) { - FileUtils.cleanDirectory(backupDir); - } - File layersJSON = new File(config.getDir(), LAYERS_JSON); - if (layersJSON.exists()) { - Date date = new Date(); - Timestamp now = new Timestamp(date.getTime()); - File layersJSONBack = new File(backupDir, now.toString().replaceAll("\\s","_").concat("-".concat(LAYERS_JSON))); - FileUtils.copyFile(layersJSON, layersJSONBack); - layersJSON.delete(); - } else { - // TODO Is this needed? - resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Not found layers.json file in portal.properties folder!"); - } - layersJSON.createNewFile(); - BufferedReader reader = req.getReader(); - OutputStream out = new FileOutputStream(layersJSON); - IOUtils.copy(reader, out, UTF_8); - - out.close(); - reader.close(); - - resp.sendError(HttpServletResponse.SC_OK); + PrintWriter writer = resp.getWriter(); + writer.write(layersTemplate); + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + ServletContext ctx = getServletContext(); + Config config = (Config) ctx.getAttribute(CONFIG); + File backupDir = new File(config.getDir(), BACKUP_FOLDER); + if (backupDir.exists()) { + FileUtils.cleanDirectory(backupDir); } - + File layersJSON = new File(config.getDir(), LAYERS_JSON); + if (layersJSON.exists()) { + Date date = new Date(); + Timestamp now = new Timestamp(date.getTime()); + File layersJSONBack = new File(backupDir, now.toString().replaceAll("\\s","_").concat("-".concat(LAYERS_JSON))); + FileUtils.copyFile(layersJSON, layersJSONBack); + layersJSON.delete(); + } else { + // TODO Is this needed? + logger.error("Not found layers.json file in portal.properties folder!"); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Not found layers.json file in portal.properties folder!"); + return; + } + layersJSON.createNewFile(); + BufferedReader reader = req.getReader(); + OutputStream out = new FileOutputStream(layersJSON); + IOUtils.copy(reader, out, UTF_8); + + out.close(); + reader.close(); + + logger.info("All OK, layers.json updated: 200"); + resp.sendError(HttpServletResponse.SC_OK); + } + } From 7112b595efa15af1210217a2bb458d6309fbb414 Mon Sep 17 00:00:00 2001 From: Micho Garcia Date: Fri, 9 Oct 2015 16:12:18 +0200 Subject: [PATCH 007/119] Added @fergonco comments Modified servlet to maintain all layers.json backups. Some minor modifications --- .../unredd/layersEditor/LayersServlet.java | 24 +++++++++---------- .../unredd/layersEditor/LayersEditorTest.java | 8 +++---- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java index 7020033..7bf1f6c 100644 --- a/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java +++ b/layers-editor/src/main/java/org/fao/unredd/layersEditor/LayersServlet.java @@ -1,5 +1,6 @@ package org.fao.unredd.layersEditor; +import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -54,9 +55,6 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) ServletContext ctx = getServletContext(); Config config = (Config) ctx.getAttribute(CONFIG); File backupDir = new File(config.getDir(), BACKUP_FOLDER); - if (backupDir.exists()) { - FileUtils.cleanDirectory(backupDir); - } File layersJSON = new File(config.getDir(), LAYERS_JSON); if (layersJSON.exists()) { Date date = new Date(); @@ -64,22 +62,22 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) File layersJSONBack = new File(backupDir, now.toString().replaceAll("\\s","_").concat("-".concat(LAYERS_JSON))); FileUtils.copyFile(layersJSON, layersJSONBack); layersJSON.delete(); + layersJSON.createNewFile(); + BufferedReader reader = req.getReader(); + OutputStream out = new FileOutputStream(layersJSON); + IOUtils.copy(reader, new BufferedOutputStream(out), UTF_8); + + out.close(); + reader.close(); + + logger.info("All OK, layers.json updated: 200"); + resp.setStatus(HttpServletResponse.SC_OK); } else { // TODO Is this needed? logger.error("Not found layers.json file in portal.properties folder!"); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Not found layers.json file in portal.properties folder!"); - return; } - layersJSON.createNewFile(); - BufferedReader reader = req.getReader(); - OutputStream out = new FileOutputStream(layersJSON); - IOUtils.copy(reader, out, UTF_8); - - out.close(); - reader.close(); - logger.info("All OK, layers.json updated: 200"); - resp.sendError(HttpServletResponse.SC_OK); } } diff --git a/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java b/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java index 0a76d50..4a1bc1a 100644 --- a/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java +++ b/layers-editor/src/test/java/org/fao/unredd/layersEditor/LayersEditorTest.java @@ -75,19 +75,17 @@ public void testDoPutHttpServletRequestHttpServletResponse() throws ServletExcep HttpServletResponse resp = mock(HttpServletResponse.class); File tmpDir = testFolder.getRoot(); + tmpDir = new File("/tmp"); when(defaultconfig.getDir()).thenReturn(tmpDir); URL url = this.getClass().getResource("/layers.json"); File layersJSON = new File(url.getFile()); - - Date date = new java.util.Date(); - Timestamp now = new Timestamp(date.getTime()); FileUtils.copyFileToDirectory(layersJSON, tmpDir); File layersJSONCopy = new File(tmpDir, "layers.json"); - FileUtils.writeStringToFile(layersJSONCopy, now.toString(), APPEND); + FileUtils.writeStringToFile(layersJSONCopy, "dirty", APPEND); BufferedReader readerLayers = new BufferedReader(new FileReader(layersJSON)); when(req.getReader()).thenReturn(readerLayers); @@ -108,6 +106,6 @@ public void testDoPutHttpServletRequestHttpServletResponse() throws ServletExcep InputStream againOriginalLayerJSONIs = new FileInputStream(new File(url.getFile())); equals = IOUtils.contentEquals(afterPutBackupIs, againOriginalLayerJSONIs); - assertTrue(!equals); + assertFalse(equals); } } From 11ec995bf11bc2d507a0a90cd3e26fd4de96b832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Tue, 13 Oct 2015 11:34:38 +0200 Subject: [PATCH 008/119] Changes for jenkins: jetty runs in 8880 and old test ignored --- .../org/fao/unredd/functional/AbstractIntegrationTest.java | 6 +++--- .../java/org/fao/unredd/functional/stats/StatsTest.java | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java b/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java index 458f5c9..368953c 100644 --- a/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java +++ b/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java @@ -98,7 +98,7 @@ public void setup() throws Exception { server.setHandler(handlers); SocketConnector connector = new SocketConnector(); - connector.setPort(8080); + connector.setPort(8880); server.setConnectors(new Connector[] { connector }); dataSource = new PGSimpleDataSource(); @@ -191,7 +191,7 @@ protected void executeLines(String resourceName, String... params) protected CloseableHttpResponse GET(String path, String... parameters) throws ClientProtocolException, IOException { - String url = "http://localhost:8080/" + CONTEXT_PATH + "/" + path + "?"; + String url = "http://localhost:8880/" + CONTEXT_PATH + "/" + path + "?"; for (int i = 0; i < parameters.length; i = i + 2) { url += parameters[i] + "=" + URLEncoder.encode(parameters[i + 1], "UTF-8") + "&"; @@ -203,7 +203,7 @@ protected CloseableHttpResponse GET(String path, String... parameters) protected CloseableHttpResponse POST(String path, String... parameters) throws ClientProtocolException, IOException { - String url = "http://localhost:8080/" + CONTEXT_PATH + "/" + path; + String url = "http://localhost:8880/" + CONTEXT_PATH + "/" + path; ArrayList parameterList = new ArrayList(); for (int i = 0; i < parameters.length; i = i + 2) { parameterList.add(new BasicNameValuePair(parameters[i], diff --git a/integration-tests/src/test/java/org/fao/unredd/functional/stats/StatsTest.java b/integration-tests/src/test/java/org/fao/unredd/functional/stats/StatsTest.java index 3c1cf63..4b6e949 100644 --- a/integration-tests/src/test/java/org/fao/unredd/functional/stats/StatsTest.java +++ b/integration-tests/src/test/java/org/fao/unredd/functional/stats/StatsTest.java @@ -15,6 +15,7 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.fao.unredd.functional.AbstractIntegrationTest; import org.fao.unredd.functional.IntegrationTest; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -213,6 +214,7 @@ public void testStatsServiceDataTypes() throws Exception { checkStatsServiceTest(root); } + @Ignore @Test public void testCalculateStats() throws Exception { String layerName = "unredd:provinces"; From 9796db0c21f744d00f5ce379c7fc56c5a43ffe29 Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Tue, 13 Oct 2015 16:49:16 +0200 Subject: [PATCH 009/119] Added layers.json schema. First attempt on form construction from schema. --- .../resources/nfms/modules/layers-editor.js | 2 +- .../resources/nfms/modules/layers-forms.js | 41 ++ .../nfms/modules/resources/layers.schema.json | 414 ++++++++++++++++++ 3 files changed, 456 insertions(+), 1 deletion(-) create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-forms.js create mode 100644 layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.js b/layers-editor/src/main/resources/nfms/modules/layers-editor.js index 9536d01..a1a9e2a 100644 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor.js +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor.js @@ -1,4 +1,4 @@ -define(["message-bus", "layers-json"], function(bus, layers) { +define(["message-bus", "layers-json", "layers-forms"], function(bus, layers, forms) { bus.listen("before-adding-layers", function() { bus.send("register-layer-action", function(layer) { diff --git a/layers-editor/src/main/resources/nfms/modules/layers-forms.js b/layers-editor/src/main/resources/nfms/modules/layers-forms.js new file mode 100644 index 0000000..8eea76b --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-forms.js @@ -0,0 +1,41 @@ +define(["text!resources/layers.schema.json", "jquery", "jquery-ui"], function(layers_shema, $) { + + var schema = JSON.parse(layers_shema); + console.log(schema); + + var field = function(type, value) { + console.log([type, value]); + if (type.type == "string") { + var div = $("
").text(type.title + ": ").appendTo(form); + var input = $("").attr("type", "text").attr("value", value).appendTo(div); + } + } + + var toc = function() { + var props = schema.definitions.toc.properties; + var vals = { + "id": "registroredd", + "label": "Registro REDD+", + "infoFile": "registro_redd.html" + } + for(var i in props) { + field(props[i], vals[i]); + } + } + + var dialog = $("
"); + dialog.dialog({ + closeOnEscape : false, + autoOpen : false, + height : 500, + minHeight : 400, + width : 325, + zIndex : 2000, + resizable : true + }); + dialog.dialog("open"); + var form = $("
"); + dialog.append(form); + toc(); + +}); diff --git a/layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json b/layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json new file mode 100644 index 0000000..95b570a --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json @@ -0,0 +1,414 @@ +{ + "$schema":"http://json-schema.org/draft-04/schema#", + "definitions":{ + "wmsLayer-base":{ + "type":"object", + "properties":{ + "title":"Identifier", + "id":{ + "type":"string" + }, + "type":{ + "title":"Layer Type", + "type":"string", + "enum":[ + "wms", + "osm", + "gmaps" + ] + }, + "title":"Is visible layer", + "visible":{ + "type":"boolean" + }, + "legend":{ + "title":"Legend URL", + "anyOf":[ + { + "enum":[ + "auto" + ] + }, + { + "type":"string" + } + ] + }, + "label":{ + "title":"Legend Label", + "type":"string" + }, + "sourceLink":{ + "title":"Source Link", + "type":"string", + "format":"uri" + }, + "sourceLabel":{ + "title":"Source Label", + "type":"string" + } + }, + "required":[ + "id" + ], + "dependencies":{ + "sourceLink":[ + "sourceLabel" + ], + "sourceLabel":[ + "sourceLink" + ], + "legend":[ + "label" + ], + "label":[ + "legend" + ] + } + }, + "wmsLayer-wmsType":{ + "allOf":[ + { + "$ref":"#/definitions/wmsLayer-base" + }, + { + "properties":{ + "type":{ + "enum":[ + "wms" + ] + }, + "baseUrl":{ + "title":"Base URL", + "type":"string" + }, + "wmsName":{ + "title":"Layer Name", + "type":"string" + }, + "imageFormat":{ + "title":"Image Format", + "type":"string", + "enum":[ + "image/png", + "image/png8", + "image/jpeg", + "image/gif" + ] + }, + "queryType":{ + "title":"Query Type", + "type":"string", + "enum":[ + "wms", + "wfs" + ] + }, + "queryUrl":{ + "title":"Query URL", + "type":"string" + }, + "queryGeomFieldName":{ + "title":"Query Geometry Field Name", + "type":"string" + }, + "queryFieldNames":{ + "title":"Query Field Names", + "type":"array", + "items":{ + "type":"string" + }, + "minItems":1, + "uniqueItems":true + }, + "queryFieldAliases":{ + "title":"Query Field Aliases", + "type":"array", + "items":{ + "type":"string" + }, + "minItems":1, + "uniqueItems":true + }, + "queryTimeFieldName":{ + "title":"Query Time Field Name", + "type":"string" + }, + "queryHighlightBounds":{ + "title":"Query Highlight Bounds", + "type":"boolean" + } + }, + "required":[ + "wmsName" + ], + "dependencies":{ + "queryFieldNames":[ + "queryFieldAliases" + ], + "queryFieldAliases":[ + "queryFieldNames" + ] + } + } + ] + }, + "wmsLayer-wmsType-wfsQueryType":{ + "allOf":[ + { + "$ref":"#/definitions/wmsLayer-wmsType" + }, + { + "properties":{ + "queryType":{ + "enum":[ + "wfs" + ] + } + }, + "required":[ + "queryType", + "queryUrl", + "queryGeomFieldName", + "queryFieldNames", + "queryFieldAliases" + ] + } + ] + }, + "wmsLayer-osmType":{ + "allOf":[ + { + "$ref":"#/definitions/wmsLayer-base" + }, + { + "properties":{ + "type":{ + "enum":[ + "osm" + ] + }, + "osmUrls":{ + "title":"Tileset URLs", + "type":"array", + "items":{ + "type":"string", + "format":"uri" + }, + "minItems":1, + "uniqueItems":true + } + }, + "required":[ + "type", + "osmUrls" + ] + } + ] + }, + "wmsLayer-gmapsType":{ + "allOf":[ + { + "$ref":"#/definitions/wmsLayer-base" + }, + { + "properties":{ + "type":{ + "enum":[ + "gmaps" + ] + }, + "gmaps-type":{ + "title":"Google Maps Layer Type", + "type":"string", + "enum":[ + "ROADMAP", + "SATELLITE", + "HYBRID", + "TERRAIN" + ] + } + }, + "required":[ + "type", + "gmaps-type" + ] + } + ] + }, + "wmsLayer":{ + "anyOf":[ + { + "$ref":"#/definitions/wmsLayer-wmsType" + }, + { + "$ref":"#/definitions/wmsLayer-wmsType-wfsQueryType" + }, + { + "$ref":"#/definitions/wmsLayer-osmType" + }, + { + "$ref":"#/definitions/wmsLayer-gmapsType" + } + ] + }, + "toc":{ + "type":"object", + "properties":{ + "id":{ + "title":"Identifier", + "type":"string" + }, + "label":{ + "title":"Label", + "type":"string" + }, + "infoFile":{ + "title":"Info File", + "type":"string" + }, + "infoLink":{ + "title":"Info Link", + "type":"string", + "format":"uri" + } + }, + "required":[ + "id", + "label" + ] + }, + "portalLayer":{ + "allOf":[ + { + "$ref":"#/definitions/toc" + }, + { + "properties":{ + "inlineLegendUrl":{ + "title":"Inline Legend URL", + "anyOf":[ + { + "enum":[ + "auto" + ] + }, + { + "type":"string" + } + ] + }, + "active":{ + "title":"Layer visible on portal load", + "type":"boolean" + }, + "layers":{ + "title":"Layer list", + "type":"array", + "items":{ + "type":"string" + }, + "minItems":1, + "uniqueItems":true + }, + "timeInstances":{ + "title":"Time instances", + "type":"string" + }, + "timeStyles":{ + "title":"Time styles", + "type":"string" + }, + "date-format":{ + "title":"Date format", + "type":"string" + }, + "feedback":{ + "title":"Users can provide feedback", + "type":"boolean" + } + }, + "dependencies":{ + "timeStyles":[ + "timeInstances" + ] + }, + "required":[ + "layers" + ] + } + ] + }, + "group":{ + "allOf":[ + { + "$ref":"#/definitions/toc" + }, + { + "properties":{ + "items":{ + "title":"Group items", + "type":"array", + "items":{ + "anyOf":[ + { + "type":"string" + }, + { + "$ref":"#/definitions/group" + } + ] + }, + "uniqueItems":true + } + }, + "required":[ + "items" + ] + } + ] + } + }, + "type":"object", + "properties":{ + "default-server":{ + "title":"Default Server", + "type":"string", + "format":"uri" + }, + "wmsLayers":{ + "title":"WMS Layers", + "type":"array", + "items":{ + "$ref":"#/definitions/wmsLayer" + }, + "minItems":1, + "uniqueItems":true + }, + "portalLayers":{ + "title":"Portal Layers", + "type":"array", + "items":{ + "$ref":"#/definitions/portalLayer" + }, + "minItems":1, + "uniqueItems":true + }, + "groups":{ + "title":"Groups", + "type":"array", + "items":{ + "$ref":"#/definitions/group" + }, + "minItems":1, + "uniqueItems":true + } + }, + "required":[ + "default-server", + "wmsLayers", + "portalLayers", + "groups" + ], + "additionalProperties":false +} \ No newline at end of file From fd11fae1c913e6dc6e510c8a8ef63e88d61ce10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Tue, 13 Oct 2015 19:13:18 +0200 Subject: [PATCH 010/119] Trying to be able to have a specific configuration for tests outside the source code structure --- integration-tests/pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 99cf11b..58ccab4 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -110,6 +110,9 @@ + + /var/portal + org.fao.unredd.functional.IntegrationTest From 186ed811e2538ae4e62da6032c6ecf851d6ac89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Tue, 13 Oct 2015 19:28:11 +0200 Subject: [PATCH 011/119] Trying to be able to have a specific configuration for tests outside the source code structure --- integration-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 58ccab4..6e641bf 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -111,7 +111,7 @@ - /var/portal + /var/testportal org.fao.unredd.functional.IntegrationTest From 3f43ec5fee5d844b1e8b82be3051d68f78b8ca90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 10:17:14 +0200 Subject: [PATCH 012/119] testing travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a98b760 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: java + From c66da9b562d880e4d63a262169e585d2f44201e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 10:39:56 +0200 Subject: [PATCH 013/119] travis: trying integration tests --- .travis.yml | 8 ++++++++ .../org/fao/unredd/functional/functional-test.properties | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a98b760..9e5b277 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,10 @@ language: java +addons: + postgresql: "9.3" + +before_script: + - psql -U postgres -c "create extension postgis" + +script: + - bundle exec mvn verify diff --git a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties index bcd3bf7..2d31881 100644 --- a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties +++ b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties @@ -1,4 +1,4 @@ -db-url=jdbc:postgresql://postgis.unredd:5432/spatialdata -db-user=spatial_user -db-password=unr3dd +db-url=jdbc:postgresql://127.0.0.1:5432/spatialdata +db-user=postgres +db-password= db-test-schema=integration_tests From 576b092ac1a271b3dac96ce628aae288a5f00f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 10:45:06 +0200 Subject: [PATCH 014/119] travis: trying integration tests --- .travis.yml | 2 +- demo/src/main/webapp/WEB-INF/default_config/portal.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e5b277..c43044c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ before_script: - psql -U postgres -c "create extension postgis" script: - - bundle exec mvn verify + - mvn verify diff --git a/demo/src/main/webapp/WEB-INF/default_config/portal.properties b/demo/src/main/webapp/WEB-INF/default_config/portal.properties index 04786fa..368a045 100644 --- a/demo/src/main/webapp/WEB-INF/default_config/portal.properties +++ b/demo/src/main/webapp/WEB-INF/default_config/portal.properties @@ -14,6 +14,6 @@ feedback-mail-username=onuredd@gmail.com feedback-mail-password=onu-redd feedback-admin-mail-title=Nuevo comentario en el portal REDD feedback-admin-mail-text=Se ha introducido un comentario en el portal de REDD con c\u00f3digo de verificaci\u00f3n "$code" -# milliseconds. CHANGE IT!! For example, use 600000 for 10 minutes -feedback-validation-check-delay=1000 +# milliseconds. For example, use 600000 for 10 minutes +feedback-validation-check-delay=600000 From f14e6e07bc29b4f5f0e700e3c63f22f1f4e68bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 10:54:11 +0200 Subject: [PATCH 015/119] travis: trying integration tests --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c43044c..687d203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ addons: before_script: - psql -U postgres -c "create extension postgis" + - psql -U postgres -c "create database spatialdata" script: - mvn verify From c5bb4845a54071b0c278b7f76153d333856a9ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 11:03:25 +0200 Subject: [PATCH 016/119] travis: trying integration tests --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 687d203..9ae9b59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,3 +9,7 @@ before_script: script: - mvn verify + +after_failure: + - cat target/surefire-reports/*.xml + From 802c102a177236d36f8e54132c8a4c2d525dbe67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 11:09:48 +0200 Subject: [PATCH 017/119] travis: trying integration tests --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ae9b59..a20128f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,4 @@ script: - mvn verify after_failure: - - cat target/surefire-reports/*.xml - + - cat integration-tests/target/failsafe-reports/*.xml From d5871c220538ff775654d22940a1a544769c2d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 11:17:19 +0200 Subject: [PATCH 018/119] travis: trying integration tests --- .travis.yml | 2 +- argentina/pom.xml | 2 +- demo/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a20128f..39ea54d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ addons: postgresql: "9.3" before_script: - - psql -U postgres -c "create extension postgis" - psql -U postgres -c "create database spatialdata" + - psql -U postgres -d spatialdata -c "create extension postgis" script: - mvn verify diff --git a/argentina/pom.xml b/argentina/pom.xml index cbb739f..ee3b991 100644 --- a/argentina/pom.xml +++ b/argentina/pom.xml @@ -139,7 +139,7 @@ optimize - true + false diff --git a/demo/pom.xml b/demo/pom.xml index 94a4e40..823c15c 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -149,7 +149,7 @@ optimize - true + false From f90edc76abc26423bc13186c591a95a50bed2709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 11:57:24 +0200 Subject: [PATCH 019/119] travis: setting mail parameter through environment variable (in order not to have it on the source code) --- integration-tests/pom.xml | 2 +- .../functional/AbstractIntegrationTest.java | 20 ++ .../functional/functional-test.properties | 6 +- integration-tests/test_config/layers.json | 265 ++++++++++++++++++ .../test_config/messages/messages.properties | 78 ++++++ .../messages/messages_en.properties | 78 ++++++ .../messages/messages_es.properties | 78 ++++++ .../messages/messages_fr.properties | 74 +++++ .../test_config/plugin-conf.json | 15 + .../test_config/portal.properties | 18 ++ .../test_config/static/img/favicon.png | Bin 0 -> 209 bytes .../test_config/static/img/flag.png | Bin 0 -> 2564 bytes .../test_config/static/img/logos.png | Bin 0 -> 4054 bytes .../test_config/static/img/printer.png | Bin 0 -> 882 bytes .../test_config/static/img/right.jpg | Bin 0 -> 37389 bytes .../test_config/static/img/ui-elements.png | Bin 0 -> 8663 bytes .../en/html/forest_classification_def.html | 8 + .../static/loc/en/html/provinces_def.html | 4 + .../loc/en/images/forest_classification.png | Bin 0 -> 2474 bytes .../static/loc/en/images/forest_mask.png | Bin 0 -> 182 bytes .../es/html/forest_classification_def.html | 5 + .../static/loc/es/html/provinces_def.html | 4 + .../loc/es/images/forest_classification.png | Bin 0 -> 2474 bytes .../static/loc/es/images/forest_mask.png | Bin 0 -> 182 bytes .../fr/html/forest_classification_def.html | 8 + .../static/loc/fr/html/provinces_def.html | 4 + .../loc/fr/images/forest_classification.png | Bin 0 -> 2474 bytes .../static/loc/fr/images/forest_mask.png | Bin 0 -> 182 bytes .../test_config/static/overrides.css | 1 + 29 files changed, 664 insertions(+), 4 deletions(-) create mode 100644 integration-tests/test_config/layers.json create mode 100644 integration-tests/test_config/messages/messages.properties create mode 100644 integration-tests/test_config/messages/messages_en.properties create mode 100644 integration-tests/test_config/messages/messages_es.properties create mode 100644 integration-tests/test_config/messages/messages_fr.properties create mode 100644 integration-tests/test_config/plugin-conf.json create mode 100644 integration-tests/test_config/portal.properties create mode 100644 integration-tests/test_config/static/img/favicon.png create mode 100644 integration-tests/test_config/static/img/flag.png create mode 100644 integration-tests/test_config/static/img/logos.png create mode 100644 integration-tests/test_config/static/img/printer.png create mode 100644 integration-tests/test_config/static/img/right.jpg create mode 100644 integration-tests/test_config/static/img/ui-elements.png create mode 100644 integration-tests/test_config/static/loc/en/html/forest_classification_def.html create mode 100644 integration-tests/test_config/static/loc/en/html/provinces_def.html create mode 100644 integration-tests/test_config/static/loc/en/images/forest_classification.png create mode 100644 integration-tests/test_config/static/loc/en/images/forest_mask.png create mode 100644 integration-tests/test_config/static/loc/es/html/forest_classification_def.html create mode 100644 integration-tests/test_config/static/loc/es/html/provinces_def.html create mode 100644 integration-tests/test_config/static/loc/es/images/forest_classification.png create mode 100644 integration-tests/test_config/static/loc/es/images/forest_mask.png create mode 100644 integration-tests/test_config/static/loc/fr/html/forest_classification_def.html create mode 100644 integration-tests/test_config/static/loc/fr/html/provinces_def.html create mode 100644 integration-tests/test_config/static/loc/fr/images/forest_classification.png create mode 100644 integration-tests/test_config/static/loc/fr/images/forest_mask.png create mode 100644 integration-tests/test_config/static/overrides.css diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 6e641bf..5d64960 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -111,7 +111,7 @@ - /var/testportal + /tmp/testportal org.fao.unredd.functional.IntegrationTest diff --git a/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java b/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java index 368953c..036be8b 100644 --- a/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java +++ b/integration-tests/src/test/java/org/fao/unredd/functional/AbstractIntegrationTest.java @@ -2,7 +2,10 @@ import static junit.framework.Assert.assertTrue; +import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -17,6 +20,7 @@ import java.util.Properties; import java.util.regex.Pattern; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; @@ -69,6 +73,22 @@ public static void setupTests() throws IOException { @Before public void setup() throws Exception { + // Create configuration folder and replace password with environment + // variable. Destination folder is configured in pom.xml + FileUtils.copyDirectory(new File("test_config"), new File( + "/tmp/testportal")); + File portalPropertiesFile = new File( + "/tmp/testportal/portal.properties"); + String portalProperties = IOUtils + .toString(portalPropertiesFile.toURI()); + portalProperties = portalProperties.replaceAll( + Pattern.quote("$password"), + System.getenv("ONUREDDMAILPASSWORD")); + BufferedOutputStream output = new BufferedOutputStream( + new FileOutputStream(portalPropertiesFile)); + IOUtils.write(portalProperties, output); + output.close(); + // Clean the database Class.forName("org.postgresql.Driver"); Connection connection = DriverManager.getConnection(dbUrl, dbUser, diff --git a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties index 2d31881..bcd3bf7 100644 --- a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties +++ b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties @@ -1,4 +1,4 @@ -db-url=jdbc:postgresql://127.0.0.1:5432/spatialdata -db-user=postgres -db-password= +db-url=jdbc:postgresql://postgis.unredd:5432/spatialdata +db-user=spatial_user +db-password=unr3dd db-test-schema=integration_tests diff --git a/integration-tests/test_config/layers.json b/integration-tests/test_config/layers.json new file mode 100644 index 0000000..487317d --- /dev/null +++ b/integration-tests/test_config/layers.json @@ -0,0 +1,265 @@ +{ + "default-server" : "http://demo1.geo-solutions.it", + "wmsLayers" : [ + { + "id" : "cycle-osm", + "type" : "osm", + "osmUrls" : [ + "http://a.tile.openstreetmap.org/${z}/${x}/${y}.png", + "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png", + "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png" + ] + }, { + "id" : "blue-marble", + "baseUrl" : "http://rdc-snsf.org/diss_geoserver/wms", + "wmsName" : "unredd:blue_marble", + "imageFormat" : "image/jpeg", + "visible" : true + }, { + "id" : "google-maps", + "type" : "gmaps", + "gmaps-type" : "SATELLITE", + "visible" : true + }, { + "id" : "nexrad", + "baseUrl" : "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi?", + "wmsName" : "nexrad-n0r-wmst", + "imageFormat" : "image/png", + "visible" : true + }, { + "id" : "forestClassification", + "label" : "${facet_forest_classification}", + "baseUrl" : "/diss_geoserver/gwc/service/wms", + "wmsName" : "unredd:drc_forest_classification", + "imageFormat" : "image/png8", + "visible" : true, + "legend" : "forest_classification.png", + "sourceLink" : "http://osfac.net/facet.html", + "sourceLabel" : "FACET" + }, { + "id" : "forest_mask", + "label" : "${forest_mask}", + "legend" : "auto", + "baseUrl" : "/diss_geoserver/wms", + "wmsName" : "unredd:drc_forest_mask_mosaic", + "imageFormat" : "image/png8", + "visible" : true, + "sourceLink" : "http://osfac.net/facet.html", + "sourceLabel" : "FACET" + }, { + "id" : "forestClassification2", + "label" : "grouped facet forest classification", + "baseUrl" : "/diss_geoserver/unredd/wms", + "wmsName" : "drc_forest_classification", + "visible" : true, + "legend" : "forest_classification.png", + "sourceLink" : "http://osfac.net/facet.html", + "sourceLabel" : "FACET2" + }, { + "id" : "forest_mask2", + "label" : "Grouped forest mask", + "baseUrl" : "/diss_geoserver/unredd/wms", + "wmsName" : "drc_forest_mask_mosaic", + "visible" : true, + "legend" : "forest_mask.png", + "sourceLink" : "http://osfac.net/facet.html", + "sourceLabel" : "FACET2" + }, { + "id" : "countryBoundaries", + "baseUrl" : "/diss_geoserver/wms", + "wmsName" : "unredd:drc_boundary", + "imageFormat" : "image/png8", + "visible" : true, + "sourceLink" : "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo", + "sourceLabel" : "WRI" + }, { + "id" : "provinces", + "baseUrl" : "/diss_geoserver/wms", + "wmsName" : "unredd:drc_provinces", + "imageFormat" : "image/png8", + "visible" : true, + "sourceLink" : "http://www.wri.org/publication/interactive-forest-atlas-democratic-republic-of-congo", + "sourceLabel" : "WRI" + }, { + "id" : "incendios_wfsinfo", + "baseUrl" : "http://geo2.ambiente.gob.ar/portal/gs/wms", + "wmsName" : "bosques_umsef_db:provincias_incendios", + "imageFormat" : "image/png8", + "legend" : "auto", + "visible" : true, + "queryType" : "wfs", + "queryUrl" : "http://geo2.ambiente.gob.ar/portal/gs/wfs", + "queryGeomFieldName" : "geom", + "queryFieldNames" : [ "nprov", "anio_f" ], + "queryFieldAliases" : [ "ID", "fecha" ], + "queryTimeFieldName" : "anio_f" + }, { + "id" : "incendios_wmsinfo", + "baseUrl" : "http://geo2.ambiente.gob.ar/portal/gs/wms", + "wmsName" : "bosques_umsef_db:provincias_incendios", + "imageFormat" : "image/png8", + "legend" : "auto", + "visible" : true, + "queryType" : "wms", + "queryHighlightBounds" : true, + "queryFieldNames" : [ "nprov", "anio_f" ], + "queryFieldAliases" : [ "Nombre", "Fecha" ], + }, { + "id" : "incendios_wmsinfo_default", + "baseUrl" : "http://geo2.ambiente.gob.ar/portal/gs/wms", + "wmsName" : "bosques_umsef_db:provincias_incendios", + "imageFormat" : "image/png8", + "legend" : "auto", + "visible" : true, + "queryType" : "wms" + }, { + "id" : "provincias_demo", + "baseUrl" : "http://demo.geomati.co:8080/geoserver/bosques/wms", + "wmsName" : "bosques:provincias", + "imageFormat" : "image/png8", + "visible" : true, + "sourceLink" : "http://demo.geomati.co:8080/geoserver/web", + "sourceLabel" : "WRI", + "queryType" : "wms" + }, { + "baseUrl": "http://geo2.ambiente.gob.ar/portal/gs/gwc/service/wms", + "label": "L\u00edmites provinciales", + "visible": true, + "imageFormat": "image/png8", + "id": "limites_provinciales", + "wmsName": "bosques_umsef_db:limites_provinciales", + "legend": "lim_prov.png", + "queryType" : "wfs", + "queryUrl" : "http://geo2.ambiente.gob.ar/portal/gs/wfs", + "queryGeomFieldName" : "geom", + "queryFieldNames" : [ "nprov", "cod_indec" ], + "queryFieldAliases" : [ "Nombre", "Codigo" ] + } + ], + + "portalLayers" : [ + { + "id" : "blue-marble", + "active" : true, + "label" : "Blue marble", + "layers" : [ "blue-marble" ] + }, + { + "id" : "osm-layer", + "active" : false, + "label" : "Open Cycle Map", + "layers" : [ "cycle-osm" ] + }, + { + "id" : "google-maps-context", + "active" : false, + "label" : "Google maps", + "layers" : [ "google-maps" ] + }, + { + "id" : "forestClassification", + "active" : false, + "infoFile" : "forest_classification_def.html", + "label" : "${facet_forest_classification}", + "layers" : [ "forestClassification" ], + "feedback" : true + }, { + "id" : "forest_mask", + "label" : "${forest_mask}", + "layers" : [ "forest_mask" ], + "infoLink": "http://nfms4redd.org/", + "timeInstances" : "2000-01-01T00:00:00.000Z,2005-01-01T00:00:00.000Z,2010-01-01T00:00:00.000Z", + "feedback" : true + }, { + "id" : "countryBoundaries", + "active" : false, + "label" : "${country_boundaries}", + "layers" : [ "countryBoundaries" ], + "inlineLegendUrl" : "auto" + }, { + "id" : "provinces", + "active" : false, + "infoFile" : "provinces_def.html", + "label" : "${provinces}", + "layers" : [ "provinces" ], + "inlineLegendUrl" : "auto" + }, { + "id" : "forest", + "active" : false, + "label" : "All forestry layers", + "layers" : [ "forest_mask2", "forestClassification2" ] + }, { + "id" : "nexrad_layer", + "active" : false, + "label" : "Nexrad", + "timeInstances" : "2010-03-01T00:00,2010-03-02T00:00,2010-03-03T00:00", + "date-format" : "DD-MM-YYYY", + "layers" : [ "nexrad" ] + }, { + "id" : "provinces_title_placeholder", + "label" : "Provincias" + }, { + "active": true, + "layers": [ + "limites_provinciales" + ], + "id": "limites_provinciales", + "infoFile": "ign.html", + "label": "L\u00edmites provinciales" + }, { + "id" : "incendios_wfsinfo", + "active" : true, + "label" : "Incendios por provincias (wfs info)", + "layers" : [ "incendios_wfsinfo" ], + "timeInstances" : "2012-01-01T00:00:00.000Z,2013-01-01T00:00:00.000Z", + "timeStyles" : "cantidad_incendios_2012,cantidad_incendios_2013" + }, { + "id" : "incendios_wmsinfo", + "active" : true, + "label" : "Incendios por provincias (wms info)", + "layers" : [ "incendios_wmsinfo" ], + "timeInstances" : "2012-01-01T00:00:00.000Z,2013-01-01T00:00:00.000Z", + "timeStyles" : "cantidad_incendios_2012,cantidad_incendios_2013" + }, { + "id" : "incendios_wmsinfo_default", + "active" : true, + "label" : "Incendios por provincias (default info)", + "layers" : [ "incendios_wmsinfo_default" ], + "timeInstances" : "2012-01-01T00:00:00.000Z,2013-01-01T00:00:00.000Z", + "timeStyles" : "cantidad_incendios_2012,cantidad_incendios_2013" + }, { + "id" : "demo_layers", + "active" : false, + "label" : "Provincias Estadísticas", + "layers" : [ "provincias_demo" ] + } + ], + + "groups" : [ + { + "id" : "base", + "label" : "${base_layers}", + "infoFile": "base_layers.html", + "items" : [ + { + "id" : "innerbase", + "label" : "General purpose", + "items": ["blue-marble", "osm-layer", "google-maps-context"] + }, { + "id" : "innerforest", + "label" : "Forest classifications", + "items": [ "forestClassification" ] + } + ] + }, { + "id" : "admin", + "label" : "${admin_areas}", + "infoLink": "http://nfms4redd.org/", + "items" : [ "countryBoundaries", "provinces_title_placeholder", "provinces", "limites_provinciales", "incendios_wfsinfo", "incendios_wmsinfo", "incendios_wmsinfo_default", "demo_layers" ] + }, { + "id" : "landcover", + "label" : "${land_cover_maps}", + "items" : [ "forest_mask", "forest", "nexrad_layer" ] + } + ] +} diff --git a/integration-tests/test_config/messages/messages.properties b/integration-tests/test_config/messages/messages.properties new file mode 100644 index 0000000..476b960 --- /dev/null +++ b/integration-tests/test_config/messages/messages.properties @@ -0,0 +1,78 @@ +# Java Resource Bundle +# Modified by Zaval JRC Editor (C) Zaval CE Group +# http://www.zaval.org/products/jrc-editor/ +# + +#Tue May 07 17:01:09 CEST 2013 +ajax_feedback_error=Error submitting feedback. +ajax_feedback_ok=Feedback successfully sent. +ajax_invalid_recaptcha=Invalid recaptcha. Please try again. +ajax_invalid_parameter=Invalid service usage. +ajax_read_error=Server couldn't read submitted content. +ajax_storing_error=Server couldn't store submitted data. +ajax_syntax_error=Submitted content has a bad syntax. +base_layers=Base Layers +cancel=Cancel +chart=Chart +data_source=Data source +email=Email +feedback=feedback +feedback_addfeature_tooltip=Add Polygon +feedback_button=Feedback +feedback_deletepolygon_confirm=Deleting polygon, are you sure? +feedback_drawing_tools=Drawing tools +feedback_editfeature_tooltip=Edit Polygon +feedback_info=Please click on the map to post feedback +feedback_query_tooltip=Get features from the selected layer +feedback_text=Use the tools to draw a geographic extent over the map. +feedback_title=Send feedback on map contents +feedback_year=Year +invalid_email_text=Your e-mail address is not valid. Please use a valid e-mail address. +invalid_email_title=Invalid e-mail +info_dialog_title=Information +layer=Layer +layers=Layers +layersjson_baseUrl_not_defined=attribute 'baseUrl' not defined +layersjson_context_def_error=Error in context definition +layersjson_context_group_error=Error in context group +layersjson_context_id_not_defined=Error in context definition: attribute 'id' not defined +layersjson_context_label_not_defined=attribute 'label' not defined +layersjson_layer_def_error=Error in layer definition +layersjson_layer_id_not_defined=Error in layer definition: attribute 'id' not defined +layersjson_layer_label_not_defined=attribute 'label' should be present when 'legend' is defined +layersjson_not_defined=not defined +layersjson_source_label_not_defined=attribute 'sourceLabel' should be present when 'sourceLink' is defined +layersjson_wmsName_not_defined=attribute 'wmsName' not defined +legend_button=Legend +months=['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Des.'] +name=Name +selected_layers=Transparency +statistics=Statistics +statistics_button=Statistics +statistics_info=Please click on the map to start drawing a polygon +statistics_not_supported=Statistics are not supported in this version +submit=Submit +subtitle=Subtitle +zoom_to_area=Zoom to area +title=NFMS Sample Portal + +# BEGIN Feedback +Feedback.all_parameters_mandatory=all parameters are mandatory: +Feedback.error_sending_mail=Error sending mail: +Feedback.the_message_has_been_validated=The comment has been validated. +Feedback.comment_not_found=Cannot find any message with the code +Feedback.mail-title=Comment on the UN-REDD portal +Feedback.verify-mail-text=Please, visit http://localhost:8080/unredd-portal/verify-comment?lang=$lang&verificationCode=$code to confirm the comment. +Feedback.validated-mail-text=The comment with verification code "$code", has been validated and can be accessed in the portal. +Feedback.invalid-email-address=The specified e-mail address is not valid +Feedback.no-geometries=At least one geometry must be drawn +Feedback.wait=Wait for the feedback to be sent... +Feedback.verify_mail_sent=An e-mail has been sent to the specified e-mail address in order to confirm the comment +Feedback.submit_error=Error sending the comment +Feedback.no_layer_visible=None of the layers enabled for feedback is active +Feedback.no-layer-selected=No layer is selected +# END Feedback + +footnote.text=This is a footnote + +layer_order=Layer Order diff --git a/integration-tests/test_config/messages/messages_en.properties b/integration-tests/test_config/messages/messages_en.properties new file mode 100644 index 0000000..c78229c --- /dev/null +++ b/integration-tests/test_config/messages/messages_en.properties @@ -0,0 +1,78 @@ +# Java Resource Bundle +# Modified by Zaval JRC Editor (C) Zaval CE Group +# http://www.zaval.org/products/jrc-editor/ +# + +#Tue May 07 17:01:09 CEST 2013 +ajax_feedback_error=Error submitting feedback. +ajax_feedback_ok=Feedback successfully sent. +ajax_invalid_recaptcha=Invalid recaptcha. Please try again. +ajax_invalid_parameter=Invalid service usage. +ajax_read_error=Server couldn't read submitted content. +ajax_storing_error=Server couldn't store submitted data. +ajax_syntax_error=Submitted content has a bad syntax. +base_layers=Base Layers +cancel=Cancel +chart=Chart +data_source=Data source +email=Email +feedback=feedback +feedback_addfeature_tooltip=Add Polygon +feedback_button=Feedback +feedback_deletepolygon_confirm=Deleting polygon, are you sure? +feedback_drawing_tools=Drawing tools +feedback_editfeature_tooltip=Edit Polygon +feedback_info=Please click on the map to post feedback +feedback_query_tooltip=Get features from the selected layer +feedback_text=Use the tools to draw a geographic extent over the map. +feedback_title=Send feedback on map contents +feedback_year=Year +invalid_email_text=Your e-mail address is not valid. Please use a valid e-mail address. +invalid_email_title=Invalid e-mail +info_dialog_title=Information +layer=Layer +layers=Layers +layersjson_baseUrl_not_defined=attribute 'baseUrl' not defined +layersjson_context_def_error=Error in context definition +layersjson_context_group_error=Error in context group +layersjson_context_id_not_defined=Error in context definition: attribute 'id' not defined +layersjson_context_label_not_defined=attribute 'label' not defined +layersjson_layer_def_error=Error in layer definition +layersjson_layer_id_not_defined=Error in layer definition: attribute 'id' not defined +layersjson_layer_label_not_defined=attribute 'label' should be present when 'legend' is defined +layersjson_not_defined=not defined +layersjson_source_label_not_defined=attribute 'sourceLabel' should be present when 'sourceLink' is defined +layersjson_wmsName_not_defined=attribute 'wmsName' not defined +legend_button=Legend +months=['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Des.'] +name=Name +selected_layers=Transparency +statistics=Statistics +statistics_button=Statistics +statistics_info=Please click on the map to start drawing a polygon +statistics_not_supported=Statistics are not supported in this version +submit=Submit +subtitle=Subtitle +zoom_to_area=Zoom to area +title=NFMS Sample Portal + +# BEGIN Feedback +Feedback.all_parameters_mandatory=all parameters are mandatory: +Feedback.error_sending_mail=Error sending mail: +Feedback.the_message_has_been_validated=The comment has been validated. +Feedback.comment_not_found=Cannot find any message with the code +Feedback.mail-title=Comment on the UN-REDD portal +Feedback.verify-mail-text=Please, visit http://localhost:8080/unredd-portal/verify-comment?lang=$lang&verificationCode=$code to confirm the comment. +Feedback.validated-mail-text=The comment with verification code "$code", has been validated and can be accessed in the portal. +Feedback.invalid-email-address=The specified e-mail address is not valid +Feedback.no-geometries=At least one geometry must be drawn +Feedback.wait=Wait for the feedback to be sent... +Feedback.verify_mail_sent=An e-mail has been sent to the specified e-mail address in order to confirm the comment +Feedback.submit_error=Error sending the comment +Feedback.no_layer_visible=None of the layers enabled for feedback is active +Feedback.no-layer-selected=No layer is selected +# END Feedback + +footnote.text=This is an English footnote + +layer_order=Layer Order diff --git a/integration-tests/test_config/messages/messages_es.properties b/integration-tests/test_config/messages/messages_es.properties new file mode 100644 index 0000000..803ff99 --- /dev/null +++ b/integration-tests/test_config/messages/messages_es.properties @@ -0,0 +1,78 @@ +# Java Resource Bundle +# Modified by Zaval JRC Editor (C) Zaval CE Group +# http://www.zaval.org/products/jrc-editor/ +# + +#Tue May 07 17:01:09 CEST 2013 +ajax_feedback_error=Error enviando comentarios. +ajax_feedback_ok=Comentarios enviados correctamente. +ajax_invalid_recaptcha=Recaptcha incorrecto. Por favor, vuelva a intentarlo. +ajax_invalid_parameter=Uso del servicio no v\u00e1lido. +ajax_read_error=El servidor no pudo leer los datos enviados. +ajax_storing_error=El servidor no pudo almacenar los datos enviados. +ajax_syntax_error=Los datos enviados tienen una sintaxis incorrecta. +base_layers=Capas Base +cancel=Cancelar +chart=Gr\u00e1fico +data_source=Fuente de datos +email=Correo electr\u00f3nico +feedback=Comentarios +feedback_addfeature_tooltip=A\u00f1adir pol\u00edgono +feedback_button=Comentarios +feedback_deletepolygon_confirm=Est\u00e1 a punto de suprimir el pol\u00edgono, \u00bfest\u00e1 seguro? +feedback_drawing_tools=Herramientas de dibujo +feedback_editfeature_tooltip=Editar pol\u00edgono +feedback_info=Por favor haga click en el mapa para enviar los comentarios +feedback_query_tooltip=Recuperar elementos de la capa seleccionada +feedback_text=Utilice las herramientas para dibujar una extensi\u00f3n geogr\u00e1fica sobre el mapa. +feedback_title=Enviar comentarios acerca de los contenidos del mapa +feedback_year=A\u00f1o +invalid_email_text=Su direcci\u00f3n de correo electr\u00f3nico no es v\u00e1lida. Por favor, introduzca una direcci\u00f3n de correo v\u00e1lida. +invalid_email_title=E-mail incorrecto +info_dialog_title=Informaci\u00f3n +layer=Capa +layers=Capas +layersjson_baseUrl_not_defined=el atributo 'baseUrl' no ha sido definido +layersjson_context_def_error=Error en la definici\u00f3n del contexto +layersjson_context_group_error=Error en el grupo de contextos +layersjson_context_id_not_defined=Error en la definici\u00f3n del contexto: el atributo 'id' no ha sido definido +layersjson_context_label_not_defined=el atributo 'label' no ha sido definido +layersjson_layer_def_error=Error en la definici\u00f3n de la capa +layersjson_layer_id_not_defined=Error en la definici\u00f3n de la capa: el atributo 'id' no ha sido definido +layersjson_layer_label_not_defined=el atributo 'label' deber\u00eda estar presente cuando se define 'legend' +layersjson_not_defined=no definido +layersjson_source_label_not_defined=el atributo 'sourceLabel' deber\u00eda estar presente cuando se define 'sourceLink' +layersjson_wmsName_not_defined=el atributo 'wmsName' no ha sido definido +legend_button=Leyenda +months=['Ene.', 'Feb.', 'Mar.', 'Abr.', 'Mayo', 'Jun.', 'Jul.', 'Ago.', 'Sep.', 'Oct.', 'Nov.', 'Dic.'] +name=Nombre +selected_layers=Transparencia +statistics=Estad\u00edsticas +statistics_button=Estad\u00edsticas +statistics_info=Por favor haga click en el mapa para dibujar un pol\u00edgono +statistics_not_supported=Las estad\u00edsticas no est\u00e1n soportadas en esta versi\u00f3n +submit=Enviar +subtitle=Subt\u00edtulo +zoom_to_area=Zoom a la zona +title=Ejemplo de portal NFMS + +# BEGIN Feedback +Feedback.all_parameters_mandatory=Todos los par\u00e1metros son obligatorios: +Feedback.error_sending_mail=Error enviando el correo: +Feedback.the_message_has_been_validated=El comentario ha sido validado. +Feedback.comment_not_found=No se encontr\u00f3 ning\u00fan comentario con el c\u00f3digo +Feedback.mail-title=Comentario en el portal ONU-REDD +Feedback.verify-mail-text=Por favor, visite http://localhost:8080/unredd-portal/verify-comment?lang=$lang&verificationCode=$code para confirmar el env\u00edo. +Feedback.validated-mail-text=El comentario con c\u00f3digo de verificaci\u00f3n "$code", ha sido validado y puede consultarse en el portal. +Feedback.invalid-email-address=La direcci\u00f3n de correo especificada no es v\u00e1lida +Feedback.no-geometries=Al menos se debe dibujar una geometr\u00eda +Feedback.wait=El mensaje de feedback est\u00e1 siendo enviado... +Feedback.verify_mail_sent=Se ha enviado un mensaje a la direcci\u00f3n de correo especificada para confirmar el comentario. +Feedback.submit_error=No se pudo realizar el env\u00edo. +Feedback.no_layer_visible=Ninguna de las capas habilitadas para feedback est\u00e1 visible +Feedback.no-layer-selected=No se ha seleccionado ninguna capa +# END Feedback + +footnote.text=Esto es una nota a pie en espa\u00f1ol + +layer_order=Orden Capas diff --git a/integration-tests/test_config/messages/messages_fr.properties b/integration-tests/test_config/messages/messages_fr.properties new file mode 100644 index 0000000..b510cfe --- /dev/null +++ b/integration-tests/test_config/messages/messages_fr.properties @@ -0,0 +1,74 @@ +# Java Resource Bundle +# Modified by Zaval JRC Editor (C) Zaval CE Group +# http://www.zaval.org/products/jrc-editor/ +# + +#Tue May 07 17:01:09 CEST 2013 +ajax_feedback_error=Erreur en envoyer les commentaires. +ajax_feedback_ok=Commentaires correctement envoy\u00e9es. +ajax_invalid_recaptcha=Recaptcha non valide. S'il vous pla\u00eet r\u00e9essayez. +ajax_invalid_parameter=Utilisation du service invalide. +ajax_read_error=Le server n'a pas pu lire le contenu soumis. +ajax_storing_error=Le server n'a pas pu stocker les donn\u00e9es soumises. +ajax_syntax_error=Le contenu a une syntaxe incorrecte. +base_layers=Fond de la Carte +cancel=Annuler +chart=Chart +data_source=Source des donn\u00e9es +email=Courrier email +feedback=Commentaires +feedback_addfeature_tooltip=Ajouter un polygone +feedback_button=Retour d'information +feedback_deletepolygon_confirm=Sur le point de supprimer ce polygone, \u00eates-vous s\u00fbr? +feedback_drawing_tools=Outils de dessin +feedback_editfeature_tooltip=Modifier Polygone +feedback_info=Cliquer sur la carte pour poster des commentaires +feedback_query_tooltip=Obtenez des \u00e9l\u00e9ments de la couche s\u00e9lectionn\u00e9e +feedback_text=Utilisez les outils pour dessiner une \u00e9tendue g\u00e9ographique sur la carte. +feedback_title=Envoyez vos remarques sur le contenu de la carte +feedback_year=Ann\u00e9e +invalid_email_text=Votre adresse e-mail n'est pas valide. S'il vous pla\u00eet corriger votr adresse e-mail. +invalid_email_title=E-mail invalid +info_dialog_title=Information +layer=Couche +layers=Couches +layersjson_baseUrl_not_defined=l'attribut 'baseUrl' n'a pas \u00e9t\u00e9 d\u00e9fini +layersjson_context_def_error=Erreur dans la d\u00e9finition du context +layersjson_context_group_error=Erreur dans le group de contextes +layersjson_context_id_not_defined=Erreur dans la d\u00e9finition du context: l'attribut 'id' n'a pas \u00e9t\u00e9 d\u00e9fini +layersjson_context_label_not_defined=l'attribut 'label' n'a pas \u00e9t\u00e9 d\u00e9fini +layersjson_layer_def_error=Erreur dans la d\u00e9finition de la couche +layersjson_layer_id_not_defined=Erreur dans la d\u00e9finition de la couche: l'attribut 'id' n'a pas \u00e9t\u00e9 d\u00e9fini +layersjson_layer_label_not_defined=l'attribute 'label' doit \u00eatre d\u00e9fini lorsque 'legend' est d\u00e9fini +layersjson_not_defined=pas d\u00e9fini +layersjson_source_label_not_defined=l'attribute 'sourceLabel' doit \u00eatre d\u00e9fini lorsque 'sourceLink' est d\u00e9fini +layersjson_wmsName_not_defined=l'attribut 'wmsName' n'a pas \u00e9t\u00e9 d\u00e9fini +legend_button=L\u00e9gende +months=['janv.', 'f\u00e9vr.', 'mars', 'avr.', 'may', 'juin', 'juill.', 'ao\u00fbt', 'sept.', 'oct.', 'nov.', 'd\u00e9c.'] +name=Nom +selected_layers=Transparence +statistics=Statistiques +statistics_button=Statistiques +statistics_info=S'il vous pla\u00eet cliquer sur la carte pour commencer \u00e0 dessiner un polygone +statistics_not_supported=Les statistiques ne sont pas support\u00e9s dans cette version +submit=Envoyer +subtitle=Soustitre +zoom_to_area=Zoom sur la zone +title=Exemple du Portail NFMS + +# BEGIN Feedback +Feedback.all_parameters_mandatory=tous les param\u00e8tres son obligatoires: +Feedback.error_sending_mail=Erreur en envoyant le mail: +Feedback.the_message_has_been_validated=Le commentaire a \u00e9t\u00e9 valid\u00e9. +Feedback.comment_not_found=Aucun commentaire avec ce code +Feedback.mail-title=Commentaire au portal ONU-REDD +Feedback.verify-mail-text=S'il vous pla\u00eet, allez visiter http://localhost:8080/unredd-portal/verify-comment?lang=$lang&verificationCode=$code pour confirmer l'envoi. +Feedback.validated-mail-text=Le commentaire avec code de v\u00e9rification "$code", a \u00e9t\u00e9 valid\u00e9 et peut se consulter au portail +Feedback.invalid-email-address=L\u2019adresse de mail sp\u00e9cifi\u00e9 n'est pas valide +Feedback.no-geometries=Aucune g\u00e9om\u00e9trie sp\u00e9cifi\u00e9 +Feedback.verify_mail_sent=A message a \u00e9t\u00e9 envoy\u00e9 a l\u2019adresse sp\u00e9cifi\u00e9 pour confirmer le commentaire. +Feedback.submit_error=Le commentaire n'a pas \u00e9t\u00e9 envoy\u00e9. +Feedback.submit_error=Le commentaire n'a pas \u00e9t\u00e9 envoy\u00e9. +Feedback.no_layer_visible=Aucune des couches configur\u00e9es pour la fonctionnalit\u00e9 de feedback est visible +Feedback.no-layer-selected=Aucune couche est s\u00e9lectionn\u00e9e +# END Feedback diff --git a/integration-tests/test_config/plugin-conf.json b/integration-tests/test_config/plugin-conf.json new file mode 100644 index 0000000..2b197f1 --- /dev/null +++ b/integration-tests/test_config/plugin-conf.json @@ -0,0 +1,15 @@ +{ + "default-conf" : { + "banner" : { + "hide" : false, + "show-flag" : true, + "show-logos" : true + }, + "info-dialog" : { + "open-in-center" : false + }, + "error-management" : { + "div-id" : null + } + } +} \ No newline at end of file diff --git a/integration-tests/test_config/portal.properties b/integration-tests/test_config/portal.properties new file mode 100644 index 0000000..43ab138 --- /dev/null +++ b/integration-tests/test_config/portal.properties @@ -0,0 +1,18 @@ +languages = {"en": "English", "fr": "Fran\u00e7ais", "es": "Espa\u00f1ol"} +languages.default = en +client.modules= +map.centerLonLat=-58,-34 +map.initialZoomLevel=5 + +#Schema where all the system tables are stored +db-schema=integration_tests + +#Feedback +feedback-mail-host=smtp.gmail.com +feedback-mail-port=587 +feedback-mail-username=onuredd@gmail.com +feedback-mail-password=$password +feedback-admin-mail-title=Nuevo comentario en el portal REDD +feedback-admin-mail-text=Se ha introducido un comentario en el portal de REDD con c\u00f3digo de verificaci\u00f3n "$code" +feedback-validation-check-delay=1000 + diff --git a/integration-tests/test_config/static/img/favicon.png b/integration-tests/test_config/static/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..25606c10ba5714f3b9442d9c7d53cb3a9cb2a6bc GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!D3?x-;bCrM;V{wqX6T`Z5GB1G~wg8_HS0Js{ zw(I_>XNN%2B|(0{3=Yq3q=7g|-tI089jvk*Kn`btM`SSr1Gg{;GcwGYBLNg-FY)ws zWxvNH$!laaVQP~UP{`cV#W95AdUArzfdk106bzdk7xo@37Iu!*KHPMl=k!iVF`Lv3 syAuyeW*q#<(wrpRd{BIX)HikpA78$6iiY-kfkrTRy85}Sb4q9e0G;bN$^ZZW literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/img/flag.png b/integration-tests/test_config/static/img/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8435eac7f86bb0bc3e15a7dd980472b788bf6e GIT binary patch literal 2564 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 IM6N<$f~Jkw0{{R3 literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/img/printer.png b/integration-tests/test_config/static/img/printer.png new file mode 100644 index 0000000000000000000000000000000000000000..c6250ee75cbd655555ef4b99148130a038d29845 GIT binary patch literal 882 zcmV-&1C9KNP)z zPiS356vlsN?tMu#R2S+-+l7>(3lUri1=}tnZrq5XTQQ(6T&hJ#p^Jjrt%$2ETo&!3 zyJ9yQ{Ie9a8buN+RT6_*NodV`_s;jZxbMBD(%j^wNfA6St9$0mZ_YV0^IiDATeN+A z>deB6a+)RR?I^hE5IXV63kToc%%^M%kQMjbUDj!`@PtQQ99UOyr79k1^J@jLZP{d- zEs}M!*~m?hTLHi}vKc)CMhaIi(!~1YWq( zig_Tq;SkHLnrjoNuzt^zYA2~7nba?igSH~kh z*w@jnt=Y!%SCz>2b6-q9dNu)6*)QFZG7>oN?T}zHW$Nr{7&M`LpACDzf1o*3NWOOn zAgPuNv3q6T%z^9(Yxwg|jB1Q0A&f=-> zT7g_NO_H@V|Hjd!hu;14t{_brEA>}yf8Am5^vMt+L45h$@gvKJ-udj8Oxlm3Rt9-t z2tZ2TcB{M=u&+P;Vv`xaQYM}56s<4nrY4Ij>wVxg(EQbI-Ve~NwEELq$G#o{%)kCb z{qX6zLp^b<;VHgq0yvi`F;>7@SIF8{3Cc)JjwP}ZUDpOkkzVeJ#{m~bnj82J^}r8r z9{CM8b3-r-J5gt->*}M$nJ1s$&dwh^b>ZXja_m_(_d5VT9zS{sxb$z|RzZRqJy;sT z@yx+f7kd3Z-P9WK6h-ud=HotY<>Q2YDJ2I|F$ar2PZWugl$AZb^rhX07*qo IM6N<$g5L3_6951J literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/img/right.jpg b/integration-tests/test_config/static/img/right.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b10a3b23fdb49a33aac816318fa4e94e86589fa GIT binary patch literal 37389 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 f{xclIyRNZTv}6f4Mq@{RJuO#~Q;- literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/loc/en/html/forest_classification_def.html b/integration-tests/test_config/static/loc/en/html/forest_classification_def.html new file mode 100644 index 0000000..2191568 --- /dev/null +++ b/integration-tests/test_config/static/loc/en/html/forest_classification_def.html @@ -0,0 +1,8 @@ + diff --git a/integration-tests/test_config/static/loc/en/html/provinces_def.html b/integration-tests/test_config/static/loc/en/html/provinces_def.html new file mode 100644 index 0000000..b2c0017 --- /dev/null +++ b/integration-tests/test_config/static/loc/en/html/provinces_def.html @@ -0,0 +1,4 @@ + diff --git a/integration-tests/test_config/static/loc/en/images/forest_classification.png b/integration-tests/test_config/static/loc/en/images/forest_classification.png new file mode 100644 index 0000000000000000000000000000000000000000..c6826c1d2b0869aa67af9da74b40f585d0a2c0d8 GIT binary patch literal 2474 zcmV;b303xqP)Px#W>8F2MF0Q*0s;aM5D*j;6e1!bB_$;@Gcz?cHAF;208gSuMn+XtRaaM6W@ct+ zXlQqLcX@evh=_=ZiHUZjYMGgtnwpxbs;aB2tGm0qyu7^F!lKN~%+1Zs-QC^G|EKQm z?*ISl|NsAj?A}BG00{C)L_t(|+U=czdg3S$fTf7^(nwRzteZ1Aj`x4bn_-3ot@doc znpki6wreB=!k=M?zznM>p$IEMaEjxfGeK|&4#6Qf1jhs5Xx09{adbv0(;mlF%6`7k z!Kmiq$zX@*DlAS9_lzTGkI(Gr9tYKZ>A`=*YAT_>XB=I=Pvg^{t1O6%YGa-Lk6tO; z$HA)n=uHJNA>#QeLxL2s z2s27Y3!k-yN_Mffv9h7p)S`?T2IZqSL$FnJaNg9TbW~OBHDwaCL#YeE)Y&8*fWyJX z){_P)^1LJ65ga@@Ur5K)SfGMUQX&CwzoA;2CZ)sVdljIjq!Uu=L4lyaB^U!IW3)c# zy646MvDh-bN#x2r@2qEO;MPuYcn889ouihIp(+~{HY8|hn0&&KS{_svW7>3$k6nfT zGmaFe5{@#@d+TWVaq9_(*RCz3qc4+A(h-kwkaSce9eTXPt58S>x2rPG`@!K&hqs3s zBGhWa0j(fdm2^j=v7HC&N*vzwNp~FKAbmiYpzZ+O(1YM596jrfSW#ms^SmSJXcWD7 z^hQwLvY+2$FRb%j@##JGe2zTbN%*^IDV(k>v8PX z{}UWjUYI--zQCltNe?7)RXvikn^BKsA3orKEg#(pcca z&FdpJmMxLVINn}RK-MXe>Q9Fyj@Rva^&Ho`N5lb5BdJP60e^=ljMz*HlMf3V&ztSx zVWk6g$8I$oR=;*@%wYrHK`$Iop(Ncwhr6c{2Pllg(=+OhxOsWRp5+M$Y*$Yx9q=_! zcLLo_?pfdvv`t_fJBWCB3hBh~5IE$mRB#9m!67&<9DH>)XAE_|fwbPVHA+=O?97rlh8Qod4gVC53n7?u zLm@5J8Ou#K(BdNIJwOQ-;+N348W1`5L78-7$|te~>ZLK#E02!pDUSY;CiGDhI_ zrooO2ti2m!TWymWM8**-$2WkIg{xKJ>mDeDMR9a+kEV$|fUsz1knW~a9j!ZVqa*R< z7&L)tRR=iWqc2Pt23(D-dt6K0m{c(g20l2p#bwR9ln&20I$D^VmC$}!-NEtz)v0W5 zH%*1&kXEJh`f-kFu|l@N>f-ujHepD{2o~bga&&L9z^7|D7D01sD|83p7`n*&r8C|;Xu88p_i8-Q3IM{>%gGM_Zv`L7KR(2Q*Fq5T1#(_FQ_wlTB zd~+Z81sp+FF3=U!U+z+Awh5mjw^BebtVW7!67&Vhv1kGhcW+C95gt1QhRT3tfD=7 zi*@4=vTLK#Ns+5Z-LY550p~6C zh+{@yM$7dqh=XSZT6V62ua2j-3cU>M@|IR0~D!CW^yd?k)6 z&o3q&|246IYqQHu5u7oK_tPL_2DWvW++(M!ti?$P_LPmL3dU>9FfT!^j#5*esTg#^ zj;`jbwDg;{dKhs1siU1AK0o601ss+^^kBhvApD#y5pmX1;P=h4HW9^KfkeMOM!oE zm_X8zIE*+#x(*FRd|IJiS4XMqao{>-)uhrqsWcjlOwX;j zhu{z#f@3}$k2kS!C64=>ShyUDQGerah2!T;EPQpgC#Wrx8hiKVu2hm@qW*#AhpNb9WLrk?vnzQJf!_x`@GF`;iu_4b;r^Z z3*>+)@ApjJWiUM)vil?`5g2v(3G%+mg`cMHm5ya67RUh;@Au3OXDXL&i#@`mITyIs z&edwdQTS>4UL4C#EX3r1iACi|Iy%OIk^|p5<$Ng}^^H{NvJ(sBfQk2eM%^)JpY%d9 z2*Ar(ci<$Yn{X64~LM1;W+i3>+vBqd#BnIkm5>WeWj Xv_JRKx|i@-5@eyLtDnm{r-UW|vY9cq literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/loc/es/html/forest_classification_def.html b/integration-tests/test_config/static/loc/es/html/forest_classification_def.html new file mode 100644 index 0000000..1fed93d --- /dev/null +++ b/integration-tests/test_config/static/loc/es/html/forest_classification_def.html @@ -0,0 +1,5 @@ + diff --git a/integration-tests/test_config/static/loc/es/html/provinces_def.html b/integration-tests/test_config/static/loc/es/html/provinces_def.html new file mode 100644 index 0000000..b73e266 --- /dev/null +++ b/integration-tests/test_config/static/loc/es/html/provinces_def.html @@ -0,0 +1,4 @@ +
+

Departamentos

+

Fuente: FAO GeoNetwork +

diff --git a/integration-tests/test_config/static/loc/es/images/forest_classification.png b/integration-tests/test_config/static/loc/es/images/forest_classification.png new file mode 100644 index 0000000000000000000000000000000000000000..c6826c1d2b0869aa67af9da74b40f585d0a2c0d8 GIT binary patch literal 2474 zcmV;b303xqP)Px#W>8F2MF0Q*0s;aM5D*j;6e1!bB_$;@Gcz?cHAF;208gSuMn+XtRaaM6W@ct+ zXlQqLcX@evh=_=ZiHUZjYMGgtnwpxbs;aB2tGm0qyu7^F!lKN~%+1Zs-QC^G|EKQm z?*ISl|NsAj?A}BG00{C)L_t(|+U=czdg3S$fTf7^(nwRzteZ1Aj`x4bn_-3ot@doc znpki6wreB=!k=M?zznM>p$IEMaEjxfGeK|&4#6Qf1jhs5Xx09{adbv0(;mlF%6`7k z!Kmiq$zX@*DlAS9_lzTGkI(Gr9tYKZ>A`=*YAT_>XB=I=Pvg^{t1O6%YGa-Lk6tO; z$HA)n=uHJNA>#QeLxL2s z2s27Y3!k-yN_Mffv9h7p)S`?T2IZqSL$FnJaNg9TbW~OBHDwaCL#YeE)Y&8*fWyJX z){_P)^1LJ65ga@@Ur5K)SfGMUQX&CwzoA;2CZ)sVdljIjq!Uu=L4lyaB^U!IW3)c# zy646MvDh-bN#x2r@2qEO;MPuYcn889ouihIp(+~{HY8|hn0&&KS{_svW7>3$k6nfT zGmaFe5{@#@d+TWVaq9_(*RCz3qc4+A(h-kwkaSce9eTXPt58S>x2rPG`@!K&hqs3s zBGhWa0j(fdm2^j=v7HC&N*vzwNp~FKAbmiYpzZ+O(1YM596jrfSW#ms^SmSJXcWD7 z^hQwLvY+2$FRb%j@##JGe2zTbN%*^IDV(k>v8PX z{}UWjUYI--zQCltNe?7)RXvikn^BKsA3orKEg#(pcca z&FdpJmMxLVINn}RK-MXe>Q9Fyj@Rva^&Ho`N5lb5BdJP60e^=ljMz*HlMf3V&ztSx zVWk6g$8I$oR=;*@%wYrHK`$Iop(Ncwhr6c{2Pllg(=+OhxOsWRp5+M$Y*$Yx9q=_! zcLLo_?pfdvv`t_fJBWCB3hBh~5IE$mRB#9m!67&<9DH>)XAE_|fwbPVHA+=O?97rlh8Qod4gVC53n7?u zLm@5J8Ou#K(BdNIJwOQ-;+N348W1`5L78-7$|te~>ZLK#E02!pDUSY;CiGDhI_ zrooO2ti2m!TWymWM8**-$2WkIg{xKJ>mDeDMR9a+kEV$|fUsz1knW~a9j!ZVqa*R< z7&L)tRR=iWqc2Pt23(D-dt6K0m{c(g20l2p#bwR9ln&20I$D^VmC$}!-NEtz)v0W5 zH%*1&kXEJh`f-kFu|l@N>f-ujHepD{2o~bga&&L9z^7|D7D01sD|83p7`n*&r8C|;Xu88p_i8-Q3IM{>%gGM_Zv`L7KR(2Q*Fq5T1#(_FQ_wlTB zd~+Z81sp+FF3=U!U+z+Awh5mjw^BebtVW7!67&Vhv1kGhcW+C95gt1QhRT3tfD=7 zi*@4=vTLK#Ns+5Z-LY550p~6C zh+{@yM$7dqh=XSZT6V62ua2j-3cU>M@|IR0~D!CW^yd?k)6 z&o3q&|246IYqQHu5u7oK_tPL_2DWvW++(M!ti?$P_LPmL3dU>9FfT!^j#5*esTg#^ zj;`jbwDg;{dKhs1siU1AK0o601ss+^^kBhvApD#y5pmX1;P=h4HW9^KfkeMOM!oE zm_X8zIE*+#x(*FRd|IJiS4XMqao{>-)uhrqsWcjlOwX;j zhu{z#f@3}$k2kS!C64=>ShyUDQGerah2!T;EPQpgC#Wrx8hiKVu2hm@qW*#AhpNb9WLrk?vnzQJf!_x`@GF`;iu_4b;r^Z z3*>+)@ApjJWiUM)vil?`5g2v(3G%+mg`cMHm5ya67RUh;@Au3OXDXL&i#@`mITyIs z&edwdQTS>4UL4C#EX3r1iACi|Iy%OIk^|p5<$Ng}^^H{NvJ(sBfQk2eM%^)JpY%d9 z2*Ar(ci<$Yn{X64~LM1;W+i3>+vBqd#BnIkm5>WeWj Xv_JRKx|i@-5@eyLtDnm{r-UW|vY9cq literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/loc/fr/html/forest_classification_def.html b/integration-tests/test_config/static/loc/fr/html/forest_classification_def.html new file mode 100644 index 0000000..44007e7 --- /dev/null +++ b/integration-tests/test_config/static/loc/fr/html/forest_classification_def.html @@ -0,0 +1,8 @@ + diff --git a/integration-tests/test_config/static/loc/fr/html/provinces_def.html b/integration-tests/test_config/static/loc/fr/html/provinces_def.html new file mode 100644 index 0000000..b2c0017 --- /dev/null +++ b/integration-tests/test_config/static/loc/fr/html/provinces_def.html @@ -0,0 +1,4 @@ + diff --git a/integration-tests/test_config/static/loc/fr/images/forest_classification.png b/integration-tests/test_config/static/loc/fr/images/forest_classification.png new file mode 100644 index 0000000000000000000000000000000000000000..c6826c1d2b0869aa67af9da74b40f585d0a2c0d8 GIT binary patch literal 2474 zcmV;b303xqP)Px#W>8F2MF0Q*0s;aM5D*j;6e1!bB_$;@Gcz?cHAF;208gSuMn+XtRaaM6W@ct+ zXlQqLcX@evh=_=ZiHUZjYMGgtnwpxbs;aB2tGm0qyu7^F!lKN~%+1Zs-QC^G|EKQm z?*ISl|NsAj?A}BG00{C)L_t(|+U=czdg3S$fTf7^(nwRzteZ1Aj`x4bn_-3ot@doc znpki6wreB=!k=M?zznM>p$IEMaEjxfGeK|&4#6Qf1jhs5Xx09{adbv0(;mlF%6`7k z!Kmiq$zX@*DlAS9_lzTGkI(Gr9tYKZ>A`=*YAT_>XB=I=Pvg^{t1O6%YGa-Lk6tO; z$HA)n=uHJNA>#QeLxL2s z2s27Y3!k-yN_Mffv9h7p)S`?T2IZqSL$FnJaNg9TbW~OBHDwaCL#YeE)Y&8*fWyJX z){_P)^1LJ65ga@@Ur5K)SfGMUQX&CwzoA;2CZ)sVdljIjq!Uu=L4lyaB^U!IW3)c# zy646MvDh-bN#x2r@2qEO;MPuYcn889ouihIp(+~{HY8|hn0&&KS{_svW7>3$k6nfT zGmaFe5{@#@d+TWVaq9_(*RCz3qc4+A(h-kwkaSce9eTXPt58S>x2rPG`@!K&hqs3s zBGhWa0j(fdm2^j=v7HC&N*vzwNp~FKAbmiYpzZ+O(1YM596jrfSW#ms^SmSJXcWD7 z^hQwLvY+2$FRb%j@##JGe2zTbN%*^IDV(k>v8PX z{}UWjUYI--zQCltNe?7)RXvikn^BKsA3orKEg#(pcca z&FdpJmMxLVINn}RK-MXe>Q9Fyj@Rva^&Ho`N5lb5BdJP60e^=ljMz*HlMf3V&ztSx zVWk6g$8I$oR=;*@%wYrHK`$Iop(Ncwhr6c{2Pllg(=+OhxOsWRp5+M$Y*$Yx9q=_! zcLLo_?pfdvv`t_fJBWCB3hBh~5IE$mRB#9m!67&<9DH>)XAE_|fwbPVHA+=O?97rlh8Qod4gVC53n7?u zLm@5J8Ou#K(BdNIJwOQ-;+N348W1`5L78-7$|te~>ZLK#E02!pDUSY;CiGDhI_ zrooO2ti2m!TWymWM8**-$2WkIg{xKJ>mDeDMR9a+kEV$|fUsz1knW~a9j!ZVqa*R< z7&L)tRR=iWqc2Pt23(D-dt6K0m{c(g20l2p#bwR9ln&20I$D^VmC$}!-NEtz)v0W5 zH%*1&kXEJh`f-kFu|l@N>f-ujHepD{2o~bga&&L9z^7|D7D01sD|83p7`n*&r8C|;Xu88p_i8-Q3IM{>%gGM_Zv`L7KR(2Q*Fq5T1#(_FQ_wlTB zd~+Z81sp+FF3=U!U+z+Awh5mjw^BebtVW7!67&Vhv1kGhcW+C95gt1QhRT3tfD=7 zi*@4=vTLK#Ns+5Z-LY550p~6C zh+{@yM$7dqh=XSZT6V62ua2j-3cU>M@|IR0~D!CW^yd?k)6 z&o3q&|246IYqQHu5u7oK_tPL_2DWvW++(M!ti?$P_LPmL3dU>9FfT!^j#5*esTg#^ zj;`jbwDg;{dKhs1siU1AK0o601ss+^^kBhvApD#y5pmX1;P=h4HW9^KfkeMOM!oE zm_X8zIE*+#x(*FRd|IJiS4XMqao{>-)uhrqsWcjlOwX;j zhu{z#f@3}$k2kS!C64=>ShyUDQGerah2!T;EPQpgC#Wrx8hiKVu2hm@qW*#AhpNb9WLrk?vnzQJf!_x`@GF`;iu_4b;r^Z z3*>+)@ApjJWiUM)vil?`5g2v(3G%+mg`cMHm5ya67RUh;@Au3OXDXL&i#@`mITyIs z&edwdQTS>4UL4C#EX3r1iACi|Iy%OIk^|p5<$Ng}^^H{NvJ(sBfQk2eM%^)JpY%d9 z2*Ar(ci<$Yn{X64~LM1;W+i3>+vBqd#BnIkm5>WeWj Xv_JRKx|i@-5@eyLtDnm{r-UW|vY9cq literal 0 HcmV?d00001 diff --git a/integration-tests/test_config/static/overrides.css b/integration-tests/test_config/static/overrides.css new file mode 100644 index 0000000..c1e5f5a --- /dev/null +++ b/integration-tests/test_config/static/overrides.css @@ -0,0 +1 @@ +/* Override styles in this file */ \ No newline at end of file From 2eb8adce776218e1b8a1406ea093da9b1037204e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 12:01:53 +0200 Subject: [PATCH 020/119] travis --- .../org/fao/unredd/functional/functional-test.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties index bcd3bf7..2d31881 100644 --- a/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties +++ b/integration-tests/src/test/resources/org/fao/unredd/functional/functional-test.properties @@ -1,4 +1,4 @@ -db-url=jdbc:postgresql://postgis.unredd:5432/spatialdata -db-user=spatial_user -db-password=unr3dd +db-url=jdbc:postgresql://127.0.0.1:5432/spatialdata +db-user=postgres +db-password= db-test-schema=integration_tests From 7128103ba39f742f441254f5e0c18caa2c31dc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 12:11:55 +0200 Subject: [PATCH 021/119] travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 39ea54d..4b154ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,4 @@ script: after_failure: - cat integration-tests/target/failsafe-reports/*.xml + - cat /tmp/testportal/portal.properties From 02917c7ffb0d7e052351bd6b9a1d3b75dbf003c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 12:25:39 +0200 Subject: [PATCH 022/119] travis --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4b154ea..3ef0bf5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,3 +13,8 @@ script: after_failure: - cat integration-tests/target/failsafe-reports/*.xml - cat /tmp/testportal/portal.properties + +notifications: + email: + - nfms4redd-desarrollo@googlegroups.com + on_success: always From 2bc0314d2bf3ef0e786bf3e305d7d76d365e11b8 Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Wed, 14 Oct 2015 12:32:06 +0200 Subject: [PATCH 023/119] Generating layers edit forms from schema & data --- .../nfms/modules/layers-editor-forms.css | 36 ++++++ .../nfms/modules/layers-editor-forms.js | 111 ++++++++++++++++++ ...ers-editor.css => layers-editor-links.css} | 0 .../nfms/modules/layers-editor-links.js | 18 +++ .../resources/nfms/modules/layers-editor.js | 34 ------ .../resources/nfms/modules/layers-forms.js | 41 ------- .../resources/nfms/modules/layers-schema.js | 3 + .../layers.schema.json => layers-schema.json} | 6 +- 8 files changed, 171 insertions(+), 78 deletions(-) create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js rename layers-editor/src/main/resources/nfms/modules/{layers-editor.css => layers-editor-links.css} (100%) create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor-links.js delete mode 100644 layers-editor/src/main/resources/nfms/modules/layers-editor.js delete mode 100644 layers-editor/src/main/resources/nfms/modules/layers-forms.js create mode 100644 layers-editor/src/main/resources/nfms/modules/layers-schema.js rename layers-editor/src/main/resources/nfms/modules/{resources/layers.schema.json => layers-schema.json} (98%) diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css new file mode 100644 index 0000000..556ded9 --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css @@ -0,0 +1,36 @@ +.layers-editor-form div { + float: left; + clear: left; + width: 100%; + padding-bottom: 0.5em; +} + +.layers-editor-form label { + float: left; + width: 150px; + margin-right: 10px; + text-align: right; +} + +.layers-editor-form fieldset { + width: 420px; + margin-bottom: 5px; +} + +.layers-editor-form input { + width: 250px; +} + +.layers-editor-form select { + width: 125px; +} + +.layers-editor-form input[type="checkbox"] { + width: auto; +} + +.fieldtypeeditornotimplemented { + color: red; + font-weight: bold; + font-style: italic; +} diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js new file mode 100644 index 0000000..9a28a09 --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js @@ -0,0 +1,111 @@ +define(["layers-json", "layers-schema", "jquery", "jquery-ui"], function(layers, schema, $) { + + function editLayer(id) { + var form = createForm("Edit Layer"); + + var portalValues = layers.getPortalLayer(id); + addPortalLayerFields(form, portalValues); + + var wmsValues = layers.getWmsLayer(portalValues.layers[0]); + addWmsLayerFields(form, wmsValues); + } + + function editGroup(id) { + var form = createForm("Edit Group"); + + var values = layers.getGroup(id); + addTocFields(form, values); + } + + function createForm(title) { + var dialog = $("
"); + dialog.dialog({ + title: title, + autoOpen: true, + height: 600, + minHeight: 400, + width: 475, + zIndex: 2000, + resizable: true, + closeOnEscape: false + }); + + var form = $("").addClass("layers-editor-form").appendTo(dialog); + return form; + } + + function addTocFields(form, values) { + var properties = schema.definitions.toc.properties; + addFields("Layer Switcher Entry", form, properties, values); + } + + function addPortalLayerFields(form, values) { + addTocFields(form, values); + var properties = schema.definitions.portalLayer.allOf[1].properties; + delete properties.layers; + addFields("Portal Layer", form, properties, values); + } + + function addWmsLayerFields(form, values) { + var properties = schema.definitions["wmsLayer-base"].properties; + addFields("Pseudo-WMS Layer", form, properties, values); + + if (properties.type && properties.type == "osm") { + properties = schema.definitions["wmsLayer-osmType"].allOf[1].properties; + addFields("OSM Layer", form, properties, values); + } else if (properties.type && properties.type == "gmaps") { + properties = schema.definitions["wmsLayer-gmapsType"].allOf[1].properties; + addFields("Google Maps Layer", form, properties, values); + } else { + properties = schema.definitions["wmsLayer-wmsType"].allOf[1].properties; + delete properties.type; + addFields("True-WMS Layer", form, properties, values); + } + } + + function addFields(title, form, properties, values) { + var fieldset = $("
").appendTo(form); + $("").text(title).appendTo(fieldset); + + for (var i in properties) { + if (!properties[i].id) { + properties[i].id = i; + } + addField(fieldset, properties[i], values[i]); + } + } + + function addField(form, schema, value) { + var div = $("
").appendTo(form); + var label = $("").addClass("layer_edit_button").click(function() { + callback.call(null, id); + }); + } + +}); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor.js b/layers-editor/src/main/resources/nfms/modules/layers-editor.js deleted file mode 100644 index a1a9e2a..0000000 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor.js +++ /dev/null @@ -1,34 +0,0 @@ -define(["message-bus", "layers-json", "layers-forms"], function(bus, layers, forms) { - - bus.listen("before-adding-layers", function() { - bus.send("register-layer-action", function(layer) { - return link(layer.id, editLayer); - }); - bus.send("register-group-action", function(group) { - return link(group.id, editGroup); - }); - }); - - function link(id, onClick) { - return $("") - .addClass("layer_edit_button") - .click(function() { - onClick.call(null, id); - }); - } - - function editLayer(id) { - var portalLayer = layers.getPortalLayer(id); - var wmsLayer = layers.getWmsLayer(portalLayer.layers[0]); - alert("Edit Layer: " + JSON.stringify({ - portalLayer: portalLayer, - wmsLayer: wmsLayer - }, null, 3)); - } - - function editGroup(id) { - var group = layers.getGroup(id); - alert("Edit Group: " + group.label); - } - -}); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-forms.js b/layers-editor/src/main/resources/nfms/modules/layers-forms.js deleted file mode 100644 index 8eea76b..0000000 --- a/layers-editor/src/main/resources/nfms/modules/layers-forms.js +++ /dev/null @@ -1,41 +0,0 @@ -define(["text!resources/layers.schema.json", "jquery", "jquery-ui"], function(layers_shema, $) { - - var schema = JSON.parse(layers_shema); - console.log(schema); - - var field = function(type, value) { - console.log([type, value]); - if (type.type == "string") { - var div = $("
").text(type.title + ": ").appendTo(form); - var input = $("").attr("type", "text").attr("value", value).appendTo(div); - } - } - - var toc = function() { - var props = schema.definitions.toc.properties; - var vals = { - "id": "registroredd", - "label": "Registro REDD+", - "infoFile": "registro_redd.html" - } - for(var i in props) { - field(props[i], vals[i]); - } - } - - var dialog = $("
"); - dialog.dialog({ - closeOnEscape : false, - autoOpen : false, - height : 500, - minHeight : 400, - width : 325, - zIndex : 2000, - resizable : true - }); - dialog.dialog("open"); - var form = $(""); - dialog.append(form); - toc(); - -}); diff --git a/layers-editor/src/main/resources/nfms/modules/layers-schema.js b/layers-editor/src/main/resources/nfms/modules/layers-schema.js new file mode 100644 index 0000000..54169da --- /dev/null +++ b/layers-editor/src/main/resources/nfms/modules/layers-schema.js @@ -0,0 +1,3 @@ +define(["text!layers-schema.json"], function(shema) { + return JSON.parse(shema); +}); diff --git a/layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json b/layers-editor/src/main/resources/nfms/modules/layers-schema.json similarity index 98% rename from layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json rename to layers-editor/src/main/resources/nfms/modules/layers-schema.json index 95b570a..bce9c1c 100644 --- a/layers-editor/src/main/resources/nfms/modules/resources/layers.schema.json +++ b/layers-editor/src/main/resources/nfms/modules/layers-schema.json @@ -4,8 +4,8 @@ "wmsLayer-base":{ "type":"object", "properties":{ - "title":"Identifier", "id":{ + "title":"Identifier", "type":"string" }, "type":{ @@ -17,8 +17,8 @@ "gmaps" ] }, - "title":"Is visible layer", "visible":{ + "title":"Is visible layer", "type":"boolean" }, "legend":{ @@ -83,7 +83,7 @@ "type":"string" }, "wmsName":{ - "title":"Layer Name", + "title":"WMS Layername", "type":"string" }, "imageFormat":{ From 627825dd9e97b0430d422ee3e71bd11ff4986392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 12:46:41 +0200 Subject: [PATCH 024/119] travis notification cannot send to google group (https://github.com/travis-ci/travis-ci/issues/2513) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3ef0bf5..dc3b2b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,5 +16,5 @@ after_failure: notifications: email: - - nfms4redd-desarrollo@googlegroups.com + - onuredd@gmail.com on_success: always From 6d5c23b9b580029031d3e2e569014a011155d3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 16:52:16 +0200 Subject: [PATCH 025/119] Restore optimize by default (removed fixing travis build) --- argentina/pom.xml | 2 +- demo/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/argentina/pom.xml b/argentina/pom.xml index ee3b991..cbb739f 100644 --- a/argentina/pom.xml +++ b/argentina/pom.xml @@ -139,7 +139,7 @@ optimize - false + true diff --git a/demo/pom.xml b/demo/pom.xml index 823c15c..94a4e40 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -149,7 +149,7 @@ optimize - false + true From c9db721af974ff3d7bee22b9f674a208f46ae5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Cort=C3=A9s?= Date: Wed, 14 Oct 2015 16:53:03 +0200 Subject: [PATCH 026/119] Don't send me an email each time someone makes a commit and triggers a CI build, thanks --- .../java/org/fao/unredd/functional/feedback/FeedbackTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/test/java/org/fao/unredd/functional/feedback/FeedbackTest.java b/integration-tests/src/test/java/org/fao/unredd/functional/feedback/FeedbackTest.java index e7d5702..93ab213 100644 --- a/integration-tests/src/test/java/org/fao/unredd/functional/feedback/FeedbackTest.java +++ b/integration-tests/src/test/java/org/fao/unredd/functional/feedback/FeedbackTest.java @@ -15,7 +15,7 @@ public class FeedbackTest extends AbstractIntegrationTest { @Test public void testCommentAndVerify() throws Exception { - String email = "fergonco@gmail.com"; + String email = "onuredd@gmail.com"; String geometry = "POINT(0 1)"; String comment = "boh"; String layerName = "classification"; From fb56815a014a6eace975d8a7a7deb4ea4d07b31d Mon Sep 17 00:00:00 2001 From: Oscar Fonts Date: Wed, 14 Oct 2015 17:52:53 +0200 Subject: [PATCH 027/119] First attempt to serialize results --- .../nfms/modules/layers-editor-forms.css | 30 ++++++- .../nfms/modules/layers-editor-forms.js | 82 +++++++++++++++---- .../resources/nfms/modules/layers-schema.json | 4 +- 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css index 556ded9..e5319a5 100644 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.css @@ -17,19 +17,43 @@ margin-bottom: 5px; } -.layers-editor-form input { +.layers-editor-form input[type="text"] { width: 250px; } +.layers-editor-form textarea { + width: 150px; + resize: vertical; +} + .layers-editor-form select { width: 125px; } -.layers-editor-form input[type="checkbox"] { +.layers-editor-form input { width: auto; } -.fieldtypeeditornotimplemented { +.layers-editor-form ul { + margin: 0; + margin-left: 120px; + list-style-type: none; +} + +.layers-editor-form ul div { + display: inline; + float: none; +} + +.layers-editor-form ul input[type="text"] { + width: 160px; +} + +.layers-editor-form ul select { + width: 75px; +} + +.layers-editor-type-not-implemented { color: red; font-weight: bold; font-style: italic; diff --git a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js index 9a28a09..5866ee8 100644 --- a/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js +++ b/layers-editor/src/main/resources/nfms/modules/layers-editor-forms.js @@ -8,6 +8,8 @@ define(["layers-json", "layers-schema", "jquery", "jquery-ui"], function(layers, var wmsValues = layers.getWmsLayer(portalValues.layers[0]); addWmsLayerFields(form, wmsValues); + + getFormValues(form); } function editGroup(id) { @@ -15,6 +17,8 @@ define(["layers-json", "layers-schema", "jquery", "jquery-ui"], function(layers, var values = layers.getGroup(id); addTocFields(form, values); + + getFormValues(form); } function createForm(title) { @@ -36,35 +40,37 @@ define(["layers-json", "layers-schema", "jquery", "jquery-ui"], function(layers, function addTocFields(form, values) { var properties = schema.definitions.toc.properties; - addFields("Layer Switcher Entry", form, properties, values); + addFields("Layer Tree Item", "toc", form, properties, values); } function addPortalLayerFields(form, values) { addTocFields(form, values); var properties = schema.definitions.portalLayer.allOf[1].properties; delete properties.layers; - addFields("Portal Layer", form, properties, values); + addFields("Portal Layer", "portalLayer", form, properties, values); } function addWmsLayerFields(form, values) { var properties = schema.definitions["wmsLayer-base"].properties; - addFields("Pseudo-WMS Layer", form, properties, values); + addFields("Pseudo-WMS Layer", "wmsLayer-base", form, properties, values); if (properties.type && properties.type == "osm") { properties = schema.definitions["wmsLayer-osmType"].allOf[1].properties; - addFields("OSM Layer", form, properties, values); + delete properties.type; + addFields("OSM Layer", "wmsLayer-osmType", form, properties, values); } else if (properties.type && properties.type == "gmaps") { properties = schema.definitions["wmsLayer-gmapsType"].allOf[1].properties; - addFields("Google Maps Layer", form, properties, values); + delete properties.type; + addFields("Google Maps Layer", "wmsLayer-gmapsType", form, properties, values); } else { properties = schema.definitions["wmsLayer-wmsType"].allOf[1].properties; delete properties.type; - addFields("True-WMS Layer", form, properties, values); + addFields("True-WMS Layer", "wmsLayer-wmsType", form, properties, values); } } - function addFields(title, form, properties, values) { - var fieldset = $("
").appendTo(form); + function addFields(title, id, form, properties, values) { + var fieldset = $("
").addClass(id).appendTo(form); $("").text(title).appendTo(fieldset); for (var i in properties) { @@ -80,29 +86,69 @@ define(["layers-json", "layers-schema", "jquery", "jquery-ui"], function(layers, var label = $("