From: Mathieu Baudier Date: Wed, 19 Oct 2022 07:46:15 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/unstable' into merge-to-testing X-Git-Tag: v2.1.26~1^2~4 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=cc03074c27d48af9e5842272c18f8c418a25d3e6;hp=7dbe300240a6003d238c697d5d4150e3d95b41c1 Merge remote-tracking branch 'origin/unstable' into merge-to-testing --- diff --git a/.gitmodules b/.gitmodules index d728334..3a63021 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "sdk/argeo-build"] path = sdk/argeo-build - url = http://git.argeo.org/cc0/argeo-build.git - branch = testing + url = https://code.argeo.org/git/cc0/argeo-build.git + branch = merge-to-testing diff --git a/.project b/.project index ff41893..4106bf4 100644 --- a/.project +++ b/.project @@ -5,6 +5,11 @@ + + org.eclipse.pde.ds.core.builder + + + diff --git a/Makefile b/Makefile index c1803c9..7214fb6 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,11 @@ org.argeo.app.api \ org.argeo.app.core \ org.argeo.app.servlet.odk \ org.argeo.app.servlet.publish \ -org.argeo.app.ui \ org.argeo.app.theme.default \ -org.argeo.app.ui.rap \ +org.argeo.app.profile.acr.fs \ +org.argeo.app.profile.acr.jcr \ +swt/org.argeo.app.swt \ +swt/org.argeo.app.ui \ A2_OUTPUT = $(SDK_BUILD_BASE)/a2 A2_BASE = $(A2_OUTPUT) @@ -21,13 +23,18 @@ DEP_CATEGORIES = \ org.argeo.tp \ org.argeo.tp.apache \ org.argeo.tp.jetty \ -org.argeo.tp.eclipse.equinox \ -org.argeo.tp.eclipse.rap \ +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.eclipse.rap \ +org.argeo.cms.jcr \ +swt/org.argeo.cms \ +swt/org.argeo.cms.jcr \ +swt/rap/org.argeo.cms \ clean: rm -rf $(BUILD_BASE) diff --git a/branch.mk b/branch.mk index 936a675..dbecaaa 100644 --- a/branch.mk +++ b/branch.mk @@ -1 +1 @@ -include $(SDK_SRC_BASE)/cnf/testing.bnd +BRANCH=testing \ No newline at end of file diff --git a/cnf/build.bnd b/cnf/build.bnd deleted file mode 100644 index a464edc..0000000 --- a/cnf/build.bnd +++ /dev/null @@ -1,3 +0,0 @@ --include: \ -${workspace}/cnf/testing.bnd, \ -${workspace}/sdk/argeo-build/argeo.bnd, \ diff --git a/cnf/testing.bnd b/cnf/testing.bnd deleted file mode 100644 index cae4d91..0000000 --- a/cnf/testing.bnd +++ /dev/null @@ -1,10 +0,0 @@ -MAJOR=2 -MINOR=1 -MICRO=25 -qualifier=.next - -category=org.argeo.suite -Bundle-RequiredExecutionEnvironment=JavaSE-11 - -argeo.rpm.stagingRepository=/srv/rpmfactory/testing/argeo-osgi-2/argeo -argeo.rpm.suffix= diff --git a/cnf/unstable.bnd b/cnf/unstable.bnd deleted file mode 100644 index 1f15381..0000000 --- a/cnf/unstable.bnd +++ /dev/null @@ -1,10 +0,0 @@ -MAJOR=2 -MINOR=3 -MICRO=5 -qualifier=.next - -category=org.argeo.suite -Bundle-RequiredExecutionEnvironment=JavaSE-11 - -argeo.rpm.stagingRepository=/srv/rpmfactory/unstable/argeo-osgi-2/argeo -argeo.rpm.suffix=-unstable diff --git a/configure b/configure old mode 100644 new mode 100755 index 9b3e980..9f49215 --- a/configure +++ b/configure @@ -1,55 +1,7 @@ #!/bin/sh -# We build where we are -SDK_BUILD_BASE=$(pwd -P)/output - # Source are located where this script is SDK_SRC_BASE="$(cd "$(dirname "$0")"; pwd -P)" -SDK_MK=$SDK_SRC_BASE/sdk.mk - -#echo SDK_BUILD_BASE=$SDK_BUILD_BASE -#echo SDK_SRC_BASE=$SDK_SRC_BASE -#echo SDK_MK=$SDK_MK - -if [ -f "$SDK_MK" ]; -then - -echo "File $SDK_MK already exists. Remove it in order to configure a new build location:" -echo "rm $SDK_MK" -exit 1 - -else - -if [ -z "$JAVA_HOME" ] -then -echo "Environment variable JAVA_HOME must be set" -exit 1 -fi - -# Create build directory, so that it can be used right away -# and we check whether we have the rights -mkdir -p $SDK_BUILD_BASE -if [ -f "$SDK_MK" ]; -then -echo "Cannot create $SDK_BUILD_BASE, SDK configuration has failed." -exit 2 -fi - -# Generate sdk.mk -cat > "$SDK_MK" < - + diff --git a/org.argeo.app.api/src/org/argeo/app/api/EntityType.java b/org.argeo.app.api/src/org/argeo/app/api/EntityType.java index 48b1266..8b9164a 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/EntityType.java +++ b/org.argeo.app.api/src/org/argeo/app/api/EntityType.java @@ -1,7 +1,9 @@ package org.argeo.app.api; +import org.argeo.api.acr.QNamed; + /** Types related to entities. */ -public enum EntityType implements JcrName { +public enum EntityType implements QNamed { // entity entity, local, relatedTo, // structure @@ -18,25 +20,30 @@ public enum EntityType implements JcrName { person, user; @Override - public String getPrefix() { - return prefix(); - } - - public static String prefix() { + public String getDefaultPrefix() { return "entity"; } +// @Override +// public String getPrefix() { +// return getDefaultPrefix(); +// } +// +// public static String prefix() { +// return "entity"; +// } + public String basePath() { return '/' + name(); } @Override public String getNamespace() { - return namespace(); - } - - public static String namespace() { return "http://www.argeo.org/ns/entity"; } +// public static String namespace() { +// return "http://www.argeo.org/ns/entity"; +// } + } diff --git a/org.argeo.app.api/src/org/argeo/app/api/IdRange.java b/org.argeo.app.api/src/org/argeo/app/api/IdRange.java new file mode 100644 index 0000000..e47eb5e --- /dev/null +++ b/org.argeo.app.api/src/org/argeo/app/api/IdRange.java @@ -0,0 +1,133 @@ +package org.argeo.app.api; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +/** A range of numerical IDs (typically numerical uid or gid). */ +public class IdRange { + // see https://systemd.io/UIDS-GIDS/#special-distribution-uid-ranges + final static long MIN_INCLUDED = Long.parseUnsignedLong("66000"); + final static long MAX_EXCLUDED = Long.parseUnsignedLong("4294967294"); + + // We use long as a de facto unsigned int + + /** included */ + private final long min; + /** included */ + private final long max; + + public IdRange(long min, long max) { + this.min = min; + this.max = max; + } + + public IdRange(long minPow10) { + this(minPow10, maxFromMinPow10(minPow10)); + } + + public long getMin() { + return min; + } + + public long getMax() { + return max; + } + + @Override + public int hashCode() { + return (int) min; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IdRange idRange) { + return min == idRange.min && max == idRange.max; + } else + return false; + } + + @Override + public String toString() { + return "[" + Long.toUnsignedString(min) + "," + Long.toUnsignedString(max) + "]"; + } + + /* + * RANGE GENERATION + */ + public static synchronized Set randomRanges10000(int count, Set forbiddenRanges) { + Set res = new HashSet<>(); + + for (int i = 0; i < count; i++) { + IdRange newRange = null; + do { + newRange = randomRange10000(); + } while (overlap(newRange, res) || overlap(newRange, forbiddenRanges)); + res.add(newRange); + } + return res; + } + + public static synchronized IdRange randomRange10000() { + // TODO make it more generic + long minPred = 7l; + long maxPred = 429496l; + + long rand = ThreadLocalRandom.current().nextLong(minPred, maxPred); + long min = rand * 10000l; + return new IdRange(min); + } + + public static boolean overlap(IdRange idRange, Set idRanges) { + for (IdRange other : idRanges) { + if (overlap(idRange, other)) + return true; + } + return false; + } + + public static boolean overlap(IdRange idRange, IdRange other) { + // see + // https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap + return idRange.min <= other.max && other.min <= idRange.max; + } + + /* + * UTILITIES + */ + + private static long maxFromMinPow10(long minPow10) { + if ((minPow10 % 100) != 0) { + throw new IllegalArgumentException(minPow10 + " must at least ends with two zeroes"); + } + int exp = 2; + exp: for (int i = exp + 1; i < 10; i++) { + if ((minPow10 % pow10(i)) != 0) + break exp; + exp++; + } +// System.out.println(exp); + + long max = minPow10 + pow10(exp) - 1; + return max; + } + + /** Power of 10. */ + private static long pow10(int exp) { + if (exp == 0) + return 1; + else + return 10 * pow10(exp - 1); + } + + public static void main(String... args) { + System.out.println(maxFromMinPow10(100)); + System.out.println(maxFromMinPow10(78500)); + System.out.println(maxFromMinPow10(716850000)); + +// System.out.println(pow10(6)); +// System.out.println(maxFromMinPow10(12)); +// System.out.println(maxFromMinPow10(124)); +// System.out.println(maxFromMinPow10(99814565)); + } +} diff --git a/org.argeo.app.api/src/org/argeo/app/api/JcrName.java b/org.argeo.app.api/src/org/argeo/app/api/JcrName.java deleted file mode 100644 index 182494a..0000000 --- a/org.argeo.app.api/src/org/argeo/app/api/JcrName.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.app.api; - -import java.util.function.Supplier; - -/** Can be applied to {@link Enum}s in order to generate prefixed names. */ -@FunctionalInterface -public interface JcrName extends Supplier { - String name(); - - default String getPrefix() { - return null; - } - - default String getNamespace() { - return null; - } - - @Override - default String get() { - String prefix = getPrefix(); - return prefix != null ? prefix + ":" + name() : name(); - } - - default String withNamespace() { - String namespace = getNamespace(); - if (namespace == null) - throw new UnsupportedOperationException("No namespace is specified for " + getClass()); - return "{" + namespace + "}" + name(); - } -} diff --git a/org.argeo.app.api/src/org/argeo/app/api/RankedObject.java b/org.argeo.app.api/src/org/argeo/app/api/RankedObject.java index 31c43a7..fab42d7 100644 --- a/org.argeo.app.api/src/org/argeo/app/api/RankedObject.java +++ b/org.argeo.app.api/src/org/argeo/app/api/RankedObject.java @@ -16,28 +16,28 @@ public class RankedObject { private T object; private Map properties; - private final Long rank; + private final int rank; public RankedObject(T object, Map properties) { this(object, properties, extractRanking(properties)); } - public RankedObject(T object, Map properties, Long rank) { + public RankedObject(T object, Map properties, int rank) { super(); this.object = object; this.properties = properties; this.rank = rank; } - private static Long extractRanking(Map properties) { + private static int extractRanking(Map properties) { if (properties == null) - return 0l; + return 0; if (properties.containsKey(SERVICE_RANKING)) - return Long.valueOf(properties.get(SERVICE_RANKING).toString()); + return (Integer) properties.get(SERVICE_RANKING); // else if (properties.containsKey(SERVICE_ID)) // return (Long) properties.get(SERVICE_ID); else - return 0l; + return 0; } public T get() { @@ -48,7 +48,7 @@ public class RankedObject { return properties; } - public Long getRank() { + public int getRank() { return rank; } @@ -62,7 +62,7 @@ public class RankedObject { if (!(obj instanceof RankedObject)) return false; RankedObject other = (RankedObject) obj; - return rank.equals(other.rank) && object.equals(other.object); + return rank == other.rank && object.equals(other.object); } @Override @@ -70,6 +70,18 @@ public class RankedObject { return object.getClass().getName() + " with rank " + rank; } + public static boolean hasHigherRank(Map> map, K key, Map properties) { + if (!map.containsKey(key)) + return true; + RankedObject rankedObject = new RankedObject<>(null, properties); + RankedObject current = map.get(key); + return current.getRank() < rankedObject.getRank(); + } + + /** + * @return the {@link RankedObject}, or null if the current one was + * kept + */ public static RankedObject putIfHigherRank(Map> map, K key, T object, Map properties) { RankedObject rankedObject = new RankedObject<>(object, properties); @@ -81,14 +93,18 @@ public class RankedObject { return rankedObject; } else { RankedObject current = map.get(key); - if (current.getRank() <= rankedObject.getRank()) { + if (current.getRank() < rankedObject.getRank()) { map.put(key, rankedObject); - if (log.isTraceEnabled()) - log.trace("Replaced " + key + " by " + object.getClass().getName() + " with rank " + if (log.isDebugEnabled()) + log.debug("Replaced " + key + " by " + object.getClass().getName() + " with rank " + rankedObject.getRank()); return rankedObject; + } else if (current.getRank() == rankedObject.getRank()) { + log.error("Already " + key + " by " + current.get().getClass().getName() + " with rank " + + rankedObject.getRank() + ", ignoring " + rankedObject.get().getClass().getName()); + return null; } else { - return current; + return null; } } diff --git a/org.argeo.app.api/src/org/argeo/app/api/RankingKey.java b/org.argeo.app.api/src/org/argeo/app/api/RankingKey.java deleted file mode 100644 index 691570c..0000000 --- a/org.argeo.app.api/src/org/argeo/app/api/RankingKey.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.argeo.app.api; - -import java.util.Map; - -/** - * Key used to classify and filter available components (typically provided by - * OSGi services). - */ -@Deprecated -public class RankingKey implements Comparable { - public final static String SERVICE_PID = "service.pid"; - public final static String SERVICE_ID = "service.id"; - public final static String SERVICE_RANKING = "service.ranking"; - public final static String DATA_TYPE = "data.type"; - - private String pid; - private Integer ranking = 0; - private Long id = 0l; - private String dataType; - private String dataPath; - - public RankingKey(String pid, Integer ranking, Long id, String dataType, String dataPath) { - super(); - this.pid = pid; - this.ranking = ranking; - this.id = id; - this.dataType = dataType; - this.dataPath = dataPath; - } - - public RankingKey(Map properties) { - this.pid = properties.containsKey(SERVICE_PID) ? properties.get(SERVICE_PID).toString() : null; - this.ranking = properties.containsKey(SERVICE_RANKING) - ? Integer.parseInt(properties.get(SERVICE_RANKING).toString()) - : 0; - this.id = properties.containsKey(SERVICE_ID) ? (Long) properties.get(SERVICE_ID) : null; - - // Argeo specific - this.dataType = properties.containsKey(DATA_TYPE) ? properties.get(DATA_TYPE).toString() : null; - } - - @Override - public int hashCode() { - Integer result = 0; - if (pid != null) - result = +pid.hashCode(); - if (ranking != null) - result = +ranking; - if (dataType != null) - result = +dataType.hashCode(); - return result; - } - - @Override - protected Object clone() throws CloneNotSupportedException { - return new RankingKey(pid, ranking, id, dataType, dataPath); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(""); - if (pid != null) - sb.append(pid); - if (ranking != null && ranking != 0) - sb.append(' ').append(ranking); - if (dataType != null) - sb.append(' ').append(dataType); - return sb.toString(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof RankingKey)) - return false; - RankingKey other = (RankingKey) obj; - return equalsOrBothNull(pid, other.pid) && equalsOrBothNull(ranking, other.ranking) - && equalsOrBothNull(id, other.id) && equalsOrBothNull(dataType, other.dataType) - && equalsOrBothNull(dataPath, other.dataPath); - } - - @Override - public int compareTo(RankingKey o) { - if (pid != null && o.pid != null) { - if (pid.equals(o.pid)) { - if (ranking.equals(o.ranking)) - if (id != null && o.id != null) - return id.compareTo(o.id); - else - return 0; - else - return ranking.compareTo(o.ranking); - } else { - return pid.compareTo(o.pid); - } - - } else { - if (dataType != null && o.dataType != null) { - if (dataType.equals(o.dataType)) { - // TODO factorise - if (ranking.equals(o.ranking)) - if (id != null && o.id != null) - return id.compareTo(o.id); - else - return 0; - else - return ranking.compareTo(o.ranking); - } else { - return dataPath.compareTo(o.dataType); - } - } - } - return -1; - } - - public String getPid() { - return pid; - } - - public Integer getRanking() { - return ranking; - } - - public Long getId() { - return id; - } - - public String getDataType() { - return dataType; - } - - public String getDataPath() { - return dataPath; - } - - public static RankingKey minPid(String pid) { - return new RankingKey(pid, Integer.MIN_VALUE, null, null, null); - } - - public static RankingKey maxPid(String pid) { - return new RankingKey(pid, Integer.MAX_VALUE, null, null, null); - } - - public static RankingKey minDataType(String dataType) { - return new RankingKey(null, Integer.MIN_VALUE, null, dataType, null); - } - - public static RankingKey maxDataType(String dataType) { - return new RankingKey(null, Integer.MAX_VALUE, null, dataType, null); - } - - private static boolean equalsOrBothNull(Object o1, Object o2) { - if (o1 == null && o2 == null) - return true; - if (o1 == null && o2 != null) - return false; - if (o1 != null && o2 == null) - return false; - return o2.equals(o1); - } -} 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 38ce11f..8bf9ec4 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 @@ -1,19 +1,47 @@ package org.argeo.app.api; +import javax.xml.namespace.QName; + +import org.argeo.api.acr.ContentName; +import org.argeo.api.acr.CrName; import org.argeo.api.cms.CmsConstants; -import org.argeo.util.naming.Distinguished; +import org.argeo.cms.auth.SystemRole; import org.argeo.util.naming.LdapAttrs; -/** Office specific roles used in the code */ -public enum SuiteRole implements Distinguished { - coworker, manager; +/** Standard suite system roles. */ +public enum SuiteRole implements SystemRole { + /** An external person who has read access to part of the information. */ + observer, + /** An active coworker. */ + coworker, + /** Someone who is allowed validate and publish information. */ + publisher, + /** Someone with manager status within an organisation. Does not necessarily give more rights. */ + manager, + // + ; + + private final static String QUALIFIER = "app."; + + private final ContentName name; + + SuiteRole() { + name = new ContentName(CrName.ROLE_NAMESPACE_URI, QUALIFIER + name()); + } + + @Override + public QName getName() { + return name; + } - public String getRolePrefix() { + @Deprecated + private String getRolePrefix() { return "org.argeo.suite"; } + @Deprecated public String dn() { return new StringBuilder(LdapAttrs.cn.name()).append("=").append(getRolePrefix()).append(".").append(name()) - .append(",").append(CmsConstants.ROLES_BASEDN).toString(); + .append(",").append(CmsConstants.SYSTEM_ROLES_BASEDN).toString(); } } diff --git a/org.argeo.app.core/.classpath b/org.argeo.app.core/.classpath index e801ebf..81fe078 100644 --- a/org.argeo.app.core/.classpath +++ b/org.argeo.app.core/.classpath @@ -1,6 +1,6 @@ - + diff --git a/org.argeo.app.core/OSGI-INF/geoToolsTest.xml b/org.argeo.app.core/OSGI-INF/geoToolsTest.xml new file mode 100644 index 0000000..68a53ab --- /dev/null +++ b/org.argeo.app.core/OSGI-INF/geoToolsTest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/org.argeo.app.core/OSGI-INF/maintenanceService.xml b/org.argeo.app.core/OSGI-INF/maintenanceService.xml index b88d68c..b320df4 100644 --- a/org.argeo.app.core/OSGI-INF/maintenanceService.xml +++ b/org.argeo.app.core/OSGI-INF/maintenanceService.xml @@ -1,7 +1,8 @@ - + - - + + + diff --git a/org.argeo.app.core/OSGI-INF/termsManager.xml b/org.argeo.app.core/OSGI-INF/termsManager.xml index 92f84c6..797c5a3 100644 --- a/org.argeo.app.core/OSGI-INF/termsManager.xml +++ b/org.argeo.app.core/OSGI-INF/termsManager.xml @@ -1,7 +1,7 @@ - + - + diff --git a/org.argeo.app.core/bnd.bnd b/org.argeo.app.core/bnd.bnd index 8604ec9..9d8228b 100644 --- a/org.argeo.app.core/bnd.bnd +++ b/org.argeo.app.core/bnd.bnd @@ -6,6 +6,7 @@ OSGI-INF/maintenanceService.xml,\ OSGI-INF/dbk4Converter.xml,\ Import-Package:\ +tech.units.indriya.unit,\ org.osgi.service.useradmin,\ javax.jcr.nodetype,\ javax.jcr.security,\ diff --git a/org.argeo.app.core/build.properties b/org.argeo.app.core/build.properties index 6210e84..76d3ee9 100644 --- a/org.argeo.app.core/build.properties +++ b/org.argeo.app.core/build.properties @@ -3,3 +3,4 @@ bin.includes = META-INF/,\ .,\ OSGI-INF/ source.. = src/ +additional.bundles = org.argeo.init 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 new file mode 100644 index 0000000..8b58fe1 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/SuiteContentTypes.java @@ -0,0 +1,82 @@ +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 532b7dd..98784f8 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 @@ -8,6 +8,7 @@ import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.jcr.security.Privilege; +import org.argeo.api.acr.spi.ProvidedRepository; import org.argeo.api.cms.CmsConstants; import org.argeo.app.api.EntityType; import org.argeo.jcr.JcrUtils; @@ -15,6 +16,15 @@ import org.argeo.maintenance.AbstractMaintenanceService; /** Initialises an Argeo Suite backend. */ public class SuiteMaintenanceService extends AbstractMaintenanceService { + @Override + public void init() { + super.init(); + + for (SuiteContentTypes types : SuiteContentTypes.values()) { + getContentRepository().registerTypes(types.getDefaultPrefix(), types.getNamespace(), + types.getResource() != null ? types.getResource().toExternalForm() : null); + } + } @Override public boolean prepareJcrTree(Session adminSession) throws RepositoryException, IOException { @@ -33,7 +43,8 @@ public class SuiteMaintenanceService extends AbstractMaintenanceService { public void configurePrivileges(Session adminSession) throws RepositoryException { JcrUtils.addPrivilege(adminSession, EntityType.user.basePath(), CmsConstants.ROLE_USER_ADMIN, Privilege.JCR_ALL); - //JcrUtils.addPrivilege(adminSession, "/", SuiteRole.coworker.dn(), Privilege.JCR_READ); + // JcrUtils.addPrivilege(adminSession, "/", SuiteRole.coworker.dn(), + // Privilege.JCR_READ); } } 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 0a31b14..531d3a3 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,6 +1,7 @@ package org.argeo.app.core; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import javax.jcr.Node; @@ -8,30 +9,32 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import javax.jcr.security.Privilege; -import javax.naming.ldap.LdapName; import javax.security.auth.x500.X500Principal; +import javax.xml.namespace.QName; +import org.argeo.api.acr.Content; import org.argeo.api.cms.CmsConstants; import org.argeo.api.cms.CmsSession; import org.argeo.app.api.EntityType; -import org.argeo.app.api.SuiteRole; -import org.argeo.jackrabbit.security.JackrabbitSecurityUtils; +import org.argeo.cms.auth.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 { - - public static String getUserNodePath(LdapName userDn) { - String uid = userDn.getRdn(userDn.size() - 1).getValue().toString(); + @Deprecated + public static String getUserNodePath(String userDn) { + String uid = RoleNameUtils.getLastRdnValue(userDn); return EntityType.user.basePath() + '/' + uid; } - public static Node getOrCreateUserNode(Session adminSession, LdapName userDn) { + @Deprecated + private static Node getOrCreateUserNode(Session adminSession, String userDn) { try { Node usersBase = adminSession.getNode(EntityType.user.basePath()); - String uid = userDn.getRdn(userDn.size() - 1).getValue().toString(); + String uid = RoleNameUtils.getLastRdnValue(userDn); Node userNode; if (!usersBase.hasNode(uid)) { userNode = usersBase.addNode(uid, NodeType.NT_UNSTRUCTURED); @@ -40,8 +43,8 @@ public class SuiteUtils { userNode.setProperty(LdapAttrs.distinguishedName.property(), userDn.toString()); userNode.setProperty(LdapAttrs.uid.property(), uid); adminSession.save(); - JackrabbitSecurityUtils.denyPrivilege(adminSession, userNode.getPath(), SuiteRole.coworker.dn(), - Privilege.JCR_READ); +// JackrabbitSecurityUtils.denyPrivilege(adminSession, userNode.getPath(), SuiteRole.coworker.dn(), +// Privilege.JCR_READ); JcrUtils.addPrivilege(adminSession, userNode.getPath(), new X500Principal(userDn.toString()).getName(), Privilege.JCR_READ); JcrUtils.addPrivilege(adminSession, userNode.getPath(), CmsConstants.ROLE_USER_ADMIN, @@ -55,6 +58,7 @@ public class SuiteUtils { } } + @Deprecated public static Node getCmsSessionNode(Session session, CmsSession cmsSession) { try { return session.getNode(getUserNodePath(cmsSession.getUserDn()) + '/' + cmsSession.getUuid().toString()); @@ -63,9 +67,10 @@ public class SuiteUtils { } } + @Deprecated public static Node getOrCreateCmsSessionNode(Session adminSession, CmsSession cmsSession) { try { - LdapName userDn = cmsSession.getUserDn(); + String userDn = cmsSession.getUserDn(); // String uid = userDn.get(userDn.size() - 1); Node userNode = getOrCreateUserNode(adminSession, userDn); // if (!usersBase.hasNode(uid)) { @@ -95,11 +100,6 @@ public class SuiteUtils { } } - /** Singleton. */ - private SuiteUtils() { - - } - public static Set extractRoles(String[] semiColArr) { Set res = new HashSet<>(); // TODO factorize and make it more robust @@ -120,4 +120,33 @@ public class SuiteUtils { return res; } + synchronized static public long findNextId(Content hierarchyUnit, QName cclass) { + if (!hierarchyUnit.hasContentClass(LdapObjs.posixGroup.qName())) + throw new IllegalArgumentException(hierarchyUnit + " is not a POSIX group"); + + long min = hierarchyUnit.get(LdapAttrs.gidNumber.qName(), Long.class).orElseThrow(); + long currentMax = 0l; + for (Content childHu : hierarchyUnit) { + if (!childHu.hasContentClass(LdapObjs.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 (id > currentMax) + currentMax = id; + } + } + } + } + if (currentMax == 0l) + return min; + return currentMax + 1; + } + + /** Singleton. */ + private SuiteUtils() { + } } diff --git a/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java b/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java index f86445c..b0678cd 100644 --- a/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java +++ b/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java @@ -78,7 +78,6 @@ public class XPathUtils { * @param propertyName * @param calendar the reference date * @param lowerOrGreater "<", ">" TODO validate ">=" - * @return * @throws RepositoryException */ public static String getPropertyDateComparaison(String propertyName, Calendar cal, String lowerOrGreater) diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/entity.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/entity.xsd new file mode 100644 index 0000000..a2fbfcd --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/entity.xsd @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.app.core/src/org/argeo/app/core/schemas/fop.xsd b/org.argeo.app.core/src/org/argeo/app/core/schemas/fop.xsd new file mode 100644 index 0000000..a787bf3 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/schemas/fop.xsd @@ -0,0 +1,4313 @@ + + + + + + + + + I'm not sure where to place this. + It applies to the page context (NOT implemented) + + + I have not coded for the functions described in 5.10 Core Function Library + They need to be segregated into groups and then inserted in the types + + common_functions + object inherited-property-value(NCName) + object from-parent( NCName) + object from-nearest-specified-value( NCName) + object merge-property-values( NCName) + + font_functions + object system-font( NCName , NCName) + + length_functions + numeric floor( numeric) + numeric ceiling(numeric) + numeric round(numeric) + numeric min( numeric , numeric) + numeric max(numeric , numeric) + numeric abs( numeric) + + table_cell_or_descendants_functions + object from-table-column( NCName) + + color_functions + color rgb(numeric , numeric , numeric) + color rgb-icc(numeric , numeric , numeric , NCName , numeric , numeric) + color system-color( NCName) + + label_functions + numeric body-start() + numeric label-end() + + (defined) + table-column_functions + numeric proportional-column-width( numeric) + + This schema has been developed in order to validate XSL FO documents for FOP + All of the elements need to be prefixed with fo: + The namespace prefix is xmlns:fo = "http://www.w3.org/1999/XSL/Format". + + This schema, as delivered, may either validate the full spec, or, just the FOP portion. + (What it validates depends upon what I was doing with it when released.) + + If you want to restrict it to just those elements and attributes implemented by FOP, + you need to edit the and tags to exclude the groups ending with _Not + + Some schema tools complain about the placement of comments in this schema and will remove or reorder them + There are fop_result and fop_fail comments on specific features not implemented by FOP + + FOP does not enforce the following schema requirements + + fo:simple-page-master model = "(region-body,region-before?,region-after?,region-start?,region-end?)" + elements can be in any order + + fo:table-cell model = "(%block;)+" + Can be empty + + fo:flow model = "(%block;)+" + Can be empty + + This schema allows the length attribute to be negative for some elements like margins. + There may be instances where I've entered %integer_Type; and it should be positive-integer or number + The schema trys to handle the text based rules re: fo:markers, fo:float, footer and fo:initial-property-set + But, allows you to do illegal things if you want because I couldn't figure out how to constrain against the illegal actions. + + Please e-mail your comments to cpaussa@myrealbox.com + + Contribution by Oleg Tkachenko + (Declarations able to include non-xsl children) + + This declaration assumes that all elements must come before other stuff, + which is not required by spec, but I cannot see any way to express such constraints in schema, + one could use instead of , but this way we lose control over (color-profile)+ constraint. + + VCP 21-Oct-2002 + Updated all (px|pt|mm|cm|in|em) to (px|pt|mm|cm|in|em|%) to allow percentage types. + Updated the restriction base of those types from NMTOKEN to string + + + + + + + + + + + + + empty group so cannot be defined + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + empty group so cannot be defined + + + + + + + + + + + + + + empty group + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Removed because I'm not sure how to handle this + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inheritable + + + + + + + + + + + + + + + + + + + + + + + + + + Inheritable + + + + + + + + + + + + + + + + Inheritable + + + + + + + + + + + + + + + + + Inheritable + + + + + + + + + + + + + + Inheritable attributes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inherited + + + + + + + + + + + + + + + + + + + + + + Inherited + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inherited + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Font properties are all inheritable + + + + + + + + + + + Font properties are all inheritable + + + + + + + + + + + + + + + + The hyphenation properties are all inheritable and so superceeded by that list + + + + + + + + + + + + + The hyphenation properties are all inheritable and so superceeded by that list + + + + + + + + + + + + + + + + + + Indent properties are inheritable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Simple Types definitions + + + + + + A signed integer value which consists of an optional '+' or '-' character followed by a sequence of digits. A property may define additional constraints on the value. + + + + + + + + + + + + + + + A signed real number which consists of an optional '+' or '-' character followed by a sequence of digits followed by an optional '.' character and sequence of digits. A property may define additional constraints on the value. + + + + + + + + + + + + + A signed length value where a 'length' is a real number plus a unit qualification. A property may define additional constraints on the value. + + + + + + + + + + + + + + + + A compound datatype, with components: minimum, optimum, maximum. Each component is a . If "minimum" is greater than optimum, it will be treated as if it had been set to "optimum". If "maximum" is less than optimum, it will be treated as if it had been set to "optimum". A property may define additional constraints on the values. + + + + + + + + + + + + + + + + + + + + + A compound datatype, with components: length, conditionality. The length component is a . The conditionality component is either "discard" or "retain". A property may define additional constraints on the values. + + + + + + + + + + + + + + + + + + + + + + + + A compound datatype, with components: block-progression-direction, and inline-progression-direction. Each component is a . A property may define additional constraints on the values. + + + + + + + + + + + + + + + + + + + + + A compound datatype, with components: minimum, optimum, maximum, precedence, and conditionality. The minimum, optimum, and maximum components are s. The precedence component is either "force" or an . The conditionality component is either "discard" or "retain". If "minimum" is greater than optimum, it will be treated as if it had been set to "optimum". If "maximum" is less than optimum, it will be treated as if it had been set to "optimum". + + + + + + + + + + + + + A representation of an angle consisting of an optional '+' or '-' character immediately followed by a immediately followed by an angle unit identifier. Angle unit identifiers are: 'deg' (for degrees), 'grad' (for grads), and 'rad' (for radians). The specified values are normalized to the range 0deg to 360deg. A property may define additional constraints on the value. + + + + + + + + + + + + + A signed real percentage which consists of an optional '+' or '-' character followed by a sequence of digits followed by an optional '.' character and sequence of digits followed by '%'. A property may define additional constraints on the value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + A string of characters representing a name. It must conform to the definition of an NCName in + + + + + + + + + + A string of characters identifying a font. + + + + + + + + + + + + + + + + + + + + + + + Either a string of characters representing a keyword or a color function defined in . The list of keyword color names is: aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow. + + + + + + + + + + + + The function proportional-column-width(N[0]) + This returns a width as a fraction of the available width as ( N[0] / sum1 ) * available space + The parent table must have width="x" and table-layout="fixed" + + + + + + + + + + + , , proportional-column-width, or common-functions + + + + + + + + + - - -
- - - \ No newline at end of file diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/map.js b/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/map.js deleted file mode 100644 index 68489fb..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/openlayers/map.js +++ /dev/null @@ -1,11 +0,0 @@ -var map = new ol.Map({ - target : 'map', - layers : [ new ol.layer.Tile({ - source : new ol.source.OSM() - }) ], - view : new ol.View({ - center : ol.proj.fromLonLat([ 34, 34 ]), - zoom : 4 - }) -}); - \ No newline at end of file diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java b/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java deleted file mode 100644 index 7e9fa6a..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/people/PeopleEntryArea.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.argeo.app.ui.people; - -import java.util.Set; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsTheme; -import org.argeo.app.api.SuiteRole; -import org.argeo.app.ui.SuiteEvent; -import org.argeo.app.ui.SuiteIcon; -import org.argeo.app.ui.dialogs.NewUserWizard; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.swt.dialogs.CmsWizardDialog; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.window.Window; -import org.eclipse.jface.wizard.Wizard; -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.ToolBar; -import org.eclipse.swt.widgets.ToolItem; -import org.osgi.service.useradmin.User; - -/** Entry to the admin area. */ -public class PeopleEntryArea implements CmsUiProvider { - - private CmsUserManager cmsUserManager; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); - parent.setLayout(new GridLayout()); - TableViewer usersViewer = new TableViewer(parent); - usersViewer.setContentProvider(new UsersContentProvider()); - - TableViewerColumn idCol = new TableViewerColumn(usersViewer, SWT.NONE); - idCol.getColumn().setWidth(70); - idCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.uid.name()); - } - }); - - TableViewerColumn givenNameCol = new TableViewerColumn(usersViewer, SWT.NONE); - givenNameCol.getColumn().setWidth(150); - givenNameCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.givenName.name()); - } - }); - - TableViewerColumn snCol = new TableViewerColumn(usersViewer, SWT.NONE); - snCol.getColumn().setWidth(150); - snCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.sn.name()); - } - }); - - TableViewerColumn mailCol = new TableViewerColumn(usersViewer, SWT.NONE); - mailCol.getColumn().setWidth(400); - mailCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.mail.name()); - } - }); - - Composite bottom = new Composite(parent, 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(SuiteIcon.delete.getSmallIcon(theme)); - ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT); - addItem.setImage(SuiteIcon.add.getSmallIcon(theme)); - usersViewer.addDoubleClickListener(new IDoubleClickListener() { - - @Override - public void doubleClick(DoubleClickEvent event) { - User user = (User) usersViewer.getStructuredSelection().getFirstElement(); - if (user != null) { -// Node userNode = getOrCreateUserNode(user, context); - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(), - SuiteEvent.eventProperties(user)); - } - - } - }); - usersViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - User user = (User) usersViewer.getStructuredSelection().getFirstElement(); - if (user != null) { -// Node userNode = getOrCreateUserNode(user, context); - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(), - SuiteEvent.eventProperties(user)); - deleteItem.setEnabled(true); - } else { - deleteItem.setEnabled(false); - } - } - }); - - addItem.addSelectionListener((Selected) (e) -> { - // SuiteUtils.getOrCreateUserNode(adminSession, userDn); - Wizard wizard = new NewUserWizard(null); - CmsWizardDialog dialog = new CmsWizardDialog(parent.getShell(), wizard); - // WizardDialog dialog = new WizardDialog(shell, wizard); - if (dialog.open() == Window.OK) { - // TODO create - } - }); - - usersViewer.getTable().setLayoutData(CmsSwtUtils.fillAll()); - usersViewer.setInput(cmsUserManager); - - return usersViewer.getTable(); - } - -// private Node getOrCreateUserNode(User user, Node context) { -// return JcrUtils.mkdirs(Jcr.getSession(context), -// "/" + EntityType.user.name() + "/" + getUserProperty(user, LdapAttrs.uid.name()), -// EntityType.user.get()); -// } - - private String getUserProperty(Object element, String key) { - Object value = ((User) element).getProperties().get(key); - return value != null ? value.toString() : null; - } - - class UsersContentProvider implements IStructuredContentProvider { - - @Override - public Object[] getElements(Object inputElement) { - CmsUserManager cum = (CmsUserManager) inputElement; - Set users = cum.listUsersInGroup(SuiteRole.coworker.dn(), null); - return users.toArray(); - } - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - } - - public void setCmsUserManager(CmsUserManager cmsUserManager) { - this.cmsUserManager = cmsUserManager; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java b/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java deleted file mode 100644 index 24b0893..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/people/PersonUiProvider.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.argeo.app.ui.people; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.app.ui.SuiteMsg; -import org.argeo.app.ui.SuiteUiUtils; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.swt.SWT; -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.Group; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; - -/** Edit a suite user. */ -public class PersonUiProvider implements CmsUiProvider { - private String[] availableRoles; - private CmsUserManager cmsUserManager; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Section main = new Section(parent, SWT.NONE, context); - main.setLayoutData(CmsSwtUtils.fillAll()); - - String uid = context.getName(); - User user = cmsUserManager.getUserFromLocalId(uid); - -// 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); - Group rolesSection = new Group(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; - } - - 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; - } - - public void init(Map properties) { - availableRoles = (String[]) properties.get("availableRoles"); - } -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUserUiProvider.java b/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUserUiProvider.java deleted file mode 100644 index 6cec22d..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUserUiProvider.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.argeo.app.ui.people; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.app.ui.SuiteMsg; -import org.argeo.app.ui.SuiteUiUtils; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.swt.SWT; -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.Group; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.useradmin.User; - -/** Edit a suite user. */ -public class SuiteUserUiProvider implements CmsUiProvider { - private String[] availableRoles; - private CmsUserManager cmsUserManager; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Section main = new Section(parent, SWT.NONE, context); - main.setLayoutData(CmsSwtUtils.fillAll()); - - String uid = context.getName(); - User user = cmsUserManager.getUserFromLocalId(uid); - -// 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); - Group rolesSection = new Group(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; - } - - 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; - } - - public void init(Map properties) { - availableRoles = (String[]) properties.get("availableRoles"); - } -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUsersEntryArea.java b/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUsersEntryArea.java deleted file mode 100644 index c6a49b6..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/people/SuiteUsersEntryArea.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.argeo.app.ui.people; - -import java.util.Set; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsTheme; -import org.argeo.app.api.SuiteRole; -import org.argeo.app.ui.SuiteEvent; -import org.argeo.app.ui.SuiteIcon; -import org.argeo.app.ui.dialogs.NewUserWizard; -import org.argeo.cms.CmsUserManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.swt.dialogs.CmsWizardDialog; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.util.naming.LdapAttrs; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.window.Window; -import org.eclipse.jface.wizard.Wizard; -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.ToolBar; -import org.eclipse.swt.widgets.ToolItem; -import org.osgi.service.useradmin.User; - -/** Entry to the admin area. */ -public class SuiteUsersEntryArea implements CmsUiProvider { - - private CmsUserManager cmsUserManager; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsTheme theme = CmsSwtUtils.getCmsTheme(parent); - parent.setLayout(new GridLayout()); - TableViewer usersViewer = new TableViewer(parent); - usersViewer.setContentProvider(new UsersContentProvider()); - - TableViewerColumn idCol = new TableViewerColumn(usersViewer, SWT.NONE); - idCol.getColumn().setWidth(70); - idCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.uid.name()); - } - }); - - TableViewerColumn givenNameCol = new TableViewerColumn(usersViewer, SWT.NONE); - givenNameCol.getColumn().setWidth(150); - givenNameCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.givenName.name()); - } - }); - - TableViewerColumn snCol = new TableViewerColumn(usersViewer, SWT.NONE); - snCol.getColumn().setWidth(150); - snCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.sn.name()); - } - }); - - TableViewerColumn mailCol = new TableViewerColumn(usersViewer, SWT.NONE); - mailCol.getColumn().setWidth(400); - mailCol.setLabelProvider(new ColumnLabelProvider() { - - @Override - public String getText(Object element) { - - return getUserProperty(element, LdapAttrs.mail.name()); - } - }); - - Composite bottom = new Composite(parent, 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(SuiteIcon.delete.getSmallIcon(theme)); - ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT); - addItem.setImage(SuiteIcon.add.getSmallIcon(theme)); - usersViewer.addDoubleClickListener(new IDoubleClickListener() { - - @Override - public void doubleClick(DoubleClickEvent event) { - User user = (User) usersViewer.getStructuredSelection().getFirstElement(); - if (user != null) { -// Node userNode = getOrCreateUserNode(user, context); - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(), - SuiteEvent.eventProperties(user)); - } - - } - }); - usersViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - User user = (User) usersViewer.getStructuredSelection().getFirstElement(); - if (user != null) { -// Node userNode = getOrCreateUserNode(user, context); - CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(), - SuiteEvent.eventProperties(user)); - deleteItem.setEnabled(true); - } else { - deleteItem.setEnabled(false); - } - } - }); - - addItem.addSelectionListener((Selected) (e) -> { - // SuiteUtils.getOrCreateUserNode(adminSession, userDn); - Wizard wizard = new NewUserWizard(null); - CmsWizardDialog dialog = new CmsWizardDialog(parent.getShell(), wizard); - // WizardDialog dialog = new WizardDialog(shell, wizard); - if (dialog.open() == Window.OK) { - // TODO create - } - }); - - usersViewer.getTable().setLayoutData(CmsSwtUtils.fillAll()); - usersViewer.setInput(cmsUserManager); - - return usersViewer.getTable(); - } - -// private Node getOrCreateUserNode(User user, Node context) { -// return JcrUtils.mkdirs(Jcr.getSession(context), -// "/" + EntityType.user.name() + "/" + getUserProperty(user, LdapAttrs.uid.name()), -// EntityType.user.get()); -// } - - private String getUserProperty(Object element, String key) { - Object value = ((User) element).getProperties().get(key); - return value != null ? value.toString() : null; - } - - class UsersContentProvider implements IStructuredContentProvider { - - @Override - public Object[] getElements(Object inputElement) { - CmsUserManager cum = (CmsUserManager) inputElement; - Set users = cum.listUsersInGroup(SuiteRole.coworker.dn(), null); - return users.toArray(); - } - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - } - - public void setCmsUserManager(CmsUserManager cmsUserManager) { - this.cmsUserManager = cmsUserManager; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/publish/DocumentUiProvider.java b/org.argeo.app.ui/src/org/argeo/app/ui/publish/DocumentUiProvider.java deleted file mode 100644 index 3829583..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/publish/DocumentUiProvider.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.argeo.app.ui.publish; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsEditable; -import org.argeo.api.cms.CmsView; -import org.argeo.app.docbook.DbkType; -import org.argeo.app.ui.docbook.AbstractDbkViewer; -import org.argeo.app.ui.docbook.DocumentTextEditor; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsLink; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.viewers.JcrVersionCmsEditable; -import org.argeo.cms.ui.widgets.ScrolledPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.browser.Browser; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -public class DocumentUiProvider implements CmsUiProvider { - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsView cmsView = CmsSwtUtils.getCmsView(parent); - CmsEditable cmsEditable = new JcrVersionCmsEditable(context); - if (context.hasNode(DbkType.article.get())) { - Node textNode = context.getNode(DbkType.article.get()); - // Title - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - CmsLink toHtml = new CmsLink("To HTML", "/html/dbk" + context.getPath()+"/index.html"); - toHtml.createUiPart(parent, context); - - ScrolledPage page = new ScrolledPage(parent, SWT.NONE); - page.setLayoutData(CmsSwtUtils.fillAll()); - page.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - cmsView.runAs(() -> { - AbstractDbkViewer dbkEditor = new DocumentTextEditor(page, SWT.NONE, textNode, cmsEditable); - dbkEditor.refresh(); - }); - return page; - - } else if (context.isNodeType(NodeType.NT_FILE)) { - String fileName = context.getName(); - if (fileName.endsWith(".pdf")) { - Browser browser = new Browser(parent, SWT.NONE); - String dataPath = CmsUiUtils.getDataPath(context); - browser.setUrl(dataPath); - browser.setLayoutData(CmsSwtUtils.fillAll()); - return browser; - } - } - return null; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java b/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java deleted file mode 100644 index d57706c..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingApp.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.argeo.app.ui.publish; - -import static org.argeo.app.ui.SuiteApp.DEFAULT_THEME_ID_PROPERTY; -import static org.argeo.app.ui.SuiteApp.DEFAULT_UI_NAME_PROPERTY; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsApp; -import org.argeo.api.cms.CmsUi; -import org.argeo.app.ui.SuiteApp; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.AbstractCmsApp; -import org.argeo.cms.ui.CmsUiProvider; -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; -import org.osgi.framework.Constants; -import org.osgi.service.event.Event; -import org.osgi.service.event.EventHandler; - -/** - * A {@link CmsApp} dedicated to publishing, typically a public or internal web - * site. - */ -public class PublishingApp extends AbstractCmsApp implements EventHandler { - private final static CmsLog log = CmsLog.getLog(PublishingApp.class); - - private String pid; - private String defaultThemeId; - private String defaultUiName = ""; - - private String publicBasePath = null; - - private CmsUiProvider landingPage; - private CmsUiProvider defaultProvider = new DocumentUiProvider(); - - private Repository repository; - - public void init(Map properties) { - if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY)) - defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY); - if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY)) - defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY); - publicBasePath = LangUtils.get(properties, SuiteApp.PUBLIC_BASE_PATH_PROPERTY); - pid = properties.get(Constants.SERVICE_PID); - - if (log.isDebugEnabled()) - log.info("Publishing App " + pid + " started"); - } - - public void destroy(Map properties) { - if (log.isDebugEnabled()) - log.info("Publishing App " + pid + " stopped"); - - } - - @Override - public Set getUiNames() { - Set uiNames = new HashSet<>(); - uiNames.add(defaultUiName); - return uiNames; - } - - @Override - public CmsUi initUi(Object uiParent) { - Composite parent = (Composite) uiParent; -// Session adminSession = NodeUtils.openDataAdminSession(getRepository(), null); - Session session = Jcr.login(getRepository(), null); - parent.setLayout(new GridLayout()); - Node indexNode = Jcr.getNode(session, publicBasePath + "/index"); -// try { -// indexNode = JcrUtils.getOrAdd(Jcr.getRootNode(adminSession), DocumentPage.WWW, DbkType.article.get()); -// adminSession.save(); -// } catch (RepositoryException e) { -// throw new IllegalStateException(e); -// } - - Control page; - if (landingPage != null) { - page = landingPage.createUiPart(parent, indexNode); - } else { - page = defaultProvider.createUiPart(parent, indexNode); - } - return (CmsUi) page; - } - - @Override - public void refreshUi(CmsUi cmsUi, String state) { - Composite parent = (Composite) cmsUi; - parent.setLayout(new GridLayout()); - if (landingPage != null) - landingPage.createUiPart(parent, null); - else - defaultProvider.createUiPart(parent, null); - } - - @Override - public void setState(CmsUi cmsUi, String state) { - - } - - @Override - protected String getThemeId(String uiName) { - return defaultThemeId; - } - - public void setLandingPage(CmsUiProvider landingPage) { - this.landingPage = landingPage; - } - - @Override - public void handleEvent(Event event) { - // TODO listen to some events - - } - - public Repository getRepository() { - return repository; - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingStyle.java b/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingStyle.java deleted file mode 100644 index c2c8231..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/publish/PublishingStyle.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.app.ui.publish; - -import org.argeo.api.cms.CmsStyle; - -/** Publishing styles. */ -public enum PublishingStyle implements CmsStyle { - // general - page, coverTitle, coverSubTitle, coverTagline, bannerLine1, bannerLine2, - // meta data - tag, menu, - // text style - title, subTitle, chapo, para, sectionTitle, subSectionTitle, - // links - internalLink, - // composite style - framed, line; - - @Override - public String getClassPrefix() { - return "argeo-publishing"; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/AbstractConnectContextMenu.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/AbstractConnectContextMenu.java deleted file mode 100644 index 7824691..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/AbstractConnectContextMenu.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.argeo.app.ui.widgets; - -import java.util.HashMap; -import java.util.Map; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** - * Generic popup context menu for TableViewer to enable single sourcing between - * CMS and Workbench - */ -public abstract class AbstractConnectContextMenu { - - private Shell parentShell; - private Shell shell; - // Local context - - private final static String KEY_ACTION_ID = "actionId"; - private final String[] defaultActions; - private Map actionButtons = new HashMap(); - - public AbstractConnectContextMenu(Display display, String[] defaultActions) { - parentShell = display.getActiveShell(); - shell = new Shell(parentShell, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - this.defaultActions = defaultActions; - } - - protected void createControl() { - shell.setLayout(EclipseUiUtils.noSpaceGridLayout()); - Composite boxCmp = new Composite(shell, SWT.NO_FOCUS | SWT.BORDER); - boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); -// CmsUiUtils.style(boxCmp, ConnectUiStyles.CONTEXT_MENU_BOX); - createContextMenu(boxCmp); - shell.addShellListener(new ActionsShellListener()); - } - - protected void createContextMenu(Composite boxCmp) { - ActionsSelListener asl = new ActionsSelListener(); - for (String actionId : defaultActions) { - Button btn = new Button(boxCmp, SWT.FLAT | SWT.LEAD); - btn.setText(getLabel(actionId)); - btn.setLayoutData(EclipseUiUtils.fillWidth()); - CmsSwtUtils.markup(btn); -// CmsUiUtils.style(btn, actionId + ConnectUiStyles.BUTTON_SUFFIX); - btn.setData(KEY_ACTION_ID, actionId); - btn.addSelectionListener(asl); - actionButtons.put(actionId, btn); - } - } - - protected void setVisible(boolean visible, String... buttonIds) { - for (String id : buttonIds) { - Button button = actionButtons.get(id); - button.setVisible(visible); - GridData gd = (GridData) button.getLayoutData(); - gd.heightHint = visible ? SWT.DEFAULT : 0; - } - } - - public void show(Control source, Point location, IStructuredSelection selection) { - if (shell.isDisposed()) { - shell = new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - createControl(); - } - if (shell.isVisible()) - shell.setVisible(false); - - if (aboutToShow(source, location, selection)) { - shell.pack(); - shell.layout(); - if (source instanceof Control) - shell.setLocation(((Control) source).toDisplay(location.x, location.y)); - shell.open(); - } - } - - protected Shell getParentShell() { - return parentShell; - } - - class StyleButton extends Label { - private static final long serialVersionUID = 7731102609123946115L; - - public StyleButton(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - } - - class ActionsSelListener extends SelectionAdapter { - private static final long serialVersionUID = -1041871937815812149L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object eventSource = e.getSource(); - if (eventSource instanceof Button) { - Button pressedBtn = (Button) eventSource; - performAction((String) pressedBtn.getData(KEY_ACTION_ID)); - shell.close(); - } - } - } - - class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { - private static final long serialVersionUID = -5092341449523150827L; - - @Override - public void shellDeactivated(ShellEvent e) { - setVisible(false); - shell.setVisible(false); - //shell.close(); - } - } - - protected abstract boolean performAction(String actionId); - - protected abstract boolean aboutToShow(Control source, Point location, IStructuredSelection selection); - - protected abstract String getLabel(String actionId); -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/ConnectAbstractDropDown.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/ConnectAbstractDropDown.java deleted file mode 100644 index d1f1a29..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/ConnectAbstractDropDown.java +++ /dev/null @@ -1,194 +0,0 @@ -package org.argeo.app.ui.widgets; - -import java.util.Arrays; -import java.util.List; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.rap.rwt.widgets.DropDown; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Widget; - -/** - * Enable easy addition of a {@code DropDown} widget to a text with listeners - * configured - */ -public abstract class ConnectAbstractDropDown { - - private final Text text; - private final DropDown dropDown; - private boolean modifyFromList = false; - - // Current displayed text - private String userText = ""; - // Current displayed list items - private String[] values; - - // Fine tuning - boolean readOnly; - boolean refreshOnFocus; - - /** Implementing classes should call refreshValues() after initialisation */ - public ConnectAbstractDropDown(Text text) { - this(text, SWT.NONE, false); - } - - /** - * Implementing classes should call refreshValues() after initialisation - * - * @param text - * @param style - * only SWT.READ_ONLY is understood, check if the entered text is - * part of the legal choices. - */ - public ConnectAbstractDropDown(Text text, int style) { - this(text, style, false); - } - - /** - * Implementers should call refreshValues() once init has been done. - * - * @param text - * @param style - * only SWT.READ_ONLY is understood, check if the entered text is - * part of the legal choices. - * @param refreshOnFocus - * if true, the possible values are computed each time the focus is - * gained. It enables, among other to fine tune the getFilteredValues - * method depending on the current context - */ - public ConnectAbstractDropDown(Text text, int style, boolean refreshOnFocus) { - this.text = text; - dropDown = new DropDown(text); - Object obj = dropDown; - if (obj instanceof Widget) - CmsSwtUtils.markup((Widget) obj); - readOnly = (style & SWT.READ_ONLY) != 0; - this.refreshOnFocus = refreshOnFocus; - addListeners(); - } - - /** - * Overwrite to force the refresh of the possible values on focus gained event - */ - protected boolean refreshOnFocus() { - return refreshOnFocus; - } - - public String getText() { - return text.getText(); - } - - public void init() { - refreshValues(); - } - - public void reset(String value) { - modifyFromList = true; - if (EclipseUiUtils.notEmpty(value)) - text.setText(value); - else - text.setText(""); - refreshValues(); - modifyFromList = false; - } - - /** Overwrite to provide specific filtering */ - protected abstract List getFilteredValues(String filter); - - protected void refreshValues() { - List filteredValues = getFilteredValues(text.getText()); - values = filteredValues.toArray(new String[filteredValues.size()]); - dropDown.setItems(values); - } - - protected void addListeners() { - addModifyListener(); - addSelectionListener(); - addDefaultSelectionListener(); - addFocusListener(); - } - - protected void addFocusListener() { - text.addFocusListener(new FocusListener() { - private static final long serialVersionUID = -7179112097626535946L; - - public void focusGained(FocusEvent event) { - if (refreshOnFocus) { - modifyFromList = true; - refreshValues(); - modifyFromList = false; - } - dropDown.setVisible(true); - } - - public void focusLost(FocusEvent event) { - dropDown.setVisible(false); - if (readOnly && values != null && !Arrays.asList(values).contains(userText)) { - modifyFromList = true; - text.setText(""); - refreshValues(); - modifyFromList = false; - } - } - }); - } - - private void addSelectionListener() { - Object obj = dropDown; - if (obj instanceof Widget) - ((Widget) obj).addListener(SWT.Selection, new Listener() { - private static final long serialVersionUID = -2357157809365135142L; - - public void handleEvent(Event event) { - if (event.index != -1) { - modifyFromList = true; - text.setText(values[event.index]); - modifyFromList = false; - text.selectAll(); - } else { - text.setText(userText); - text.setSelection(userText.length(), userText.length()); - text.setFocus(); - } - } - }); - } - - private void addDefaultSelectionListener() { - Object obj = dropDown; - if (obj instanceof Widget) - ((Widget) obj).addListener(SWT.DefaultSelection, new Listener() { - private static final long serialVersionUID = -5958008322630466068L; - - public void handleEvent(Event event) { - if (event.index != -1) { - text.setText(values[event.index]); - text.setSelection(event.text.length()); - dropDown.setVisible(false); - } - } - }); - } - - private void addModifyListener() { - text.addListener(SWT.Modify, new Listener() { - private static final long serialVersionUID = -4373972835244263346L; - - public void handleEvent(Event event) { - if (!modifyFromList) { - userText = text.getText(); - refreshValues(); - if (values.length == 1) - dropDown.setSelectionIndex(0); - dropDown.setVisible(true); - } - } - }); - } -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java deleted file mode 100644 index ecf6639..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.argeo.app.ui.widgets; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.eclipse.rap.rwt.service.ServerPushSession; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; - -/** - * A text input which notifies changes after a delay, typically in order to - * apply a filter. - */ -public class DelayedText { - private final static ScheduledExecutorService scheduler; - static { - // create only one scheduler, in order not to exhaust threads - scheduler = Executors.newScheduledThreadPool(0, (r) -> { - Thread thread = new Thread(r, "Delayed text scheduler"); - // we mark threads as deamons so that the shutdown hook is triggered - thread.setDaemon(true); - return thread; - }); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - scheduler.shutdown(); - }, "Shutdown delayed text scheduler")); - } - private final static int DEFAULT_DELAY = 800; - - private final long delay; - private final InternalModifyListener modifyListener; - private final Text text; - protected List> toDos = new ArrayList<>(); - private ServerPushSession pushSession; - - private ScheduledFuture lastTask; - - public DelayedText(Composite parent, int style) { - this(parent, style, DEFAULT_DELAY); - } - - public DelayedText(Composite parent, int style, long delayInMs) { - this.delay = delayInMs; - this.modifyListener = new InternalModifyListener(); - pushSession = new ServerPushSession(); - pushSession.start(); - text = new Text(parent, style); - text.addModifyListener(modifyListener); - } - - protected void notifyText(String txt) { - // text.getDisplay().syncExec(()-> pushSession.start()); - for (Consumer toDo : toDos) { - text.getDisplay().syncExec(() -> toDo.accept(txt)); - } - // text.getDisplay().syncExec(()->pushSession.stop()); - } - - public Text getText() { - return text; - } - - public void addListener(Consumer toDo) { - toDos.add(toDo); - } - - private class InternalModifyListener implements ModifyListener { - private static final long serialVersionUID = -6178431173400385005L; - - public void modifyText(ModifyEvent e) { - String txt = text.getText(); - ScheduledFuture task = scheduler.schedule(() -> { - notifyText(txt); - return txt; - }, delay, TimeUnit.MILLISECONDS); - // cancel previous task - if (lastTask != null && !lastTask.isDone()) { - lastTask.cancel(false); - } - lastTask = task; - } - }; - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TabbedArea.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TabbedArea.java deleted file mode 100644 index c214207..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TabbedArea.java +++ /dev/null @@ -1,256 +0,0 @@ -package org.argeo.app.ui.widgets; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.jcr.Jcr; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StackLayout; -import org.eclipse.swt.graphics.Image; -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; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; - -/** Manages {@link Section} in a tab-like structure. */ -public class TabbedArea extends Composite { - private static final long serialVersionUID = 8659669229482033444L; - - private Composite headers; - private Composite body; - - private List
sections = new ArrayList<>(); - - private Node previousNode; - private CmsUiProvider previousUiProvider; - private CmsUiProvider currentUiProvider; - - private String tabStyle; - private String tabSelectedStyle; - private String bodyStyle; - private Image closeIcon; - - private StackLayout stackLayout; - - private boolean singleTab = false; - - public TabbedArea(Composite parent, int style) { - super(parent, SWT.NONE); - CmsSwtUtils.style(parent, bodyStyle); - - setLayout(CmsSwtUtils.noSpaceGridLayout()); - - // TODO manage tabs at bottom or sides - headers = new Composite(this, SWT.NONE); - headers.setLayoutData(CmsSwtUtils.fillWidth()); - body = new Composite(this, SWT.NONE); - body.setLayoutData(CmsSwtUtils.fillAll()); - // body.setLayout(new FormLayout()); - stackLayout = new StackLayout(); - body.setLayout(stackLayout); - emptyState(); - } - - protected void refreshTabHeaders() { - int tabCount = sections.size() > 0 ? sections.size() : 1; - for (Control tab : headers.getChildren()) - tab.dispose(); - - headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true))); - - if (sections.size() == 0) { - Composite emptyHeader = new Composite(headers, SWT.NONE); - emptyHeader.setLayoutData(CmsSwtUtils.fillAll()); - emptyHeader.setLayout(new GridLayout()); - Label lbl = new Label(emptyHeader, SWT.NONE); - lbl.setText(""); - lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); - - } - - Section currentSection = getCurrentSection(); - for (Section section : sections) { - boolean selected = section == currentSection; - Composite sectionHeader = section.createHeader(headers); - CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle); - int headerColumns = singleTab ? 1 : 2; - sectionHeader.setLayout(new GridLayout(headerColumns, false)); - sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns)); - Button title = new Button(sectionHeader, SWT.FLAT); - CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle); - title.setLayoutData(CmsSwtUtils.fillWidth()); - title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode()))); - Node node = section.getNode(); - String titleStr = Jcr.getTitle(node); - // TODO internationalize - title.setText(titleStr); - if (!singleTab) { - ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE); - ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT); - if (closeIcon != null) - closeItem.setImage(closeIcon); - else - closeItem.setText("X"); - CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle); - closeItem.addSelectionListener((Selected) (e) -> closeTab(section)); - } - } - - } - - public void view(CmsUiProvider uiProvider, Node context) { - if (body.isDisposed()) - return; - int index = tabIndex(context); - if (index >= 0) { - showTab(index); - previousNode = context; - previousUiProvider = uiProvider; - return; - } - Section section = (Section) body.getChildren()[0]; - previousNode = section.getNode(); - if (previousNode == null) {// empty state - previousNode = context; - previousUiProvider = uiProvider; - } else { - previousUiProvider = currentUiProvider; - } - currentUiProvider = uiProvider; - section.setNode(context); - // section.setLayoutData(CmsUiUtils.coverAll()); - build(section, uiProvider, context); - if (sections.size() == 0) - sections.add(section); - refreshTabHeaders(); - index = tabIndex(context); - showTab(index); - layout(true, true); - } - - public void open(CmsUiProvider uiProvider, Node context) { - if (singleTab) - throw new UnsupportedOperationException("Open is not supported in single tab mode."); - - if (previousNode != null && Jcr.getIdentifier(previousNode).equals(Jcr.getIdentifier(context))) { - // does nothing - return; - } - if (sections.size() == 0) - CmsSwtUtils.clear(body); - Section currentSection = getCurrentSection(); - int currentIndex = sections.indexOf(currentSection); - Section previousSection = new Section(body, SWT.NONE, context); - build(previousSection, previousUiProvider, previousNode); - // previousSection.setLayoutData(CmsUiUtils.coverAll()); - int index = currentIndex + 1; - sections.add(index, previousSection); - showTab(index); - refreshTabHeaders(); - layout(true, true); - } - - public void showTab(int index) { - Section sectionToShow = sections.get(index); - // sectionToShow.moveAbove(null); - stackLayout.topControl = sectionToShow; - refreshTabHeaders(); - layout(true, true); - } - - protected void build(Section section, CmsUiProvider uiProvider, Node context) { - for (Control child : section.getChildren()) - child.dispose(); - CmsSwtUtils.style(section, bodyStyle); - section.setNode(context); - uiProvider.createUiPart(section, context); - - } - - private int tabIndex(Node node) { - for (int i = 0; i < sections.size(); i++) { - Section section = sections.get(i); - if (Jcr.getIdentifier(section.getNode()).equals(Jcr.getIdentifier(node))) - return i; - } - return -1; - } - - public void closeTab(Section section) { - int currentIndex = sections.indexOf(section); - int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1; - sections.remove(section); - section.dispose(); - if (sections.size() == 0) { - emptyState(); - refreshTabHeaders(); - layout(true, true); - return; - } - refreshTabHeaders(); - showTab(nextIndex); - } - - public void closeAllTabs() { - for(Section section:sections) { - section.dispose(); - } - sections.clear(); - emptyState(); - refreshTabHeaders(); - layout(true, true); - } - - protected void emptyState() { - new Section(body, SWT.NONE, null); - refreshTabHeaders(); - } - - public Composite getCurrent() { - return getCurrentSection(); - } - - protected Section getCurrentSection() { - return (Section) stackLayout.topControl; - } - - public Node getCurrentContext() { - Section section = getCurrentSection(); - if (section != null) { - return section.getNode(); - } else { - return null; - } - } - - public void setTabStyle(String tabStyle) { - this.tabStyle = tabStyle; - } - - public void setTabSelectedStyle(String tabSelectedStyle) { - this.tabSelectedStyle = tabSelectedStyle; - } - - public void setBodyStyle(String bodyStyle) { - this.bodyStyle = bodyStyle; - } - - public void setCloseIcon(Image closeIcon) { - this.closeIcon = closeIcon; - } - - public void setSingleTab(boolean singleTab) { - this.singleTab = singleTab; - } - -} diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TreeOrSearchArea.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TreeOrSearchArea.java deleted file mode 100644 index 2b8f54e..0000000 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/TreeOrSearchArea.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.app.ui.widgets; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StackLayout; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Text; - -/** - * Displays a tree by default, which becomes a list if the search text field is - * used. - */ -public class TreeOrSearchArea extends Composite { - private static final long serialVersionUID = -1302546480076719532L; - - private Text searchT; - private StackLayout bodyLayout; - - private TreeViewer treeViewer; - private TreeViewer searchResultsViewer; - - public TreeOrSearchArea(Composite parent, int style) { - super(parent, style); - createUi(this); - } - - protected void createUi(Composite parent) { - parent.setLayout(new GridLayout()); - Composite searchC = new Composite(parent, SWT.NONE); - searchC.setLayout(new GridLayout()); - searchC.setLayoutData(CmsSwtUtils.fillWidth()); - createSearchUi(searchC); - - Composite bodyC = new Composite(parent, SWT.NONE); - bodyC.setLayoutData(CmsSwtUtils.fillAll()); - bodyLayout = new StackLayout(); - bodyC.setLayout(bodyLayout); - Composite treeC = new Composite(bodyC, SWT.NONE); - createTreeUi(treeC); - Composite searchResultsC = new Composite(bodyC, SWT.NONE); - createSearchResultsUi(searchResultsC); - - bodyLayout.topControl = treeC; - } - - protected void createSearchUi(Composite parent) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - searchT = new Text(parent, SWT.MULTI | SWT.BORDER); - searchT.setLayoutData(CmsSwtUtils.fillWidth()); - } - - protected void createTreeUi(Composite parent) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - treeViewer = new TreeViewer(parent); - treeViewer.getTree().setLayoutData(CmsSwtUtils.fillAll()); - } - - protected void createSearchResultsUi(Composite parent) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - searchResultsViewer = new TreeViewer(parent); - searchResultsViewer.getTree().setLayoutData(CmsSwtUtils.fillAll()); - } - - public TreeViewer getTreeViewer() { - return treeViewer; - } - - public TreeViewer getSearchResultsViewer() { - return searchResultsViewer; - } - -} diff --git a/sdk/argeo-suite-rap.properties b/sdk/argeo-suite-rap.properties deleted file mode 100644 index 2b8edd3..0000000 --- a/sdk/argeo-suite-rap.properties +++ /dev/null @@ -1,51 +0,0 @@ -argeo.osgi.start.2.node=\ -org.eclipse.equinox.http.servlet,\ -org.eclipse.equinox.metatype,\ -org.eclipse.equinox.cm,\ -org.eclipse.rap.rwt.osgi,\ -org.argeo.init - -argeo.osgi.start.2.suite=\ -org.apache.tika.parsers - -argeo.osgi.start.3.node=\ -org.argeo.cms,\ -org.argeo.cms.jcr,\ -org.argeo.cms.servlet,\ - -argeo.osgi.start.5.suite=\ -org.argeo.app.core,\ -org.argeo.app.ui,\ -org.argeo.app.theme.default,\ -org.argeo.app.ui.rap - -argeo.osgi.start.6.suite=\ -org.argeo.app.servlet.publish,\ -org.argeo.app.servlet.odk - - -# Local -argeo.node.repo.type=h2 -org.osgi.service.http.port=7070 -#org.osgi.service.http.port.secure=7073 - -#argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@localhost:10389/dc=example,dc=com - -argeo.node.init=../../init - -argeo.i18n.locales=en,fr -argeo.i18n.defaultLocale=en - -#tika.config=/home/mbaudier/dev/git/gpl/argeo-suite/sdk/exec/argeo-office-e4-rap/data/indexes/node/tika-config.xml - -# Logging -log.org.argeo=DEBUG - -# DON'T CHANGE BELOW -org.eclipse.equinox.http.jetty.autostart=false -org.osgi.framework.bootdelegation=com.sun.jndi.ldap,\ -com.sun.jndi.ldap.sasl,\ -com.sun.security.jgss,\ -com.sun.jndi.dns,\ -com.sun.nio.file,\ -com.sun.nio.sctp diff --git a/sdk/argeo-suite-rcp.properties b/sdk/argeo-suite-rcp.properties index 48fa221..ea6457e 100644 --- a/sdk/argeo-suite-rcp.properties +++ b/sdk/argeo-suite-rcp.properties @@ -10,6 +10,7 @@ org.apache.tika.parsers argeo.osgi.start.3.node=\ org.argeo.cms,\ org.argeo.cms.jcr,\ +org.argeo.cms.servlet,\ org.argeo.cms.ui.rcp argeo.osgi.start.5.suite=\ @@ -24,7 +25,7 @@ org.argeo.app.servlet.odk # Local argeo.node.repo.type=h2 -org.osgi.service.http.port=7070 +org.osgi.service.http.port=0 #org.osgi.service.http.port.secure=7073 #argeo.node.useradmin.uris=ldap://cn=Directory%20Manager:argeoargeo@localhost:10389/dc=example,dc=com diff --git a/sdk/argeo-suite.properties b/sdk/argeo-suite.properties new file mode 100644 index 0000000..5de69f5 --- /dev/null +++ b/sdk/argeo-suite.properties @@ -0,0 +1,47 @@ +argeo.osgi.start.2=\ +org.eclipse.equinox.http.servlet,\ +org.apache.felix.scr,\ +org.eclipse.rap.rwt.osgi,\ +org.apache.tika.parsers,\ +org.argeo.init + +argeo.osgi.start.3=\ +org.argeo.cms,\ +org.argeo.cms.swt.rap,\ +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=7070 + +argeo.directory=dc=example,dc=com + +# Logging +log.org.argeo=DEBUG + +# DON'T CHANGE BELOW +org.eclipse.equinox.http.jetty.autostart=false +org.osgi.framework.system.packages.extra=\ +com.sun.net.httpserver,\ +com.sun.jndi.ldap,\ +com.sun.jndi.ldap.sasl,\ +com.sun.jndi.dns,\ +com.sun.security.jgss,\ +com.sun.nio.file,\ +com.sun.nio.sctp \ No newline at end of file diff --git a/sdk/branches/testing.bnd b/sdk/branches/testing.bnd new file mode 100644 index 0000000..22ef9a8 --- /dev/null +++ b/sdk/branches/testing.bnd @@ -0,0 +1,4 @@ +major=2 +minor=1 +micro=25 +qualifier=.next \ No newline at end of file diff --git a/sdk/branches/unstable.bnd b/sdk/branches/unstable.bnd new file mode 100644 index 0000000..e590c4c --- /dev/null +++ b/sdk/branches/unstable.bnd @@ -0,0 +1,4 @@ +major=2 +minor=3 +micro=7 +qualifier=.next \ No newline at end of file diff --git a/sdk/init/node/dc=example,dc=com.ldif b/sdk/init/node/dc=example,dc=com.ldif deleted file mode 100644 index 5371306..0000000 --- a/sdk/init/node/dc=example,dc=com.ldif +++ /dev/null @@ -1,39 +0,0 @@ -dn: uid=coworker,ou=People,dc=example,dc=com -objectClass: inetOrgPerson -objectClass: organizationalPerson -objectClass: person -objectClass: top -givenName: John -sn: Coworker -userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 -mail: coworker@localhost -uid: coworker -cn: John Coworker -description: A regular coworker - -dn: uid=manager,ou=People,dc=example,dc=com -objectClass: inetOrgPerson -objectClass: organizationalPerson -objectClass: person -objectClass: top -givenName: Mary -sn: Manager -userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 -mail: manager@localhost -uid: manager -cn: Mary Manager -description: A manager - -dn: uid=root,ou=People,dc=example,dc=com -objectClass: inetOrgPerson -objectClass: person -objectClass: organizationalPerson -objectClass: top -givenName: Super -sn: User -userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 -mail: root@localhost -uid: root -cn: Super User -description: Superuser - diff --git a/sdk/init/node/ou=roles,ou=node.ldif b/sdk/init/node/ou=roles,ou=node.ldif deleted file mode 100644 index 3c70185..0000000 --- a/sdk/init/node/ou=roles,ou=node.ldif +++ /dev/null @@ -1,26 +0,0 @@ -dn: cn=admin,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: admin -member: uid=root,ou=People,dc=example,dc=com - -dn: cn=org.argeo.suite.coworker,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.suite.coworker -member: cn=org.argeo.suite.manager,ou=roles,ou=node -member: uid=coworker,ou=People,dc=example,dc=com - -dn: cn=org.argeo.suite.manager,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: org.argeo.suite.manager -member: uid=manager,ou=People,dc=example,dc=com -member: uid=root,ou=People,dc=example,dc=com - -dn: cn=userAdmin,ou=roles,ou=node -objectClass: groupOfNames -objectClass: top -cn: userAdmin -member: cn=admin,ou=roles,ou=node - diff --git a/sdk/init/private/dc=example,dc=com.ldif b/sdk/init/private/dc=example,dc=com.ldif new file mode 100644 index 0000000..5371306 --- /dev/null +++ b/sdk/init/private/dc=example,dc=com.ldif @@ -0,0 +1,39 @@ +dn: uid=coworker,ou=People,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +givenName: John +sn: Coworker +userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 +mail: coworker@localhost +uid: coworker +cn: John Coworker +description: A regular coworker + +dn: uid=manager,ou=People,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +objectClass: top +givenName: Mary +sn: Manager +userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 +mail: manager@localhost +uid: manager +cn: Mary Manager +description: A manager + +dn: uid=root,ou=People,dc=example,dc=com +objectClass: inetOrgPerson +objectClass: person +objectClass: organizationalPerson +objectClass: top +givenName: Super +sn: User +userPassword:: e1NIQX1pZVNWNTVRYytlUU9hWURSU2hhL0Fqek5USkU9 +mail: root@localhost +uid: root +cn: Super User +description: Superuser + diff --git a/sdk/init/private/ou=roles,ou=node.ldif b/sdk/init/private/ou=roles,ou=node.ldif new file mode 100644 index 0000000..3c70185 --- /dev/null +++ b/sdk/init/private/ou=roles,ou=node.ldif @@ -0,0 +1,26 @@ +dn: cn=admin,ou=roles,ou=node +objectClass: groupOfNames +objectClass: top +cn: admin +member: uid=root,ou=People,dc=example,dc=com + +dn: cn=org.argeo.suite.coworker,ou=roles,ou=node +objectClass: groupOfNames +objectClass: top +cn: org.argeo.suite.coworker +member: cn=org.argeo.suite.manager,ou=roles,ou=node +member: uid=coworker,ou=People,dc=example,dc=com + +dn: cn=org.argeo.suite.manager,ou=roles,ou=node +objectClass: groupOfNames +objectClass: top +cn: org.argeo.suite.manager +member: uid=manager,ou=People,dc=example,dc=com +member: uid=root,ou=People,dc=example,dc=com + +dn: cn=userAdmin,ou=roles,ou=node +objectClass: groupOfNames +objectClass: top +cn: userAdmin +member: cn=admin,ou=roles,ou=node + diff --git a/swt/org.argeo.app.swt/.classpath b/swt/org.argeo.app.swt/.classpath new file mode 100644 index 0000000..81fe078 --- /dev/null +++ b/swt/org.argeo.app.swt/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/swt/org.argeo.app.swt/.project b/swt/org.argeo.app.swt/.project new file mode 100644 index 0000000..11c6368 --- /dev/null +++ b/swt/org.argeo.app.swt/.project @@ -0,0 +1,28 @@ + + + org.argeo.app.swt + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/swt/org.argeo.app.swt/bnd.bnd b/swt/org.argeo.app.swt/bnd.bnd new file mode 100644 index 0000000..bbc81c5 --- /dev/null +++ b/swt/org.argeo.app.swt/bnd.bnd @@ -0,0 +1,7 @@ +Import-Package:\ +org.eclipse.swt,\ +org.argeo.util.naming,\ +org.argeo.api.cms.ux,\ +org.argeo.cms.ux.acr,\ +org.argeo.app.api,\ +* \ No newline at end of file diff --git a/swt/org.argeo.app.swt/build.properties b/swt/org.argeo.app.swt/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/swt/org.argeo.app.swt/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java new file mode 100644 index 0000000..5fe67e1 --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImageManager.java @@ -0,0 +1,130 @@ +package org.argeo.app.swt.docbook; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.DName; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.app.api.EntityNames; +import org.argeo.app.api.EntityType; +import org.argeo.app.docbook.DbkAttr; +import org.argeo.app.docbook.DbkType; +import org.argeo.cms.acr.SvgAttrs; +import org.argeo.cms.swt.acr.AcrSwtImageManager; +import org.eclipse.swt.graphics.ImageData; + +/** Add DocBook images support to {@link CmsImageManager}. */ +public class DbkImageManager extends AcrSwtImageManager { + private Content baseFolder = null; + + public DbkImageManager(Content baseFolder) { + this.baseFolder = baseFolder; + } + + Content getImageDataNode(Content mediaObjectNode) { + return mediaObjectNode.child(DbkType.imageobject).child(DbkType.imagedata); + } + +// @Override +// public Binary getImageBinary(Node node) { +// Node fileNode = null; +// if (DbkUtils.isDbk(node, DbkType.mediaobject)) { +// Node imageDataNode = getImageDataNode(node); +// fileNode = getFileNode(imageDataNode); +// } +// try { +// if (node.isNodeType(NT_FILE)) { +// fileNode = node; +// } +// if (fileNode != null) { +// return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); +// } else { +// return null; +// } +// } catch (RepositoryException e) { +// throw new JcrException(e); +// } +// } + + public Cms2DSize getImageSize(Content mediaObjectNode) { + Content imageDataNode = getImageDataNode(mediaObjectNode); + Content fileNode = getFileNode(imageDataNode); + if (fileNode == null) + return new Cms2DSize(0, 0); + Cms2DSize intrinsicSize; + if (fileNode.containsKey(SvgAttrs.width) && fileNode.containsKey(SvgAttrs.height)) { + int width = fileNode.get(SvgAttrs.width, Integer.class).orElseThrow(); + int height = fileNode.get(SvgAttrs.height, Integer.class).orElseThrow(); + intrinsicSize = new Cms2DSize(width, height); + } else { + try (InputStream in = fileNode.open(InputStream.class)) { + ImageData id = new ImageData(in); + intrinsicSize = updateSize(fileNode, id); + } catch (IOException e) { + throw new RuntimeException("Cannot load file " + fileNode, e); + } + } + // TODO interpret image data infos + return intrinsicSize; + } + + protected Cms2DSize updateSize(Content fileNode, ImageData id) { + fileNode.addContentClasses(EntityType.box.qName()); + fileNode.put(SvgAttrs.width, id.width); + fileNode.put(SvgAttrs.height, id.height); + return new Cms2DSize(id.width, id.height); + } + +// @Override +// protected void processNewImageFile(Content mediaObjectNode, Content fileNode, ImageData id) throws IOException { +// Node imageDataNode = getImageDataNode(mediaObjectNode); +// updateSize(fileNode, id); +// String filePath = fileNode.getPath(); +// String relPath = filePath.substring(baseFolder.getPath().length() + 1); +// imageDataNode.setProperty(DbkAttr.fileref.name(), relPath); +// } + + @Override + public String getImageUrl(Content mediaObjectNode) { + Content imageDataNode = getImageDataNode(mediaObjectNode); + // TODO factorise + String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null); + if (fileref == null) + return null; + URI fileUri; + try { + // FIXME it messes up with the '/' + fileUri = new URI(URLEncoder.encode(fileref, StandardCharsets.UTF_8.toString())); + } catch (URISyntaxException | UnsupportedEncodingException e) { + throw new IllegalArgumentException("File ref in " + imageDataNode + " is badly formatted", e); + } + if (fileUri.getScheme() != null) + return fileUri.toString(); + // local + Content fileNode = getFileNode(imageDataNode); + String url = getDataPathForUrl(fileNode); + return url; + } + + protected Content getFileNode(Content imageDataNode) { + // FIXME make URL use case more robust + String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null); + if (fileref == null) + return null; + return ((ProvidedContent) baseFolder).getContent(fileref); + } + + protected Content getMediaFolder() { + // TODO check edition status + Content mediaFolder = baseFolder.anyOrAddChild(EntityNames.MEDIA, DName.collection.qName()); + return mediaFolder; + } +} diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java new file mode 100644 index 0000000..9984209 --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkImg.java @@ -0,0 +1,65 @@ +package org.argeo.app.swt.docbook; + +import org.argeo.api.acr.Content; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.acr.Img; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** DocBook specific image area. */ +public class DbkImg extends Img { + private static final long serialVersionUID = -6150996708899219074L; + + public DbkImg(Composite parent, int swtStyle, Content imgNode, DbkImageManager imageManager) { + super(parent, swtStyle, imgNode, imageManager); + // FIXME deal with style and initialisation + setStyle((String) null); + } + + @Override + protected Content getUploadFolder() { + Content mediaFolder = ((DbkImageManager) getImageManager()).getMediaFolder(); + return mediaFolder; + } + + @Override + protected String getUploadName() { + return null; + } + + @Override + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + + @Override + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + +// @Override +// protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { +// FileUploadHandler fileUploadHandler = super.prepareUpload(receiver); +// fileUploadHandler.addUploadListener(new FileUploadListener() { +// +// @Override +// public void uploadProgress(FileUploadEvent event) { +// // TODO Auto-generated method stub +// +// } +// +// @Override +// public void uploadFinished(FileUploadEvent event) { +// } +// +// @Override +// public void uploadFailed(FileUploadEvent event) { +// // TODO Auto-generated method stub +// +// } +// }); +// return fileUploadHandler; +// } + +} diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkSectionTitle.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkSectionTitle.java new file mode 100644 index 0000000..58dd263 --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkSectionTitle.java @@ -0,0 +1,30 @@ +package org.argeo.app.swt.docbook; + +import org.argeo.api.acr.Content; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.swt.widgets.EditableText; +import org.argeo.cms.ux.acr.ContentPart; +import org.eclipse.swt.widgets.Composite; + +/** The title of a section, based on an XML text node. */ +public class DbkSectionTitle extends EditableText implements SwtEditablePart, ContentPart { + private static final long serialVersionUID = -1787983154946583171L; + + private final TextSection section; + + public DbkSectionTitle(Composite parent, int swtStyle, Content titleNode) { + super(parent, swtStyle); + section = (TextSection) TextSection.findSection(this); + setData(titleNode); + } + + public TextSection getSection() { + return section; + } + + @Override + public Content getContent() { + return (Content) getData(); + } + +} diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkTextInterpreter.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkTextInterpreter.java new file mode 100644 index 0000000..1eff7a4 --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkTextInterpreter.java @@ -0,0 +1,283 @@ +package org.argeo.app.swt.docbook; + +import static org.argeo.app.docbook.DbkAcrUtils.isDbk; +import static org.argeo.app.docbook.DbkType.para; +import static org.argeo.app.docbook.DbkType.title; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.List; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.io.IOUtils; +import org.argeo.api.acr.Content; +import org.argeo.app.docbook.DbkType; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +/** Based on HTML with a few Wiki-like shortcuts. */ +public class DbkTextInterpreter implements TextInterpreter { + + private TransformerFactory transformerFactory = TransformerFactory.newDefaultInstance(); + + private String linkCssClass = DbkType.link.name(); + + @Override + public void write(Content node, String content) { + if (isDbk(node, para) || isDbk(node, title)) { + String raw = convertToStorage(node, content); + validateBeforeStoring(raw); + + String jcrUuid = null;// node.getIdentifier(); +// if (node.hasProperty(Property.JCR_UUID)) +// jcrUuid = node.getProperty(Property.JCR_UUID).getString(); +// else { +// // TODO use time based +// jcrUuid = UUID.randomUUID().toString(); +// node.setProperty(Property.JCR_UUID, jcrUuid); +// node.getSession().save(); +// } + + StringBuilder namespaces = new StringBuilder(); + namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\""); + namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\""); + namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\""); + raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + ""; +// System.out.println(raw); +// try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) { +// node.getSession().importXML(node.getParent().getPath(), in, +// ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); +// // node.getSession().save(); +// } catch (IOException e) { +// throw new IllegalArgumentException("Cannot parse raw content of " + node, e); +// } + +// try { +// DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); +// Document document; +// try (Reader in = new StringReader(raw)) { +// document = documentBuilder.parse(new InputSource(in)); +// } +// NodeList nl = document.getChildNodes(); +// for (int i = 0; i < nl.getLength(); i++) { +// org.w3c.dom.Node n = nl.item(i); +// if (node instanceof Text) { +// +// } +// } +// } catch (ParserConfigurationException | SAXException | IOException e) { +// throw new IllegalArgumentException("Cannot parse raw content of " + node, e); +// } + +// Node jcrText; +// if (!node.hasNode(Jcr.JCR_XMLTEXT)) +// jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT); +// else +// jcrText = node.getNode(Jcr.JCR_XMLTEXT); +// jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw); + } else { + throw new IllegalArgumentException("Don't know how to interpret " + node); + } + } + + @Override + public String read(Content item) { + String raw = raw(item); + return convertFromStorage(item, raw); + } + + @Override + public String raw(Content node) { + if (isDbk(node, para) || isDbk(node, title)) { + try (StringWriter stringWriter = new StringWriter()) { + Source source = node.adapt(Source.class); + Result result = new StreamResult(stringWriter); + transformerFactory.newTransformer().transform(source, result); + return stringWriter.toString(); + } catch (TransformerException | IOException e) { + throw new RuntimeException("Could not convert " + node + " to XML", e); + } + +// StringBuilder sb = new StringBuilder(); +// readXml(node, sb); +// NodeIterator nit = node.getNodes(); +// while (nit.hasNext()) { +// Node child = nit.nextNode(); +// if (child.getName().equals(Jcr.JCR_XMLTEXT)) { +// Node jcrText = node.getNode(Jcr.JCR_XMLTEXT); +// String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString(); +// // TODO make it more robust +// // txt = txt.replace("\n", "").replace("\t", ""); +// txt = txt.replace("\t", " "); +// sb.append(txt); +// } else { +// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { +// child.getSession().exportDocumentView(child.getPath(), out, true, false); +// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8)); +// } catch (IOException e) { +// throw new IllegalStateException("Cannot export " + child, e); +// } +// } +// } +// return sb.toString(); + } else { + throw new IllegalArgumentException("Don't know how to interpret " + node); + } + } + +// private void readXml(Content node, StringBuilder sb){ +// +// NodeIterator nit = node.getNodes(); +// while (nit.hasNext()) { +// Node child = nit.nextNode(); +// if (child.getName().equals(Jcr.JCR_XMLTEXT)) { +// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString(); +// // TODO make it more robust +// // txt = txt.replace("\n", "").replace("\t", ""); +// txt = txt.replace("\t", " "); +// sb.append(txt); +// } else { +// sb.append('<').append(child.getName()); +// PropertyIterator pit = child.getProperties(); +// properties: while (pit.hasNext()) { +// Property p = pit.nextProperty(); +// if (p.getName().startsWith("jcr:")) +// continue properties; +// sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"'); +// } +// sb.append('>'); +// readXml(child, sb); +//// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { +//// child.getSession().exportDocumentView(child.getPath(), out, true, false); +//// sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8)); +//// } catch (IOException e) { +//// throw new IllegalStateException("Cannot export " + child, e); +//// } +// sb.append("'); +// } +// } +// } + + private void readAsSimpleHtml(Content node, StringBuilder sb) { + DOMResult result = new DOMResult(); + try { + Source source = node.adapt(Source.class); + transformerFactory.newTransformer().transform(source, result); + } catch (TransformerException e) { + throw new RuntimeException("Could not convert " + node + " to XML", e); + } + + NodeList nl = result.getNode().getChildNodes(); + for (int i = 0; i < nl.getLength(); i++) { + Node n = nl.item(i); +// if (n instanceof Text) { +// Text text = (Text) n; +// sb.append(text.getTextContent()); +// } else + if (n instanceof Element) { + Element elem = (Element) n; + sb.append(elem.getTextContent()); + } + } + +// NodeIterator nit = node.getNodes(); +// while (nit.hasNext()) { +// Node child = nit.nextNode(); +// if (child.getName().equals(Jcr.JCR_XMLTEXT)) { +// String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString(); +// // TODO make it more robust +// // txt = txt.replace("\n", "").replace("\t", ""); +// txt = txt.replace("\t", " "); +// String html = textToSimpleHtml(txt); +// sb.append(html); +// } else if (child.getName().equals(DbkType.link.get())) { +// if (child.hasProperty(DbkAttr.XLINK_HREF)) { +// String href = child.getProperty(DbkAttr.XLINK_HREF).getString(); +// // TODO deal with other forbidden XML characters? +// href = href.replace("&", "&"); +// sb.append(""); +// readAsSimpleHtml(child, sb); +// sb.append(""); +// } +// } else { +// // ignore +// } +// } + } + + private String textToSimpleHtml(String raw) { + // FIXME the saved data should be corrected instead. + if (raw.indexOf('&') >= 0) { + raw = raw.replace("&", "&"); + } + if (raw.indexOf('<') >= 0) { + raw = raw.replace("<", "<"); + } + if (raw.indexOf('>') >= 0) { + raw = raw.replace(">", ">"); + } + if (raw.indexOf('\"') >= 0) { + raw = raw.replace("\"", """); + } + if (raw.indexOf('\'') >= 0) { + raw = raw.replace("\'", "'"); + } +// raw = "" + raw + ""; + if (raw.length() == 0) + return raw; + try (StringReader reader = new StringReader(raw)) { + List lines = IOUtils.readLines(reader); + if (lines.size() == 1) + return lines.get(0); + StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) + sb.append("
"); + sb.append(lines.get(i)); + } + return sb.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + final static int BR_LENGTH = "
".length(); + + public String readSimpleHtml(Content item) { + StringBuilder sb = new StringBuilder(); +// sb.append("
"); + readAsSimpleHtml(item, sb); +// sb.append("
"); +// System.out.println(sb); + return sb.toString(); + } + + // EXTENSIBILITY + /** + * To be overridden, in order to make sure that only valid strings are being + * stored. + */ + protected void validateBeforeStoring(String raw) { + } + + /** To be overridden, in order to support additional formatting. */ + protected String convertToStorage(Content item, String content) { + return content; + + } + + /** To be overridden, in order to support additional formatting. */ + protected String convertFromStorage(Content item, String content) { + return content; + } +} 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 new file mode 100644 index 0000000..17ed0e0 --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/docbook/DbkVideo.java @@ -0,0 +1,211 @@ +package org.argeo.app.swt.docbook; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.app.docbook.DbkAcrUtils; +import org.argeo.app.docbook.DbkAttr; +import org.argeo.app.docbook.DbkType; +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.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; +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.Text; + +public class DbkVideo extends StyledControl implements SwtSectionPart, ContentPart { + private static final long serialVersionUID = -8753232181570351880L; + private SwtSection section; + + private int width = 640; + private int height = 360; + + private boolean editable; + + public DbkVideo(Composite parent, int style, Content node) { + this(SwtSection.findSection(parent), parent, style, node); + } + + DbkVideo(SwtSection section, Composite parent, int style, Content node) { + super(parent, style); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + this.section = section; + setStyle(DbkType.videoobject.name()); + setData(node); + } + + @Override + protected Control createControl(Composite box, String style) { + Content mediaobject = getNode(); + Composite wrapper = new Composite(box, SWT.NONE); + wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + Composite browserC = new Composite(wrapper, SWT.NONE); + browserC.setLayout(CmsSwtUtils.noSpaceGridLayout()); + GridData gd = new GridData(SWT.CENTER, SWT.FILL, true, true); + gd.widthHint = getWidth(); + gd.heightHint = getHeight(); + browserC.setLayoutData(gd); +// wrapper.setLayoutData(CmsUiUtils.fillAll()); + Browser browser = new Browser(browserC, SWT.NONE); + + if (editable) { + Composite editor = new Composite(wrapper, SWT.BORDER); + editor.setLayout(new GridLayout(3, false)); + editor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + String fileref = DbkAcrUtils.getMediaFileref(mediaobject); + Text text = new Text(editor, SWT.SINGLE); + if (fileref != null) + text.setText(fileref); + else + text.setMessage("Embed URL of the video"); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + Button updateB = new Button(editor, SWT.FLAT); + updateB.setText("Update"); + updateB.addSelectionListener(new Selected() { + + @Override + public void widgetSelected(SelectionEvent e) { + Content videodata = mediaobject.child(DbkType.videoobject).child(DbkType.videodata); + String txt = text.getText(); + URI uri; + try { + uri = new URI(txt); + } catch (URISyntaxException e1) { + text.setText(""); + text.setMessage("Invalid URL"); + return; + } + + // Transform watch URL in embed + // YouTube + String videoId = null; + if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost()) + || "youtu.be".equals(uri.getHost())) { + if ("www.youtube.com".equals(uri.getHost()) || "youtube.com".equals(uri.getHost())) { + if ("/watch".equals(uri.getPath())) { + Map> map = NamingUtils.queryToMap(uri); + videoId = map.get("v").get(0); + } + } else if ("youtu.be".equals(uri.getHost())) { + videoId = uri.getPath().substring(1); + } + if (videoId != null) { + try { + uri = new URI("https://www.youtube.com/embed/" + videoId); + text.setText(uri.toString()); + } catch (URISyntaxException e1) { + throw new IllegalStateException(e1); + } + } + } + + // Vimeo + if ("vimeo.com".equals(uri.getHost())) { + videoId = uri.getPath().substring(1); + if (videoId != null) { + try { + uri = new URI("https://player.vimeo.com/video/" + videoId); + text.setText(uri.toString()); + } catch (URISyntaxException e1) { + throw new IllegalStateException(e1); + } + } + } + + videodata.put(DbkAttr.fileref, uri.toString()); + // TODO better integrate it in the edition lifecycle +// videodata.getSession().save(); + load(browser); + + } + }); + + Button deleteB = new Button(editor, SWT.FLAT); + deleteB.setText("Delete"); + deleteB.addSelectionListener(new Selected() { + + @Override + public void widgetSelected(SelectionEvent e) { + mediaobject.remove(); +// mediaobject.getSession().save(); + dispose(); + getSection().getParent().layout(true, true); + + } + }); + } + + // TODO caption + return browser; + } + + public void load(Control control) { + if (control instanceof Browser) { + Browser browser = (Browser) control; +// getNode().getSession(); + String fileref = DbkAcrUtils.getMediaFileref(getContent()); + if (fileref != null) { + // TODO manage self-hosted videos + // TODO for YouTube videos, check whether the URL starts with + // https://www.youtube.com/embed/ and not https://www.youtube.com/watch?v= + StringBuilder html = new StringBuilder(); + html.append( + "