Merge remote-tracking branch 'origin/sleroy'
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 4 Nov 2020 08:00:22 +0000 (09:00 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 4 Nov 2020 08:00:22 +0000 (09:00 +0100)
46 files changed:
dep/org.argeo.suite.dep.ui.rap/pom.xml
knowledge/org.argeo.support.geonames/.classpath [new file with mode: 0644]
knowledge/org.argeo.support.geonames/.gitignore [new file with mode: 0644]
knowledge/org.argeo.support.geonames/.project [new file with mode: 0644]
knowledge/org.argeo.support.geonames/META-INF/.gitignore [new file with mode: 0644]
knowledge/org.argeo.support.geonames/bnd.bnd [new file with mode: 0644]
knowledge/org.argeo.support.geonames/build.properties [new file with mode: 0644]
knowledge/org.argeo.support.geonames/pom.xml [new file with mode: 0644]
knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/GeonamesAdm.java [new file with mode: 0644]
knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/ImportGeonamesAdmin.java [new file with mode: 0644]
knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml
knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml
knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml
knowledge/org.argeo.support.odk/bnd.bnd
knowledge/org.argeo.support.odk/pom.xml
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkNames.java [new file with mode: 0644]
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java [new file with mode: 0644]
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd [new file with mode: 0644]
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java
knowledge/org.argeo.support.xforms/.classpath [new file with mode: 0644]
knowledge/org.argeo.support.xforms/.gitignore [new file with mode: 0644]
knowledge/org.argeo.support.xforms/.project [new file with mode: 0644]
knowledge/org.argeo.support.xforms/META-INF/.gitignore [new file with mode: 0644]
knowledge/org.argeo.support.xforms/bnd.bnd [new file with mode: 0644]
knowledge/org.argeo.support.xforms/build.properties [new file with mode: 0644]
knowledge/org.argeo.support.xforms/pom.xml [new file with mode: 0644]
knowledge/org.argeo.support.xforms/src/org/argeo/support/xforms/xforms.cnd [new file with mode: 0644]
knowledge/org.argeo.support.xforms/xsd/XForms-Schema.xsd [new file with mode: 0644]
knowledge/pom.xml
org.argeo.entity.api/bnd.bnd
org.argeo.entity.api/src/org/argeo/entity/EntityNames.java
org.argeo.entity.api/src/org/argeo/entity/EntityType.java [new file with mode: 0644]
org.argeo.entity.api/src/org/argeo/entity/EntityTypes.java
org.argeo.entity.api/src/org/argeo/entity/JcrName.java [new file with mode: 0644]
org.argeo.entity.api/src/org/argeo/entity/entity.cnd
org.argeo.entity.core/src/org/argeo/entity/core/JcrEntityDefinition.java
org.argeo.suite.core/.project
org.argeo.suite.core/OSGI-INF/maintenanceService.xml [new file with mode: 0644]
org.argeo.suite.core/bnd.bnd
org.argeo.suite.core/build.properties
org.argeo.suite.core/pom.xml
org.argeo.suite.core/src/org/argeo/suite/RankedObject.java
org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java [new file with mode: 0644]

index fa251f4f4448c8a3e3ac8ea59f82be5af0ad2684..8cba39e20b5eb14098268262c02616f3604e5764 100644 (file)
                </dependency>
 
                <!-- Argeo Knowledge -->
+               <dependency>
+                       <groupId>org.argeo.suite</groupId>
+                       <artifactId>org.argeo.support.xforms</artifactId>
+                       <version>2.1.16-SNAPSHOT</version>
+               </dependency>
                <dependency>
                        <groupId>org.argeo.suite</groupId>
                        <artifactId>org.argeo.support.odk</artifactId>
                        <version>2.1.16-SNAPSHOT</version>
                </dependency>
+               <dependency>
+                       <groupId>org.argeo.suite</groupId>
+                       <artifactId>org.argeo.support.geonames</artifactId>
+                       <version>2.1.16-SNAPSHOT</version>
+               </dependency>
 
                <!-- Base CMS distribution -->
                <dependency>
diff --git a/knowledge/org.argeo.support.geonames/.classpath b/knowledge/org.argeo.support.geonames/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/knowledge/org.argeo.support.geonames/.gitignore b/knowledge/org.argeo.support.geonames/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/knowledge/org.argeo.support.geonames/.project b/knowledge/org.argeo.support.geonames/.project
new file mode 100644 (file)
index 0000000..3c8181f
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.support.geonames</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/knowledge/org.argeo.support.geonames/META-INF/.gitignore b/knowledge/org.argeo.support.geonames/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/knowledge/org.argeo.support.geonames/bnd.bnd b/knowledge/org.argeo.support.geonames/bnd.bnd
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/knowledge/org.argeo.support.geonames/build.properties b/knowledge/org.argeo.support.geonames/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/knowledge/org.argeo.support.geonames/pom.xml b/knowledge/org.argeo.support.geonames/pom.xml
new file mode 100644 (file)
index 0000000..83244c2
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.suite</groupId>
+               <artifactId>knowledge</artifactId>
+               <version>2.1.16-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.support.geonames</artifactId>
+       <name>Geonames support</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms</artifactId>
+                       <version>${version.argeo-commons}</version>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/GeonamesAdm.java b/knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/GeonamesAdm.java
new file mode 100644 (file)
index 0000000..d578272
--- /dev/null
@@ -0,0 +1,157 @@
+package org.argeo.support.geonames;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+/** A Geonames administrative subdivision. */
+public class GeonamesAdm {
+       private final Long geonameId;
+       private final String countryCode;
+       private final String adminCode1;
+       private final String admLevel;
+       private final Integer level;
+       private final String name;
+       private final String asciiName;
+       private final List<String> alternateNames;
+       private final Double lat;
+       private final Double lng;
+       private final LocalDate lastUpdated;
+       private final ZoneId timeZone;
+
+       private final Long[] upperLevelIds = new Long[5];
+       private final List<GeonamesAdm> upperLevels = new ArrayList<>();
+
+       private List<String> row;
+
+       /** Initialise from a row in the main Geonames table. */
+       public GeonamesAdm(List<String> row) {
+               geonameId = Long.parseLong(row.get(0));
+               admLevel = row.get(7);
+               countryCode = row.get(8);
+               adminCode1 = row.get(10);
+               if (admLevel.startsWith("ADM")) {
+                       if (admLevel.endsWith("H"))
+                               level = Integer.parseInt(admLevel.substring(3, admLevel.length() - 1));
+                       else
+                               level = Integer.parseInt(admLevel.substring(3));
+               } else if (admLevel.equals("PCLI")) {
+                       level = 0;
+               } else {
+                       throw new IllegalArgumentException("Unsupported admin level " + admLevel);
+               }
+               name = row.get(1);
+               asciiName = row.get(2);
+               alternateNames = Arrays.asList(row.get(3).split(","));
+               lat = Double.parseDouble(row.get(4));
+               lng = Double.parseDouble(row.get(5));
+               lastUpdated = LocalDate.parse(row.get(18));
+               timeZone = ZoneId.of(row.get(17));
+               // upper levels
+               if (row.get(11) != null && !row.get(11).trim().equals(""))
+                       upperLevelIds[2] = Long.parseLong(row.get(11));
+               if (row.get(12) != null && !row.get(12).trim().equals(""))
+                       upperLevelIds[3] = Long.parseLong(row.get(12));
+               if (row.get(13) != null && !row.get(13).trim().equals(""))
+                       upperLevelIds[4] = Long.parseLong(row.get(13));
+               this.row = row;
+       }
+
+       public void mapUpperLevels(Map<Long, GeonamesAdm> index) {
+               for (int i = 0; i < level; i++) {
+                       Long geonameId = upperLevelIds[i];
+                       upperLevels.add(i, index.get(geonameId));
+               }
+       }
+
+       public Long getGeonameId() {
+               return geonameId;
+       }
+
+       public Integer getLevel() {
+               return level;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public String getName(Function<String, String> transform) {
+               if (transform != null)
+                       return transform.apply(name);
+               else
+                       return name;
+
+       }
+
+       public String getAsciiName() {
+               return asciiName;
+       }
+
+       public List<String> getAlternateNames() {
+               return alternateNames;
+       }
+
+       public Double getLat() {
+               return lat;
+       }
+
+       public Double getLng() {
+               return lng;
+       }
+
+       public String getCountryCode() {
+               return countryCode;
+       }
+
+       public String getAdmLevel() {
+               return admLevel;
+       }
+
+       public List<String> getRow() {
+               return row;
+       }
+
+       public LocalDate getLastUpdated() {
+               return lastUpdated;
+       }
+
+       public ZoneId getTimeZone() {
+               return timeZone;
+       }
+
+       public String getAdminCode1() {
+               return adminCode1;
+       }
+
+       public Long[] getUpperLevelIds() {
+               return upperLevelIds;
+       }
+
+       public List<GeonamesAdm> getUpperLevels() {
+               return upperLevels;
+       }
+
+       @Override
+       public int hashCode() {
+               return geonameId.intValue();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof GeonamesAdm))
+                       return false;
+               GeonamesAdm other = (GeonamesAdm) obj;
+               return geonameId.equals(other.geonameId);
+       }
+
+       @Override
+       public String toString() {
+               return name + " (ADM" + level + " " + geonameId + ")";
+       }
+
+}
diff --git a/knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/ImportGeonamesAdmin.java b/knowledge/org.argeo.support.geonames/src/org/argeo/support/geonames/ImportGeonamesAdmin.java
new file mode 100644 (file)
index 0000000..9af5987
--- /dev/null
@@ -0,0 +1,93 @@
+package org.argeo.support.geonames;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.util.CsvParser;
+import org.argeo.util.CsvWriter;
+
+/** Import GeoNames administrative division from the main table. */
+public class ImportGeonamesAdmin {
+       // private Log log = LogFactory.getLog(ImportGeonamesAdmin.class);
+       private Map<Long, GeonamesAdm> geonamesAdms = new HashMap<>();
+
+       /** Loads the data. */
+       public void parse(InputStream in) {
+               Map<String, Long> countryGeonameIds = new HashMap<>();
+               Map<String, Long> admin1GeonameIds = new HashMap<>();
+               CsvParser csvParser = new CsvParser() {
+
+                       @Override
+                       protected void processLine(Integer lineNumber, List<String> header, List<String> tokens) {
+                               if (!"A".equals(tokens.get(6)))
+                                       return;
+                               GeonamesAdm geonamesAdm = new GeonamesAdm(tokens);
+                               geonamesAdms.put(geonamesAdm.getGeonameId(), geonamesAdm);
+                               if (geonamesAdm.getAdmLevel().equals("PCLI"))
+                                       countryGeonameIds.put(geonamesAdm.getCountryCode(), geonamesAdm.getGeonameId());
+                               if (geonamesAdm.getAdmLevel().equals("ADM1"))
+                                       admin1GeonameIds.put(geonamesAdm.getAdminCode1(), geonamesAdm.getGeonameId());
+                       }
+               };
+               csvParser.setSeparator('\t');
+               csvParser.setNoHeader(true);
+               csvParser.parse(in, StandardCharsets.UTF_8);
+
+               // fill upper levels
+               for (GeonamesAdm adm : geonamesAdms.values()) {
+                       adm.getUpperLevelIds()[0] = countryGeonameIds.get(adm.getCountryCode());
+                       if (adm.getLevel() > 0)
+                               adm.getUpperLevelIds()[1] = admin1GeonameIds.get(adm.getAdminCode1());
+                       adm.mapUpperLevels(geonamesAdms);
+               }
+
+       }
+
+       public Map<Long, GeonamesAdm> getGeonamesAdms() {
+               return geonamesAdms;
+       }
+
+       /**
+        * Copies only the Geonames of feature class 'A' (administrative subdivisions).
+        */
+       public static void filterGeonamesAdm(InputStream in, OutputStream out) {
+               CsvWriter csvWriter = new CsvWriter(out, StandardCharsets.UTF_8);
+               csvWriter.setSeparator('\t');
+               CsvParser csvParser = new CsvParser() {
+
+                       @Override
+                       protected void processLine(Integer lineNumber, List<String> header, List<String> tokens) {
+                               if (tokens.size() < 7 || !"A".equals(tokens.get(6)))
+                                       return;
+                               csvWriter.writeLine(tokens);
+                       }
+               };
+               csvParser.setSeparator('\t');
+               csvParser.setNoHeader(true);
+               csvParser.parse(in, StandardCharsets.UTF_8);
+       }
+
+       public static void main(String[] args) throws IOException {
+//             String country = "allCountries";
+               String country = "CI";
+//             try (InputStream in = Files
+//                             .newInputStream(Paths.get(System.getProperty("user.home") + "/gis/data/geonames/" + country + ".txt"));
+//                             OutputStream out = Files.newOutputStream(
+//                                             Paths.get(System.getProperty("user.home") + "/gis/data/geonames/" + country + "-adm.txt"))) {
+//                     ImportGeonamesAdmin.filterGeonamesAdm(in, out);
+//             }
+               try (InputStream in = Files.newInputStream(
+                               Paths.get(System.getProperty("user.home") + "/gis/data/geonames/" + country + "-adm.txt"))) {
+                       ImportGeonamesAdmin importGeonamesAdmin = new ImportGeonamesAdmin();
+                       importGeonamesAdmin.parse(in);
+               }
+       }
+
+}
index 852423b16a199c9103172e916433e691645f6a19..e290933fd9bfd91701dc9ef685a5c6262ae1c2fe 100644 (file)
@@ -7,4 +7,5 @@
    <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/formList"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <reference bind="addForm" cardinality="0..n" interface="org.argeo.support.odk.OdkForm" name="OdkForm" policy="dynamic" unbind="removeForm"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index 59d2de217fc8180378ccc2cbcb808df39547bc8c..7f7e4dbfdf64076913ab875470a96b0e86ef6024 100644 (file)
