From: Mathieu Date: Tue, 6 Dec 2022 05:11:48 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/unstable' into merge-to-testing X-Git-Tag: v2.1.26~1^2~1 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=e615f8bcc753618520ee55506b7285b65bf264f4;hp=c418a9dffc6b5d69fd4c07beba237cb1378d777a Merge remote-tracking branch 'origin/unstable' into merge-to-testing --- diff --git a/Makefile b/Makefile index 7214fb6..1a573a5 100644 --- a/Makefile +++ b/Makefile @@ -21,15 +21,14 @@ A2_BASE = $(A2_OUTPUT) DEP_CATEGORIES = \ org.argeo.tp \ -org.argeo.tp.apache \ org.argeo.tp.jetty \ +org.argeo.tp.jcr \ +org.argeo.tp.utils \ +org.argeo.tp.gis \ osgi/api/org.argeo.tp.osgi \ osgi/equinox/org.argeo.tp.eclipse \ swt/rap/org.argeo.tp.swt \ swt/rap/org.argeo.tp.swt.workbench \ -org.argeo.tp.jcr \ -org.argeo.tp.formats \ -org.argeo.tp.gis \ org.argeo.cms \ org.argeo.cms.jcr \ swt/org.argeo.cms \ diff --git a/org.argeo.app.api/src/org/argeo/app/api/EntityNames.java b/org.argeo.app.api/src/org/argeo/app/api/EntityNames.java index 7776056..b60a436 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/EntityNames.java +++ b/org.argeo.app.api/src/org/argeo/app/api/EntityNames.java @@ -1,6 +1,6 @@ package org.argeo.app.api; -import org.argeo.util.naming.LdapAttrs; +import org.argeo.api.acr.ldap.LdapAttr; /** Constants used to name entity structures. */ public interface EntityNames { @@ -35,16 +35,16 @@ public interface EntityNames { // LDAP-LIKE ENTITIES @Deprecated - final String DISPLAY_NAME = LdapAttrs.displayName.property(); + final String DISPLAY_NAME = LdapAttr.displayName.property(); // Persons @Deprecated - final String GIVEN_NAME = LdapAttrs.givenName.property(); + final String GIVEN_NAME = LdapAttr.givenName.property(); @Deprecated - final String SURNAME = LdapAttrs.sn.property(); + final String SURNAME = LdapAttr.sn.property(); @Deprecated - final String EMAIL = LdapAttrs.mail.property(); + final String EMAIL = LdapAttr.mail.property(); @Deprecated - final String OU = LdapAttrs.ou.property(); + final String OU = LdapAttr.ou.property(); // WGS84 final String GEO_LAT = "geo:lat"; diff --git a/org.argeo.app.api/src/org/argeo/app/api/EntityTypes.java b/org.argeo.app.api/src/org/argeo/app/api/EntityTypes.java deleted file mode 100644 index f320794..0000000 --- a/org.argeo.app.api/src/org/argeo/app/api/EntityTypes.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.app.api; - -/** Types related to entities. */ -@Deprecated -public interface EntityTypes { - final static String ENTITY_ENTITY = "entity:entity"; - final static String ENTITY_DEFINITION = "entity:definition"; - - final static String ENTITY_PERSON = "entity:person"; -} diff --git a/org.argeo.app.api/src/org/argeo/app/api/SuiteRole.java b/org.argeo.app.api/src/org/argeo/app/api/SuiteRole.java index 8bf9ec4..5a2fc6b 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/SuiteRole.java +++ b/org.argeo.app.api/src/org/argeo/app/api/SuiteRole.java @@ -2,11 +2,11 @@ package org.argeo.app.api; import javax.xml.namespace.QName; +import org.argeo.api.acr.ArgeoNamespace; import org.argeo.api.acr.ContentName; -import org.argeo.api.acr.CrName; +import org.argeo.api.acr.ldap.LdapAttr; import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.auth.SystemRole; -import org.argeo.util.naming.LdapAttrs; +import org.argeo.cms.SystemRole; /** Standard suite system roles. */ public enum SuiteRole implements SystemRole { @@ -26,11 +26,11 @@ public enum SuiteRole implements SystemRole { private final ContentName name; SuiteRole() { - name = new ContentName(CrName.ROLE_NAMESPACE_URI, QUALIFIER + name()); + name = new ContentName(ArgeoNamespace.ROLE_NAMESPACE_URI, QUALIFIER + name()); } @Override - public QName getName() { + public QName qName() { return name; } @@ -41,7 +41,7 @@ public enum SuiteRole implements SystemRole { @Deprecated public String dn() { - return new StringBuilder(LdapAttrs.cn.name()).append("=").append(getRolePrefix()).append(".").append(name()) + return new StringBuilder(LdapAttr.cn.name()).append("=").append(getRolePrefix()).append(".").append(name()) .append(",").append(CmsConstants.SYSTEM_ROLES_BASEDN).toString(); } } diff --git a/org.argeo.app.api/src/org/argeo/app/api/entity.cnd b/org.argeo.app.api/src/org/argeo/app/api/entity.cnd index 2ea89f9..396b6f2 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/entity.cnd +++ b/org.argeo.app.api/src/org/argeo/app/api/entity.cnd @@ -4,6 +4,7 @@ // see https://www.w3.org/2003/01/geo/ + @@ -95,7 +96,9 @@ mixin mixin - ldap:sn (String) - ldap:givenName (String) +- ldap:cn (String) - ldap:mail (String) * +- ldap:description (String) [entity:user] > entity:person mixin diff --git a/org.argeo.app.core/OSGI-INF/maintenanceService.xml b/org.argeo.app.core/OSGI-INF/maintenanceService.xml index b320df4..965d82b 100644 --- a/org.argeo.app.core/OSGI-INF/maintenanceService.xml +++ b/org.argeo.app.core/OSGI-INF/maintenanceService.xml @@ -2,7 +2,7 @@ - + diff --git a/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java b/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java index 4b69883..a4b1fff 100644 --- a/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java +++ b/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java @@ -25,7 +25,7 @@ public abstract class CustomMaintenanceService extends AbstractMaintenanceServic } protected String getTypologiesLoadBase() { - return "/sys/terms"; + return ""; } protected void loadTypologies(Node customBaseNode) throws RepositoryException, IOException { @@ -44,9 +44,11 @@ public abstract class CustomMaintenanceService extends AbstractMaintenanceServic try { // if (termsBase.hasNode(name)) // return; - - String termsLoadPath = getTypologiesLoadBase() + '/' + name + ".xml"; - URL termsUrl = getClass().getClassLoader().getResource(termsLoadPath); + String typologiesLoadBase = getTypologiesLoadBase(); + if (typologiesLoadBase.contains("/") && !typologiesLoadBase.endsWith("/")) + typologiesLoadBase = typologiesLoadBase + "/"; + String termsLoadPath = typologiesLoadBase + name + ".xml"; + URL termsUrl = getClass().getResource(termsLoadPath); if (termsUrl == null) throw new IllegalArgumentException("Terms '" + name + "' not found."); try (InputStream in = termsUrl.openStream()) { diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteContentNamespace.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteContentNamespace.java new file mode 100644 index 0000000..48c508b --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/SuiteContentNamespace.java @@ -0,0 +1,95 @@ +package org.argeo.app.core; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Objects; + +import org.argeo.api.acr.spi.ContentNamespace; + +public enum SuiteContentNamespace implements ContentNamespace { + // + // ARGEO + // + ENTITY("entity", "http://www.argeo.org/ns/entity", "entity.xsd", null), + // + ARGEO_DBK("argeodbk", "http://www.argeo.org/ns/argeodbk", null, null), + // + // EXTERNAL + // + DOCBOOK5("dbk", "http://docbook.org/ns/docbook", "docbook.xsd", "http://docbook.org/xml/5.0.1/xsd/docbook.xsd"), + // + XML_EVENTS("ev", "http://www.w3.org/2001/xml-events", "xml-events-attribs-1.xsd", + "http://www.w3.org/MarkUp/SCHEMA/xml-events-attribs-1.xsd"), + // + XFORMS("xforms", "http://www.w3.org/2002/xforms", "XForms-11-Schema.xsd", + "https://www.w3.org/MarkUp/Forms/2007/XForms-11-Schema.xsd"), + // + XCARD("xcard", "urn:ietf:params:xml:ns:vcard-4.0", "xCard-4.0.xsd", null), + // + XSL_FO("fo", "http://www.w3.org/1999/XSL/Format", "fop.xsd", + "https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk/fop/src/foschema/fop.xsd"), + // +// XCAL_2_0("xcal", "urn:ietf:params:xml:ns:icalendar-2.0", "xCal-2.0.xsd", null), + // + XHTML("h", "http://www.w3.org/1999/xhtml", null, "https://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd"), + // + // ODK + // + JR("jr", "http://openrosa.org/javarosa", null, null), + // + ORX("orx", "http://openrosa.org/xforms", null, null), + // + ORX_LIST("orxList", "http://openrosa.org/xforms/xformsList", null, null), + // + ORX_MANIFEST("orxManifest", "http://openrosa.org/xforms/xformsManifest", null, null), + // + ODK("odk", "http://www.opendatakit.org/xforms", null, null), + // + WGS84("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#", null, null), + // + ; + + private final static String RESOURCE_BASE = "/org/argeo/app/core/schemas/"; + + private String defaultPrefix; + private String namespace; + private URL resource; + private URL publicUrl; + + SuiteContentNamespace(String defaultPrefix, String namespace, String resourceFileName, String publicUrl) { + Objects.requireNonNull(namespace); + this.defaultPrefix = defaultPrefix; + Objects.requireNonNull(namespace); + this.namespace = namespace; + if (resourceFileName != null) { + resource = getClass().getResource(RESOURCE_BASE + resourceFileName); + Objects.requireNonNull(resource); + } + if (publicUrl != null) + try { + this.publicUrl = new URL(publicUrl); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot interpret public URL", e); + } + } + + @Override + public String getDefaultPrefix() { + return defaultPrefix; + } + + @Override + public String getNamespaceURI() { + return namespace; + } + + @Override + public URL getSchemaResource() { + return resource; + } + + public URL getPublicUrl() { + return publicUrl; + } + +} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteContentTypes.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteContentTypes.java deleted file mode 100644 index 8b58fe1..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteContentTypes.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.app.core; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Objects; - -public enum SuiteContentTypes { - // - // ARGEO - // - ENTITY("entity", "http://www.argeo.org/ns/entity", "entity.xsd", null), - // - ARGEO_DBK("argeodbk", "http://www.argeo.org/ns/argeodbk", null, null), - // - // EXTERNAL - // - XCARD_4_0("xcard", "urn:ietf:params:xml:ns:vcard-4.0", "xCard-4.0.xsd", null), - // - XSL_FO_1999("fo", "http://www.w3.org/1999/XSL/Format", "fop.xsd", - "https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk/fop/src/foschema/fop.xsd"), - // -// XCAL_2_0("xcal", "urn:ietf:params:xml:ns:icalendar-2.0", "xCal-2.0.xsd", null), - // - XHTML_1_1("h", "http://www.w3.org/1999/xhtml", null, "https://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd"), - // - // ODK - // - JR("jr", "http://openrosa.org/javarosa", null, null), - // - ORX("orx", "http://openrosa.org/xforms", null, null), - // - ORX_LIST("orxList", "http://openrosa.org/xforms/xformsList", null, null), - // - ORX_MANIFEST("orxManifest", "http://openrosa.org/xforms/xformsManifest", null, null), - // - ODK("odk", "http://www.opendatakit.org/xforms", null, null), - // - WGS84("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#", null, null), - // - ; - - private final static String RESOURCE_BASE = "/org/argeo/app/core/schemas/"; - - private String defaultPrefix; - private String namespace; - private URL resource; - private URL publicUrl; - - SuiteContentTypes(String defaultPrefix, String namespace, String resourceFileName, String publicUrl) { - Objects.requireNonNull(namespace); - this.defaultPrefix = defaultPrefix; - Objects.requireNonNull(namespace); - this.namespace = namespace; - if (resourceFileName != null) { - resource = getClass().getResource(RESOURCE_BASE + resourceFileName); - Objects.requireNonNull(resource); - } - if (publicUrl != null) - try { - this.publicUrl = new URL(publicUrl); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Cannot interpret public URL", e); - } - } - - public String getDefaultPrefix() { - return defaultPrefix; - } - - public String getNamespace() { - return namespace; - } - - public URL getResource() { - return resource; - } - - public URL getPublicUrl() { - return publicUrl; - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenanceService.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenanceService.java index 98784f8..9c74dde 100644 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenanceService.java +++ b/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenanceService.java @@ -1,29 +1,64 @@ package org.argeo.app.core; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.jcr.security.Privilege; +import javax.measure.Quantity; +import javax.measure.quantity.Area; -import org.argeo.api.acr.spi.ProvidedRepository; +import org.argeo.api.acr.spi.ContentNamespace; import org.argeo.api.cms.CmsConstants; import org.argeo.app.api.EntityType; import org.argeo.jcr.JcrUtils; import org.argeo.maintenance.AbstractMaintenanceService; +import org.geotools.gml3.v3_2.GML; + +import si.uom.SI; +import tech.units.indriya.quantity.Quantities; /** Initialises an Argeo Suite backend. */ public class SuiteMaintenanceService extends AbstractMaintenanceService { @Override public void init() { + // make sure that the unit system is initialised + Quantity dummy = Quantities.getQuantity(0, SI.SQUARE_METRE); + super.init(); - for (SuiteContentTypes types : SuiteContentTypes.values()) { - getContentRepository().registerTypes(types.getDefaultPrefix(), types.getNamespace(), - types.getResource() != null ? types.getResource().toExternalForm() : null); - } + getContentRepository().registerTypes(SuiteContentNamespace.values()); +// for (SuiteContentTypes types : SuiteContentTypes.values()) { +// getContentRepository().registerTypes(types.getDefaultPrefix(), types.getNamespace(), +// types.getResource() != null ? types.getResource().toExternalForm() : null); +// } + + // GML schema import fails because of xlinks issues + getContentRepository().registerTypes(new ContentNamespace() { + + @Override + public URL getSchemaResource() { + try { + return new URL(GML.getInstance().getSchemaLocation()); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public String getNamespaceURI() { + return GML.getInstance().getNamespaceURI(); + } + + @Override + public String getDefaultPrefix() { + return "gml"; + } + }); } @Override diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteUtils.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteUtils.java index 531d3a3..f225064 100644 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/core/SuiteUtils.java @@ -1,10 +1,10 @@ package org.argeo.app.core; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import javax.jcr.Node; +import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; @@ -13,25 +13,27 @@ import javax.security.auth.x500.X500Principal; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsSession; import org.argeo.app.api.EntityType; -import org.argeo.cms.auth.RoleNameUtils; +import org.argeo.cms.RoleNameUtils; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; /** Utilities around the Argeo Suite APIs. */ public class SuiteUtils { - @Deprecated + public final static String USER_STATE_NODE_NAME = "state"; + public final static String USER_DEVICES_NODE_NAME = "devices"; + public final static String USER_SESSIONS_NODE_NAME = "sessions"; + public static String getUserNodePath(String userDn) { String uid = RoleNameUtils.getLastRdnValue(userDn); return EntityType.user.basePath() + '/' + uid; } - @Deprecated - private static Node getOrCreateUserNode(Session adminSession, String userDn) { + public static Node getOrCreateUserNode(Session adminSession, String userDn) { try { Node usersBase = adminSession.getNode(EntityType.user.basePath()); String uid = RoleNameUtils.getLastRdnValue(userDn); @@ -40,8 +42,27 @@ public class SuiteUtils { userNode = usersBase.addNode(uid, NodeType.NT_UNSTRUCTURED); userNode.addMixin(EntityType.user.get()); userNode.addMixin(NodeType.MIX_CREATED); - userNode.setProperty(LdapAttrs.distinguishedName.property(), userDn.toString()); - userNode.setProperty(LdapAttrs.uid.property(), uid); + userNode.setProperty(LdapAttr.distinguishedName.get(), userDn.toString()); + userNode.setProperty(LdapAttr.uid.get(), uid); + } else { + userNode = usersBase.getNode(uid); + } + + if (!userNode.hasNode(USER_SESSIONS_NODE_NAME)) { + // Migrate existing user node + Node sessionsNode = userNode.addNode(USER_SESSIONS_NODE_NAME, NodeType.NT_UNSTRUCTURED); + oldSessions: for (NodeIterator nit = userNode.getNodes(); nit.hasNext();) { + Node child = nit.nextNode(); + if (USER_SESSIONS_NODE_NAME.equals(child.getName()) || child.getName().startsWith("rep:") + || child.getName().startsWith("jcr:")) + continue oldSessions; + Node target = sessionsNode.addNode(child.getName()); + JcrUtils.copy(child, target); + } + + Node userStateNode = userNode.addNode(USER_STATE_NODE_NAME, NodeType.NT_UNSTRUCTURED); + Node userDevicesNode = userNode.addNode(USER_DEVICES_NODE_NAME, NodeType.NT_UNSTRUCTURED); + adminSession.save(); // JackrabbitSecurityUtils.denyPrivilege(adminSession, userNode.getPath(), SuiteRole.coworker.dn(), // Privilege.JCR_READ); @@ -49,8 +70,9 @@ public class SuiteUtils { Privilege.JCR_READ); JcrUtils.addPrivilege(adminSession, userNode.getPath(), CmsConstants.ROLE_USER_ADMIN, Privilege.JCR_ALL); - } else { - userNode = usersBase.getNode(uid); + + JcrUtils.addPrivilege(adminSession, userStateNode.getPath(), userDn, Privilege.JCR_ALL); + JcrUtils.addPrivilege(adminSession, userDevicesNode.getPath(), userDn, Privilege.JCR_ALL); } return userNode; } catch (RepositoryException e) { @@ -58,41 +80,30 @@ public class SuiteUtils { } } - @Deprecated public static Node getCmsSessionNode(Session session, CmsSession cmsSession) { try { - return session.getNode(getUserNodePath(cmsSession.getUserDn()) + '/' + cmsSession.getUuid().toString()); + return session.getNode(getUserNodePath(cmsSession.getUserDn()) + '/' + USER_SESSIONS_NODE_NAME + '/' + + cmsSession.getUuid().toString()); } catch (RepositoryException e) { throw new JcrException("Cannot get session dir for " + cmsSession, e); } } - @Deprecated public static Node getOrCreateCmsSessionNode(Session adminSession, CmsSession cmsSession) { try { String userDn = cmsSession.getUserDn(); -// String uid = userDn.get(userDn.size() - 1); Node userNode = getOrCreateUserNode(adminSession, userDn); -// if (!usersBase.hasNode(uid)) { -// userNode = usersBase.addNode(uid, NodeType.NT_UNSTRUCTURED); -// userNode.addMixin(EntityType.user.get()); -// userNode.addMixin(NodeType.MIX_CREATED); -// usersBase.setProperty(LdapAttrs.uid.property(), uid); -// usersBase.setProperty(LdapAttrs.distinguishedName.property(), userDn.toString()); -// adminSession.save(); -// } else { -// userNode = usersBase.getNode(uid); -// } + Node sessionsNode = userNode.getNode(USER_SESSIONS_NODE_NAME); String cmsSessionUuid = cmsSession.getUuid().toString(); Node cmsSessionNode; - if (!userNode.hasNode(cmsSessionUuid)) { - cmsSessionNode = userNode.addNode(cmsSessionUuid, NodeType.NT_UNSTRUCTURED); + if (!sessionsNode.hasNode(cmsSessionUuid)) { + cmsSessionNode = sessionsNode.addNode(cmsSessionUuid, NodeType.NT_UNSTRUCTURED); cmsSessionNode.addMixin(NodeType.MIX_CREATED); adminSession.save(); JcrUtils.addPrivilege(adminSession, cmsSessionNode.getPath(), cmsSession.getUserRole(), Privilege.JCR_ALL); } else { - cmsSessionNode = userNode.getNode(cmsSessionUuid); + cmsSessionNode = sessionsNode.getNode(cmsSessionUuid); } return cmsSessionNode; } catch (RepositoryException e) { @@ -121,20 +132,20 @@ public class SuiteUtils { } synchronized static public long findNextId(Content hierarchyUnit, QName cclass) { - if (!hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName())) + if (!hierarchyUnit.hasContentClass(LdapObj.posixGroup.qName())) throw new IllegalArgumentException(hierarchyUnit + " is not a POSIX group"); - - long min = hierarchyUnit.get(LdapAttrs.gidNumber.qName(), Long.class).orElseThrow(); + + long min = hierarchyUnit.get(LdapAttr.gidNumber.qName(), Long.class).orElseThrow(); long currentMax = 0l; for (Content childHu : hierarchyUnit) { - if (!childHu.hasContentClass(LdapObjs.organizationalUnit.qName())) + if (!childHu.hasContentClass(LdapObj.organizationalUnit.qName())) continue; // FIXME filter out functional hierarchy unit for (Content role : childHu) { if (role.hasContentClass(cclass)) { - if (LdapObjs.posixAccount.qName().equals(cclass)) { - Long id = role.get(LdapAttrs.uidNumber.qName(), Long.class).orElseThrow(); + if (LdapObj.posixAccount.qName().equals(cclass)) { + Long id = role.get(LdapAttr.uidNumber.qName(), Long.class).orElseThrow(); if (id > currentMax) currentMax = id; } diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/XForms-11-Schema.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/XForms-11-Schema.xsd new file mode 100644 index 0000000..881bfcb --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/XForms-11-Schema.xsd @@ -0,0 +1,1571 @@ + + + + + + + + + + Attributes for _every_ element in XForms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + instance container. + + + + + + + + + + + + + + Definition of bind container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Attributes for _every_ action in XForms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + submit info container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This type defines the basic lexical properties for a dataypte that can be used to represent + various ID numbers such as for debit and credit cards. + This type does not apply the Luhn checksum algorithm. + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.dtd b/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.dtd new file mode 100644 index 0000000..e8e8f76 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.dtd @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +%xs-datatypes; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.xsd new file mode 100644 index 0000000..12c2209 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/XMLSchema.xsd @@ -0,0 +1,2534 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]> + + + + Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp + Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp + + + + + + The schema corresponding to this document is normative, + with respect to the syntactic constraints it expresses in the + XML Schema language. The documentation (within <documentation> elements) + below, is not normative, but rather highlights important aspects of + the W3C Recommendation of which this is a part + + + + + The simpleType element and all of its members are defined + towards the end of this schema document + + + + + + Get access to the xml: attribute groups for xml:lang + as declared on 'schema' and 'documentation' below + + + + + + + + This type is extended by almost all schema types + to allow attributes from other namespaces to be + added to user schemas. + + + + + + + + + + + + + This type is extended by all types which allow annotation + other than <schema> itself + + + + + + + + + + + + + + + + This group is for the + elements which occur freely at the top level of schemas. + All of their types are based on the "annotated" type by extension. + + + + + + + + + + + + + This group is for the + elements which can self-redefine (see <redefine> below). + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction} + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {extension, restriction, list, union} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for maxOccurs + + + + + + + + + + + + for all particles + + + + + + + for element, group and attributeGroup, + which both define and reference + + + + + + + + 'complexType' uses this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This branch is short for + <complexContent> + <restriction base="xs:anyType"> + ... + </restriction> + </complexContent> + + + + + + + + + + + + + + + Will be restricted to required or forbidden + + + + + + Not allowed if simpleContent child is chosen. + May be overriden by setting on complexContent child. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overrides any setting on complexType parent. + + + + + + + + + + + + + + + This choice is added simply to + make this a valid restriction per the REC + + + + + + + + + + + + + + + + + No typeDefParticle group reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + #all or (possibly empty) subset of {substitution, extension, + restriction} + + + + + + + + + + + + + + + + + + + + + + + + + The element element can be used either + at the top level to define an element-type binding globally, + or within a content model to either reference a globally-defined + element or type or declare an element-type binding locally. + The ref form is not allowed at the top level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for explicit groups, named top-level groups and + group references + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group type for the three kinds of group + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This choice with min/max is here to + avoid a pblm with the Elt:All/Choice/Seq + Particle derivation constraint + + + + + + + + + + restricted max/min + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Only elements allowed inside + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simple type for the value of the 'namespace' attr of + 'any' and 'anyAttribute' + + + + Value is + ##any - - any non-conflicting WFXML/attribute at all + + ##other - - any non-conflicting WFXML/attribute from + namespace other than targetNS + + ##local - - any unqualified non-conflicting WFXML/attribute + + one or - - any non-conflicting WFXML/attribute from + more URI the listed namespaces + references + (space separated) + + ##targetNamespace or ##local may appear in the above list, to + refer to the targetNamespace of the enclosing + schema or an absent targetNamespace respectively + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in selectors + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the following EBNF: + Selector ::= Path ( '|' Path )* + Path ::= ('.//')? Step ( '/' Step )* + Step ::= '.' | NameTest + NameTest ::= QName | '*' | NCName ':' '*' + child:: is also allowed + + + + + + + + + + + + + + + + + + + + + + + A subset of XPath expressions for use +in fields + A utility type, not for public +use + + + + The following pattern is intended to allow XPath + expressions per the same EBNF as for selector, + with the following change: + Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest ) + + + + + + + + + + + + + + + + + + + + + + + + + + + The three kinds of identity constraints, all with + type of or derived from 'keybase'. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + A public identifier, per ISO 8879 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + notations for use within XML Schema schemas + + + + + + + + + Not the real urType, but as close an approximation as we can + get in the XML representation + + + + + + + + + + First the built-in primitive datatypes. These definitions are for + information only, the real built-in definitions are magic. + + + + For each built-in datatype in this schema (both primitive and + derived) can be uniquely addressed via a URI constructed + as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype + + For example, to address the int datatype, the URI is: + + http://www.w3.org/2001/XMLSchema#int + + Additionally, each facet definition element can be uniquely + addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the facet + + For example, to address the maxInclusive facet, the URI is: + + http://www.w3.org/2001/XMLSchema#maxInclusive + + Additionally, each facet usage in a built-in datatype definition + can be uniquely addressed via a URI constructed as follows: + 1) the base URI is the URI of the XML Schema namespace + 2) the fragment identifier is the name of the datatype, followed + by a period (".") followed by the name of the facet + + For example, to address the usage of the maxInclusive facet in + the definition of int, the URI is: + + http://www.w3.org/2001/XMLSchema#int.maxInclusive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTATION cannot be used directly in a schema; rather a type + must be derived from it by specifying at least one enumeration + facet whose value is the name of a NOTATION declared in the + schema. + + + + + + + + + + Now the derived primitive types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern specifies the content of section 2.12 of XML 1.0e2 + and RFC 3066 (Revised version of RFC 1766). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 7 from the XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern matches production 5 from the XML spec + + + + + + + + + + + + + + + pattern matches production 4 from the Namespaces in XML spec + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + #all or (possibly empty) subset of {restriction, union, list} + + + A utility type, not for public use + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Can be restricted to required or forbidden + + + + + + + + + + + + + + + + + + Required at the top level + + + + + + + + + + + + + + + + + + + Forbidden when nested + + + + + + + + + + + + + + + + + + + We should use a substitution group for facets, but + that's ruled out because it would allow users to + add their own, which we're not ready for yet. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + base attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + itemType attribute and simpleType child are mutually + exclusive, but one or other is required + + + + + + + + + + + + + + + + + + memberTypes attribute must be non-empty or there must be + at least one simpleType child + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/datatypes.dtd b/org.argeo.app.core/src/org/argeo/app/core/schemas/datatypes.dtd new file mode 100644 index 0000000..8e48553 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/datatypes.dtd @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/docbook.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/docbook.xsd new file mode 100644 index 0000000..f2c9aed --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/docbook.xsd @@ -0,0 +1,17461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/xlink.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/xlink.xsd new file mode 100644 index 0000000..e9ce635 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/xlink.xsd @@ -0,0 +1,293 @@ + + + + + This schema is not normative, or even definitive. The +prose copy in the XLink 1.1 recommendation (http://www.w3.org/TR/xlink11/) is +definitive, although it should not differ from this file, except for the +absence of these two initial comments. + + + + In keeping with the W3C's standard versioning + policy, this schema document will persist at + http://www.w3.org/XML/2008/06/xlink.xsd. + At the date of issue it can also be found at + http://www.w3.org/1999/xlink.xsd. + The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML Schema + itself, or with the XLink namespace itself. In other words, if the XML + Schema or XLink namespaces change, the version of this document at + http://www.w3.org/1999/xlink.xsd will change + accordingly; the version at + http://www.w3.org/2008/06/xlink.xsd will not change. + + + + + This schema document provides attribute declarations and +attribute group, complex type and simple type definitions which can be used in +the construction of user schemas to define the structure of particular linking +constructs, e.g. + +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xl="http://www.w3.org/1999/xlink"> + + <xs:import namespace="http://www.w3.org/1999/xlink" + location="http://www.w3.org/1999/xlink.xsd"> + + <xs:element name="mySimple"> + <xs:complexType> + ... + <xs:attributeGroup ref="xl:simpleAttrs"/> + ... + </xs:complexType> + </xs:element> + ... +</xs:schema> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Intended for use as the type of user-declared elements to make them + simple links. + + + + + + + + + + + + + + + + + + + + + + + + + Intended for use as the type of user-declared elements to make them + extended links. + Note that the elements referenced in the content model are all abstract. + The intention is that by simply declaring elements with these as their + substitutionGroup, all the right things will happen. + + + + + + + + + + + + + + xml:lang is not required, but provides much of the + motivation for title elements in addition to attributes, and so + is provided here for convenience. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + label is not required, but locators have no particular + XLink function if they are not labeled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + from and to have default behavior when values are missing + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/xml-events-attribs-1.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/xml-events-attribs-1.xsd new file mode 100644 index 0000000..ef99128 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/xml-events-attribs-1.xsd @@ -0,0 +1,73 @@ + + + + + + This is the XML Schema for XML Events global attributes + + URI: http://www.w3.org/MarkUp/SCHEMA/xml-events-attribs-1.xsd + $Id: xml-events-attribs-1.xsd,v 1.7 2004/11/22 17:09:15 ahby Exp $ + + + + + + + XML Event Attributes + + These "global" event attributes are defined in "Attaching + Attributes Directly to the Observer Element" of the XML + Events specification. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/xml.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/xml.xsd new file mode 100644 index 0000000..aea7d0d --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+ + + + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ + + diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java b/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java index 6c959f4..b0d352b 100644 --- a/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java @@ -70,7 +70,7 @@ public class DbkUtils { public static void setTitle(Node node, String txt) { Node titleNode = getOrAddDbk(node, DbkType.title); - JcrxApi.setXmlValue(node, titleNode, txt); + JcrxApi.setXmlValue(titleNode, txt); } public static Node getMetadata(Node infoContainer) { @@ -103,7 +103,7 @@ public class DbkUtils { public static Node addParagraph(Node node, String txt) { Node p = addDbk(node, para); - JcrxApi.setXmlValue(node, p, txt); + JcrxApi.setXmlValue(p, txt); return p; } diff --git a/org.argeo.app.core/src/org/argeo/app/geo/GmlAttr.java b/org.argeo.app.core/src/org/argeo/app/geo/GmlAttr.java new file mode 100644 index 0000000..77b0885 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/geo/GmlAttr.java @@ -0,0 +1,22 @@ +package org.argeo.app.geo; + +import org.argeo.api.acr.QNamed; + +public enum GmlAttr implements QNamed { + uom + // + ; + + public final static String UOM_SQUARE_METERS = "m2"; + + @Override + public String getNamespace() { + return "http://www.opengis.net/gml/3.2"; + } + + @Override + public String getDefaultPrefix() { + return "gml"; + } + +} diff --git a/org.argeo.app.core/src/org/argeo/app/geo/GmlType.java b/org.argeo.app.core/src/org/argeo/app/geo/GmlType.java new file mode 100644 index 0000000..980a5e4 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/geo/GmlType.java @@ -0,0 +1,20 @@ +package org.argeo.app.geo; + +import org.argeo.api.acr.QNamed; + +public enum GmlType implements QNamed { + measure + // + ; + + @Override + public String getNamespace() { + return "http://www.opengis.net/gml/3.2"; + } + + @Override + public String getDefaultPrefix() { + return "gml"; + } + +} diff --git a/org.argeo.app.core/src/org/argeo/app/geo/GpxUtils.java b/org.argeo.app.core/src/org/argeo/app/geo/GpxUtils.java index be028d3..5bb7d5c 100644 --- a/org.argeo.app.core/src/org/argeo/app/geo/GpxUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/geo/GpxUtils.java @@ -33,7 +33,6 @@ public class GpxUtils { GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(); List coordinates = new ArrayList<>(); SAXParserFactory factory = SAXParserFactory.newInstance(); - Double[] startCoord = new Double[2]; SAXParser saxParser = factory.newSAXParser(); saxParser.parse(in, new DefaultHandler() { diff --git a/org.argeo.app.core/src/org/argeo/app/geo/geonames/ImportGeonamesAdmin.java b/org.argeo.app.core/src/org/argeo/app/geo/geonames/ImportGeonamesAdmin.java index 52456bb..be2b507 100644 --- a/org.argeo.app.core/src/org/argeo/app/geo/geonames/ImportGeonamesAdmin.java +++ b/org.argeo.app.core/src/org/argeo/app/geo/geonames/ImportGeonamesAdmin.java @@ -10,8 +10,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.argeo.util.CsvParser; -import org.argeo.util.CsvWriter; +import org.argeo.cms.util.CsvParser; +import org.argeo.cms.util.CsvWriter; /** Import GeoNames administrative division from the main table. */ public class ImportGeonamesAdmin { diff --git a/org.argeo.app.core/src/org/argeo/app/image/ImageProcessor.java b/org.argeo.app.core/src/org/argeo/app/image/ImageProcessor.java index 7fd308d..72c334f 100644 --- a/org.argeo.app.core/src/org/argeo/app/image/ImageProcessor.java +++ b/org.argeo.app.core/src/org/argeo/app/image/ImageProcessor.java @@ -66,9 +66,9 @@ public class ImageProcessor { Files.deleteIfExists(temp); } } else { - try (OutputStream out = outSupplier.call()) { - copyWithMetadata(() -> in, metadata); - } +// try (OutputStream out = outSupplier.call()) { + copyWithMetadata(() -> in, metadata); +// } } } } catch (Exception e) { diff --git a/org.argeo.app.core/src/org/argeo/app/library/DocxExtractor.java b/org.argeo.app.core/src/org/argeo/app/library/DocxExtractor.java index 17c6cf2..c96126c 100644 --- a/org.argeo.app.core/src/org/argeo/app/library/DocxExtractor.java +++ b/org.argeo.app.core/src/org/argeo/app/library/DocxExtractor.java @@ -22,7 +22,7 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.argeo.util.DigestUtils; +import org.argeo.cms.util.DigestUtils; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; diff --git a/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java b/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java index ddb5650..4d2f521 100644 --- a/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java @@ -18,10 +18,10 @@ import javax.jcr.nodetype.NodeType; import org.argeo.api.cms.CmsLog; import org.argeo.app.api.EntityMimeType; import org.argeo.app.api.EntityType; +import org.argeo.cms.util.DigestUtils; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrUtils; import org.argeo.jcr.JcrxApi; -import org.argeo.util.DigestUtils; /** Utilities around ODK. */ public class OdkUtils { diff --git a/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java b/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java index 7122892..0dff64c 100644 --- a/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java +++ b/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java @@ -5,6 +5,9 @@ import javax.jcr.RepositoryException; /** Called when a user has received a new form submission. */ public interface FormSubmissionListener { - /** Called after a form submission has been stored in the user area. */ + /** + * Called after a form submission has been stored in the user area. The + * submission will be deleted if any exception is thrown. + */ void formSubmissionReceived(Node node) throws RepositoryException; } diff --git a/org.argeo.app.servlet.odk/OSGI-INF/odkFormListServlet.xml b/org.argeo.app.servlet.odk/OSGI-INF/odkFormListServlet.xml index 18f3465..3430eda 100644 --- a/org.argeo.app.servlet.odk/OSGI-INF/odkFormListServlet.xml +++ b/org.argeo.app.servlet.odk/OSGI-INF/odkFormListServlet.xml @@ -6,6 +6,5 @@ - - + diff --git a/org.argeo.app.servlet.odk/OSGI-INF/odkFormServlet.xml b/org.argeo.app.servlet.odk/OSGI-INF/odkFormServlet.xml index 816524a..82ed712 100644 --- a/org.argeo.app.servlet.odk/OSGI-INF/odkFormServlet.xml +++ b/org.argeo.app.servlet.odk/OSGI-INF/odkFormServlet.xml @@ -6,6 +6,5 @@ - - + diff --git a/org.argeo.app.servlet.odk/OSGI-INF/odkManifestServlet.xml b/org.argeo.app.servlet.odk/OSGI-INF/odkManifestServlet.xml index 503a148..6cac13e 100644 --- a/org.argeo.app.servlet.odk/OSGI-INF/odkManifestServlet.xml +++ b/org.argeo.app.servlet.odk/OSGI-INF/odkManifestServlet.xml @@ -6,5 +6,5 @@ - + diff --git a/org.argeo.app.servlet.odk/OSGI-INF/odkSubmissionServlet.xml b/org.argeo.app.servlet.odk/OSGI-INF/odkSubmissionServlet.xml index b9380d5..40ed568 100644 --- a/org.argeo.app.servlet.odk/OSGI-INF/odkSubmissionServlet.xml +++ b/org.argeo.app.servlet.odk/OSGI-INF/odkSubmissionServlet.xml @@ -7,6 +7,6 @@ - + diff --git a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkFormListServlet.java b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkFormListServlet.java index 21b3421..41a3039 100644 --- a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkFormListServlet.java +++ b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkFormListServlet.java @@ -23,6 +23,7 @@ import org.argeo.app.odk.OrxListName; import org.argeo.app.odk.OrxManifestName; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.cms.servlet.ServletUtils; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrxApi; @@ -39,9 +40,17 @@ public class OdkFormListServlet extends HttpServlet { resp.setHeader("X-OpenRosa-Version", "1.0"); resp.setDateHeader("Date", System.currentTimeMillis()); - String serverName = req.getServerName(); - int serverPort = req.getServerPort(); - String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; +//// String serverName = req.getServerName(); +//// int serverPort = req.getServerPort(); +//// String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; +//// String baseServer = protocol + "://" + serverName +//// + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort); +// String requestUri=req.getRequestURI(); +// String forwardedHost = req.getHeader("X-Forwarded-Host"); +// URL requestUrl = new URL(req.getRequestURL().toString()); +// String baseServer = requestUrl.getProtocol() + "://" + requestUrl.getHost() +// + (requestUrl.getPort() > 0 ? ":" + requestUrl.getPort() : ""); + StringBuilder baseServer = ServletUtils.getRequestUrlBase(req); String pathInfo = req.getPathInfo(); @@ -77,23 +86,17 @@ public class OdkFormListServlet extends HttpServlet { sb.append("md5:" + JcrxApi.getChecksum(node, JcrxApi.MD5) + ""); if (node.hasProperty(Property.JCR_DESCRIPTION)) sb.append("" + node.getProperty(Property.JCR_DESCRIPTION).getString() + ""); - sb.append("" + protocol + "://" + serverName - + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form" - + node.getPath() + ""); + sb.append("" + baseServer + "/api/odk/form" + node.getPath() + ""); if (node.hasNode(OrxManifestName.manifest.name())) { - sb.append("" + protocol + "://" + serverName - + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) - + "/api/odk/formManifest" + node.getNode(OrxManifestName.manifest.name()).getPath() - + ""); + sb.append("" + baseServer + "/api/odk/formManifest" + + node.getNode(OrxManifestName.manifest.name()).getPath() + ""); } sb.append(""); } else if (node.isNodeType(EntityType.formSet.get())) { sb.append(""); sb.append("" + node.getPath() + ""); sb.append("" + node.getProperty(Property.JCR_TITLE).getString() + ""); - sb.append("" + protocol + "://" + serverName - + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/formList" - + node.getPath() + ""); + sb.append("" + baseServer + "/api/odk/formList" + node.getPath() + ""); sb.append(""); } String str = sb.toString(); diff --git a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java index f296170..2c62ba1 100644 --- a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java +++ b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java @@ -33,10 +33,11 @@ import org.argeo.app.api.EntityMimeType; import org.argeo.app.odk.OrxManifestName; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.cms.servlet.ServletUtils; +import org.argeo.cms.util.CsvWriter; +import org.argeo.cms.util.DigestUtils; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; -import org.argeo.util.CsvWriter; -import org.argeo.util.DigestUtils; /** Describe additional files. */ public class OdkManifestServlet extends HttpServlet { @@ -53,9 +54,12 @@ public class OdkManifestServlet extends HttpServlet { if (pathInfo.startsWith("//")) pathInfo = pathInfo.substring(1); - String serverName = req.getServerName(); - int serverPort = req.getServerPort(); - String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; +// String serverName = req.getServerName(); +// int serverPort = req.getServerPort(); +// String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; +// String baseServer = protocol + "://" + serverName +// + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort); + StringBuilder baseServer = ServletUtils.getRequestUrlBase(req); Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), new ServletHttpRequest(req)); @@ -98,9 +102,8 @@ public class OdkManifestServlet extends HttpServlet { writer.append("md5sum:" + DigestUtils.toHexString(out.getMessageDigest().digest())); writer.append(""); } - writer.append("" + protocol + "://" + serverName - + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) - + "/api/odk/formManifest" + file.getPath() + ""); + writer.append("" + baseServer + "/api/odk/formManifest" + file.getPath() + + ""); } writer.append(""); } diff --git a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java index a5864d8..8dd8b0b 100644 --- a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java +++ b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java @@ -14,7 +14,6 @@ import javax.jcr.ImportUUIDBehavior; import javax.jcr.Node; import javax.jcr.Property; import javax.jcr.Repository; -import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.servlet.ServletException; @@ -60,24 +59,24 @@ public class OdkSubmissionServlet extends HttpServlet { RemoteAuthRequest request = new ServletHttpRequest(req); Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), request); - try { - CmsSession cmsSession = RemoteAuthUtils.getCmsSession(request); + CmsSession cmsSession = RemoteAuthUtils.getCmsSession(request); - Session adminSession = null; - try { - // TODO centralise at a deeper level - adminSession = CmsJcrUtils.openDataAdminSession(repository, null); - SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); - } finally { - Jcr.logout(adminSession); - } + Session adminSession = null; + try { + // TODO centralise at a deeper level + adminSession = CmsJcrUtils.openDataAdminSession(repository, null); + SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); + } finally { + Jcr.logout(adminSession); + } + try { Node cmsSessionNode = SuiteUtils.getCmsSessionNode(session, cmsSession); Node submission = cmsSessionNode.addNode(submissionNameFormatter.format(Instant.now()), OrxType.submission.get()); for (Part part : req.getParts()) { - if (log.isDebugEnabled()) - log.debug("Part: " + part.getName() + ", " + part.getContentType()); + if (log.isTraceEnabled()) + log.trace("Part: " + part.getName() + ", " + part.getContentType()); if (part.getName().equals(XML_SUBMISSION_FILE)) { Node xml = submission.addNode(XML_SUBMISSION_FILE, NodeType.NT_UNSTRUCTURED); @@ -112,12 +111,22 @@ public class OdkSubmissionServlet extends HttpServlet { } } } + session.save(); - for (FormSubmissionListener submissionListener : submissionListeners) { - submissionListener.formSubmissionReceived(submission); + try { + for (FormSubmissionListener submissionListener : submissionListeners) { + submissionListener.formSubmissionReceived(submission); + } + } catch (Exception e) { + log.error("Cannot save submision, cancelling...", e); + submission.remove(); + session.save(); + resp.setStatus(503); + return; } - } catch (RepositoryException e) { - e.printStackTrace(); + + } catch (Exception e) { + log.error("Cannot save submision", e); resp.setStatus(503); return; } finally { diff --git a/org.argeo.app.servlet.publish/bnd.bnd b/org.argeo.app.servlet.publish/bnd.bnd index 126547a..5535251 100644 --- a/org.argeo.app.servlet.publish/bnd.bnd +++ b/org.argeo.app.servlet.publish/bnd.bnd @@ -2,13 +2,10 @@ Import-Package:\ org.apache.xmlgraphics.image.loader,\ org.osgi.service.http.context,\ javax.jcr.nodetype,\ -org.osgi.service.event,\ -org.eclipse.swt,\ -org.eclipse.jface.viewers,\ org.osgi.framework,\ org.apache.xml.serializer,\ -org.eclipse.rap.rwt,\ org.argeo.app.api,\ +org.argeo.cms.acr.xml,\ javax.servlet.*;version="[3,5)",\ * diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java index dfef911..f2d38e1 100644 --- a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java @@ -43,6 +43,7 @@ import org.apache.commons.io.IOUtils; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.xalan.processor.TransformerFactoryImpl; +import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.ux.CmsTheme; import org.argeo.app.docbook.DbkType; import org.argeo.app.docbook.DbkUtils; @@ -59,6 +60,8 @@ import org.w3c.dom.Document; public class DbkServlet extends HttpServlet { private static final long serialVersionUID = 6906020513498289335L; + private CmsLog log = CmsLog.getLog(DbkServlet.class); + private Repository repository; private DocumentBuilderFactory documentBuilderFactory; @@ -70,6 +73,8 @@ public class DbkServlet extends HttpServlet { private Map themes = Collections.synchronizedMap(new HashMap<>()); + private String xslBase; + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -139,7 +144,7 @@ public class DbkServlet extends HttpServlet { Source xmlInput = new DOMSource(doc); if (pdf) { - //String baseUri = req.getRequestURI(); + // String baseUri = req.getRequestURI(); FopFactory fopFactory = FopFactory.newInstance(URI.create(req.getRequestURL().toString())); resp.setContentType("application/pdf"); @@ -161,16 +166,16 @@ public class DbkServlet extends HttpServlet { // fopTransformer.transform(src, fopResult); // } // - + Fop fop = fopFactory.newFop("application/pdf", resp.getOutputStream()); - Transformer docBookTransformer = docBoookFoTemplates.newTransformer(); + Transformer docBookTransformer = getDocBoookFoTemplates().newTransformer(); Result fopResult = new SAXResult(fop.getDefaultHandler()); docBookTransformer.transform(xmlInput, fopResult); } else { Result xmlOutput = new StreamResult(resp.getOutputStream()); resp.setContentType("text/html"); - Transformer docBookTransformer = docBoookHtmlTemplates.newTransformer(); + Transformer docBookTransformer = getDocBoookHtmlTemplates().newTransformer(); // gather CSS if (cmsTheme != null) { @@ -226,7 +231,7 @@ public class DbkServlet extends HttpServlet { public void init() throws ServletException { // TODO improve configuration and provisioning of DocBook XSL - String xslBase = System.getProperty("argeo.docbook.xsl"); + xslBase = System.getProperty("argeo.docbook.xsl"); if (xslBase == null) { // We need namespace aware XSL! // Fedora (sudo dnf install docbook5-style-xsl) @@ -234,8 +239,8 @@ public class DbkServlet extends HttpServlet { if (!Files.exists(Paths.get(defaultXslBase))) { defaultXslBase = "/opt/docbook-xsl"; if (!Files.exists(Paths.get(defaultXslBase))) { - throw new ServletException("System property argeo.docbook.xsl is not set and default location " - + defaultXslBase + " does not exist."); + log.error("System property argeo.docbook.xsl is not set and default location " + defaultXslBase + + " does not exist."); } } xslBase = defaultXslBase; @@ -246,18 +251,15 @@ public class DbkServlet extends HttpServlet { documentBuilderFactory.setXIncludeAware(true); documentBuilderFactory.setNamespaceAware(true); - // We must explicitly use the non-XSLTC transformer, as XSLTC is not working - // with DocBook stylesheets - transformerFactory = new TransformerFactoryImpl(); - - String htmlXsl = xslBase + "/html/docbook.xsl"; - docBoookHtmlTemplates = createDocBookTemplates(htmlXsl); - String foXsl = xslBase + "/fo/docbook.xsl"; - docBoookFoTemplates = createDocBookTemplates(foXsl); } protected Templates createDocBookTemplates(String xsl) { try { + if (transformerFactory == null) { + // We must explicitly use the non-XSLTC transformer, as XSLTC is not working + // with DocBook stylesheets + transformerFactory = new TransformerFactoryImpl(); + } Source xslSource = new StreamSource(xsl); Templates templates = transformerFactory.newTemplates(xslSource); if (templates == null) @@ -269,6 +271,22 @@ public class DbkServlet extends HttpServlet { } + protected Templates getDocBoookHtmlTemplates() { + if (docBoookHtmlTemplates == null) { + String htmlXsl = xslBase + "/html/docbook.xsl"; + docBoookHtmlTemplates = createDocBookTemplates(htmlXsl); + } + return docBoookHtmlTemplates; + } + + protected Templates getDocBoookFoTemplates() { + if (docBoookFoTemplates == null) { + String foXsl = xslBase + "/fo/docbook.xsl"; + docBoookFoTemplates = createDocBookTemplates(foXsl); + } + return docBoookFoTemplates; + } + public void setRepository(Repository repository) { this.repository = repository; } diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java index bcba255..1ee19f0 100644 --- a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java @@ -3,6 +3,8 @@ package org.argeo.app.servlet.publish; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; import java.net.URI; import java.net.URL; import java.net.URLDecoder; @@ -39,9 +41,15 @@ import org.apache.xmlgraphics.io.ResourceResolver; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentRepository; import org.argeo.api.acr.ContentSession; +import org.argeo.app.geo.GeoUtils; +import org.argeo.app.geo.GpxUtils; +import org.argeo.cms.acr.xml.XmlNormalizer; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.servlet.ServletHttpRequest; -import org.argeo.util.LangUtils; +import org.argeo.cms.util.LangUtils; +import org.geotools.data.collection.ListFeatureCollection; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.opengis.feature.simple.SimpleFeature; /** * A servlet transforming an XML view of the data to either FOP or PDF. @@ -70,8 +78,6 @@ public class FopServlet extends HttpServlet { boolean pdf = ".pdf".equals(ext); ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(), new ServletHttpRequest(req)); Content content = session.get(path); - Source xmlInput = content.adapt(Source.class); - xmlInput.setSystemId(req.getRequestURI()); // dev only final boolean DEV = false; @@ -84,19 +90,48 @@ public class FopServlet extends HttpServlet { } catch (TransformerConfigurationException | IOException e) { throw new IllegalStateException("Cannot instantiate XSL " + xslUrl, e); } + + Source xmlInput = content.adapt(Source.class); + XmlNormalizer.print(xmlInput,0); } + Source xmlInput = content.adapt(Source.class); + String systemId = req.getRequestURL().toString(); + xmlInput.setSystemId(systemId); + URIResolver uriResolver = (href, base) -> { try { - URL url = new URL(href); - if (url.getProtocol().equals("file")) { - InputStream in = Files.newInputStream(Paths.get(URI.create(url.toString()))); - return new StreamSource(in); + URI url = URI.create(href); + if (url.getScheme() != null) { + if (url.getScheme().equals("file")) { + InputStream in = Files.newInputStream(Paths.get(URI.create(url.toString()))); + return new StreamSource(in); + } + if (url.getScheme().equals("geo2svg")) { + String includePath = path + url.getPath(); + String geoExt = includePath.substring(includePath.lastIndexOf('.')); + Content geoContent = session.get(includePath); + if (".gpx".equals(geoExt)) { + try (InputStream in = geoContent.open(InputStream.class)) { + SimpleFeature field = GpxUtils.parseGpxToPolygon(in); + SimpleFeatureCollection features = new ListFeatureCollection(field.getType(), field); + try (StringWriter writer = new StringWriter()) { + GeoUtils.exportToSvg(features, writer, 100, 100); + StreamSource res = new StreamSource(new StringReader(writer.toString())); + return res; + } + } + } else { + throw new UnsupportedOperationException(geoExt + " is not supported"); + } + } } } catch (IOException e) { - // silent + throw new RuntimeException("Cannot process " + href); } - Content subContent = session.get(href); + + String p = href.startsWith("/") ? href : path + '/' + href; + Content subContent = session.get(p); return subContent.adapt(Source.class); }; diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java new file mode 100644 index 0000000..c22b834 --- /dev/null +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/GeoToSvgServlet.java @@ -0,0 +1,67 @@ +package org.argeo.app.servlet.publish; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentRepository; +import org.argeo.api.acr.ContentSession; +import org.argeo.app.geo.GeoUtils; +import org.argeo.app.geo.GpxUtils; +import org.argeo.cms.auth.RemoteAuthUtils; +import org.argeo.cms.servlet.ServletHttpRequest; +import org.geotools.data.collection.ListFeatureCollection; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.opengis.feature.simple.SimpleFeature; + +/** + * A servlet transforming an geographical data to SVG. + */ +public class GeoToSvgServlet extends HttpServlet { + private static final long serialVersionUID = -6346379324580671894L; + private ContentRepository contentRepository; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String servletPath = req.getServletPath(); + + servletPath = servletPath.substring(1, servletPath.lastIndexOf('.')); + servletPath = servletPath.substring(servletPath.indexOf('/'), servletPath.length()); + String path = URLDecoder.decode(servletPath, StandardCharsets.UTF_8); + String ext = servletPath.substring(path.lastIndexOf('.')); + + resp.setContentType("image/svg+xml"); + + ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(), new ServletHttpRequest(req)); + Content content = session.get(path); + if (".gpx".equals(ext)) { + try (InputStream in = content.open(InputStream.class)) { + SimpleFeature field = GpxUtils.parseGpxToPolygon(in); + + SimpleFeatureCollection features = new ListFeatureCollection(field.getType(), field); + GeoUtils.exportToSvg(features, resp.getWriter(), 100, 100); +// log.debug("SVG:\n" + writer.toString() + "\n"); + } + } + } + + public void start(Map properties) { + } + + public void stop(Map properties) { + + } + + public void setContentRepository(ContentRepository contentRepository) { + this.contentRepository = contentRepository; + } + +} diff --git a/org.argeo.app.theme.default/bnd.bnd b/org.argeo.app.theme.default/bnd.bnd index 48c9586..4743076 100644 --- a/org.argeo.app.theme.default/bnd.bnd +++ b/org.argeo.app.theme.default/bnd.bnd @@ -6,6 +6,6 @@ Private-Package: * Export-Package: !* Import-Package:\ -org.argeo.cms.swt.osgi,\ +org.argeo.cms.swt.osgi;resolution:="optional",\ javax.servlet.*;version="[3,5)",\ * \ No newline at end of file diff --git a/sdk/argeo-build b/sdk/argeo-build index 639ffc6..5bf3887 160000 --- a/sdk/argeo-build +++ b/sdk/argeo-build @@ -1 +1 @@ -Subproject commit 639ffc61c03f6aebf9cfe5e0f79dd0c4c6aa632a +Subproject commit 5bf388711048a659992c292c4b9bdcbc595e066b diff --git a/sdk/branches/unstable.bnd b/sdk/branches/unstable.bnd index e590c4c..b8e5aef 100644 --- a/sdk/branches/unstable.bnd +++ b/sdk/branches/unstable.bnd @@ -1,4 +1,4 @@ major=2 minor=3 -micro=7 +micro=12 qualifier=.next \ No newline at end of file diff --git a/sdk/deploy/.gitignore b/sdk/deploy/.gitignore new file mode 100644 index 0000000..f5135fa --- /dev/null +++ b/sdk/deploy/.gitignore @@ -0,0 +1 @@ +!bin \ No newline at end of file diff --git a/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/config.ini b/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/config.ini new file mode 100644 index 0000000..b8dab60 --- /dev/null +++ b/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/config.ini @@ -0,0 +1,36 @@ +osgi.clean=true + +argeo.osgi.start.2=\ +org.eclipse.equinox.http.servlet,\ +org.eclipse.equinox.console,\ +org.apache.felix.scr,\ +org.eclipse.core.runtime,\ +org.eclipse.e4.ui.css.swt,\ + +argeo.osgi.start.3=\ +org.argeo.cms,\ +org.argeo.cms.swt.rcp,\ +org.argeo.cms.ee,\ +org.argeo.cms.lib.sshd,\ +org.argeo.cms.lib.equinox,\ +org.argeo.cms.lib.jetty,\ + +argeo.osgi.start.4=\ +org.argeo.cms.jcr + +argeo.osgi.start.5=\ +org.argeo.app.profile.acr.fs,\ +org.argeo.app.core,\ +org.argeo.app.ui,\ +org.argeo.app.theme.default,\ +org.argeo.app.servlet.publish,\ +org.argeo.app.servlet.odk + + +# Local +argeo.node.repo.type=h2 +argeo.http.port=0 + +argeo.osgi.sources=\ +a2+reference:///?swt=rcp,\ +a2+reference:///usr/lib/a2/?swt=rcp diff --git a/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/jvm.args b/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/jvm.args new file mode 100644 index 0000000..e69de29 diff --git a/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/system.properties b/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/system.properties new file mode 100644 index 0000000..62fe6da --- /dev/null +++ b/sdk/deploy/argeo-desktop/etc/argeo.user.d/desktop/system.properties @@ -0,0 +1 @@ +log.org.argeo=INFO \ No newline at end of file diff --git a/sdk/deploy/argeo-desktop/usr/share/applications/argeo.desktop b/sdk/deploy/argeo-desktop/usr/share/applications/argeo.desktop new file mode 100644 index 0000000..b0666ce --- /dev/null +++ b/sdk/deploy/argeo-desktop/usr/share/applications/argeo.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=Argeo +Exec=/usr/bin/argeo-desktop-open argeo/app +#Icon= +Type=Application diff --git a/sdk/deploy/argeo-server/etc/argeo.d/server/config.ini b/sdk/deploy/argeo-server/etc/argeo.d/server/config.ini new file mode 100644 index 0000000..8d9f12c --- /dev/null +++ b/sdk/deploy/argeo-server/etc/argeo.d/server/config.ini @@ -0,0 +1,44 @@ +osgi.clean=true + +argeo.osgi.start.2=\ +org.eclipse.equinox.http.servlet,\ +org.eclipse.equinox.console,\ +org.apache.felix.scr,\ +org.eclipse.rap.rwt.osgi,\ + +argeo.osgi.start.3=\ +org.argeo.cms,\ +org.argeo.cms.swt.rap,\ +org.argeo.cms.ee,\ +org.argeo.cms.lib.sshd,\ +org.argeo.cms.lib.equinox,\ +org.argeo.cms.lib.jetty,\ + +argeo.osgi.start.4=\ +org.argeo.cms.jcr + +argeo.osgi.start.5=\ +org.argeo.app.profile.acr.fs,\ +org.argeo.app.core,\ +org.argeo.app.ui,\ +org.argeo.app.theme.default,\ +org.argeo.app.servlet.publish,\ +org.argeo.app.servlet.odk + +# Local +argeo.http.port=8080 +#argeo.https.port=8443 + +argeo.directory=dc=example,dc=com.ldif +#argeo.directory=ldap://cn=Directory%20Manager:password@localhost/dc=example,dc=com +#argeo.directory=ipa:/// + +argeo.node.repo.type=h2 + +#argeo.node.repo.type=postgresql_ds +#argeo.node.repo.dburl=jdbc:postgresql://localhost/argeo +#argeo.node.repo.dbuser= +#argeo.node.repo.dbpassword= + +argeo.osgi.sources=\ +a2+reference:///?swt=rap diff --git a/sdk/deploy/argeo-server/etc/argeo.d/server/jvm.args b/sdk/deploy/argeo-server/etc/argeo.d/server/jvm.args new file mode 100644 index 0000000..e69de29 diff --git a/sdk/deploy/argeo-server/etc/argeo.d/server/system.properties b/sdk/deploy/argeo-server/etc/argeo.d/server/system.properties new file mode 100644 index 0000000..62fe6da --- /dev/null +++ b/sdk/deploy/argeo-server/etc/argeo.d/server/system.properties @@ -0,0 +1 @@ +log.org.argeo=INFO \ No newline at end of file diff --git a/sdk/output-cms-rap.target b/sdk/output-cms-rap.target index b6f3036..5031f01 100644 --- a/sdk/output-cms-rap.target +++ b/sdk/output-cms-rap.target @@ -3,16 +3,8 @@ - - - - - - - - - - - + + + \ No newline at end of file diff --git a/sdk/output-cms-rcp.target b/sdk/output-cms-rcp.target index 9e81871..7eec71a 100644 --- a/sdk/output-cms-rcp.target +++ b/sdk/output-cms-rcp.target @@ -3,16 +3,8 @@ - - - - - - - - - - - + + + \ No newline at end of file diff --git a/sdk/output-suite-rcp.target b/sdk/output-suite-rcp.target new file mode 100644 index 0000000..7d766c9 --- /dev/null +++ b/sdk/output-suite-rcp.target @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/swt/org.argeo.app.swt/bnd.bnd b/swt/org.argeo.app.swt/bnd.bnd index bbc81c5..fac182d 100644 --- a/swt/org.argeo.app.swt/bnd.bnd +++ b/swt/org.argeo.app.swt/bnd.bnd @@ -1,6 +1,5 @@ Import-Package:\ org.eclipse.swt,\ -org.argeo.util.naming,\ org.argeo.api.cms.ux,\ org.argeo.cms.ux.acr,\ org.argeo.app.api,\ diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java index 17ed0e0..8055634 100644 --- a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import org.argeo.api.acr.Content; +import org.argeo.api.acr.ldap.NamingUtils; import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.app.docbook.DbkAcrUtils; import org.argeo.app.docbook.DbkAttr; @@ -16,7 +17,6 @@ import org.argeo.cms.swt.acr.SwtSection; import org.argeo.cms.swt.acr.SwtSectionPart; import org.argeo.cms.swt.widgets.StyledControl; import org.argeo.cms.ux.acr.ContentPart; -import org.argeo.util.naming.NamingUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.events.SelectionEvent; diff --git a/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml b/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml index 88f19ea..f9de1dd 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/cmsApp.xml @@ -8,7 +8,8 @@ - + + diff --git a/swt/org.argeo.app.ui/OSGI-INF/groupUiProvider.xml b/swt/org.argeo.app.ui/OSGI-INF/groupUiProvider.xml index bb57f8d..64eb06f 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/groupUiProvider.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/groupUiProvider.xml @@ -6,5 +6,5 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/hierarchyUnitUiProvider.xml b/swt/org.argeo.app.ui/OSGI-INF/hierarchyUnitUiProvider.xml index 64f49c0..1a8b347 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/hierarchyUnitUiProvider.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/hierarchyUnitUiProvider.xml @@ -6,5 +6,5 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties index 0dd3532..d4bf08a 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties +++ b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties @@ -10,8 +10,10 @@ appTitle=Argeo Suite # PEOPLE # org.argeo.people.ui.PeopleMsg # -person=Person -organisation=Organisation +person=person +user=user +org=organisation +group=group # NewPersonWizard firstName=First Name @@ -20,6 +22,7 @@ salutation=Salutation email=Email personWizardWindowTitle=New person personWizardPageTitle=Create a contact +personWizardFeedback=Contact was created # NewOrgWizard legalName=Legal name @@ -27,7 +30,16 @@ legalForm=Legal form vatId=VAT ID orgWizardWindowTitle=New organisation orgWizardPageTitle=Create an organisation +orgWizardFeedback=Organisation was created +# Roles +userAdminRole=Can create users and modify them +groupAdminRole=Can create groups and organisations and modify them +publisherRole=Can validate and publish content +coworkerRole=Is an active user of the organisation + +# Group +chooseAMember=Choose a member # ContextAddressComposite chooseAnOrganisation=Choose an organisation diff --git a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle_fr.properties b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle_fr.properties index 3a9ad4d..0015269 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle_fr.properties +++ b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle_fr.properties @@ -14,8 +14,10 @@ appTitle=Argeo Suite # PEOPLE # org.argeo.people.ui.PeopleMsg # -person=Personne -organisation=Organisation +person=personne +user=utilisateur +org=organisation +group=groupe # NewPersonWizard firstName=Prénom @@ -24,6 +26,7 @@ salutation=Salutation email=Email personWizardWindowTitle=Nouvelle personne personWizardPageTitle=Créer un contact +personWizardFeedback=Le contact a été créé # NewOrgWizard legalName=Nom @@ -31,7 +34,16 @@ legalForm=Forme l vatId=ID TVA orgWizardWindowTitle=Nouvelle organisation orgWizardPageTitle=Créer une organisation +orgWizardFeedback=L'organisation a été crée +# Roles +userAdminRole=Peut créer des utilisateurs et les modifier +groupAdminRole=Peut créer des groupes et des organisations et les modifier +publisherRole=Peut publier et valider du contenu +coworkerRole=Est un membre en activité de l'organisation + +# Group +chooseAMember=Choisir un membre # ContextAddressComposite chooseAnOrganisation=Choisir une organisation diff --git a/swt/org.argeo.app.ui/OSGI-INF/peopleEntryArea.xml b/swt/org.argeo.app.ui/OSGI-INF/peopleEntryArea.xml index 4073704..d3d5b29 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/peopleEntryArea.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/peopleEntryArea.xml @@ -6,6 +6,6 @@ - + diff --git a/swt/org.argeo.app.ui/OSGI-INF/personUiProvider.xml b/swt/org.argeo.app.ui/OSGI-INF/personUiProvider.xml index 45dae41..950301f 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/personUiProvider.xml +++ b/swt/org.argeo.app.ui/OSGI-INF/personUiProvider.xml @@ -8,5 +8,5 @@ - + diff --git a/swt/org.argeo.app.ui/config/hierarchyUnitUiProvider.properties b/swt/org.argeo.app.ui/config/hierarchyUnitUiProvider.properties index b67b2c8..2611a39 100644 --- a/swt/org.argeo.app.ui/config/hierarchyUnitUiProvider.properties +++ b/swt/org.argeo.app.ui/config/hierarchyUnitUiProvider.properties @@ -1,3 +1,3 @@ service.pid=argeo.people.ui.hierarchyUnitUiProvider -entity.type=ldap:organizationalUnit \ No newline at end of file +entity.type=ldap:organizationalUnit,ldap:nsContainer,ldap:dcObject \ No newline at end of file diff --git a/swt/org.argeo.app.ui/config/peopleLayer.properties b/swt/org.argeo.app.ui/config/peopleLayer.properties index 3c649af..9224d1b 100644 --- a/swt/org.argeo.app.ui/config/peopleLayer.properties +++ b/swt/org.argeo.app.ui/config/peopleLayer.properties @@ -1,7 +1,7 @@ service.pid=argeo.people.ui.peopleLayer icon=people -weights=3000,7000 +weights=4000,6000 title=%people -entity.type=ldap:inetOrgPerson,ldap:groupOfNames,ldap:organizationalUnit \ No newline at end of file +entity.type=ldap:inetOrgPerson,ldap:groupOfNames,ldap:organizationalUnit,ldap:nsContainer,ldap:dcObject \ No newline at end of file diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultDashboard.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultDashboard.java index 2a2e1ba..aebacfa 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultDashboard.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultDashboard.java @@ -2,7 +2,7 @@ package org.argeo.app.ui; import org.argeo.api.acr.Content; import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.CurrentUser; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.ui.CmsUiProvider; import org.eclipse.swt.SWT; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultEditionLayer.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultEditionLayer.java index 9e399f0..dfccbe2 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultEditionLayer.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultEditionLayer.java @@ -10,7 +10,7 @@ import org.argeo.cms.swt.CmsSwtTheme; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.acr.SwtTabbedArea; import org.argeo.cms.swt.acr.SwtUiProvider; -import org.argeo.util.LangUtils; +import org.argeo.cms.util.LangUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.layout.GridLayout; @@ -31,6 +31,7 @@ public class DefaultEditionLayer implements SuiteLayer { private boolean fixedEntryArea = false; private boolean singleTab = false; private Localized title = null; + private Localized singleTabTitle = null; @Override public Control createUiPart(Composite parent, Content context) { @@ -161,6 +162,29 @@ public class DefaultEditionLayer implements SuiteLayer { title = new Localized.Untranslated(titleStr); } } + + String singleTabTitleStr = (String) properties.get(SuiteLayer.Property.singleTabTitle.name()); + if (singleTabTitleStr != null) { + if (singleTabTitleStr.startsWith("%")) { + singleTabTitle = new Localized() { + + @Override + public String name() { + return singleTabTitleStr; + } + + @Override + public ClassLoader getL10nClassLoader() { + return bundleContext != null + ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader() + : getClass().getClassLoader(); + } + }; + } else { + singleTabTitle = new Localized.Untranslated(singleTabTitleStr); + } + } + } public void destroy(BundleContext bundleContext, Map properties) { @@ -182,6 +206,8 @@ public class DefaultEditionLayer implements SuiteLayer { SwtTabbedArea createTabbedArea(Composite parent, CmsSwtTheme theme) { SwtTabbedArea tabbedArea = new SwtTabbedArea(parent, SWT.NONE); tabbedArea.setSingleTab(singleTab); + if (singleTabTitle != null) + tabbedArea.setSingleTabTitle(singleTabTitle.lead()); tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.style()); tabbedArea.setTabStyle(SuiteStyle.mainTab.style()); tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.style()); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java index b8d78c9..9231f4a 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultHeader.java @@ -4,19 +4,16 @@ import java.util.Map; import org.argeo.api.acr.Content; import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.CurrentUser; import org.argeo.cms.Localized; -import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.swt.CmsSwtTheme; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.ui.CmsUiProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java index 7b7a031..0f3fb2e 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLeadPane.java @@ -3,7 +3,6 @@ package org.argeo.app.ui; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -14,8 +13,8 @@ import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.ux.CmsView; import org.argeo.app.api.RankedObject; import org.argeo.app.core.SuiteUtils; +import org.argeo.cms.CurrentUser; import org.argeo.cms.Localized; -import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.ui.CmsUiProvider; import org.eclipse.swt.SWT; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java index c92e3db..1cb1f95 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/DefaultLoginScreen.java @@ -3,7 +3,7 @@ package org.argeo.app.ui; import org.argeo.api.acr.Content; import org.argeo.api.cms.CmsContext; import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.CurrentUser; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.auth.CmsLogin; import org.argeo.cms.ui.CmsUiProvider; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java index 53bcdfe..03927d4 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java @@ -105,8 +105,8 @@ public class RecentItems implements CmsUiProvider { public void doubleClick(DoubleClickEvent event) { Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement(); if (node != null) - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(), - SuiteEvent.eventProperties(node)); + CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.openNewPart.topic(), + SuiteUxEvent.eventProperties(node)); } }); @@ -114,8 +114,8 @@ public class RecentItems implements CmsUiProvider { public void selectionChanged(SelectionChangedEvent event) { Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement(); if (node != null) { - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(), - SuiteEvent.eventProperties(node)); + CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.refreshPart.topic(), + SuiteUxEvent.eventProperties(node)); deleteItem.setEnabled(true); } else { deleteItem.setEnabled(false); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java index a8b9b2e..3dc5007 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteApp.java @@ -25,6 +25,7 @@ import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsEventSubscriber; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.directory.CmsUserManager; import org.argeo.api.cms.ux.CmsTheme; import org.argeo.api.cms.ux.CmsUi; import org.argeo.api.cms.ux.CmsView; @@ -32,20 +33,21 @@ import org.argeo.app.api.EntityConstants; import org.argeo.app.api.EntityNames; import org.argeo.app.api.EntityType; import org.argeo.app.api.RankedObject; +import org.argeo.app.core.SuiteUtils; import org.argeo.cms.AbstractCmsApp; -import org.argeo.cms.CmsUserManager; import org.argeo.cms.LocaleUtils; import org.argeo.cms.Localized; import org.argeo.cms.acr.ContentUtils; import org.argeo.cms.jcr.CmsJcrUtils; import org.argeo.cms.jcr.acr.JcrContent; +import org.argeo.cms.jcr.acr.JcrContentProvider; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.acr.SwtUiProvider; import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.argeo.cms.util.LangUtils; import org.argeo.cms.ux.CmsUxUtils; import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.jcr.JcrException; -import org.argeo.util.LangUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.osgi.framework.Constants; @@ -93,13 +95,13 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { // ACR private ContentRepository contentRepository; -// private JcrContentProvider jcrContentProvider; + private JcrContentProvider jcrContentProvider; // JCR // private Repository repository; public void init(Map properties) { - for (SuiteEvent event : SuiteEvent.values()) { + for (SuiteUxEvent event : SuiteUxEvent.values()) { getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this); } @@ -222,39 +224,32 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { if (LOGIN.equals(state)) state = null; if (ui.isLoginScreen()) { -// if (state == null) -// state = ui.getPostLoginState(); ui.setLoginScreen(false); -// ui.setPostLoginState(null); } CmsSession cmsSession = cmsView.getCmsSession(); - if (ui.getUserDirNode() == null) { + if (ui.getUserDir() == null) { // FIXME NPE on CMSSession when logging in from anonymous if (cmsSession == null || cmsView.isAnonymous()) { assert publicBasePath != null; Content userDir = contentSession .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath); ui.setUserDir(userDir); -// ui.initSessions(getRepository(), publicBasePath); } else { -// Session adminSession = null; -// try { -// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), null); -// Node userDirNode = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); -// Content userDir = contentSession.get(CmsConstants.SYS_WORKSPACE + userDirNode.getPath()); -// ui.setUserDir(userDir); -//// ui.initSessions(getRepository(), userDirNode.getPath()); -// } finally { -// Jcr.logout(adminSession); -// } - Content userDir = contentSession.getSessionRunDir(); + Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> { + Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); + return node; + }); + Content userDir = contentSession + .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath()); ui.setUserDir(userDir); +// Content userDir = contentSession.getSessionRunDir(); +// ui.setUserDir(userDir); } } initLocale(cmsSession); context = stateToNode(ui, state); if (context == null) - context = ui.getUserDirNode(); + context = ui.getUserDir(); if (headerUiProvider != null) refreshPart(headerUiProvider, ui.getHeader(), context); @@ -343,9 +338,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { } } -// if (context.getPath().equals("/")) {// root node -// types.add("nt:folder"); -// } if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node types.add("nt:folder"); } @@ -362,7 +354,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { } } else { - List objectClasses = content.getContentClasses(); Set types = new TreeSet<>(); for (QName cc : objectClasses) { @@ -377,8 +368,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { if (!byType.containsKey(type)) throw new IllegalArgumentException("No component found for " + content + " with type " + type); return byType.get(type).get(); - // throw new UnsupportedOperationException("Content " + - // content.getClass().getName() + " is not supported."); } } @@ -423,15 +412,14 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { } Map properties = new HashMap<>(); String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state; - properties.put(SuiteEvent.LAYER, layerId); - properties.put(SuiteEvent.NODE_PATH, HOME_STATE); - ui.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), properties); + properties.put(SuiteUxEvent.LAYER, layerId); + properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE); + ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties); } return; } SuiteUi suiteUi = (SuiteUi) cmsUi; if (suiteUi.isLoginScreen()) { -// suiteUi.setPostLoginState(state); return; } @@ -439,8 +427,8 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { if (node == null) { suiteUi.getCmsView().navigateTo(HOME_STATE); } else { - suiteUi.getCmsView().sendEvent(SuiteEvent.switchLayer.topic(), SuiteEvent.eventProperties(node)); - suiteUi.getCmsView().sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(node)); + suiteUi.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node)); + suiteUi.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node)); } } @@ -456,35 +444,10 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { return null; String path = state; -// String path = state.substring(1); -// String workspace; -// if (path.equals("")) { -// workspace = null; -// path = "/"; -// } else { -// int index = path.indexOf('/'); -// if (index == 0) { -// log.error("Cannot interpret " + state); -//// cmsView.navigateTo("~"); -// return null; -// } else if (index > 0) { -// workspace = path.substring(0, index); -// path = path.substring(index); -// } else {// index<0, assuming root node -// workspace = path; -// path = "/"; -// } -// } ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, suiteUi.getCmsView()); return contentSession.get(path); -// Session session = jcrContentProvider.getJcrSession(contentSession, workspace); -//// Session session = suiteUi.getSession(workspace); -// if (session == null) -// return null; -// Node node = Jcr.getNode(session, path); -// return node; } /* @@ -504,10 +467,8 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { if (ui.getTitle() != null) appTitle = ui.getTitle().lead() + " - "; -// String currentLayerId = ui.getCurrentLayerId(); -// SuiteLayer currentLayer = currentLayerId != null ? layersByPid.get(currentLayerId).get() : null; - if (SuiteUiUtils.isTopic(topic, SuiteEvent.refreshPart)) { - Content node = getNode(ui, event); + if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.refreshPart)) { + Content node = getContentFromEvent(ui, event); if (node == null) return; SwtUiProvider uiProvider = findByType(uiProvidersByType, node); @@ -515,8 +476,8 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { ui.switchToLayer(layer, node); layer.view(uiProvider, ui.getCurrentWorkArea(), node); ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node)); - } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.openNewPart)) { - Content node = getNode(ui, event); + } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.openNewPart)) { + Content node = getContentFromEvent(ui, event); if (node == null) return; SwtUiProvider uiProvider = findByType(uiProvidersByType, node); @@ -524,28 +485,27 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { ui.switchToLayer(layer, node); layer.open(uiProvider, ui.getCurrentWorkArea(), node); ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node)); - } else if (SuiteUiUtils.isTopic(topic, SuiteEvent.switchLayer)) { - String layerId = get(event, SuiteEvent.LAYER); + } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.switchLayer)) { + String layerId = get(event, SuiteUxEvent.LAYER); if (layerId != null) { -// ui.switchToLayer(layerId, ui.getUserDir()); SuiteLayer suiteLayer = findLayer(layerId); if (suiteLayer == null) throw new IllegalArgumentException("No layer '" + layerId + "' available."); Localized layerTitle = suiteLayer.getTitle(); // FIXME make sure we don't rebuild the work area twice - Composite workArea = ui.switchToLayer(layerId, ui.getUserDirNode()); + Composite workArea = ui.switchToLayer(layerId, ui.getUserDir()); String title = null; if (layerTitle != null) title = layerTitle.lead(); - Content nodeFromState = getNode(ui, event); - if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDirNode().getPath())) { + Content nodeFromState = getContentFromEvent(ui, event); + if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) { // default layer view is forced String state = defaultLayerPid.equals(layerId) ? "~" : layerId; ui.getCmsView().stateChanged(state, appTitle + title); suiteLayer.view(null, workArea, nodeFromState); } else { Content layerCurrentContext = suiteLayer.getCurrentContext(workArea); - if (layerCurrentContext != null) { + if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) { // layer was already showing a context so we set the state to it ui.getCmsView().stateChanged(nodeToState(layerCurrentContext), appTitle + CmsUxUtils.getTitle(layerCurrentContext)); @@ -555,7 +515,7 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { } } } else { - Content node = getNode(ui, event); + Content node = getContentFromEvent(ui, event); if (node != null) { SuiteLayer layer = findByType(layersByType, node); ui.switchToLayer(layer, node); @@ -569,50 +529,24 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { }); } - private Content getNode(SuiteUi ui, Map event) { + protected Content getContentFromEvent(SuiteUi ui, Map event) { ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, ui.getCmsView()); - String path = get(event, SuiteEvent.CONTENT_PATH); + String path = get(event, SuiteUxEvent.CONTENT_PATH); -// String nodePath = get(event, SuiteEvent.NODE_PATH); - if (path != null && path.equals(HOME_STATE)) + if (path != null && (path.equals(HOME_STATE) || path.equals(""))) return ui.getUserDir(); -// String workspace = get(event, SuiteEvent.WORKSPACE); - -// Session session = jcrContentProvider.getJcrSession(contentSession, workspace); -//// Session session = ui.getSession(workspace); Content node; if (path == null) { // look for a user - String username = get(event, SuiteEvent.USERNAME); + String username = get(event, SuiteUxEvent.USERNAME); if (username == null) return null; User user = cmsUserManager.getUser(username); if (user == null) return null; node = ContentUtils.roleToContent(cmsUserManager, contentSession, user); -// LdapName userDn; -// try { -// userDn = new LdapName(user.getName()); -// } catch (InvalidNameException e) { -// throw new IllegalArgumentException("Badly formatted username", e); -// } -// String userNodePath = SuiteUtils.getUserNodePath(userDn); - // FIXME deal with home path -// return null; -// if (Jcr.itemExists(session, userNodePath)) -// node = Jcr.getNode(session, userNodePath); -// else { -// Session adminSession = null; -// try { -// adminSession = CmsJcrUtils.openDataAdminSession(getRepository(), workspace); -// SuiteUtils.getOrCreateUserNode(adminSession, userDn); -// } finally { -// Jcr.logout(adminSession); -// } -// node = Jcr.getNode(session, userNodePath); -// } } else { node = contentSession.get(path); } @@ -627,7 +561,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { Object value = eventProperties.get(key); if (value == null) return null; -// throw new IllegalArgumentException("Property " + key + " must be set"); return value.toString(); } @@ -670,32 +603,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { } } -// public void addLayer(SuiteLayer layer, Map properties) { -// if (!properties.containsKey(Constants.SERVICE_PID)) -// throw new IllegalArgumentException("A layer must have an ID"); -// String pid = (String) properties.get(Constants.SERVICE_PID); -// List types = properties.containsKey(EntityConstants.TYPE) -// ? LangUtils.toStringList(properties.get(EntityConstants.TYPE)) -// : new ArrayList<>(); -// if (types.isEmpty()) { -// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties); -// } else { -// if (layersByPid.containsKey(pid)) { -// RankedObject current = layersByPid.get(pid); -// List currentTypes = current.getProperties().containsKey(EntityConstants.TYPE) -// ? LangUtils.toStringList(current.getProperties().get(EntityConstants.TYPE)) -// : new ArrayList<>(); -// if (!types.containsAll(currentTypes)) { -// throw new IllegalArgumentException("Higher-ranked layer " + pid + " contains only types " + types -// + ", while it must override all " + currentTypes); -// } -// } -// RankedObject.putIfHigherRank(layersByPid, pid, layer, properties); -// for (String type : types) -// RankedObject.putIfHigherRank(layersByType, type, layer, properties); -// } -// } - public void addLayer(SuiteLayer layer, Map properties) { if (properties.containsKey(Constants.SERVICE_PID)) { String pid = (String) properties.get(Constants.SERVICE_PID); @@ -733,14 +640,6 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { this.cmsUserManager = cmsUserManager; } -// protected Repository getRepository() { -// return repository; -// } -// -// public void setRepository(Repository repository) { -// this.repository = repository; -// } - protected ContentRepository getContentRepository() { return contentRepository; } @@ -749,8 +648,8 @@ public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber { this.contentRepository = contentRepository; } -// public void setJcrContentProvider(JcrContentProvider jcrContentProvider) { -// this.jcrContentProvider = jcrContentProvider; -// } + public void setJcrContentProvider(JcrContentProvider jcrContentProvider) { + this.jcrContentProvider = jcrContentProvider; + } } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteEvent.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteEvent.java deleted file mode 100644 index b408232..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.argeo.app.ui; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; - -import org.argeo.api.acr.Content; -import org.argeo.api.cms.CmsEvent; -import org.argeo.jcr.Jcr; -import org.osgi.service.useradmin.User; - -/** Events specific to Argeo Suite. */ -public enum SuiteEvent implements CmsEvent { - openNewPart, refreshPart, switchLayer; - - public final static String LAYER = "layer"; - public final static String USERNAME = "username"; - - // ACR - public final static String CONTENT_PATH = "contentPath"; - - // JCR - @Deprecated - public final static String NODE_PATH = "path"; - @Deprecated - public final static String WORKSPACE = "workspace"; - - public String getTopicBase() { - return "argeo.suite.ui"; - } - - public static Map eventProperties(Content content) { - Map properties = new HashMap<>(); - properties.put(CONTENT_PATH, content.getPath()); - return properties; - } - - @Deprecated - public static Map eventProperties(Node node) { - Map properties = new HashMap<>(); - String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node); - properties.put(CONTENT_PATH, contentPath); -// properties.put(NODE_PATH, Jcr.getPath(node)); -// properties.put(WORKSPACE, Jcr.getWorkspaceName(node)); - return properties; - } - - public static Map eventProperties(User user) { - Map properties = new HashMap<>(); - properties.put(USERNAME, user.getName()); - return properties; - } -} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteIcon.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteIcon.java index 3f1947e..fae2852 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteIcon.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteIcon.java @@ -14,7 +14,7 @@ public enum SuiteIcon implements CmsIcon { // admin and settings settings, user, // misc - task, tag, location, inbox, map, + task, tag, location, inbox, map, todo, // actions openUserMenu, // diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteLayer.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteLayer.java index b97230d..6ee8ca0 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteLayer.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteLayer.java @@ -8,7 +8,7 @@ import org.eclipse.swt.widgets.Composite; /** An UI layer for the main work area. */ public interface SuiteLayer extends SwtUiProvider { static enum Property { - title, icon, weights, startMaximized, singleTab, fixedEntryArea; + title, icon, weights, startMaximized, singleTab, singleTabTitle, fixedEntryArea; } String getId(); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteMsg.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteMsg.java index 1162144..4d515d7 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteMsg.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteMsg.java @@ -4,11 +4,18 @@ import org.argeo.cms.Localized; /** Localized messages. */ public enum SuiteMsg implements Localized { + // Entities + user, org, person, group, + // UI parts dashboard, people, documents, locations, recentItems, // NewPersonWizard - firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle, + firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle, personWizardFeedback, // NewOrgWizard - orgWizardWindowTitle, orgWizardPageTitle, legalName, legalForm, vatId, + orgWizardWindowTitle, orgWizardPageTitle, orgWizardFeedback, legalName, legalForm, vatId, + // Roles + userAdminRole, groupAdminRole, publisherRole, coworkerRole, + // Group + chooseAMember, // ContextAddressComposite chooseAnOrganisation, street, streetComplement, zipCode, city, state, country, geopoint, // FilteredOrderableEntityTable @@ -31,7 +38,7 @@ public enum SuiteMsg implements Localized { label, aCustomLabel, description, value, name, primary, add, save, pickup, // Tag confirmNewTag, cannotCreateTag, - // Feddback messages + // Feedback messages allFieldsMustBeSet, // ; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUi.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUi.java index 02ff38e..c332929 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUi.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUi.java @@ -12,7 +12,7 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; -/** The view for the default ergonomics of Argeo Suite. */ +/** The view for the default UX of Argeo Suite. */ class SuiteUi extends CmsSwtUi { private static final long serialVersionUID = 6207018859086689108L; private final static CmsLog log = CmsLog.getLog(SuiteUi.class); @@ -25,8 +25,6 @@ class SuiteUi extends CmsSwtUi { private Composite sidePane; private Composite dynamicArea; -// private Session sysSession; -// private Session homeSession; private Content userDir; private Map layers = new HashMap<>(); @@ -34,7 +32,6 @@ class SuiteUi extends CmsSwtUi { private String currentLayerId = null; private boolean loginScreen = false; -// private String postLoginState; public SuiteUi(Composite parent, int style) { super(parent, style); @@ -116,13 +113,12 @@ class SuiteUi extends CmsSwtUi { } if (context == null) { if (!getCmsView().isAnonymous()) - context = getUserDirNode(); + context = getUserDir(); } Composite toShow = getLayer(layerId, context); if (toShow != null) { currentLayerId = layerId; if (!isDisposed()) { -// getDisplay().syncExec(() -> { if (!toShow.isDisposed()) { toShow.moveAbove(null); } else { @@ -131,7 +127,6 @@ class SuiteUi extends CmsSwtUi { toShow.moveAbove(null); } dynamicArea.layout(true, true); -// }); } return toShow; } else { @@ -174,8 +169,6 @@ class SuiteUi extends CmsSwtUi { synchronized void logout() { userDir = null; -// Jcr.logout(sysSession); -// Jcr.logout(homeSession); currentLayerId = null; workAreas.clear(); } @@ -204,27 +197,6 @@ class SuiteUi extends CmsSwtUi { return belowHeader; } -// Session getSysSession() { -// return sysSession; -// } -// -// synchronized void initSessions(Repository repository, String userDirPath) throws RepositoryException { -// this.sysSession = repository.login(); -// this.homeSession = repository.login(CmsConstants.HOME_WORKSPACE); -// userDir = sysSession.getNode(userDirPath); -// addDisposeListener((e) -> { -// Jcr.logout(sysSession); -// Jcr.logout(homeSession); -// }); -// } - - @Deprecated - Content getUserDirNode() { - if (userDir == null) - return null; - return userDir; - } - Content getUserDir() { return userDir; } @@ -233,21 +205,6 @@ class SuiteUi extends CmsSwtUi { this.userDir = userDir; } -// Session getSysSession() { -// return sysSession; -// } - -// Session getSession(String workspaceName) { -// if (workspaceName == null) -// return sysSession; -// if (CmsConstants.SYS_WORKSPACE.equals(workspaceName)) -// return sysSession; -// else if (CmsConstants.HOME_WORKSPACE.equals(workspaceName)) -// return homeSession; -// else -// throw new IllegalArgumentException("Unknown workspace " + workspaceName); -// } - public Localized getTitle() { return title; } @@ -263,13 +220,4 @@ class SuiteUi extends CmsSwtUi { public void setLoginScreen(boolean loginScreen) { this.loginScreen = loginScreen; } - -// public String getPostLoginState() { -// return postLoginState; -// } -// -// public void setPostLoginState(String postLoginState) { -// this.postLoginState = postLoginState; -// } - } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java index 9d9395b..504e8ed 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/SuiteUiUtils.java @@ -17,13 +17,10 @@ import org.argeo.api.cms.CmsEvent; import org.argeo.api.cms.ux.CmsEditable; import org.argeo.api.cms.ux.CmsIcon; import org.argeo.api.cms.ux.CmsStyle; -import org.argeo.api.cms.ux.CmsView; import org.argeo.app.api.EntityNames; import org.argeo.app.api.EntityType; -import org.argeo.app.api.SuiteRole; import org.argeo.cms.LocaleUtils; import org.argeo.cms.Localized; -import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.jcr.acr.JcrContent; import org.argeo.cms.swt.CmsSwtTheme; import org.argeo.cms.swt.CmsSwtUtils; @@ -74,6 +71,10 @@ public class SuiteUiUtils { } } + public static Label addFormLabel(Composite parent, Localized msg) { + return addFormLabel(parent, msg.lead()); + } + public static Label addFormLabel(Composite parent, String label) { Label lbl = new Label(parent, SWT.WRAP); lbl.setText(label); @@ -241,7 +242,8 @@ public class SuiteUiUtils { boolean test = false; if (test) { try (InputStream in = JcrUtils.getFileAsStream(fileNode); - OutputStream out = Files.newOutputStream(Paths.get("/home/mbaudier/tmp/" + fileNode.getName()));) { + OutputStream out = Files.newOutputStream( + Paths.get(System.getProperty("user.home") + "/tmp/" + fileNode.getName()));) { // BufferedImage img = ImageIO.read(in); // System.out.println(fileNode.getName() + ": width=" + img.getWidth() + ", height=" + img.getHeight()); IOUtils.copy(in, out); @@ -381,7 +383,9 @@ public class SuiteUiUtils { Label lbl = new Label(parent, SWT.NONE); CmsSwtUtils.markup(lbl); StringBuilder txt = new StringBuilder(); - txt.append(" eventProperties(Content content) { + Map properties = new HashMap<>(); + properties.put(CONTENT_PATH, content.getPath()); + return properties; + } + + @Deprecated + public static Map eventProperties(Node node) { + Map properties = new HashMap<>(); + String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node); + properties.put(CONTENT_PATH, contentPath); + return properties; + } + + public static Map eventProperties(User user) { + Map properties = new HashMap<>(); + properties.put(USERNAME, user.getName()); + return properties; + } +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java index c8aee51..c911700 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java @@ -9,6 +9,7 @@ import javax.jcr.Item; import javax.jcr.Node; import javax.jcr.RepositoryException; +import org.argeo.api.acr.ldap.NamingUtils; import org.argeo.app.docbook.DbkAttr; import org.argeo.app.docbook.DbkType; import org.argeo.app.docbook.DbkUtils; @@ -20,7 +21,6 @@ import org.argeo.cms.ui.viewers.SectionPart; import org.argeo.cms.ui.widgets.StyledControl; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; -import org.argeo.util.naming.NamingUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.events.SelectionEvent; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/ContentEntryArea.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/ContentEntryArea.java index e5b474b..b34c605 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/ContentEntryArea.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/ContentEntryArea.java @@ -5,7 +5,7 @@ import org.argeo.api.acr.spi.ProvidedContent; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.ux.CmsView; import org.argeo.app.api.EntityType; -import org.argeo.app.ui.SuiteEvent; +import org.argeo.app.ui.SuiteUxEvent; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.acr.SwtUiProvider; import org.argeo.cms.swt.widgets.SwtTreeView; @@ -46,7 +46,7 @@ public class ContentEntryArea implements SwtUiProvider { contentPart.onSelected((o) -> { Content c = (Content) o; log.debug(c.getPath()); - cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(c)); + cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(c)); }); return view; } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsFolderUiProvider.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsFolderUiProvider.java index 614e877..657a851 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsFolderUiProvider.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsFolderUiProvider.java @@ -7,7 +7,7 @@ import javax.jcr.Node; import javax.jcr.RepositoryException; import org.argeo.api.cms.ux.CmsView; -import org.argeo.app.ui.SuiteEvent; +import org.argeo.app.ui.SuiteUxEvent; import org.argeo.cms.fs.CmsFsUtils; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.ui.CmsUiProvider; @@ -29,7 +29,7 @@ public class DocumentsFolderUiProvider implements CmsUiProvider { protected void externalNavigateTo(Path path) { Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path)); parent.addDisposeListener((e1) -> Jcr.logout(folderNode)); - cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode)); + cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUxEvent.eventProperties(folderNode)); } }; dfc.setLayoutData(CmsSwtUtils.fillAll()); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsTreeUiProvider.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsTreeUiProvider.java index 10ce4a3..6c0bdfc 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsTreeUiProvider.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/DocumentsTreeUiProvider.java @@ -8,7 +8,7 @@ import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.RepositoryException; -import org.argeo.app.ui.SuiteEvent; +import org.argeo.app.ui.SuiteUxEvent; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.ux.CmsView; import org.argeo.cms.fs.CmsFsUtils; @@ -46,7 +46,7 @@ public class DocumentsTreeUiProvider implements CmsUiProvider { if (Files.isDirectory(newSelected)) { Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected)); parent.addDisposeListener((e1) -> Jcr.logout(folderNode)); - cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(folderNode)); + cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(folderNode)); } } }); @@ -59,7 +59,7 @@ public class DocumentsTreeUiProvider implements CmsUiProvider { if (Files.isDirectory(newSelected)) { Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected)); parent.addDisposeListener((e1) -> Jcr.logout(folderNode)); - cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(folderNode)); + cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUxEvent.eventProperties(folderNode)); } } }); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/JcrContentEntryArea.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/JcrContentEntryArea.java index d86eef2..0fbb5cb 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/JcrContentEntryArea.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/library/JcrContentEntryArea.java @@ -13,7 +13,7 @@ import javax.jcr.query.Query; import org.argeo.api.acr.Content; import org.argeo.api.cms.CmsConstants; import org.argeo.app.api.EntityType; -import org.argeo.app.ui.SuiteEvent; +import org.argeo.app.ui.SuiteUxEvent; import org.argeo.app.ui.SuiteIcon; import org.argeo.app.ui.widgets.TreeOrSearchArea; import org.argeo.cms.jcr.acr.JcrContentProvider; @@ -85,8 +85,8 @@ public class JcrContentEntryArea implements CmsUiProvider { public void doubleClick(DoubleClickEvent event) { Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement(); if (user != null) { - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(), - SuiteEvent.eventProperties(user)); + CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.openNewPart.topic(), + SuiteUxEvent.eventProperties(user)); } } @@ -95,8 +95,8 @@ public class JcrContentEntryArea implements CmsUiProvider { public void selectionChanged(SelectionChangedEvent event) { Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement(); if (user != null) { - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(), - SuiteEvent.eventProperties(user)); + CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.refreshPart.topic(), + SuiteUxEvent.eventProperties(user)); } } }); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/OpenLayersMap.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/OpenLayersMap.java index 28a84b0..97c0e7c 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/OpenLayersMap.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/OpenLayersMap.java @@ -15,7 +15,7 @@ import javax.jcr.RepositoryException; import org.apache.commons.io.IOUtils; import org.argeo.app.api.EntityNames; import org.argeo.app.api.EntityType; -import org.argeo.app.ui.SuiteEvent; +import org.argeo.app.ui.SuiteUxEvent; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.ux.CmsView; import org.argeo.api.cms.CmsConstants; @@ -275,8 +275,8 @@ public class OpenLayersMap extends Composite { Map properties = new HashMap<>(); // properties.put(SuiteEvent.NODE_PATH, path); // properties.put(SuiteEvent.WORKSPACE, CmsConstants.SYS_WORKSPACE); - properties.put(SuiteEvent.CONTENT_PATH, '/' + CmsConstants.SYS_WORKSPACE + path); - cmsView.sendEvent(SuiteEvent.refreshPart.topic(), properties); + properties.put(SuiteUxEvent.CONTENT_PATH, '/' + CmsConstants.SYS_WORKSPACE + path); + cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), properties); return null; } } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/ChooseUserDialog.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/ChooseUserDialog.java new file mode 100644 index 0000000..99c7872 --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/ChooseUserDialog.java @@ -0,0 +1,76 @@ +package org.argeo.app.ui.people; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentSession; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.dialogs.CmsMessageDialog; +import org.argeo.cms.swt.widgets.SwtTableView; +import org.argeo.cms.swt.widgets.SwtTreeView; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +/** Pick up a user within a hierarchy. */ +public class ChooseUserDialog extends CmsMessageDialog { + private ContentSession contentSession; + private CmsUserManager cmsUserManager; + private HierarchyUnit defaultHierarchyUnit; + + private Content selected; + + public ChooseUserDialog(Shell parentShell, String message, ContentSession contentSession, + CmsUserManager cmsUserManager, HierarchyUnit defaultHierarchyUnit) { + super(parentShell, message, CmsMessageDialog.QUESTION); + this.contentSession = contentSession; + this.cmsUserManager = cmsUserManager; + this.defaultHierarchyUnit = defaultHierarchyUnit; + } + + @Override + protected Control createInputArea(Composite parent) { + SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL); + CmsSwtUtils.fill(sashForm); + + HierarchyUnitPart hierarchyPart = new HierarchyUnitPart(contentSession, cmsUserManager); + SwtTreeView directoriesView = new SwtTreeView<>(sashForm, SWT.BORDER, hierarchyPart); + + UsersPart usersPart = new UsersPart(contentSession, cmsUserManager); + + SwtTableView usersView = new SwtTableView<>(sashForm, SWT.BORDER, usersPart); + usersView.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // CONTROLLER + hierarchyPart.onSelected((o) -> { + if (o instanceof HierarchyUnit) { + HierarchyUnit hierarchyUnit = (HierarchyUnit) o; + usersPart.setInput(hierarchyUnit); + } + }); + + usersPart.onSelected((o) -> { + Content user = (Content) o; + selected = user; + }); + + hierarchyPart.refresh(); + + sashForm.setWeights(new int[] { 40, 60 }); + return sashForm; + } + + public Content getSelected() { + return selected; + } + + @Override + protected Point getInitialSize() { + return new Point(800, 600); + } + +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/GroupUiProvider.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/GroupUiProvider.java index 6b5eccd..f0de1ca 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/GroupUiProvider.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/GroupUiProvider.java @@ -1,19 +1,125 @@ package org.argeo.app.ui.people; import org.argeo.api.acr.Content; -import org.argeo.cms.CmsUserManager; +import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.ldap.LdapAcrUtils; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.api.cms.directory.CmsGroup; +import org.argeo.api.cms.directory.CmsUser; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.app.ui.SuiteIcon; +import org.argeo.app.ui.SuiteMsg; +import org.argeo.app.ui.SuiteUiUtils; +import org.argeo.cms.CurrentUser; +import org.argeo.cms.acr.ContentUtils; +import org.argeo.cms.auth.CmsRole; +import org.argeo.cms.swt.CmsSwtTheme; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; +import org.argeo.cms.swt.acr.SwtSection; import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.cms.swt.widgets.SwtTableView; +import org.argeo.cms.ux.widgets.AbstractTabularPart; +import org.argeo.cms.ux.widgets.CmsDialog; +import org.argeo.cms.ux.widgets.TabularPart; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.osgi.service.useradmin.Role; public class GroupUiProvider implements SwtUiProvider { private CmsUserManager cmsUserManager; @Override public Control createUiPart(Composite parent, Content context) { - new Label(parent, 0).setText("Group " + context); - return null; + CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent); + + Content hierarchyUnitContent = context.getParent().getParent(); + HierarchyUnit hierarchyUnit = hierarchyUnitContent.adapt(HierarchyUnit.class); + + ContentSession contentSession = ((ProvidedContent) context).getSession(); + + TabularPart membersPart = new AbstractTabularPart() { + Role[] roles; + + @Override + public int getItemCount() { + roles = context.adapt(CmsGroup.class).getMembers(); + return roles.length; + } + + @Override + public Content getData(int row) { + Role role = roles[row]; + Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, role); + return content; + } + + }; + membersPart.addColumn(new UserColumn()); + + // VIEW + SwtSection area = new SwtSection(parent, 0, context); + area.setLayoutData(CmsSwtUtils.fillAll()); + area.setLayout(new GridLayout()); + + // title + // TODO localise at content level + String title = (context.hasContentClass(LdapObj.organization) ? SuiteMsg.org.lead() : SuiteMsg.group.lead()) + + " " + LdapAcrUtils.getLocalized(context, LdapAttr.cn.qName(), CurrentUser.locale()) + " (" + + hierarchyUnit.getHierarchyUnitLabel(CurrentUser.locale()) + ")"; + SuiteUiUtils.addFormLabel(area, title); + + // toolbar + ToolBar toolBar = new ToolBar(area, SWT.NONE); + toolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + + ToolItem deleteItem = new ToolItem(toolBar, SWT.FLAT); + deleteItem.setEnabled(false); + deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete)); + + ToolItem addItem = new ToolItem(toolBar, SWT.FLAT); + addItem.setImage(theme.getSmallIcon(SuiteIcon.add)); + addItem.setEnabled(CurrentUser.implies(CmsRole.groupAdmin, hierarchyUnit.getBase())); + + // members view + SwtTableView membersView = new SwtTableView<>(area, SWT.BORDER, membersPart); + membersView.setLayoutData(CmsSwtUtils.fillAll()); + membersView.refresh(); + + // CONTROLLER + membersPart.onSelected((model) -> { + deleteItem.setEnabled(CurrentUser.implies(CmsRole.groupAdmin, hierarchyUnit.getBase())); + deleteItem.setData(model); + }); + + addItem.addSelectionListener((Selected) (e) -> { + ChooseUserDialog chooseUserDialog = new ChooseUserDialog(parent.getDisplay().getActiveShell(), + SuiteMsg.chooseAMember.lead(), contentSession, cmsUserManager, hierarchyUnit); + if (chooseUserDialog.open() == CmsDialog.OK) { + Content chosen = chooseUserDialog.getSelected(); + cmsUserManager.addMember(context.adapt(CmsGroup.class), chosen.adapt(CmsUser.class)); + membersPart.refresh(); + } + }); + + deleteItem.addSelectionListener((Selected) (e) -> { + if (deleteItem.getData() != null) { + Content chosen = (Content) deleteItem.getData(); + cmsUserManager.removeMember(context.adapt(CmsGroup.class), chosen.adapt(CmsUser.class)); + membersPart.refresh(); + } + }); + + return membersView; + } public void setCmsUserManager(CmsUserManager cmsUserManager) { diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitPart.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitPart.java new file mode 100644 index 0000000..3316031 --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitPart.java @@ -0,0 +1,74 @@ +package org.argeo.app.ui.people; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.directory.CmsDirectory; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.api.cms.directory.UserDirectory; +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.app.ui.SuiteIcon; +import org.argeo.cms.CurrentUser; +import org.argeo.cms.acr.ContentUtils; +import org.argeo.cms.auth.CmsRole; +import org.argeo.cms.ux.widgets.AbstractHierarchicalPart; +import org.argeo.cms.ux.widgets.Column; + +public class HierarchyUnitPart extends AbstractHierarchicalPart { + private ContentSession contentSession; + private CmsUserManager cmsUserManager; + + public HierarchyUnitPart(ContentSession contentSession, CmsUserManager cmsUserManager) { + this.contentSession = contentSession; + this.cmsUserManager = cmsUserManager; + + addColumn(new Column() { + + @Override + public String getText(HierarchyUnit model) { + return model.getHierarchyUnitLabel(CurrentUser.locale()); + } + + @Override + public CmsIcon getIcon(HierarchyUnit model) { + Content content = ContentUtils.hierarchyUnitToContent(contentSession, model); + if (content.hasContentClass(LdapObj.organization)) + return SuiteIcon.organisation; + else if (content.hasContentClass(LdapObj.posixGroup)) + return SuiteIcon.users; + else + return SuiteIcon.addressBook; + } + }); + } + + @Override + public List getChildren(HierarchyUnit parent) { + List visible = new ArrayList<>(); + if (parent != null) { + if (parent instanceof CmsDirectory) // do no show children of the directories + return visible; + for (HierarchyUnit hu : parent.getDirectHierarchyUnits(true)) { + visible.add(hu); + } + } else { + for (UserDirectory directory : cmsUserManager.getUserDirectories()) { + if (CurrentUser.implies(CmsRole.userAdmin, directory.getBase())) { + visible.add(directory); + } + for (HierarchyUnit hu : directory.getDirectHierarchyUnits(true)) { + if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase())) { + visible.add(hu); + } + } + + } + } + return visible; + } + +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitUiProvider.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitUiProvider.java index 9738be8..374e194 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitUiProvider.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/HierarchyUnitUiProvider.java @@ -1,7 +1,7 @@ package org.argeo.app.ui.people; import org.argeo.api.acr.Content; -import org.argeo.cms.CmsUserManager; +import org.argeo.api.cms.directory.CmsUserManager; import org.argeo.cms.swt.acr.SwtUiProvider; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewOrgForm.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewOrgForm.java new file mode 100644 index 0000000..e39ba0a --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewOrgForm.java @@ -0,0 +1,119 @@ +package org.argeo.app.ui.people; + +import static org.argeo.eclipse.ui.EclipseUiUtils.isEmpty; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.directory.CmsGroup; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.app.ui.SuiteMsg; +import org.argeo.app.ui.SuiteUiUtils; +import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.argeo.cms.swt.widgets.SwtGuidedFormPage; +import org.argeo.cms.ux.widgets.AbstractGuidedForm; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + +/** Form to create a new organisation. */ +public class NewOrgForm extends AbstractGuidedForm { + private Content hierarchyUnit; + private CmsUserManager cmsUserManager; + + protected Text orgNameT; + + public NewOrgForm(CmsUserManager cmsUserManager, Content hierarchyUnit) { + this.hierarchyUnit = hierarchyUnit; + this.cmsUserManager = cmsUserManager; + } + + @Override + public void addPages() { + try { + MainInfoPage page = new MainInfoPage("main"); + addPage(page); + } catch (Exception e) { + throw new RuntimeException("Cannot add page to wizard", e); + } + setFormTitle(SuiteMsg.orgWizardWindowTitle.lead()); + } + + @Override + public boolean performFinish() { + String orgName = orgNameT.getText(); + if (EclipseUiUtils.isEmpty(orgName)) { + CmsFeedback.show(SuiteMsg.allFieldsMustBeSet.lead()); + return false; + } else { + HierarchyUnit hu = hierarchyUnit.adapt(HierarchyUnit.class); + String dn = "cn=" + orgName + ",ou=Groups," + hu.getBase(); + + CmsGroup user = cmsUserManager.createGroup(dn); + + Map additionalProperties = new HashMap<>(); + additionalProperties.put(LdapAttr.o.name(), orgName); + + Set objectClasses = new HashSet<>(); + objectClasses.add(LdapObj.organization.name()); + cmsUserManager.addObjectClasses(user, objectClasses, additionalProperties); + return true; + } + } + + @Override + public boolean performCancel() { + return true; + } + + @Override + public boolean canFinish() { + String firstName = orgNameT.getText(); + if (isEmpty(firstName)) { + return false; + } else + return true; + } + + protected class MainInfoPage extends SwtGuidedFormPage { + + public MainInfoPage(String pageName) { + super(pageName); + setTitle(SuiteMsg.orgWizardPageTitle.lead()); + } + + public void createControl(Composite parent) { + parent.setLayout(new GridLayout(2, false)); + + // FirstName + SuiteUiUtils.createBoldLabel(parent, SuiteMsg.org); + orgNameT = new Text(parent, SWT.BORDER); + // firstNameTxt.setMessage("a first name"); + orgNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + ModifyListener ml = new ModifyListener() { + private static final long serialVersionUID = 1939491923843870844L; + + @Override + public void modifyText(ModifyEvent event) { + getView().updateButtons(); + } + }; + + orgNameT.addModifyListener(ml); + + orgNameT.setFocus(); + } + } +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewUserForm.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewUserForm.java index ed9c4f4..5a73b1b 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewUserForm.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/NewUserForm.java @@ -9,18 +9,18 @@ import java.util.Set; import java.util.UUID; import org.argeo.api.acr.Content; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.directory.CmsUser; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; import org.argeo.app.core.SuiteUtils; import org.argeo.app.ui.SuiteMsg; import org.argeo.app.ui.SuiteUiUtils; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.acr.ContentUtils; import org.argeo.cms.swt.dialogs.CmsFeedback; import org.argeo.cms.swt.widgets.SwtGuidedFormPage; import org.argeo.cms.ux.widgets.AbstractGuidedForm; import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.util.directory.HierarchyUnit; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; @@ -28,7 +28,6 @@ import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; /** Ask first & last name. Update the passed node on finish */ public class NewUserForm extends AbstractGuidedForm { @@ -41,7 +40,7 @@ public class NewUserForm extends AbstractGuidedForm { public NewUserForm(CmsUserManager cmsUserManager, Content hierarchyUnit) { this.hierarchyUnit = hierarchyUnit; - if (!hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName())) + if (!hierarchyUnit.hasContentClass(LdapObj.posixGroup.qName())) throw new IllegalArgumentException(hierarchyUnit + " is not a POSIX group"); this.cmsUserManager = cmsUserManager; } @@ -49,7 +48,7 @@ public class NewUserForm extends AbstractGuidedForm { @Override public void addPages() { try { - MainInfoPage page = new MainInfoPage("Main page"); + MainInfoPage page = new MainInfoPage("main"); addPage(page); } catch (Exception e) { throw new RuntimeException("Cannot add page to wizard", e); @@ -77,25 +76,25 @@ public class NewUserForm extends AbstractGuidedForm { String username = "uid=" + uid + ",ou=People," + hu.getBase(); Map properties = new HashMap<>(); - properties.put(LdapAttrs.givenName.name(), firstName); - properties.put(LdapAttrs.sn.name(), lastName); - properties.put(LdapAttrs.mail.name(), email); - properties.put(LdapAttrs.cn.name(), firstName + " " + lastName); - properties.put(LdapAttrs.employeeNumber.name(), uuid.toString()); + properties.put(LdapAttr.givenName.name(), firstName); + properties.put(LdapAttr.sn.name(), lastName); + properties.put(LdapAttr.mail.name(), email); + properties.put(LdapAttr.cn.name(), firstName + " " + lastName); + properties.put(LdapAttr.employeeNumber.name(), uuid.toString()); Map credentials = new HashMap<>(); - User user = cmsUserManager.createUser(username, properties, credentials); + CmsUser user = cmsUserManager.createUser(username, properties, credentials); - Long huGidNumber = hierarchyUnit.get(LdapAttrs.gidNumber.qName(), Long.class).orElseThrow(); - Long nextUserId = SuiteUtils.findNextId(hierarchyUnit, LdapObjs.posixAccount.qName()); + Long huGidNumber = hierarchyUnit.get(LdapAttr.gidNumber.qName(), Long.class).orElseThrow(); + Long nextUserId = SuiteUtils.findNextId(hierarchyUnit, LdapObj.posixAccount.qName()); String homeDirectory = "/home/" + uid; Map additionalProperties = new HashMap<>(); - additionalProperties.put(LdapAttrs.uidNumber.name(), nextUserId.toString()); - additionalProperties.put(LdapAttrs.gidNumber.name(), huGidNumber.toString()); - additionalProperties.put(LdapAttrs.homeDirectory.name(), homeDirectory); + additionalProperties.put(LdapAttr.uidNumber.name(), nextUserId.toString()); + additionalProperties.put(LdapAttr.gidNumber.name(), huGidNumber.toString()); + additionalProperties.put(LdapAttr.homeDirectory.name(), homeDirectory); Set objectClasses = new HashSet<>(); - objectClasses.add(LdapObjs.posixAccount.name()); + objectClasses.add(LdapObj.posixAccount.name()); cmsUserManager.addObjectClasses(user, objectClasses, additionalProperties); return true; } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java index 789698b..f7141a4 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java @@ -1,53 +1,47 @@ package org.argeo.app.ui.people; -import java.util.ArrayList; -import java.util.List; - import javax.jcr.Node; import javax.jcr.RepositoryException; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentRepository; import org.argeo.api.acr.ContentSession; -import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; import org.argeo.api.cms.ux.CmsView; -import org.argeo.app.ui.SuiteEvent; import org.argeo.app.ui.SuiteIcon; -import org.argeo.cms.CmsUserManager; +import org.argeo.app.ui.SuiteMsg; +import org.argeo.app.ui.SuiteUxEvent; +import org.argeo.cms.CurrentUser; import org.argeo.cms.acr.ContentUtils; import org.argeo.cms.auth.CmsRole; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.UserAdminUtils; import org.argeo.cms.jcr.acr.JcrContent; import org.argeo.cms.swt.CmsSwtTheme; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.Selected; import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.cms.swt.dialogs.CmsFeedback; import org.argeo.cms.swt.widgets.SwtGuidedFormDialog; import org.argeo.cms.swt.widgets.SwtTableView; import org.argeo.cms.swt.widgets.SwtTreeView; import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ux.widgets.AbstractHierarchicalPart; +import org.argeo.cms.ux.widgets.CmsDialog; import org.argeo.cms.ux.widgets.Column; -import org.argeo.cms.ux.widgets.DefaultTabularPart; import org.argeo.cms.ux.widgets.GuidedForm; -import org.argeo.cms.ux.widgets.HierarchicalPart; -import org.argeo.osgi.useradmin.UserDirectory; -import org.argeo.util.directory.HierarchyUnit; -import org.argeo.util.directory.ldap.IpaUtils; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; -import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.User; /** Entry to the admin area. */ public class PeopleEntryArea implements SwtUiProvider, CmsUiProvider { @@ -66,190 +60,113 @@ public class PeopleEntryArea implements SwtUiProvider, CmsUiProvider { SashForm sashForm = new SashForm(parent, SWT.VERTICAL); CmsSwtUtils.fill(sashForm); - // MODEL -// List directories = new ArrayList<>(); -// // List orgs = cmsUserManager.listGroups(null, true, false); -// for (UserDirectory directory : cmsUserManager.getUserDirectories()) { -// if (CurrentUser.implies(CmsRole.userAdmin, directory.getContext())) { -// directories.add(directory); -// } -// -// } - // VIEW - HierarchicalPart hierarchyPart = new AbstractHierarchicalPart<>() { + HierarchyUnitPart hierarchyPart = new HierarchyUnitPart(contentSession, cmsUserManager); + SwtTreeView directoriesView = new SwtTreeView<>(sashForm, SWT.BORDER, hierarchyPart); - @Override - public List getChildren(HierarchyUnit parent) { - List visible = new ArrayList<>(); - if (parent != null) { - for (HierarchyUnit hu : parent.getDirectHierarchyUnits(true)) { - // if parent was visible, it is visible - // TODO restrict more? - -// if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase()) // -// ) // IPA -// { - visible.add(hu); -// } - } - } else { - for (UserDirectory directory : cmsUserManager.getUserDirectories()) { - if (CurrentUser.implies(CmsRole.userAdmin, directory.getBase()) // - || CurrentUser.implies(CmsRole.userAdmin, - IpaUtils.IPA_ACCOUNTS_RDN + "," + directory.getBase())) // IPA - { - // TODO show base level - } - for (HierarchyUnit hu : directory.getDirectHierarchyUnits(true)) { - if (CurrentUser.implies(CmsRole.userAdmin, hu.getBase())) { - visible.add(hu); - } - } - - } - } - return visible; - } - - @Override - public String getText(HierarchyUnit model) { - return model.getHierarchyUnitLabel(CurrentUser.locale()); - } - - @Override - public CmsIcon getIcon(HierarchyUnit model) { - Content content = ContentUtils.hierarchyUnitToContent(contentSession, model); - if (content.hasContentClass(LdapObjs.organization.qName())) - return SuiteIcon.organisation; - else if (content.hasContentClass(LdapObjs.posixGroup.qName())) - return SuiteIcon.users; - else - return SuiteIcon.addressBook; - } - - }; - SwtTreeView directoriesView = new SwtTreeView<>(sashForm, SWT.NONE, hierarchyPart); - - DefaultTabularPart usersPart = new DefaultTabularPart<>() { - - @Override - protected List asList(HierarchyUnit hu) { - List roles = new ArrayList<>(); - UserDirectory ud = (UserDirectory) hu.getDirectory(); - if (ud.getRealm().isPresent()) { - for (Role r : ud.getHierarchyUnitRoles(ud, null, true)) { - Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r); - // if (r instanceof Person || r instanceof Organization) - if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(), LdapObjs.organization.qName())) - roles.add(content); - } - - } else { - for (HierarchyUnit directChild : hu.getDirectHierarchyUnits(false)) { - if (!directChild.isFunctional()) { - for (Role r : ud.getHierarchyUnitRoles(directChild, null, false)) { - Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r); - // if (r instanceof Person || r instanceof Organization) - if (content.hasContentClass(LdapObjs.inetOrgPerson.qName(), - LdapObjs.organization.qName())) - roles.add(content); - } - } - } - } - return roles; - } - }; + UsersPart usersPart = new UsersPart(contentSession, cmsUserManager); usersPart.addColumn(new Column() { @Override public String getText(Content role) { - if (role.isContentClass(LdapObjs.inetOrgPerson.qName())) - return UserAdminUtils.getUserDisplayName(role.adapt(User.class)); - else if (role.isContentClass(LdapObjs.organization.qName())) - return role.attr(LdapAttrs.o.qName()); - else if (role.isContentClass(LdapObjs.groupOfNames.qName())) - return role.attr(LdapAttrs.cn.qName()); - else - return null; - } - - @Override - public CmsIcon getIcon(Content role) { - if (role.hasContentClass(LdapObjs.posixAccount.qName())) - return SuiteIcon.user; - else if (role.isContentClass(LdapObjs.inetOrgPerson.qName())) - return SuiteIcon.person; - else if (role.isContentClass(LdapObjs.organization.qName())) - return SuiteIcon.organisationContact; - else if (role.isContentClass(LdapObjs.groupOfNames.qName())) - return SuiteIcon.group; - else - return null; + return role.attr(LdapAttr.mail); } @Override public int getWidth() { return 300; } - }); - usersPart.addColumn((Column) (role) -> role.attr(LdapAttrs.mail.qName())); - - SwtTableView usersView = new SwtTableView<>(sashForm, SWT.NONE, usersPart); - // toolbar - Composite bottom = new Composite(parent, SWT.NONE); + Composite bottom = new Composite(sashForm, SWT.NONE); bottom.setLayoutData(CmsSwtUtils.fillWidth()); bottom.setLayout(CmsSwtUtils.noSpaceGridLayout()); ToolBar bottomToolBar = new ToolBar(bottom, SWT.NONE); bottomToolBar.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT); - deleteItem.setEnabled(false); -// CmsUiUtils.style(deleteItem, SuiteStyle.recentItems); - deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete)); - ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT); + +// ToolItem deleteItem = new ToolItem(bottomToolBar, SWT.FLAT); +// deleteItem.setEnabled(false); +// deleteItem.setImage(theme.getSmallIcon(SuiteIcon.delete)); + + Menu menu = new Menu(Display.getCurrent().getActiveShell(), SWT.POP_UP); + // TODO display add user only if hierarchy unit is a POSIX group + // hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName()) + MenuItem addUserItem = new MenuItem(menu, SWT.PUSH); + addUserItem.setImage(theme.getSmallIcon(SuiteIcon.user)); + addUserItem.setText(SuiteMsg.user.lead()); + addUserItem.addSelectionListener((Selected) (e) -> { + HierarchyUnit hierarchyUnit = usersPart.getInput(); + Content huContent = ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit); + GuidedForm wizard = new NewUserForm(cmsUserManager, huContent); + SwtGuidedFormDialog dialog = new SwtGuidedFormDialog(parent.getShell(), wizard); + if (dialog.open() == CmsDialog.OK) { + CmsFeedback.show(SuiteMsg.personWizardFeedback.lead()); + usersPart.refresh(); + } + }); + + MenuItem addOrgItem = new MenuItem(menu, SWT.PUSH); + addOrgItem.setImage(theme.getSmallIcon(SuiteIcon.organisation)); + addOrgItem.setText(SuiteMsg.org.lead()); + addOrgItem.addSelectionListener((Selected) (e) -> { + HierarchyUnit hierarchyUnit = usersPart.getInput(); + Content huContent = ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit); + GuidedForm wizard = new NewOrgForm(cmsUserManager, huContent); + SwtGuidedFormDialog dialog = new SwtGuidedFormDialog(parent.getShell(), wizard); + if (dialog.open() == CmsDialog.OK) { + CmsFeedback.show(SuiteMsg.orgWizardFeedback.lead()); + usersPart.refresh(); + } + }); + + ToolItem addItem = new ToolItem(bottomToolBar, SWT.PUSH); + addItem.setEnabled(false); addItem.setImage(theme.getSmallIcon(SuiteIcon.add)); sashForm.setWeights(new int[] { 30, 70 }); + SwtTableView usersView = new SwtTableView<>(bottom, SWT.BORDER, usersPart); + usersView.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + // CONTROLLER hierarchyPart.onSelected((o) -> { if (o instanceof HierarchyUnit) { HierarchyUnit hierarchyUnit = (HierarchyUnit) o; usersPart.setInput(hierarchyUnit); - cmsView.sendEvent(SuiteEvent.refreshPart.topic(), - SuiteEvent.eventProperties(ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit))); + addItem.setEnabled(true); + + addOrgItem.setEnabled(usersPart.getInput() != null + && CurrentUser.implies(CmsRole.groupAdmin, usersPart.getInput().getBase())); +// cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent +// .eventProperties(ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit))); } }); usersPart.onSelected((o) -> { Content user = (Content) o; if (user != null) { - cmsView.sendEvent(SuiteEvent.refreshPart.topic(), SuiteEvent.eventProperties(user)); - deleteItem.setEnabled(true); + cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(user)); +// deleteItem.setEnabled(true); } else { - deleteItem.setEnabled(false); +// deleteItem.setEnabled(false); } }); usersPart.onAction((o) -> { Content user = (Content) o; if (user != null) { - cmsView.sendEvent(SuiteEvent.openNewPart.topic(), SuiteEvent.eventProperties(user)); + cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUxEvent.eventProperties(user)); } }); addItem.addSelectionListener((Selected) (e) -> { - HierarchyUnit hierarchyUnit = usersPart.getInput(); - Content huContent = ContentUtils.hierarchyUnitToContent(contentSession, hierarchyUnit); - GuidedForm wizard = new NewUserForm(cmsUserManager, huContent); - SwtGuidedFormDialog dialog = new SwtGuidedFormDialog(parent.getShell(), wizard); - // WizardDialog dialog = new WizardDialog(shell, wizard); - if (dialog.open() == Window.OK) { - // TODO create - } +// if (e.detail == SWT.ARROW) { + Rectangle rect = addItem.getBounds(); + Point pt = new Point(rect.x, rect.y + rect.height); + pt = bottomToolBar.toDisplay(pt); + menu.setLocation(pt.x, pt.y); + menu.setVisible(true); +// } }); directoriesView.refresh(); @@ -258,20 +175,6 @@ public class PeopleEntryArea implements SwtUiProvider, CmsUiProvider { return sashForm; } -// static String getProperty(Role role, LdapAttrs attr) { -// Object value = role.getProperties().get(attr.name()); -// return value != null ? value.toString() : null; -// } - -// private boolean isOrganisation(Role role) { -// String[] objectClasses = role.getProperties().get(LdapAttrs.objectClasses.name()).toString().split("\\n"); -// for (String objectClass : objectClasses) { -// if (LdapObjs.organization.name().equalsIgnoreCase(objectClass)) -// return true; -// } -// return false; -// } - public void setCmsUserManager(CmsUserManager cmsUserManager) { this.cmsUserManager = cmsUserManager; } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java index d1dfd78..8a22a10 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java @@ -5,17 +5,30 @@ import java.util.List; import java.util.Map; import org.argeo.api.acr.Content; +import org.argeo.api.acr.QNamed; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.directory.CmsGroup; +import org.argeo.api.cms.directory.CmsUser; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.api.cms.directory.HierarchyUnit.Type; +import org.argeo.app.api.SuiteRole; import org.argeo.app.ui.SuiteMsg; import org.argeo.app.ui.SuiteStyle; import org.argeo.app.ui.SuiteUiUtils; -import org.argeo.cms.CmsUserManager; +import org.argeo.cms.CmsMsg; +import org.argeo.cms.CurrentUser; import org.argeo.cms.Localized; +import org.argeo.cms.RoleNameUtils; +import org.argeo.cms.SystemRole; +import org.argeo.cms.auth.CmsRole; import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.Selected; import org.argeo.cms.swt.acr.SwtSection; import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.cms.swt.dialogs.CmsFeedback; import org.argeo.cms.swt.widgets.EditableText; -import org.argeo.util.naming.LdapAttrs; -import org.argeo.util.naming.LdapObjs; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; @@ -26,12 +39,11 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; /** Edit a suite user. */ public class PersonUiProvider implements SwtUiProvider { - private String[] availableRoles; private CmsUserManager cmsUserManager; @Override @@ -41,27 +53,33 @@ public class PersonUiProvider implements SwtUiProvider { main.setLayout(new GridLayout(2, false)); - User user = context.adapt(User.class); - - if (context.hasContentClass(LdapObjs.person.qName())) { - addFormLine(main, SuiteMsg.firstName, context, LdapAttrs.givenName); - addFormLine(main, SuiteMsg.lastName, context, LdapAttrs.sn); - addFormLine(main, SuiteMsg.email, context, LdapAttrs.mail); - - Composite rolesSection = new Composite(main, SWT.NONE); - // rolesSection.setText("Roles"); - rolesSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); - rolesSection.setLayout(new GridLayout()); - // new Label(rolesSection, SWT.NONE).setText("Roles:"); - List roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName())); - for (String role : roles) { - // new Label(rolesSection, SWT.NONE).setText(role); - Button radio = new Button(rolesSection, SWT.CHECK); - radio.setText(role); - if (roles.contains(role)) - radio.setSelection(true); - } + CmsUser user = context.adapt(CmsUser.class); + + Content hierarchyUnitContent = context.getParent().getParent(); + HierarchyUnit hierarchyUnit = hierarchyUnitContent.adapt(HierarchyUnit.class); + + String roleContext = RoleNameUtils.getContext(user.getName()); + + if (context.hasContentClass(LdapObj.person.qName())) { + addFormLine(main, SuiteMsg.firstName, context, LdapAttr.givenName); + addFormLine(main, SuiteMsg.lastName, context, LdapAttr.sn); + addFormLine(main, SuiteMsg.email, context, LdapAttr.mail); + } + + if (context.hasContentClass(LdapObj.posixAccount.qName())) { + if (hierarchyUnitContent.hasContentClass(LdapObj.organization)) { + SwtSection rolesSection = new SwtSection(main, SWT.NONE); + rolesSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + rolesSection.setLayout(new GridLayout(2, false)); + List roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName())); + addRoleCheckBox(rolesSection, hierarchyUnit, user, SuiteMsg.coworkerRole, SuiteRole.coworker, + roleContext, roles); + addRoleCheckBox(rolesSection, hierarchyUnit, user, SuiteMsg.publisherRole, SuiteRole.publisher, + roleContext, roles); + addRoleCheckBox(rolesSection, hierarchyUnit, user, SuiteMsg.userAdminRole, CmsRole.userAdmin, + roleContext, roles); + } // Composite facetsSection = new Composite(main, SWT.NONE); // facetsSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); // facetsSection.setLayout(new GridLayout()); @@ -71,66 +89,56 @@ public class PersonUiProvider implements SwtUiProvider { // new Label(facetsSection, SWT.NONE).setText(member); // } // } + if (CurrentUser.implies(CmsRole.userAdmin, roleContext)) { + SwtSection changePasswordSection = new SwtSection(main, SWT.BORDER); + changePasswordSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + changePasswordSection.setLayout(new GridLayout(2, false)); +// SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.changePassword) +// .setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, 2, 1)); + SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.newPassword); + Text newPasswordT = SuiteUiUtils.addFormTextField(changePasswordSection, null, null, + SWT.PASSWORD | SWT.BORDER); + newPasswordT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.repeatNewPassword); + Text repeatNewPasswordT = SuiteUiUtils.addFormTextField(changePasswordSection, null, null, + SWT.PASSWORD | SWT.BORDER); + repeatNewPasswordT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + Button apply = new Button(changePasswordSection, SWT.FLAT); + apply.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false, 2, 1)); + apply.setText(CmsMsg.changePassword.lead()); + apply.addSelectionListener((Selected) (e) -> { + try { + char[] newPassword = newPasswordT.getTextChars(); + char[] repeatNewPassword = repeatNewPasswordT.getTextChars(); + if (newPassword.length > 0 && Arrays.equals(newPassword, repeatNewPassword)) { + cmsUserManager.resetPassword(user.getName(), newPassword); + CmsFeedback.show(CmsMsg.passwordChanged.lead()); + } else { + CmsFeedback.error(CmsMsg.invalidPassword.lead(), null); + } + } catch (Exception e1) { + CmsFeedback.error(CmsMsg.invalidPassword.lead(), e1); + } + }); + } } -// if (user instanceof Group) { -// String cn = context.getName().getLocalPart(); -// Text cnT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name())); -// cnT.setText(cn); -// -// } else { -// String uid = context.getName().getLocalPart(); -// -//// Text givenName = new Text(main, SWT.SINGLE); -//// givenName.setText(getUserProperty(user, LdapAttrs.givenName.name())); -// Text givenName = SuiteUiUtils.addFormInput(main, SuiteMsg.firstName.lead(), -// getUserProperty(user, LdapAttrs.givenName.name())); -// -// Text sn = SuiteUiUtils.addFormInput(main, SuiteMsg.lastName.lead(), -// getUserProperty(user, LdapAttrs.sn.name())); -// // sn.setText(getUserProperty(user, LdapAttrs.sn.name())); -// -// Text email = SuiteUiUtils.addFormInput(main, SuiteMsg.email.lead(), -// getUserProperty(user, LdapAttrs.mail.name())); -// // email.setText(getUserProperty(user, LdapAttrs.mail.name())); -// -// Text uidT = SuiteUiUtils.addFormLine(main, "uid", getUserProperty(user, LdapAttrs.uid.name())); -// uidT.setText(uid); -// -//// Label dnL = new Label(main, SWT.NONE); -//// dnL.setText(user.getName()); -// -// // roles -// // Section rolesSection = new Section(main, SWT.NONE, context); -// Composite rolesSection = new Composite(main, SWT.NONE); -// // rolesSection.setText("Roles"); -// rolesSection.setLayoutData(CmsSwtUtils.fillWidth()); -// rolesSection.setLayout(new GridLayout()); -// // new Label(rolesSection, SWT.NONE).setText("Roles:"); -// List roles = Arrays.asList(cmsUserManager.getUserRoles(user.getName())); -// for (String role : availableRoles) { -// // new Label(rolesSection, SWT.NONE).setText(role); -// Button radio = new Button(rolesSection, SWT.CHECK); -// radio.setText(role); -// if (roles.contains(role)) -// radio.setSelection(true); -// } -// } - return main; } - private void addFormLine(SwtSection parent, Localized msg, Content context, LdapAttrs attr) { + private void addFormLine(SwtSection parent, Localized msg, Content content, QNamed attr) { SuiteUiUtils.addFormLabel(parent, msg.lead()); EditableText text = new EditableText(parent, SWT.SINGLE | SWT.FLAT); text.setLayoutData(CmsSwtUtils.fillWidth()); text.setStyle(SuiteStyle.simpleInput); - String txt = context.attr(attr.qName()); + String txt = content.attr(attr); if (txt == null) // FIXME understand why email is not found in IPA txt = ""; text.setText(txt); text.setMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 1L; + @Override public void mouseDoubleClick(MouseEvent e) { String currentTxt = text.getText(); @@ -138,6 +146,8 @@ public class PersonUiProvider implements SwtUiProvider { text.setText(currentTxt); ((Text) text.getControl()).addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 1L; + @Override public void widgetSelected(SelectionEvent e) { } @@ -145,6 +155,7 @@ public class PersonUiProvider implements SwtUiProvider { @Override public void widgetDefaultSelected(SelectionEvent e) { String editedTxt = text.getText(); + content.put(attr, editedTxt); text.stopEditing(); text.setText(editedTxt); text.getParent().layout(new Control[] { text.getControl() }); @@ -155,17 +166,50 @@ public class PersonUiProvider implements SwtUiProvider { }); } + private void addRoleCheckBox(SwtSection parent, HierarchyUnit hierarchyUnit, CmsUser user, Localized msg, + SystemRole systemRole, String roleContext, List roles) { + Button radio = new Button(parent, SWT.CHECK); + radio.setSelection(false); + roles: for (String dn : roles) { + if (systemRole.implied(dn, roleContext)) { + radio.setSelection(true); + break roles; + } + } + + if (systemRole.equals(CmsRole.userAdmin)) { + if (!CurrentUser.isUserContext(roleContext) && CurrentUser.implies(CmsRole.userAdmin, roleContext)) { + // a user admin cannot modify the user admins of their own context + radio.setEnabled(true); + } else { + radio.setEnabled(false); + } + } else { + radio.setEnabled(CurrentUser.implies(CmsRole.userAdmin, roleContext)); + } + + radio.addSelectionListener((Selected) (e) -> { + HierarchyUnit rolesHu = hierarchyUnit.getDirectChild(Type.ROLES); + CmsGroup roleGroup = cmsUserManager.getOrCreateSystemRole(rolesHu, systemRole.qName()); + if (radio.getSelection()) + cmsUserManager.addMember(roleGroup, user); + else + cmsUserManager.removeMember(roleGroup, user); + }); + + new Label(parent, 0).setText(msg.lead()); + + } + public void setCmsUserManager(CmsUserManager cmsUserManager) { this.cmsUserManager = cmsUserManager; } - private String getUserProperty(Object element, String key) { - Object value = ((User) element).getProperties().get(key); - return value != null ? value.toString() : null; - } +// private String getUserProperty(Object element, String key) { +// Object value = ((User) element).getProperties().get(key); +// return value != null ? value.toString() : null; +// } public void init(Map properties) { - availableRoles = (String[]) properties.get("availableRoles"); - // cmsUserManager.getRoles(null); } } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UserColumn.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UserColumn.java new file mode 100644 index 0000000..9dae8a4 --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UserColumn.java @@ -0,0 +1,43 @@ +package org.argeo.app.ui.people; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ldap.LdapAcrUtils; +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.ux.CmsIcon; +import org.argeo.app.ui.SuiteIcon; +import org.argeo.cms.CurrentUser; +import org.argeo.cms.auth.UserAdminUtils; +import org.argeo.cms.ux.widgets.Column; +import org.osgi.service.useradmin.User; + +public class UserColumn implements Column { + @Override + public String getText(Content role) { + if (role.hasContentClass(LdapObj.inetOrgPerson)) + return UserAdminUtils.getUserDisplayName(role.adapt(User.class)); + else if (role.hasContentClass(LdapObj.organization)) + return role.attr(LdapAttr.o); + else if (role.hasContentClass(LdapObj.groupOfNames)) { + // TODO make it more generic at ACR level + Object label = LdapAcrUtils.getLocalized(role, LdapAttr.cn.qName(), CurrentUser.locale()); + return label.toString(); + } else + return null; + } + + @Override + public CmsIcon getIcon(Content role) { + if (role.hasContentClass(LdapObj.posixAccount)) + return SuiteIcon.user; + else if (role.hasContentClass(LdapObj.inetOrgPerson)) + return SuiteIcon.person; + else if (role.hasContentClass(LdapObj.organization)) + return SuiteIcon.organisationContact; + else if (role.hasContentClass(LdapObj.groupOfNames)) + return SuiteIcon.group; + else + return null; + } + +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UsersPart.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UsersPart.java new file mode 100644 index 0000000..c615ebf --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/UsersPart.java @@ -0,0 +1,59 @@ +package org.argeo.app.ui.people; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentSession; +import org.argeo.api.acr.ldap.LdapObj; +import org.argeo.api.cms.directory.CmsUserManager; +import org.argeo.api.cms.directory.HierarchyUnit; +import org.argeo.api.cms.directory.UserDirectory; +import org.argeo.cms.acr.ContentUtils; +import org.argeo.cms.ux.widgets.DefaultTabularPart; +import org.osgi.service.useradmin.Role; + +public class UsersPart extends DefaultTabularPart { + private ContentSession contentSession; + private CmsUserManager cmsUserManager; + + public UsersPart(ContentSession contentSession, CmsUserManager cmsUserManager) { + this.contentSession = contentSession; + this.cmsUserManager = cmsUserManager; + addColumn(new UserColumn() { + + @Override + public int getWidth() { + return 300; + } + + }); + } + + @Override + protected List asList(HierarchyUnit hu) { + List roles = new ArrayList<>(); + UserDirectory ud = (UserDirectory) hu.getDirectory(); + if (ud.getRealm().isPresent()) { + for (Role r : ud.getHierarchyUnitRoles(ud, null, true)) { + Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r); + if (content.hasContentClass(LdapObj.inetOrgPerson, LdapObj.organization)) + roles.add(content); + } + + } else { + for (HierarchyUnit directChild : hu.getDirectHierarchyUnits(false)) { + if (!(directChild.isType(HierarchyUnit.Type.FUNCTIONAL) + || directChild.isType(HierarchyUnit.Type.ROLES))) { + for (Role r : ud.getHierarchyUnitRoles(directChild, null, false)) { + Content content = ContentUtils.roleToContent(cmsUserManager, contentSession, r); + if (content.hasContentClass(LdapObj.inetOrgPerson, LdapObj.organization, LdapObj.groupOfNames)) + roles.add(content); + } + } + } + } + return roles; + } + +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/VCardExporter.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/VCardExporter.java deleted file mode 100644 index 389d73e..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/VCardExporter.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.app.ui.people; - -import ezvcard.Ezvcard; -import ezvcard.VCard; - -public class VCardExporter { - - public static void main(String[] args) { - String str = "BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "N:Doe;Jonathan;;Mr;\r\n" + "FN:John Doe\r\n" - + "END:VCARD\r\n"; - - VCard vcard = Ezvcard.parse(str).first(); - String fullName = vcard.getFormattedName().getValue(); - String lastName = vcard.getStructuredName().getFamily(); - } - -} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/vcard/VCardExporter.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/vcard/VCardExporter.java new file mode 100644 index 0000000..1b5274e --- /dev/null +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/people/vcard/VCardExporter.java @@ -0,0 +1,17 @@ +package org.argeo.app.ui.people.vcard; + +import ezvcard.Ezvcard; +import ezvcard.VCard; + +public class VCardExporter { + + public static void main(String[] args) { + String str = "BEGIN:VCARD\r\n" + "VERSION:4.0\r\n" + "N:Doe;Jonathan;;Mr;\r\n" + "FN:John Doe\r\n" + + "END:VCARD\r\n"; + + VCard vcard = Ezvcard.parse(str).first(); + String fullName = vcard.getFormattedName().getValue(); + String lastName = vcard.getStructuredName().getFamily(); + } + +} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java index 988b06f..ad8a913 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java @@ -17,8 +17,8 @@ import org.argeo.api.cms.ux.CmsUi; import org.argeo.app.ui.SuiteApp; import org.argeo.cms.AbstractCmsApp; import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.util.LangUtils; import org.argeo.jcr.Jcr; -import org.argeo.util.LangUtils; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control;