@@ -4,7 +4,8 @@
    <service>
       <provide interface="javax.servlet.Servlet"/>
    </service>
-   <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/*.xml"/>
+   <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/form/*"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <reference bind="addForm" cardinality="0..n" interface="org.argeo.support.odk.OdkForm" name="OdkForm" policy="dynamic" unbind="removeForm"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index e9153b4b9f0b5788bca4dc693f0366e59c693583..1e1bcb1277143346cead26d6d7f73fbc64ae429d 100644 (file)
@@ -7,4 +7,5 @@
    <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/submission"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <property name="osgi.http.whiteboard.servlet.multipart.enabled" type="String" value="true"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index 9db4e0c84b83475b492f88a7272780c50e0cd3e6..01549855f63d922168494419e640589cc00d0397 100644 (file)
@@ -1,9 +1,17 @@
-Import-Package:\
-org.osgi.service.http.context,\
-*
+Require-Capability:\
+cms.datamodel;filter:="(name=entity)",\
+cms.datamodel;filter:="(name=xforms)"
+
+Provide-Capability:\
+cms.datamodel; name=odk; cnd=/org/argeo/support/odk/odk.cnd
 
 Service-Component:\
 OSGI-INF/odkServletContext.xml,\
 OSGI-INF/odkFormListServlet.xml,\
 OSGI-INF/odkFormServlet.xml,\
 OSGI-INF/odkSubmissionServlet.xml
+
+Import-Package:\
+org.osgi.service.http.context,\
+org.argeo.api,\
+*
index 26177c91d3ad6278346f8db2a0376da3591c2306..69cc95e7c4827d1ae5dfaaa7e938c6585e463a2d 100644 (file)
@@ -14,9 +14,9 @@
        <packaging>jar</packaging>
        <dependencies>
                <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms</artifactId>
-                       <version>${version.argeo-commons}</version>
+                       <groupId>org.argeo.suite</groupId>
+                       <artifactId>org.argeo.suite.core</artifactId>
+                       <version>2.1.16-SNAPSHOT</version>
                </dependency>
        </dependencies>
 </project>
diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkNames.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkNames.java
new file mode 100644 (file)
index 0000000..fbb2087
--- /dev/null
@@ -0,0 +1,7 @@
+package org.argeo.support.odk;
+
+/** Names related to ODK. */
+public interface OdkNames {
+
+       public final static String H_HTML = "h:html";
+}
diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java
new file mode 100644 (file)
index 0000000..c51484c
--- /dev/null
@@ -0,0 +1,27 @@
+package org.argeo.support.odk;
+
+import org.argeo.entity.JcrName;
+
+/** Types related to the http://openrosa.org/xforms/xformsList namespace. */
+public enum OrxListType implements JcrName {
+       xform;
+
+       @Override
+       public String getPrefix() {
+               return prefix();
+       }
+
+       public static String prefix() {
+               return "orxList";
+       }
+
+       @Override
+       public String getNamespace() {
+               return namespace();
+       }
+
+       public static String namespace() {
+               return "http://openrosa.org/xforms/xformsList";
+       }
+
+}
diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd
new file mode 100644 (file)
index 0000000..0f6fcc8
--- /dev/null
@@ -0,0 +1,39 @@
+<jr = "http://openrosa.org/javarosa">
+<orx = "http://openrosa.org/xforms">
+<orxList = "http://openrosa.org/xforms/xformsList">
+<orxManifest = "http://openrosa.org/xforms/xformsManifest">
+<odk = "http://www.opendatakit.org/xforms">
+
+[orxList:xform] > mix:created, mix:lastModified, jcrx:csum, entity:form
++ h:html (odk:html) = odk:html
+
+[odk:head]
++ h:title (jcrx:xmlvalue) = jcrx:xmlvalue
++ xforms:model (odk:model) = odk:model
+
+[odk:body] > xforms:ui
+
+
+[odk:html] > mix:referenceable
++ h:head (odk:head) = odk:head
++ h:body (odk:body) = odk:body
+
+[odk:model] > xforms:model
++ odk:setgeopoint (odk:setgeopoint) = odk:setgeopoint
++ xforms:itext (odk:itext) = odk:itext
+
+[odk:itext]
++ xforms:translation (odk:translation) = odk:translation *
+
+[odk:translation]
+- lang (STRING) m
+- default (STRING)
++ xforms:text (odk:text) = odk:text *
+
+[odk:text]
+- id (STRING) m
++ xforms:value (jcrx:xmlvalue) = jcrx:xmlvalue
+
+[odk:setgeopoint]
+- event (STRING)
+- ref (STRING)
index 8398c7fa54769a3110aa6eaac54a7dbb7554b8a2..c8a66bf14a806a2c20899a5e9c4f9735fcd8686c 100644 (file)
@@ -2,10 +2,23 @@ package org.argeo.support.odk.servlet;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.security.AccessControlContext;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.security.auth.Subject;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -13,7 +26,14 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.api.NodeConstants;
+import org.argeo.cms.servlet.ServletAuthUtils;
+import org.argeo.entity.EntityType;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.JcrxApi;
 import org.argeo.support.odk.OdkForm;
+import org.argeo.support.odk.OrxListType;
 
 /** Lists available forms. */
 public class OdkFormListServlet extends HttpServlet {
@@ -22,6 +42,11 @@ public class OdkFormListServlet extends HttpServlet {
 
        private Set<OdkForm> odkForms = Collections.synchronizedSet(new HashSet<>());
 
+       private DateTimeFormatter versionFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmm")
+                       .withZone(ZoneId.from(ZoneOffset.UTC));
+
+       private Repository repository;
+
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.setContentType("text/xml; charset=utf-8");
@@ -32,26 +57,100 @@ public class OdkFormListServlet extends HttpServlet {
                int serverPort = req.getServerPort();
                String protocol = serverPort == 443 || req.isSecure() ? "https" : "http";
 
+               String pathInfo = req.getPathInfo();
+
+               Subject subject = Subject
+                               .getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName()));
+               log.debug("SERVLET CONTEXT: " + subject);
+
+               Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, NodeConstants.SYS_WORKSPACE), req);
+//             session = NodeUtils.openDataAdminSession(repository, NodeConstants.SYS_WORKSPACE);
                Writer writer = resp.getWriter();
                writer.append("<?xml version='1.0' encoding='UTF-8' ?>");
                writer.append("<xforms xmlns=\"http://openrosa.org/xforms/xformsList\">");
-               for (OdkForm form : odkForms) {
-                       StringBuilder sb = new StringBuilder();
-                       sb.append("<xform>");
-                       sb.append("<formID>" + form.getFormId() + "</formID>");
-                       sb.append("<name>" + form.getName() + "</name>");
-                       sb.append("<version>" + form.getVersion() + "</version>");
-                       sb.append("<hash>" + form.getHash(null) + "</hash>");
-                       sb.append("<descriptionText>" + form.getDescription() + "</descriptionText>");
-                       sb.append("<downloadUrl>" + protocol + "://" + serverName
-                                       + (serverPort == 80
-                                                       || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/" + form.getFileName()
-                                       + "</downloadUrl>");
-                       sb.append("</xform>");
-                       String str = sb.toString();
-                       if (log.isDebugEnabled())
-                               log.debug(str);
-                       writer.append(str);
+               boolean oldApproach = false;
+               if (oldApproach) {
+                       try {
+
+                               Query query;
+                               if (pathInfo == null) {
+//                             query = session.getWorkspace().getQueryManager()
+//                                             .createQuery("SELECT * FROM [nt:unstructured]", Query.JCR_SQL2);
+                                       query = session.getWorkspace().getQueryManager()
+                                                       .createQuery("SELECT * FROM [" + OrxListType.xform.get() + "]", Query.JCR_SQL2);
+                               } else {
+                                       query = session.getWorkspace().getQueryManager()
+                                                       .createQuery(
+                                                                       "SELECT node FROM [" + OrxListType.xform.get()
+                                                                                       + "] AS node WHERE ISDESCENDANTNODE (node, '" + pathInfo + "')",
+                                                                       Query.JCR_SQL2);
+                               }
+                               QueryResult queryResult = query.execute();
+
+                               NodeIterator nit = queryResult.getNodes();
+//                             log.debug(session.getUserID());
+//                             log.debug(session.getWorkspace().getName());
+//                             NodeIterator nit = session.getRootNode().getNodes();
+//                             while (nit.hasNext()) {
+//                                     log.debug(nit.nextNode());
+//                             }
+                               while (nit.hasNext()) {
+                                       StringBuilder sb = new StringBuilder();
+                                       Node node = nit.nextNode();
+                                       if (node.isNodeType(OrxListType.xform.get())) {
+                                               sb.append("<xform>");
+                                               sb.append("<formID>" + node.getNode("h:html").getIdentifier() + "</formID>");
+                                               sb.append("<name>" + Jcr.getTitle(node) + "</name>");
+                                               sb.append("<version>" + versionFormatter.format(JcrUtils.getModified(node)) + "</version>");
+                                               sb.append("<hash>md5:" + JcrxApi.getChecksum(node, JcrxApi.MD5) + "</hash>");
+                                               if (node.hasProperty(Property.JCR_DESCRIPTION))
+                                                       sb.append("<name>" + node.getProperty(Property.JCR_DESCRIPTION).getString() + "</name>");
+                                               sb.append("<downloadUrl>" + protocol + "://" + serverName
+                                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/"
+                                                               + node.getPath() + "</downloadUrl>");
+                                               sb.append("</xform>");
+                                       } else if (node.isNodeType(EntityType.formSet.get())) {
+                                               sb.append("<xforms-group>");
+                                               sb.append("<groupId>" + node.getPath() + "</groupId>");
+                                               sb.append("<name>" + node.getProperty(Property.JCR_TITLE).getString() + "</name>");
+                                               sb.append("<listUrl>" + protocol + "://" + serverName
+                                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/formList"
+                                                               + node.getPath() + "</listUrl>");
+                                               sb.append("</xforms-group>");
+                                       }
+                                       String str = sb.toString();
+                                       if (!str.equals("")) {
+                                               if (log.isDebugEnabled())
+                                                       log.debug(str);
+                                               writer.append(str);
+                                       }
+                               }
+                       } catch (RepositoryException e) {
+                               e.printStackTrace();
+                               // TODO error message
+                               // resp.sendError(500);
+                       } finally {
+                               Jcr.logout(session);
+                       }
+
+               } else {
+                       for (OdkForm form : odkForms) {
+                               StringBuilder sb = new StringBuilder();
+                               sb.append("<xform>");
+                               sb.append("<formID>" + form.getFormId() + "</formID>");
+                               sb.append("<name>" + form.getName() + "</name>");
+                               sb.append("<version>" + form.getVersion() + "</version>");
+                               sb.append("<hash>" + form.getHash(null) + "</hash>");
+                               sb.append("<descriptionText>" + form.getDescription() + "</descriptionText>");
+                               sb.append("<downloadUrl>" + protocol + "://" + serverName
+                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/"
+                                               + form.getFileName() + "</downloadUrl>");
+                               sb.append("</xform>");
+                               String str = sb.toString();
+                               if (log.isDebugEnabled())
+                                       log.debug(str);
+                               writer.append(str);
+                       }
                }
                writer.append("</xforms>");
        }
@@ -63,4 +162,9 @@ public class OdkFormListServlet extends HttpServlet {
        public void removeForm(OdkForm odkForm) {
                odkForms.remove(odkForm);
        }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
index 3746da852aaefbb0826cc32673a40fec0b53266d..301e1bb433d9bcc52c8768829378677d6416e831 100644 (file)
@@ -7,35 +7,59 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.io.FilenameUtils;
+import org.argeo.cms.servlet.ServletAuthUtils;
+import org.argeo.jcr.Jcr;
 import org.argeo.support.odk.OdkForm;
+import org.argeo.support.odk.OdkNames;
 
 /** Retrieves a single form. */
 public class OdkFormServlet extends HttpServlet {
        private static final long serialVersionUID = 7838305967987687370L;
 
+       private Repository repository;
        private Map<String, OdkForm> odkForms = Collections.synchronizedMap(new HashMap<>());
 
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.setContentType("text/xml; charset=utf-8");
 
-               String path = req.getServletPath();
-               String fileName = FilenameUtils.getName(path);
-               OdkForm form = odkForms.get(fileName);
-               if (form == null)
-                       throw new IllegalArgumentException("No form named " + fileName + " was found");
-
-               byte[] buffer = new byte[1024];
-               try (InputStream in = form.openStream(); OutputStream out = resp.getOutputStream();) {
-                       int bytesRead;
-                       while ((bytesRead = in.read(buffer)) != -1)
-                               out.write(buffer, 0, bytesRead);
+               Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, null), req);
+
+               String pathInfo = req.getPathInfo();
+
+               boolean oldApproach = false;
+               try {
+                       if (!oldApproach) {
+                               session.exportDocumentView(pathInfo + "/" + OdkNames.H_HTML, resp.getOutputStream(), true, false);
+                       } else {
+
+                               String fileName = FilenameUtils.getName(pathInfo);
+                               OdkForm form = odkForms.get(fileName);
+                               if (form == null)
+                                       throw new IllegalArgumentException("No form named " + fileName + " was found");
+
+                               byte[] buffer = new byte[1024];
+                               try (InputStream in = form.openStream(); OutputStream out = resp.getOutputStream();) {
+                                       int bytesRead;
+                                       while ((bytesRead = in.read(buffer)) != -1)
+                                               out.write(buffer, 0, bytesRead);
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       e.printStackTrace();
+                       // TODO error message
+                       resp.sendError(500);
+               } finally {
+                       Jcr.logout(session);
                }
        }
 
@@ -47,4 +71,8 @@ public class OdkFormServlet extends HttpServlet {
                odkForms.remove(odkForm.getFileName());
        }
 
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
index 2c37edccbdf99985273f58c3396de9a681eeed05..1711f2dfa6e7599a62fe5d8bcbe52d37a635d1e9 100644 (file)
@@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.argeo.cms.servlet.PrivateWwwAuthServletContext;
 
+/** OSK specific authentication (with additional headers).*/
 public class OdkServletContext extends PrivateWwwAuthServletContext {
 
        @Override
index 3a975804b69ccf8c6781329fb3adff5ede6071a9..e5f5a3ffa6310cda67599c417f98d6b5f94246fa 100644 (file)
@@ -4,6 +4,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
 
+import javax.jcr.Repository;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -26,6 +27,8 @@ public class OdkSubmissionServlet extends HttpServlet {
 
        private final static String XML_SUBMISSION_FILE = "xml_submission_file";
 
+       private Repository repository;
+
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                for (Part part : req.getParts()) {
@@ -58,4 +61,9 @@ public class OdkSubmissionServlet extends HttpServlet {
                                + "<message>Form Received!</message>" + "</OpenRosaResponse>");
 
        }
+       
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
diff --git a/knowledge/org.argeo.support.xforms/.classpath b/knowledge/org.argeo.support.xforms/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/knowledge/org.argeo.support.xforms/.gitignore b/knowledge/org.argeo.support.xforms/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/knowledge/org.argeo.support.xforms/.project b/knowledge/org.argeo.support.xforms/.project
new file mode 100644 (file)
index 0000000..3c50bfa
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.support.xforms</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/knowledge/org.argeo.support.xforms/META-INF/.gitignore b/knowledge/org.argeo.support.xforms/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/knowledge/org.argeo.support.xforms/bnd.bnd b/knowledge/org.argeo.support.xforms/bnd.bnd
new file mode 100644 (file)
index 0000000..23c86a9
--- /dev/null
@@ -0,0 +1,5 @@
+Require-Capability:\
+cms.datamodel;filter:="(name=entity)"
+
+Provide-Capability:\
+cms.datamodel; name=xforms; cnd=/org/argeo/support/xforms/xforms.cnd
diff --git a/knowledge/org.argeo.support.xforms/build.properties b/knowledge/org.argeo.support.xforms/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/knowledge/org.argeo.support.xforms/pom.xml b/knowledge/org.argeo.support.xforms/pom.xml
new file mode 100644 (file)
index 0000000..0d75c9a
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.suite</groupId>
+               <artifactId>knowledge</artifactId>
+               <version>2.1.16-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.support.xforms</artifactId>
+       <name>XForms support</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.suite</groupId>
+                       <artifactId>org.argeo.suite.core</artifactId>
+                       <version>2.1.16-SNAPSHOT</version>
+               </dependency>
+       </dependencies>
+</project>
diff --git a/knowledge/org.argeo.support.xforms/src/org/argeo/support/xforms/xforms.cnd b/knowledge/org.argeo.support.xforms/src/org/argeo/support/xforms/xforms.cnd
new file mode 100644 (file)
index 0000000..c24dbae
--- /dev/null
@@ -0,0 +1,39 @@
+<xforms = "http://www.w3.org/2002/xforms">
+
+[xforms:model]
++ xforms:instance (xforms:instance) = xforms:instance *
++ xforms:bind (xforms:bind) = xforms:bind *
++ xforms:setvalue (xforms:setvalue) = xforms:setvalue *
+
+[xforms:instance] > nt:unstructured
+
+[xforms:bind]
+- * (STRING)
+
+[xforms:setvalue]
+- * (STRING)
+
+[xforms:select] > xforms:input
++ xforms:itemset (xforms:itemset) = xforms:itemset
+
+[xforms:itemset]
+- nodeset (STRING)
++ xforms:label (jcrx:xmlvalue) = jcrx:xmlvalue
++ xforms:value (jcrx:xmlvalue) = jcrx:xmlvalue
+
+[xforms:ui]
+- * (STRING)
++ xforms:label (jcrx:xmlvalue) = jcrx:xmlvalue *
++ xforms:hint (jcrx:xmlvalue) = jcrx:xmlvalue *
++ xforms:input (xforms:input) = xforms:input *
++ xforms:select (xforms:select) = xforms:select *
++ xforms:select1 (xforms:select) = xforms:select *
++ xforms:trigger (xforms:input) = xforms:input *
++ xforms:upload (xforms:input) = xforms:input *
++ xforms:group (xforms:ui) = xforms:ui *
++ xforms:repeat (xforms:ui) = xforms:ui *
+
+[xforms:input]
+- * (STRING)
++ xforms:label (jcrx:xmlvalue) = jcrx:xmlvalue
++ xforms:hint (jcrx:xmlvalue) = jcrx:xmlvalue *
diff --git a/knowledge/org.argeo.support.xforms/xsd/XForms-Schema.xsd b/knowledge/org.argeo.support.xforms/xsd/XForms-Schema.xsd
new file mode 100644 (file)
index 0000000..6383f86
--- /dev/null
@@ -0,0 +1,858 @@
+<xsd:schema targetNamespace="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" elementFormDefault="qualified">
+ <!--
+  Changes:
+26-Aug MJD fixed typo where more than one child allowed on <instance>
+04-Sep MJD fixed typo on <send> : attribute 'submission' is required
+04-Sep MJD fixed typo on <rebuild><recalculate><revalidate><refresh>: 'model' attribute is required
+06-Sep MJD clarified specific allowed values of @level on <message>
+06-Sep MJD removed UI Common attributes from <help><hint><alert><label>
+09-Sep MJD changed minOccurrs and maxOccurs to use XPath expressions, default values
+09-Sep MJD fixed typo: added linking attributes to <message>
+09-Sep MJD removed <extension> from content models of <mode> and UI common elements
+09-Sep MJD fixed typo: removed 'format' attribute
+17-Sep MJD fixed typo: <output> now uses the attribute group for binding attributes, instead of similarly named individual attrs
+17-Sep MJD added XPathExpression simpleType for internal use. This doesn't actually change anything, but makes
+the Schema a better documentation resource (instead of using xsd:string for everything)
+17-Sep MJD removed 'mediatype' attribute from <submission>, as it was unused
+17-Sep MJD fixed typo: only 'ref' and 'bind' attributes, not 'model' on <submission>
+17-Sep MJD added XML Events attributes to <model>
+17-Sep MJD in accordance with 3.2.1, removed all id attributes
+19-Sep MJD fixed typo: clarified that nested <action>s are permitted.
+19-Sep MJD factor UI.Inline into <group>. Renamed <group>s and <attributeGroup>s to match the prose names
+19-Sep MJD changed to agreed-upon namespace for CR
+25-Sep MJD fixed typo: added UI.Inlne to content model of <message>, enabled mixed content
+29-Sep MJD fixed typo: 'model' required on <reset>
+29-Sep MJD fixed typo: binding attributes allowed on <submit>
+29-Sep MJD fixed typo: explicit enumerated values for 'show' on <load>
+04-Oct MJD 'resource' attribute not required
+
+12-Nov 2002 : Published as CR
+
+13-Jan MJD added new attribute includenamespaceprefixes on <submission>
+13-Jan MJD added UI Common elements to content model of <group>
+03-Feb MJD synchoninzed duration types with 15 Nov Query Operators document
+31-Mar MJD added mediatype attribute on <submission>
+14-May MJD typo : "xsd:NCName"
+26-Jun MJD removed 'accesskey' and 'navindex' (over to host language definition)
+
+01-Aug 2003 : Published as PR
+
+15-Sep MJD final namespace
+15-Sep MJD corrected content model of <value>
+15-Sep MJD changed the name of the import for XML Events to highlight that only the attributes are used
+
+1.0 Second Edition errata
+
+16-Apr 2005 RAM - erratum E4 - optional @model
+16-Apr 2005 RAM - erratum E22 - default value for @show
+16-Apr 2005 RAM - erratum E54 - remove xforms:minOccurs and xforms:maxOccurs
+26-Jun 2005 RAM - erratum E71 - allow an empty case element
+
+16-Jun 2006 JMB - erratum E69 - instance attribute in submission; id in common attributes
+
+15-Aug-2006 CFW - erratum E18 on 2nd ed. Added Action to content model for Case
+
+09-Sep 2006 JMB - non-substantive: explicitly declared some use="optional" settings, 
+                  substantive: erratum E18 on 2nd ed. Declared default false for selected attribute of case
+                  substantive: erratum E21 on 2nd ed. Added multipart-post to enumeration of method attribute
+23-Nov 2006 JMB - substantive: erratum E32 on 2nd ed. switch in repeat
+17-Jul 2007 JMB - substantive: erratum E41 on 2nd ed. version attribute and associated simple types
+-->
+ <xsd:import namespace="http://www.w3.org/2001/xml-events" schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xml-events-attribs-1.xsd"/>
+ <xsd:import namespace="http://www.w3.org/2001/XMLSchema" schemaLocation="http://www.w3.org/2001/XMLSchema.xsd"/>
+ <!--
+structural elements
+-->
+ <xsd:attributeGroup name="Common.Attributes">
+  <xsd:annotation>
+   <xsd:documentation>Attributes for _every_ element in XForms</xsd:documentation>
+  </xsd:annotation>
+                <xsd:attribute name="id" type="xsd:ID" use="optional"/>  
+  <xsd:anyAttribute namespace="##other"/>
+ </xsd:attributeGroup>
+ <xsd:element name="model">
+  <xsd:complexType>
+   <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+    <xsd:choice>
+     <xsd:element ref="xforms:instance"/>
+     <xsd:element ref="xsd:schema"/>
+     <xsd:element ref="xforms:submission"/>
+     <xsd:element ref="xforms:bind"/>
+     <xsd:group ref="xforms:Action"/>
+    </xsd:choice>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <xsd:attribute name="functions" type="xforms:QNameList" use="optional"/>
+   <xsd:attribute name="schema" type="xforms:anyURIList" use="optional"/>
+   <xsd:attribute name="version" type="xforms:versionList" use="optional"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="instance">
+  <xsd:annotation>
+   <xsd:documentation>instance container.</xsd:documentation>
+  </xsd:annotation>
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:any namespace="##any" processContents="skip" minOccurs="0"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="submission">
+  <xsd:annotation>
+   <xsd:documentation>submit info container.</xsd:documentation>
+  </xsd:annotation>
+  <xsd:complexType>
+   <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+    <xsd:group ref="xforms:Action"/>
+   </xsd:sequence>
+
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+
+   <xsd:attribute name="ref" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="bind" type="xsd:IDREF" use="optional"/>
+   <xsd:attribute name="action" type="xsd:anyURI" use="optional"/>
+
+   <xsd:attribute name="method" use="required">
+    <xsd:simpleType>
+     <xsd:union memberTypes="xforms:QNameButNotNCNAME">
+      <xsd:simpleType>
+       <xsd:restriction base="xsd:string">
+        <xsd:enumeration value="post"/>
+        <xsd:enumeration value="put"/>
+        <xsd:enumeration value="get"/>
+        <xsd:enumeration value="multipart-post"/>
+        <xsd:enumeration value="form-data-post"/>
+        <xsd:enumeration value="urlencoded-post"/>
+       </xsd:restriction>
+      </xsd:simpleType>
+     </xsd:union>
+    </xsd:simpleType>
+   </xsd:attribute>
+
+   <xsd:attribute name="version" type="xsd:NMTOKEN" use="optional"/>
+   <xsd:attribute name="indent" type="xsd:boolean" use="optional"/>
+   <xsd:attribute name="mediatype" type="xsd:string" use="optional"/>
+   <xsd:attribute name="encoding" type="xsd:string" use="optional"/>
+   <xsd:attribute name="omit-xml-declaration" type="xsd:boolean" use="optional"/>
+   <xsd:attribute name="standalone" type="xsd:boolean" use="optional"/>
+   <xsd:attribute name="cdata-section-elements" type="xforms:QNameList" use="optional"/>
+
+                       <xsd:attribute name="replace" use="optional" default="all">
+    <xsd:simpleType>
+     <xsd:union memberTypes="xforms:QNameButNotNCNAME">
+      <xsd:simpleType>
+       <xsd:restriction base="xsd:string">
+        <xsd:enumeration value="all"/>
+        <xsd:enumeration value="instance"/>
+        <xsd:enumeration value="none"/>
+       </xsd:restriction>
+      </xsd:simpleType>
+     </xsd:union>
+    </xsd:simpleType>
+   </xsd:attribute>
+
+                        <xsd:attribute name="instance" type="xsd:IDREF" use="optional"/>
+
+   <xsd:attribute name="separator" use="optional" default=";">
+    <xsd:simpleType>
+     <xsd:restriction base="xsd:string">
+      <xsd:enumeration value=";"/>
+      <xsd:enumeration value="&amp;"/>
+     </xsd:restriction>
+    </xsd:simpleType>
+   </xsd:attribute>
+
+                        <xsd:attribute name="includenamespaceprefixes" use="optional">
+                          <xsd:simpleType>
+                            <xsd:list>
+                              <xsd:simpleType>
+                                <xsd:union>
+                                  <xsd:simpleType>
+                                    <xsd:restriction base='xsd:NCName'/>
+                                  </xsd:simpleType>
+                                  <xsd:simpleType>
+                                    <xsd:restriction base='xsd:string'>
+                                      <xsd:enumeration value='#default'/>
+                                    </xsd:restriction>
+                                  </xsd:simpleType>
+                                </xsd:union>
+                              </xsd:simpleType>
+                            </xsd:list>
+                          </xsd:simpleType>
+                        </xsd:attribute>
+
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:attributeGroup name="Linking.Attributes">
+  <xsd:attribute name="src" type="xsd:anyURI"/>
+ </xsd:attributeGroup>
+ <xsd:element name="bind">
+  <xsd:annotation>
+   <xsd:documentation>Definition of bind container.</xsd:documentation>
+  </xsd:annotation>
+  <xsd:complexType>
+   <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+    <xsd:element ref="xforms:bind"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="nodeset" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="type" type="xsd:QName" use="optional"/>
+   <xsd:attribute name="readonly" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="required" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="relevant" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="constraint" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="calculate" type="xforms:XPathExpression" use="optional"/>
+   <xsd:attribute name="p3ptype" type="xsd:string" use="optional"/>
+   <!-- E54 -->
+  </xsd:complexType>
+ </xsd:element>
+ <!--
+User Interface form controls
+-->
+ <xsd:group name="Form.Controls">
+  <xsd:choice>
+   <xsd:element ref="xforms:input"/>
+   <xsd:element ref="xforms:textarea"/>
+   <xsd:element ref="xforms:secret"/>
+   <xsd:element ref="xforms:output"/>
+   <xsd:element ref="xforms:upload"/>
+   <xsd:element ref="xforms:select1"/>
+   <xsd:element ref="xforms:select"/>
+   <xsd:element ref="xforms:range"/>
+   <xsd:element ref="xforms:submit"/>
+   <xsd:element ref="xforms:trigger"/>
+  </xsd:choice>
+ </xsd:group>
+ <xsd:attributeGroup name="Single.Node.Binding.Attributes">
+  <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+  <xsd:attribute name="ref" type="xforms:XPathExpression" use="optional"/>
+  <xsd:attribute name="bind" type="xsd:IDREF" use="optional"/>
+ </xsd:attributeGroup>
+ <xsd:attributeGroup name="Nodeset.Binding.Attributes">
+  <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+  <xsd:attribute name="nodeset" type="xforms:XPathExpression" use="optional"/>
+  <xsd:attribute name="bind" type="xsd:IDREF" use="optional"/>
+ </xsd:attributeGroup>
+ <xsd:attributeGroup name="UI.Common.Attrs">
+  <xsd:attribute name="appearance" type="xforms:appearanceType" use="optional"/>
+ </xsd:attributeGroup>
+ <xsd:group name="UI.Inline">
+  <xsd:sequence>
+   <xsd:choice minOccurs="0">
+    <xsd:element ref="xforms:output"/>
+    <!-- containing document language to add additional allowed content here -->
+   </xsd:choice>
+  </xsd:sequence>
+ </xsd:group>
+ <xsd:element name="label">
+  <xsd:complexType mixed="true">
+   <xsd:group ref="xforms:UI.Inline"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="hint">
+  <xsd:complexType mixed="true">
+   <xsd:group ref="xforms:UI.Inline"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="help">
+  <xsd:complexType mixed="true">
+   <xsd:group ref="xforms:UI.Inline"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="alert">
+  <xsd:complexType mixed="true">
+   <xsd:group ref="xforms:UI.Inline"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="extension">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:any namespace="##other"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="choices">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label" minOccurs="0"/>
+    <xsd:sequence maxOccurs="unbounded">
+     <xsd:choice>
+      <xsd:element ref="xforms:choices"/>
+      <xsd:element ref="xforms:item"/>
+      <xsd:element ref="xforms:itemset"/>
+     </xsd:choice>
+    </xsd:sequence>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="value">
+  <xsd:complexType mixed="true">
+   <xsd:sequence>
+    <xsd:any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="item">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:element ref="xforms:value"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="itemset">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:choice>
+     <xsd:element ref="xforms:value"/>
+     <xsd:element ref="xforms:copy"/>
+    </xsd:choice>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Nodeset.Binding.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="copy">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="filename">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="mediatype">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:group name="UI.Common">
+  <xsd:sequence>
+   <xsd:choice minOccurs="0" maxOccurs="unbounded">
+    <xsd:element ref="xforms:help"/>
+    <xsd:element ref="xforms:hint"/>
+    <xsd:element ref="xforms:alert"/>
+    <xsd:group ref="xforms:Action"/>
+   </xsd:choice>
+  </xsd:sequence>
+ </xsd:group>
+ <xsd:element name="input">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="inputmode" type="xsd:string" use="optional"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="false"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="textarea">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="inputmode" type="xsd:string" use="optional"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="false"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="secret">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="inputmode" type="xsd:string" use="optional"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="false"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="upload">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:element ref="xforms:filename" minOccurs="0"/>
+    <xsd:element ref="xforms:mediatype" minOccurs="0"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="mediatype" type="xsd:string" use="optional"/>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="false"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:group name="List.UI.Common">
+  <xsd:sequence>
+   <xsd:choice>
+    <xsd:element ref="xforms:item"/>
+    <xsd:element ref="xforms:itemset"/>
+    <xsd:element ref="xforms:choices"/>
+   </xsd:choice>
+  </xsd:sequence>
+ </xsd:group>
+ <xsd:element name="select1">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:List.UI.Common" maxOccurs="unbounded"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="selection" use="optional" default="closed">
+    <xsd:simpleType>
+     <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="open"/>
+      <xsd:enumeration value="closed"/>
+     </xsd:restriction>
+    </xsd:simpleType>
+   </xsd:attribute>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="true"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="select">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:List.UI.Common" maxOccurs="unbounded"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="selection" use="optional" default="closed">
+    <xsd:simpleType>
+     <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="open"/>
+      <xsd:enumeration value="closed"/>
+     </xsd:restriction>
+    </xsd:simpleType>
+   </xsd:attribute>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="true"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="range">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="start" type="xsd:string" use="optional"/>
+   <xsd:attribute name="end" type="xsd:string" use="optional"/>
+   <xsd:attribute name="step" type="xsd:string" use="optional"/>
+   <xsd:attribute name="incremental" type="xsd:boolean" use="optional" default="false"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="trigger">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="output">
+  <xsd:complexType>
+   <xsd:sequence minOccurs="0">
+    <xsd:element ref="xforms:label"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="appearance" type="xforms:appearanceType" use="optional"/>
+   <xsd:attribute name="value" type="xforms:XPathExpression" use="optional"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="submit">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label"/>
+    <xsd:group ref="xforms:UI.Common" minOccurs="0" maxOccurs="unbounded"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="submission" type="xsd:IDREF" use="required"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+  </xsd:complexType>
+ </xsd:element>
+ <!--
+XForms Actions
+-->
+ <xsd:attributeGroup name="XML.Events">
+  <xsd:attribute ref="ev:event"/>
+  <xsd:attribute ref="ev:observer"/>
+  <xsd:attribute ref="ev:target"/>
+  <xsd:attribute ref="ev:handler"/>
+  <xsd:attribute ref="ev:phase"/>
+  <xsd:attribute ref="ev:propagate"/>
+  <xsd:attribute ref="ev:defaultAction"/>
+ </xsd:attributeGroup>
+ <xsd:group name="Action">
+  <xsd:sequence>
+   <xsd:choice minOccurs="0" maxOccurs="unbounded">
+    <xsd:element ref="xforms:action"/>
+    <xsd:element ref="xforms:dispatch"/>
+    <xsd:element ref="xforms:rebuild"/>
+    <xsd:element ref="xforms:recalculate"/>
+    <xsd:element ref="xforms:revalidate"/>
+    <xsd:element ref="xforms:refresh"/>
+    <xsd:element ref="xforms:setfocus"/>
+    <xsd:element ref="xforms:load"/>
+    <xsd:element ref="xforms:setvalue"/>
+    <xsd:element ref="xforms:send"/>
+    <xsd:element ref="xforms:reset"/>
+    <xsd:element ref="xforms:insert"/>
+    <xsd:element ref="xforms:delete"/>
+    <xsd:element ref="xforms:setindex"/>
+    <xsd:element ref="xforms:toggle"/>
+    <xsd:element ref="xforms:message"/>
+   </xsd:choice>
+  </xsd:sequence>
+ </xsd:group>
+ <xsd:element name="action">
+  <xsd:complexType>
+   <xsd:sequence maxOccurs="unbounded">
+    <xsd:group ref="xforms:Action"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="dispatch">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="name" type="xsd:NMTOKEN" use="required"/>
+   <xsd:attribute name="target" type="xsd:IDREF" use="required"/>
+   <xsd:attribute name="bubbles" type="xsd:boolean" use="optional" default="true"/>
+   <xsd:attribute name="cancelable" type="xsd:boolean" use="optional" default="true"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="rebuild">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <!-- E4 -->
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="revalidate">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <!-- E4 -->
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="recalculate">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <!-- E4 -->
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="refresh">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <!-- E4 -->
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="setfocus">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="control" type="xsd:IDREF" use="required"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="load">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="resource" type="xsd:anyURI"/>
+   <xsd:attribute name="show" use="optional" default="replace">
+    <xsd:simpleType>
+     <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="new"/>
+      <xsd:enumeration value="replace"/>
+     </xsd:restriction>
+    </xsd:simpleType>
+    <!-- E22 -->
+   </xsd:attribute>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="setvalue">
+  <xsd:complexType>
+   <xsd:simpleContent>
+    <xsd:extension base="xsd:string">
+     <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+     <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+     <xsd:attribute name="value" type="xforms:XPathExpression" use="optional"/>
+     <xsd:attributeGroup ref="xforms:XML.Events"/>
+    </xsd:extension>
+   </xsd:simpleContent>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="send">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="submission" type="xsd:IDREF" use="required"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="reset">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+   <xsd:attribute name="model" type="xsd:IDREF" use="optional"/>
+   <!-- E4 -->
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="insert">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Nodeset.Binding.Attributes"/>
+   <xsd:attribute name="at" type="xforms:XPathExpression" use="required"/>
+   <xsd:attribute name="position" use="required">
+    <xsd:simpleType>
+     <xsd:restriction base="xsd:string">
+      <xsd:enumeration value="before"/>
+      <xsd:enumeration value="after"/>
+     </xsd:restriction>
+    </xsd:simpleType>
+   </xsd:attribute>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="delete">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Nodeset.Binding.Attributes"/>
+   <xsd:attribute name="at" type="xforms:XPathExpression" use="required"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="setindex">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="repeat" type="xsd:IDREF" use="required"/>
+   <xsd:attribute name="index" type="xforms:XPathExpression" use="required"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="toggle">
+  <xsd:complexType>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attribute name="case" type="xsd:IDREF" use="required"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="message">
+  <xsd:complexType mixed="true">
+   <xsd:group ref="xforms:UI.Inline"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attribute name="level" use="required">
+    <xsd:simpleType>
+     <xsd:union memberTypes="xforms:QNameButNotNCNAME">
+      <xsd:simpleType>
+       <xsd:restriction base="xsd:string">
+        <xsd:enumeration value="ephemeral"/>
+        <xsd:enumeration value="modeless"/>
+        <xsd:enumeration value="modal"/>
+       </xsd:restriction>
+      </xsd:simpleType>
+     </xsd:union>
+    </xsd:simpleType>
+   </xsd:attribute>
+   <xsd:attributeGroup ref="xforms:Linking.Attributes"/>
+   <xsd:attributeGroup ref="xforms:XML.Events"/>
+  </xsd:complexType>
+ </xsd:element>
+ <!--
+Advanced User Interface
+-->
+ <xsd:attribute name="repeat-nodeset" type="xforms:XPathExpression"/>
+ <xsd:attribute name="repeat-model" type="xsd:IDREF"/>
+ <xsd:attribute name="repeat-bind" type="xsd:IDREF"/>
+ <xsd:attribute name="repeat-startindex" type="xsd:positiveInteger"/>
+ <xsd:attribute name="repeat-number" type="xsd:nonNegativeInteger"/>
+ <xsd:element name="repeat">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+     <xsd:choice>
+      <xsd:group ref="xforms:Form.Controls"/>
+      <xsd:element ref="xforms:group"/>
+      <xsd:element ref="xforms:switch"/>
+      <xsd:element ref="xforms:repeat"/>
+      <!-- containing document language to add additional allowed content here -->
+     </xsd:choice>
+    </xsd:sequence>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Nodeset.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+   <xsd:attribute name="startindex" type="xsd:positiveInteger"/>
+   <xsd:attribute name="number" type="xsd:nonNegativeInteger"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="group">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label" minOccurs="0"/>
+    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+     <xsd:choice>
+      <xsd:group ref="xforms:UI.Common"/>
+      <xsd:group ref="xforms:Form.Controls"/>
+      <xsd:element ref="xforms:group"/>
+      <xsd:element ref="xforms:switch"/>
+      <xsd:element ref="xforms:repeat"/>
+      <!-- containing document language to add additional allowed content here -->
+     </xsd:choice>
+    </xsd:sequence>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="switch">
+  <xsd:complexType>
+   <xsd:sequence maxOccurs="unbounded">
+    <xsd:element ref="xforms:case"/>
+   </xsd:sequence>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+   <xsd:attributeGroup ref="xforms:Single.Node.Binding.Attributes"/>
+   <xsd:attributeGroup ref="xforms:UI.Common.Attrs"/>
+  </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="case">
+  <xsd:complexType>
+   <xsd:sequence>
+    <xsd:element ref="xforms:label" minOccurs="0"/>
+    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+     <xsd:choice>
+      <xsd:group ref="xforms:Form.Controls"/>
+      <xsd:group ref="xforms:Action"/>
+      <xsd:element ref="xforms:group"/>
+      <xsd:element ref="xforms:switch"/>
+      <xsd:element ref="xforms:repeat"/>
+      <!-- containing document language to add additional allowed content here -->
+     </xsd:choice>
+     <!-- E71 -->
+    </xsd:sequence>
+   </xsd:sequence>
+   <xsd:attribute name="selected" type="xsd:boolean" use="optional" default="false"/>
+   <xsd:attributeGroup ref="xforms:Common.Attributes"/>
+  </xsd:complexType>
+ </xsd:element>
+ <!--
+New simpleTypes
+-->
+
+ <xsd:simpleType name="versionList">
+  <xsd:list itemType="xforms:versionNumber"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="versionNumber">
+  <xsd:restriction base="xsd:string">
+   <xsd:pattern value="[1-9]\d*\.\d+"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="XPathExpression">
+  <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="QNameList">
+  <xsd:list itemType="xsd:QName"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="anyURIList">
+  <xsd:list itemType="xsd:anyURI"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="QNameButNotNCNAME">
+  <xsd:restriction base="xsd:QName">
+   <xsd:pattern value="[^:]+:[^:]+"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="appearanceType">
+  <xsd:union memberTypes="xforms:QNameButNotNCNAME">
+   <xsd:simpleType>
+    <xsd:restriction base="xsd:string">
+     <xsd:enumeration value="full"/>
+     <xsd:enumeration value="compact"/>
+     <xsd:enumeration value="minimal"/>
+    </xsd:restriction>
+   </xsd:simpleType>
+  </xsd:union>
+ </xsd:simpleType>
+ <xsd:simpleType name="listItem">
+  <xsd:restriction base="xsd:string">
+   <xsd:pattern value="\S+"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="listItems">
+  <xsd:list itemType="xforms:listItem"/>
+ </xsd:simpleType>
+ <xsd:simpleType name="dayTimeDuration">
+  <xsd:restriction base="xsd:duration">
+   <xsd:pattern value="[\-]?P([0-9]+D(T([0-9]+(H([0-9]+(M([0-9]+(\.[0-9]*)?S
+               |\.[0-9]+S)?|(\.[0-9]*)?S)|(\.[0-9]*)?S)?|M([0-9]+
+               (\.[0-9]*)?S|\.[0-9]+S)?|(\.[0-9]*)?S)|\.[0-9]+S))?
+               |T([0-9]+(H([0-9]+(M([0-9]+(\.[0-9]*)?S|\.[0-9]+S)?
+               |(\.[0-9]*)?S)|(\.[0-9]*)?S)?|M([0-9]+(\.[0-9]*)?S|\.[0-9]+S)?
+               |(\.[0-9]*)?S)|\.[0-9]+S))"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType name="yearMonthDuration">
+  <xsd:restriction base="xsd:duration">
+   <xsd:pattern value="[\-]?P[0-9]+(Y([0-9]+M)?|M)"/>
+  </xsd:restriction>
+ </xsd:simpleType>
+</xsd:schema>
index 4a8c550b8a28a4fcd40f277ff10a48f5f1143592..91c2af32dc7f7faca5c0d92ab678eece98aec168 100644 (file)
@@ -11,6 +11,8 @@
        <name>Argeo Knowledge Components</name>
        <packaging>pom</packaging>
        <modules>
+               <module>org.argeo.support.xforms</module>
                <module>org.argeo.support.odk</module>
+               <module>org.argeo.support.geonames</module>
        </modules>
 </project>
index a7d5dd1f5bc02749f6b2a27aee53660bcfc8c194..ab46172b78359868766e969e95dbd025c40215ee 100644 (file)
@@ -1,3 +1,5 @@
+Require-Capability:\
+cms.datamodel;filter:="(name=jcrx)"
 
 Provide-Capability:\
 cms.datamodel; name=entity; cnd=/org/argeo/entity/entity.cnd
index 973947199ff3a6e62804a69f77279a7f00e56073..f32c262ab07fc36f1f3bd47f31def6980a8a33d7 100644 (file)
@@ -4,13 +4,23 @@ import org.argeo.naming.LdapAttrs;
 
 /** Constants used to name entity structures. */
 public interface EntityNames {
-       final String ENTITY_DEFINITIONS_PATH = "/entity:entityDefinitions";
+       final String FORM_BASE = "form";
+       final String TERM_BASE = "term";
+
+       final String ENTITY_DEFINITIONS_PATH = "/entity";
+       @Deprecated
+       final String TYPOLOGIES_PATH = "/" + TERM_BASE;
+       /** Administrative units. */
+       final String ADM = "adm";
 
        final String ENTITY_TYPE = "entity:type";
        final String ENTITY_UID = "entity:uid";
+       final String ENTITY_NAME = "entity:name";
 
        // GENERIC CONCEPTS
-       /** The date which is clearly relevant for this entity. */
+       /** The language which is relevant. */
+       final String XML_LANG = "xml:lang";
+       /** The date which is relevant. */
        final String ENTITY_DATE = "entity:date";
        final String ENTITY_RELATED_TO = "entity:relatedTo";
 
@@ -21,4 +31,10 @@ public interface EntityNames {
        final String SURNAME = LdapAttrs.sn.property();
        final String EMAIL = LdapAttrs.mail.property();
 
+       final String OU = LdapAttrs.ou.property();
+
+       // WGS84
+       final String GEO_LAT = "geo:lat";
+       final String GEO_LONG = "geo:long";
+       final String GEO_ALT = "geo:alt";
 }
diff --git a/org.argeo.entity.api/src/org/argeo/entity/EntityType.java b/org.argeo.entity.api/src/org/argeo/entity/EntityType.java
new file mode 100644 (file)
index 0000000..6548d75
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.entity;
+
+/** Types related to entities. */
+public enum EntityType implements JcrName {
+       // entity
+       entity, definition,
+       // typology
+       typologies, terms, term,
+       // form
+       form, formSet,
+       // ldap
+       person;
+
+       @Override
+       public String getPrefix() {
+               return prefix();
+       }
+
+       public static String prefix() {
+               return "entity";
+       }
+
+       @Override
+       public String getNamespace() {
+               return namespace();
+       }
+
+       public static String namespace() {
+               return "http://www.argeo.org/ns/entity";
+       }
+
+}
index f4b3e5e59eb2c16885777336caf98ad3959c437e..ef35147212647f16a2b89aa8ad88ee59bc4cd5dd 100644 (file)
@@ -1,6 +1,7 @@
 package org.argeo.entity;
 
 /** Types related to entities. */
+@Deprecated
 public interface EntityTypes {
        final static String ENTITY_ENTITY = "entity:entity";
        final static String ENTITY_DEFINITION = "entity:definition";
diff --git a/org.argeo.entity.api/src/org/argeo/entity/JcrName.java b/org.argeo.entity.api/src/org/argeo/entity/JcrName.java
new file mode 100644 (file)
index 0000000..43057aa
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.entity;
+
+/** Can be applied to {@link Enum}s in order to generate prefixed names. */
+public interface JcrName {
+       String name();
+
+       default String getPrefix() {
+               return null;
+       }
+
+       default String getNamespace() {
+               return null;
+       }
+
+       default String get() {
+               String prefix = getPrefix();
+               return prefix != null ? prefix + ":" + name() : name();
+       }
+
+       default String withNamespace() {
+               String namespace = getNamespace();
+               if (namespace == null)
+                       throw new UnsupportedOperationException("No namespace is specified for " + getClass());
+               return "{" + namespace + "}" + name();
+       }
+}
index 67116cd69ca88a05a9dd225c006930b8d87792cb..78fb8ccb1c0f6fc7d1b530ce82be1ec839c39347 100644 (file)
@@ -1,28 +1,59 @@
-<ldap = 'http://www.argeo.org/ns/ldap'>
+// Standard namespaces
+<xsd = "http://www.w3.org/2001/XMLSchema">
+<h = "http://www.w3.org/1999/xhtml">
+// see https://www.w3.org/2003/01/geo/
+//<geo = "http://www.w3.org/2003/01/geo/wgs84_pos#">
+
+<ldap = "http://www.argeo.org/ns/ldap">
 <entity = 'http://www.argeo.org/ns/entity'>
 
-[entity:entity] > mix:title, mix:created, mix:lastModified, mix:referenceable
+[entity:entity] > mix:created, mix:referenceable
 mixin
 //- entity:uid (String) m // an implementation dependent UID for each entity
 //- entity:type (String) // the type of this entity
 
-[entity:definition] > entity:composite, mix:title, mix:created, mix:lastModified, mix:referenceable
-mixin
+//
+// ENTITY DEFINITION
+//
+[entity:definition] > entity:composite, mix:created, mix:lastModified, mix:referenceable
+//- entity:type (String) multiple
 
-[entity:part]
-mixin
+//[entity:part]
 
-[entity:reference]
-mixin
+//[entity:reference]
 
 [entity:composite]
-mixin
 orderable
-+ * (entity:part)
-+ * (entity:reference)
-+ * (entity:composite)
+//+ * (entity:part)
+//+ * (entity:reference)
+//+ * (entity:composite)
+
+//
+// TYPOLOGY
+//
+[entity:typologies]
++ * (entity:terms) = entity:terms
+
+[entity:term]
+orderable
++ * (entity:term) = entity:term *
+
+[entity:terms] > mix:referenceable
+orderable
++ * (entity:term) = entity:term *
+
+//
+// FORM
+//
+[entity:form]
+mixin
+
+[entity:formSet]
+mixin
 
 // LDAP-LIKE ENTITIES
 // A real person
 [entity:person] > entity:entity
 mixin
+- ldap:sn (String)
+- ldap:givenName (String)
index 15fb812c0567505fef6c80ee9a9b228bb444780e..f76eb63fa96491b4df5874f246ff896de56cdfa1 100644 (file)
@@ -32,8 +32,8 @@ public class JcrEntityDefinition implements EntityDefinition {
                        defaultEditoryId = properties.get(EntityConstants.DEFAULT_EDITORY_ID);
                        String definitionPath = EntityNames.ENTITY_DEFINITIONS_PATH + '/' + type;
                        if (!adminSession.itemExists(definitionPath)) {
-                               Node entityDefinition = JcrUtils.mkdirs(adminSession, definitionPath);
-                               entityDefinition.addMixin(EntityTypes.ENTITY_DEFINITION);
+                               Node entityDefinition = JcrUtils.mkdirs(adminSession, definitionPath, EntityTypes.ENTITY_DEFINITION);
+//                             entityDefinition.addMixin(EntityTypes.ENTITY_DEFINITION);
                                adminSession.save();
                        }
                        initJcr(adminSession);
index f47b00e9cbc90d9a1a84e1d07be05897cc3cfce3..ab084afcc38232c96bbb56981db910d346ac26eb 100644 (file)
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.pde.PluginNature</nature>
diff --git a/org.argeo.suite.core/OSGI-INF/maintenanceService.xml b/org.argeo.suite.core/OSGI-INF/maintenanceService.xml
new file mode 100644 (file)
index 0000000..2d495c8
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Suite Maintenance Service">
+   <implementation class="org.argeo.suite.core.SuiteMaintenanceService"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=entity)"/>
+   <reference bind="setUserTransaction" cardinality="1..1" interface="javax.transaction.UserTransaction" name="UserTransaction" policy="static"/>
+   <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
+</scr:component>
index d70ca8c988a14e84692e1c66e0d8f8273fd2942e..48876750a06b57ddca0fd5123ef40659d59b3327 100644 (file)
@@ -1,4 +1,10 @@
+Service-Component:\
+OSGI-INF/maintenanceService.xml
 
 Import-Package:\
+javax.transaction,\
+org.osgi.service.useradmin,\
+javax.jcr.nodetype,\
 org.argeo.api,\
+org.argeo.entity,\
 *
\ No newline at end of file
index 34d2e4d2dad529ceaeb953bfcdb63c51d69ffed2..6210e849b591d26fa9e17057ad3e8d09511917b6 100644 (file)
@@ -1,4 +1,5 @@
-source.. = src/
 output.. = bin/
 bin.includes = META-INF/,\
-               .
+               .,\
+               OSGI-INF/
+source.. = src/
index c6a7d4f15576eccd9812ae6bbe51964e0f525d9e..9c89ce5e77a8a4532b8836816fbde05ca4f3a1d2 100644 (file)
                        <artifactId>org.argeo.cms</artifactId>
                        <version>${version.argeo-commons}</version>
                </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.maintenance</artifactId>
+                       <version>${version.argeo-commons}</version>
+               </dependency>
        </dependencies>
 </project>
index f0165af75f3905a88a2580e6ddd37caae0df72ea..bfba46e449879f6cfa717daafc70d4d815c064a2 100644 (file)
@@ -76,16 +76,16 @@ public class RankedObject<T> {
                RankedObject<T> rankedObject = new RankedObject<>(object, properties);
                if (!map.containsKey(key)) {
                        map.put(key, rankedObject);
-                       if (log.isDebugEnabled())
-                               log.debug(
+                       if (log.isTraceEnabled())
+                               log.trace(
                                                "Added " + key + " as " + object.getClass().getName() + " with rank " + rankedObject.getRank());
                        return rankedObject;
                } else {
                        RankedObject<T> current = map.get(key);
                        if (current.getRank() <= rankedObject.getRank()) {
                                map.put(key, rankedObject);
-                               if (log.isDebugEnabled())
-                                       log.debug("Replaced " + key + " by " + object.getClass().getName() + " with rank "
+                               if (log.isTraceEnabled())
+                                       log.trace("Replaced " + key + " by " + object.getClass().getName() + " with rank "
                                                        + rankedObject.getRank());
                                return rankedObject;
                        } else {
diff --git a/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java b/org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java
new file mode 100644 (file)
index 0000000..44d218c
--- /dev/null
@@ -0,0 +1,29 @@
+package org.argeo.suite.core;
+
+import java.io.IOException;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.entity.EntityNames;
+import org.argeo.entity.EntityType;
+import org.argeo.maintenance.AbstractMaintenanceService;
+
+/** Initialises an Argeo Suite backend. */
+public class SuiteMaintenanceService extends AbstractMaintenanceService {
+
+       @Override
+       public boolean prepareJcrTree(Session adminSession) throws RepositoryException, IOException {
+               boolean modified = false;
+               Node rootNode = adminSession.getRootNode();
+               if (!rootNode.hasNode(EntityNames.TERM_BASE)) {
+                       rootNode.addNode(EntityNames.TERM_BASE, EntityType.typologies.get());
+                       modified = true;
+               }
+               if (modified)
+                       adminSession.save();
+               return modified;
+       }
+
+}