From: Mathieu Baudier Date: Tue, 13 Jun 2023 08:28:44 +0000 (+0200) Subject: Extract JCR in a separate bundle X-Git-Tag: v2.3.15~21 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=d6c9d33b61e475914d3f8d7534374ed30eca8150 Extract JCR in a separate bundle --- diff --git a/Makefile b/Makefile index b082fcf..4f2a414 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ A2_CATEGORY = org.argeo.suite BUNDLES = \ org.argeo.app.api \ org.argeo.app.core \ +org.argeo.app.jcr \ org.argeo.app.servlet.odk \ org.argeo.app.servlet.publish \ org.argeo.app.theme.default \ @@ -19,6 +20,7 @@ org.argeo.app.profile.acr.fs \ org.argeo.app.profile.acr.jcr \ swt/org.argeo.app.swt \ swt/org.argeo.app.ui \ +org.argeo.product.knowledge \ DEP_CATEGORIES = \ org.argeo.tp \ diff --git a/org.argeo.app.core/OSGI-INF/appUserState.xml b/org.argeo.app.core/OSGI-INF/appUserState.xml deleted file mode 100644 index 82c3ec0..0000000 --- a/org.argeo.app.core/OSGI-INF/appUserState.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.app.core/OSGI-INF/l10n/bundle.properties b/org.argeo.app.core/OSGI-INF/l10n/bundle.properties index d4bf08a..2cb3bb2 100644 --- a/org.argeo.app.core/OSGI-INF/l10n/bundle.properties +++ b/org.argeo.app.core/OSGI-INF/l10n/bundle.properties @@ -4,8 +4,6 @@ documents=documents locations=locations recentItems=recent items -appTitle=Argeo Suite - # # PEOPLE # org.argeo.people.ui.PeopleMsg diff --git a/org.argeo.app.core/OSGI-INF/maintenanceService.xml b/org.argeo.app.core/OSGI-INF/maintenanceService.xml deleted file mode 100644 index 965d82b..0000000 --- a/org.argeo.app.core/OSGI-INF/maintenanceService.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.app.core/OSGI-INF/suiteMaintenance.xml b/org.argeo.app.core/OSGI-INF/suiteMaintenance.xml new file mode 100644 index 0000000..d06afa6 --- /dev/null +++ b/org.argeo.app.core/OSGI-INF/suiteMaintenance.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/org.argeo.app.core/OSGI-INF/termsManager.xml b/org.argeo.app.core/OSGI-INF/termsManager.xml deleted file mode 100644 index 797c5a3..0000000 --- a/org.argeo.app.core/OSGI-INF/termsManager.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.app.core/bnd.bnd b/org.argeo.app.core/bnd.bnd index dce5772..7a92b9b 100644 --- a/org.argeo.app.core/bnd.bnd +++ b/org.argeo.app.core/bnd.bnd @@ -1,17 +1,12 @@ Bundle-ActivationPolicy: lazy Service-Component:\ -OSGI-INF/termsManager.xml,\ -OSGI-INF/maintenanceService.xml,\ -OSGI-INF/appUserState.xml,\ +OSGI-INF/suiteMaintenance.xml,\ Import-Package:\ tech.units.indriya.unit,\ org.osgi.service.useradmin,\ -javax.jcr.nodetype,\ -javax.jcr.security,\ com.fasterxml.jackson.core,\ -org.apache.jackrabbit.*;version="[1,4)",\ org.argeo.cms.acr,\ * diff --git a/org.argeo.app.core/build.properties b/org.argeo.app.core/build.properties index dc82853..4974d56 100644 --- a/org.argeo.app.core/build.properties +++ b/org.argeo.app.core/build.properties @@ -1,7 +1,8 @@ bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - OSGI-INF/appUserState.xml + OSGI-INF/appUserState.xml,\ + OSGI-INF/suiteMaintenance.xml additional.bundles = org.argeo.init source.. = src/ output.. = bin/ diff --git a/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java b/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java deleted file mode 100644 index a4b1fff..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/CustomMaintenanceService.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.app.core; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.ImportUUIDBehavior; -import javax.jcr.ItemExistsException; -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.app.api.EntityType; -import org.argeo.jcr.JcrUtils; -import org.argeo.maintenance.AbstractMaintenanceService; - -/** Base for custom initialisations. */ -public abstract class CustomMaintenanceService extends AbstractMaintenanceService { - private final static CmsLog log = CmsLog.getLog(AbstractMaintenanceService.class); - - protected List getTypologies() { - return new ArrayList<>(); - } - - protected String getTypologiesLoadBase() { - return ""; - } - - protected void loadTypologies(Node customBaseNode) throws RepositoryException, IOException { - List typologies = getTypologies(); - if (!typologies.isEmpty()) { - Node termsBase = JcrUtils.getOrAdd(customBaseNode, EntityType.terms.name(), EntityType.typologies.get()); - for (String terms : typologies) { - loadTerms(termsBase, terms); - } - // TODO do not save here, so that upper layers can decide when to save - termsBase.getSession().save(); - } - } - - protected void loadTerms(Node termsBase, String name) throws IOException, RepositoryException { - try { -// if (termsBase.hasNode(name)) -// return; - String typologiesLoadBase = getTypologiesLoadBase(); - if (typologiesLoadBase.contains("/") && !typologiesLoadBase.endsWith("/")) - typologiesLoadBase = typologiesLoadBase + "/"; - String termsLoadPath = typologiesLoadBase + name + ".xml"; - URL termsUrl = getClass().getResource(termsLoadPath); - if (termsUrl == null) - throw new IllegalArgumentException("Terms '" + name + "' not found."); - try (InputStream in = termsUrl.openStream()) { - termsBase.getSession().importXML(termsBase.getPath(), in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } catch (ItemExistsException e) { - log.warn("Terms " + name + " exists with another UUID, removing it..."); - termsBase.getNode(name).remove(); - try (InputStream in = termsUrl.openStream()) { - termsBase.getSession().importXML(termsBase.getPath(), in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } - } - if (log.isDebugEnabled()) - log.debug("Terms '" + name + "' loaded."); - // TODO do not save here, so that upper layers can decide when to save - termsBase.getSession().save(); - } catch (RepositoryException | IOException e) { - log.error("Cannot load terms '" + name + "': " + e.getMessage()); - throw e; - } - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/JcrEntityDefinition.java b/org.argeo.app.core/src/org/argeo/app/core/JcrEntityDefinition.java deleted file mode 100644 index 10c27a8..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/JcrEntityDefinition.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.argeo.app.core; - -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.app.api.EntityConstants; -import org.argeo.app.api.EntityDefinition; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.jcr.Jcr; -import org.osgi.framework.BundleContext; - -/** An entity definition based on a JCR data structure. */ -public class JcrEntityDefinition implements EntityDefinition { - private Repository repository; - - private String type; - private String defaultEditorId; - - public void init(BundleContext bundleContext, Map properties) throws RepositoryException { - Session adminSession = CmsJcrUtils.openDataAdminSession(repository, null); - try { - type = properties.get(EntityConstants.TYPE); - if (type == null) - throw new IllegalArgumentException("Entity type property " + EntityConstants.TYPE + " must be set."); - defaultEditorId = properties.get(EntityConstants.DEFAULT_EDITOR_ID); -// String definitionPath = EntityNames.ENTITY_DEFINITIONS_PATH + '/' + type; -// if (!adminSession.itemExists(definitionPath)) { -// Node entityDefinition = JcrUtils.mkdirs(adminSession, definitionPath, EntityTypes.ENTITY_DEFINITION); -//// entityDefinition.addMixin(EntityTypes.ENTITY_DEFINITION); -// adminSession.save(); -// } - initJcr(adminSession); - } finally { - Jcr.logout(adminSession); - } - } - - /** To be overridden in order to perform additional initialisations. */ - protected void initJcr(Session adminSession) throws RepositoryException { - - } - - public void destroy(BundleContext bundleContext, Map properties) throws RepositoryException { - - } - - @Override - public String getEditorId(Node entity) { - return defaultEditorId; - } - - @Override - public String getType() { - return type; - } - - protected Repository getRepository() { - return repository; - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public String toString() { - return "Entity Definition " + getType(); - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenance.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenance.java new file mode 100644 index 0000000..2104145 --- /dev/null +++ b/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenance.java @@ -0,0 +1,70 @@ +package org.argeo.app.core; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.measure.Quantity; +import javax.measure.quantity.Area; + +import org.argeo.api.acr.spi.ContentNamespace; +import org.argeo.api.acr.spi.ProvidedRepository; +import org.geotools.gml3.v3_2.GML; + +import si.uom.SI; +import tech.units.indriya.quantity.Quantities; + +/** + * Background service starting and stopping with the whole system, and making + * sure it is in a proper state. + */ +public class SuiteMaintenance { + private ProvidedRepository contentRepository; + + public void start() { + // make sure that the unit system is initialised + Quantity dummy = Quantities.getQuantity(0, SI.SQUARE_METRE); + + getContentRepository().registerTypes(SuiteContentNamespace.values()); +// for (SuiteContentTypes types : SuiteContentTypes.values()) { +// getContentRepository().registerTypes(types.getDefaultPrefix(), types.getNamespace(), +// types.getResource() != null ? types.getResource().toExternalForm() : null); +// } + + // GML schema import fails because of xlinks issues + getContentRepository().registerTypes(new ContentNamespace() { + + @Override + public URL getSchemaResource() { + try { + return new URL(GML.getInstance().getSchemaLocation()); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public String getNamespaceURI() { + return GML.getInstance().getNamespaceURI(); + } + + @Override + public String getDefaultPrefix() { + return "gml"; + } + }); + + } + + public void stop() { + + } + + protected ProvidedRepository getContentRepository() { + return contentRepository; + } + + public void setContentRepository(ProvidedRepository contentRepository) { + this.contentRepository = contentRepository; + } + +} 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 deleted file mode 100644 index 9c74dde..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteMaintenanceService.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.app.core; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.security.Privilege; -import javax.measure.Quantity; -import javax.measure.quantity.Area; - -import org.argeo.api.acr.spi.ContentNamespace; -import org.argeo.api.cms.CmsConstants; -import org.argeo.app.api.EntityType; -import org.argeo.jcr.JcrUtils; -import org.argeo.maintenance.AbstractMaintenanceService; -import org.geotools.gml3.v3_2.GML; - -import si.uom.SI; -import tech.units.indriya.quantity.Quantities; - -/** Initialises an Argeo Suite backend. */ -public class SuiteMaintenanceService extends AbstractMaintenanceService { - @Override - public void init() { - // make sure that the unit system is initialised - Quantity dummy = Quantities.getQuantity(0, SI.SQUARE_METRE); - - super.init(); - - getContentRepository().registerTypes(SuiteContentNamespace.values()); -// for (SuiteContentTypes types : SuiteContentTypes.values()) { -// getContentRepository().registerTypes(types.getDefaultPrefix(), types.getNamespace(), -// types.getResource() != null ? types.getResource().toExternalForm() : null); -// } - - // GML schema import fails because of xlinks issues - getContentRepository().registerTypes(new ContentNamespace() { - - @Override - public URL getSchemaResource() { - try { - return new URL(GML.getInstance().getSchemaLocation()); - } catch (MalformedURLException e) { - throw new IllegalArgumentException(e); - } - } - - @Override - public String getNamespaceURI() { - return GML.getInstance().getNamespaceURI(); - } - - @Override - public String getDefaultPrefix() { - return "gml"; - } - }); - } - - @Override - public boolean prepareJcrTree(Session adminSession) throws RepositoryException, IOException { - boolean modified = false; - Node rootNode = adminSession.getRootNode(); - if (!rootNode.hasNode(EntityType.user.name())) { - rootNode.addNode(EntityType.user.name(), NodeType.NT_UNSTRUCTURED); - modified = true; - } - if (modified) - adminSession.save(); - return modified; - } - - @Override - 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); - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteTerm.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteTerm.java deleted file mode 100644 index aef23b5..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteTerm.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.argeo.app.core; - -import java.util.ArrayList; -import java.util.List; - -import org.argeo.app.api.Term; - -/** - * A single term. Helper to optimise {@link SuiteTermsManager} implementation. - */ -class SuiteTerm implements Term { - private final String name; - private final String relativePath; - private final SuiteTypology typology; - private final String id; - - private final SuiteTerm parentTerm; - private final List subTerms = new ArrayList<>(); - - SuiteTerm(SuiteTypology typology, String relativePath, SuiteTerm parentTerm) { - this.typology = typology; - this.parentTerm = parentTerm; - this.relativePath = relativePath; - int index = relativePath.lastIndexOf('/'); - if (index > 0) { - this.name = relativePath.substring(index + 1); - } else { - this.name = relativePath; - } - id = typology.getName() + '/' + relativePath; - } - - @Override - public String getId() { - return id; - } - - @Override - public String getName() { - return name; - } - - public String getRelativePath() { - return relativePath; - } - - @Override - public SuiteTypology getTypology() { - return typology; - } - - @Override - public List getSubTerms() { - return subTerms; - } - - @Override - public SuiteTerm getParentTerm() { - return parentTerm; - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteTermsManager.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteTermsManager.java deleted file mode 100644 index c14f871..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteTermsManager.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.argeo.app.core; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.app.api.EntityNames; -import org.argeo.app.api.EntityType; -import org.argeo.app.api.Term; -import org.argeo.app.api.TermsManager; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.jcr.Jcr; -import org.argeo.jcr.JcrException; - -/** Argeo Suite implementation of terms manager. */ -public class SuiteTermsManager implements TermsManager { - private final Map terms = new HashMap<>(); - private final Map typologies = new HashMap<>(); - - // JCR - private Repository repository; - private Session adminSession; - - public void init() { - adminSession = CmsJcrUtils.openDataAdminSession(repository, CmsConstants.SYS_WORKSPACE); - } - - @Override - public List listAllTerms(String typology) { - List res = new ArrayList<>(); - SuiteTypology t = getTypology(typology); - for (SuiteTerm term : t.getAllTerms()) { - res.add(term); - } - return res; - } - - @Override - public SuiteTerm getTerm(String termId) { - return terms.get(termId); - } - - @Override - public SuiteTypology getTypology(String typology) { - SuiteTypology t = typologies.get(typology); - if (t == null) { - Node termsNode = Jcr.getNode(adminSession, "SELECT * FROM [{0}] WHERE NAME()=\"{1}\"", - EntityType.terms.get(), typology); - if (termsNode == null) - throw new IllegalArgumentException("Typology " + typology + " not found."); - t = loadTypology(termsNode); - } - return t; - } - - SuiteTypology loadTypology(Node termsNode) { - try { - SuiteTypology typology = new SuiteTypology(termsNode); - for (Node termNode : Jcr.iterate(termsNode.getNodes())) { - if (termNode.isNodeType(EntityType.term.get())) { - SuiteTerm term = loadTerm(typology, termNode, null); - if (!term.getSubTerms().isEmpty()) - typology.markNotFlat(); - typology.getSubTerms().add(term); - } - } - typologies.put(typology.getName(), typology); - return typology; - } catch (RepositoryException e) { - throw new JcrException("Cannot load typology from " + termsNode, e); - } - } - - SuiteTerm loadTerm(SuiteTypology typology, Node termNode, SuiteTerm parentTerm) throws RepositoryException { - String name = termNode.getProperty(EntityNames.NAME).getString(); - String relativePath = parentTerm == null ? name : parentTerm.getRelativePath() + '/' + name; - SuiteTerm term = new SuiteTerm(typology, relativePath, parentTerm); - terms.put(term.getId(), term); - for (Node subTermNode : Jcr.iterate(termNode.getNodes())) { - if (termNode.isNodeType(EntityType.term.get())) { - SuiteTerm subTerm = loadTerm(typology, subTermNode, term); - term.getSubTerms().add(subTerm); - } - } - return term; - } - - public void destroy() { - Jcr.logout(adminSession); - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/core/SuiteTypology.java b/org.argeo.app.core/src/org/argeo/app/core/SuiteTypology.java deleted file mode 100644 index 7838274..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/SuiteTypology.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.argeo.app.core; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; - -import org.argeo.app.api.Term; -import org.argeo.app.api.Typology; -import org.argeo.jcr.Jcr; - -/** A typology. Helper to optimise {@link SuiteTermsManager} implementation. */ -class SuiteTypology implements Typology { - private final String name; - private final Node node; - private boolean isFlat = true; - - private final List subTerms = new ArrayList<>(); - - public SuiteTypology(Node node) { - this.node = node; - this.name = Jcr.getName(this.node); - } - - @Override - public String getId() { - return name; - } - - public String getName() { - return name; - } - - public Node getNode() { - return node; - } - - void markNotFlat() { - if (isFlat) - isFlat = false; - } - - @Override - public boolean isFlat() { - return isFlat; - } - - @Override - public List getSubTerms() { - return subTerms; - } - - public List getAllTerms() { - if (isFlat) - return subTerms; - else { - List terms = new ArrayList<>(); - for (SuiteTerm subTerm : subTerms) { - terms.add(subTerm); - collectSubTerms(terms, subTerm); - } - return terms; - } - } - - public Term findTermByName(String name) { - List collected = new ArrayList<>(); - for (SuiteTerm subTerm : subTerms) { - collectTermsByName(subTerm, name, collected); - } - if (collected.isEmpty()) - return null; - if (collected.size() == 1) - return collected.get(0); - throw new IllegalArgumentException( - "There are " + collected.size() + " terms with name " + name + " in typology " + getId()); - } - - private void collectTermsByName(SuiteTerm term, String name, List collected) { - if (term.getName().equals(name)) { - collected.add(term); - } - for (SuiteTerm subTerm : term.getSubTerms()) { - collectTermsByName(subTerm, name, collected); - } - } - - private void collectSubTerms(List terms, SuiteTerm term) { - for (SuiteTerm subTerm : term.getSubTerms()) { - terms.add(subTerm); - collectSubTerms(terms, subTerm); - } - } - -} 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 f225064..7b614a7 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 @@ -3,24 +3,13 @@ package org.argeo.app.core; import java.util.HashSet; import java.util.Set; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.security.Privilege; -import javax.security.auth.x500.X500Principal; import javax.xml.namespace.QName; import org.argeo.api.acr.Content; import org.argeo.api.acr.ldap.LdapAttr; import org.argeo.api.acr.ldap.LdapObj; -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsSession; import org.argeo.app.api.EntityType; import org.argeo.cms.RoleNameUtils; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; /** Utilities around the Argeo Suite APIs. */ public class SuiteUtils { @@ -33,84 +22,6 @@ public class SuiteUtils { return EntityType.user.basePath() + '/' + uid; } - public static Node getOrCreateUserNode(Session adminSession, String userDn) { - try { - Node usersBase = adminSession.getNode(EntityType.user.basePath()); - String uid = RoleNameUtils.getLastRdnValue(userDn); - Node userNode; - if (!usersBase.hasNode(uid)) { - userNode = usersBase.addNode(uid, NodeType.NT_UNSTRUCTURED); - userNode.addMixin(EntityType.user.get()); - userNode.addMixin(NodeType.MIX_CREATED); - userNode.setProperty(LdapAttr.distinguishedName.get(), userDn.toString()); - userNode.setProperty(LdapAttr.uid.get(), uid); - } else { - userNode = usersBase.getNode(uid); - } - - if (!userNode.hasNode(USER_SESSIONS_NODE_NAME)) { - // Migrate existing user node - Node sessionsNode = userNode.addNode(USER_SESSIONS_NODE_NAME, NodeType.NT_UNSTRUCTURED); - oldSessions: for (NodeIterator nit = userNode.getNodes(); nit.hasNext();) { - Node child = nit.nextNode(); - if (USER_SESSIONS_NODE_NAME.equals(child.getName()) || child.getName().startsWith("rep:") - || child.getName().startsWith("jcr:")) - continue oldSessions; - Node target = sessionsNode.addNode(child.getName()); - JcrUtils.copy(child, target); - } - - Node userStateNode = userNode.addNode(USER_STATE_NODE_NAME, NodeType.NT_UNSTRUCTURED); - Node userDevicesNode = userNode.addNode(USER_DEVICES_NODE_NAME, NodeType.NT_UNSTRUCTURED); - - adminSession.save(); -// JackrabbitSecurityUtils.denyPrivilege(adminSession, userNode.getPath(), SuiteRole.coworker.dn(), -// Privilege.JCR_READ); - JcrUtils.addPrivilege(adminSession, userNode.getPath(), new X500Principal(userDn.toString()).getName(), - Privilege.JCR_READ); - JcrUtils.addPrivilege(adminSession, userNode.getPath(), CmsConstants.ROLE_USER_ADMIN, - Privilege.JCR_ALL); - - JcrUtils.addPrivilege(adminSession, userStateNode.getPath(), userDn, Privilege.JCR_ALL); - JcrUtils.addPrivilege(adminSession, userDevicesNode.getPath(), userDn, Privilege.JCR_ALL); - } - return userNode; - } catch (RepositoryException e) { - throw new JcrException("Cannot create user node for " + userDn, e); - } - } - - public static Node getCmsSessionNode(Session session, CmsSession cmsSession) { - try { - return session.getNode(getUserNodePath(cmsSession.getUserDn()) + '/' + USER_SESSIONS_NODE_NAME + '/' - + cmsSession.getUuid().toString()); - } catch (RepositoryException e) { - throw new JcrException("Cannot get session dir for " + cmsSession, e); - } - } - - public static Node getOrCreateCmsSessionNode(Session adminSession, CmsSession cmsSession) { - try { - String userDn = cmsSession.getUserDn(); - Node userNode = getOrCreateUserNode(adminSession, userDn); - Node sessionsNode = userNode.getNode(USER_SESSIONS_NODE_NAME); - String cmsSessionUuid = cmsSession.getUuid().toString(); - Node cmsSessionNode; - if (!sessionsNode.hasNode(cmsSessionUuid)) { - cmsSessionNode = sessionsNode.addNode(cmsSessionUuid, NodeType.NT_UNSTRUCTURED); - cmsSessionNode.addMixin(NodeType.MIX_CREATED); - adminSession.save(); - JcrUtils.addPrivilege(adminSession, cmsSessionNode.getPath(), cmsSession.getUserRole(), - Privilege.JCR_ALL); - } else { - cmsSessionNode = sessionsNode.getNode(cmsSessionUuid); - } - return cmsSessionNode; - } catch (RepositoryException e) { - throw new JcrException("Cannot create session dir for " + cmsSession, e); - } - } - public static Set extractRoles(String[] semiColArr) { Set res = new HashSet<>(); // TODO factorize and make it more robust 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 deleted file mode 100644 index b0678cd..0000000 --- a/org.argeo.app.core/src/org/argeo/app/core/XPathUtils.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.app.core; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.query.Query; -import javax.jcr.query.QueryManager; - -import org.apache.jackrabbit.util.ISO9075; -import org.argeo.api.cms.CmsLog; - -/** Ease XPath generation for JCR requests */ -public class XPathUtils { - private final static CmsLog log = CmsLog.getLog(XPathUtils.class); - - private final static String QUERY_XPATH = "xpath"; - - public static String descendantFrom(String parentPath) { - if (notEmpty(parentPath)) { - if ("/".equals(parentPath)) - parentPath = ""; - // Hardcoded dependency to Jackrabbit. Remove - String result = "/jcr:root" + ISO9075.encodePath(parentPath); - if (log.isTraceEnabled()) { - String result2 = "/jcr:root" + parentPath; - if (!result2.equals(result)) - log.warn("Encoded Path " + result2 + " --> " + result); - } - return result; - } else - return ""; - } - - public static String localAnd(String... conditions) { - StringBuilder builder = new StringBuilder(); - for (String condition : conditions) { - if (notEmpty(condition)) { - builder.append(" ").append(condition).append(" and "); - } - } - if (builder.length() > 3) - return builder.substring(0, builder.length() - 4); - else - return ""; - } - - public static String xPathNot(String condition) { - if (notEmpty(condition)) - return "not(" + condition + ")"; - else - return ""; - } - - public static String getFreeTextConstraint(String filter) throws RepositoryException { - StringBuilder builder = new StringBuilder(); - if (notEmpty(filter)) { - String[] strs = filter.trim().split(" "); - for (String token : strs) { - builder.append("jcr:contains(.,'*" + encodeXPathStringValue(token) + "*') and "); - } - return builder.substring(0, builder.length() - 4); - } - return ""; - } - - public static String getPropertyContains(String propertyName, String filter) throws RepositoryException { - if (notEmpty(filter)) - return "jcr:contains(@" + propertyName + ",'*" + encodeXPathStringValue(filter) + "*')"; - return ""; - } - - private final static DateFormat jcrRefFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'+02:00'"); - - /** - * @param propertyName - * @param calendar the reference date - * @param lowerOrGreater "<", ">" TODO validate ">=" - * @throws RepositoryException - */ - public static String getPropertyDateComparaison(String propertyName, Calendar cal, String lowerOrGreater) - throws RepositoryException { - if (cal != null) { - String jcrDateStr = jcrRefFormatter.format(cal.getTime()); - - // jcrDateStr = "2015-08-03T05:00:03:000Z"; - String result = "@" + propertyName + " " + lowerOrGreater + " xs:dateTime('" + jcrDateStr + "')"; - return result; - } - return ""; - } - - public static String getPropertyEquals(String propertyName, String value) { - if (notEmpty(value)) - return "@" + propertyName + "='" + encodeXPathStringValue(value) + "'"; - return ""; - } - - public static String encodeXPathStringValue(String propertyValue) { - // TODO implement safer mechanism to escape invalid characters - // Also check why we have used this regex in ResourceSerrviceImpl l 474 - // String cleanedKey = key.replaceAll("(?:')", "''"); - String result = propertyValue.replaceAll("'", "''"); - return result; - } - - public static void andAppend(StringBuilder builder, String condition) { - if (notEmpty(condition)) { - builder.append(condition); - builder.append(" and "); - } - } - - public static void appendOrderByProperties(StringBuilder builder, boolean ascending, String... propertyNames) { - if (propertyNames.length > 0) { - builder.append(" order by "); - for (String propName : propertyNames) - builder.append("@").append(propName).append(", "); - builder = builder.delete(builder.length() - 2, builder.length()); - if (ascending) - builder.append(" ascending "); - else - builder.append(" descending "); - } - } - - public static void appendAndPropStringCondition(StringBuilder builder, String propertyName, String filter) - throws RepositoryException { - if (notEmpty(filter)) { - andAppend(builder, getPropertyContains(propertyName, filter)); - } - } - - public static void appendAndNotPropStringCondition(StringBuilder builder, String propertyName, String filter) - throws RepositoryException { - if (notEmpty(filter)) { - String cond = getPropertyContains(propertyName, filter); - builder.append(xPathNot(cond)); - builder.append(" and "); - } - } - - public static Query createQuery(Session session, String queryString) throws RepositoryException { - QueryManager queryManager = session.getWorkspace().getQueryManager(); - // Localise JCR properties for XPATH - queryString = localiseJcrItemNames(queryString); - return queryManager.createQuery(queryString, QUERY_XPATH); - } - - private final static String NS_JCR = "\\{http://www.jcp.org/jcr/1.0\\}"; - private final static String NS_NT = "\\{http://www.jcp.org/jcr/nt/1.0\\}"; - private final static String NS_MIX = "\\{http://www.jcp.org/jcr/mix/1.0\\}"; - - /** - * Replace the generic namespace with the local "jcr:", "nt:", "mix:" values. It - * is a workaround that must be later cleaned - */ - public static String localiseJcrItemNames(String name) { - name = name.replaceAll(NS_JCR, "jcr:"); - name = name.replaceAll(NS_NT, "nt:"); - name = name.replaceAll(NS_MIX, "mix:"); - return name; - } - - private static boolean notEmpty(String stringToTest) { - return !(stringToTest == null || "".equals(stringToTest.trim())); - } - - /** Singleton. */ - private XPathUtils() { - - } -} diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/Dbk4Converter.java b/org.argeo.app.core/src/org/argeo/app/docbook/Dbk4Converter.java deleted file mode 100644 index f213c02..0000000 --- a/org.argeo.app.core/src/org/argeo/app/docbook/Dbk4Converter.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.argeo.app.docbook; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import javax.jcr.ImportUUIDBehavior; -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Templates; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.argeo.jcr.JcrException; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import net.sf.saxon.BasicTransformerFactory; -import net.sf.saxon.TransformerFactoryImpl; - -/** Convert from DocBook v4 to DocBook v5, using the official XSL. */ -public class Dbk4Converter { - private final Templates templates; - - public Dbk4Converter() { - try (InputStream in = getClass().getResourceAsStream("db4-upgrade.xsl")) { - Source xsl = new StreamSource(in); - TransformerFactory transformerFactory = new BasicTransformerFactory(); -// TransformerFactory transformerFactory = new TransformerFactoryImpl(); - templates = transformerFactory.newTemplates(xsl); - } catch (IOException | TransformerConfigurationException e) { - throw new RuntimeException("Cannot initialise DocBook v4 converter", e); - } - } - - public void importXml(Node baseNode, InputStream in) throws IOException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream();) { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setXIncludeAware(true); - factory.setNamespaceAware(true); - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(new InputSource(in)); - Source xmlInput = new DOMSource(doc); - -// ContentHandler contentHandler = baseNode.getSession().getImportContentHandler(baseNode.getPath(), -// ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - - Transformer transformer = templates.newTransformer(); - Result xmlOutput = new StreamResult(out); - transformer.transform(xmlInput, xmlOutput); - try (InputStream dbk5in = new ByteArrayInputStream(out.toByteArray())) { - baseNode.getSession().importXML(baseNode.getPath(), dbk5in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } - } catch (RepositoryException e) { - throw new JcrException("Cannot import XML to " + baseNode, e); - } catch (TransformerException | SAXException | ParserConfigurationException e) { - throw new RuntimeException("Cannot import DocBook v4 to " + baseNode, e); - } - - } - - public static void main(String[] args) { - try { - - Source xsl = new StreamSource(new File("/usr/share/xml/docbook5/stylesheet/upgrade/db4-upgrade.xsl")); - TransformerFactory transformerFactory = new TransformerFactoryImpl(); - Templates templates = transformerFactory.newTemplates(xsl); - - File inputDir = new File(args[0]); - File outputDir = new File(args[1]); - - for (File inputFile : inputDir.listFiles()) { - Result xmlOutput = new StreamResult(new File(outputDir, inputFile.getName())); - - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setXIncludeAware(true); - factory.setNamespaceAware(true); - DocumentBuilder docBuilder = factory.newDocumentBuilder(); - Document doc = docBuilder.parse(inputFile); - Source xmlInput = new DOMSource(doc); - Transformer transformer = templates.newTransformer(); - transformer.transform(xmlInput, xmlOutput); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java b/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java deleted file mode 100644 index b0d352b..0000000 --- a/org.argeo.app.core/src/org/argeo/app/docbook/DbkUtils.java +++ /dev/null @@ -1,253 +0,0 @@ -package org.argeo.app.docbook; - -import static org.argeo.app.docbook.DbkType.para; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; - -import javax.jcr.ImportUUIDBehavior; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.PathNotFoundException; -import javax.jcr.RepositoryException; -import javax.jcr.ValueFormatException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.app.api.EntityType; -import org.argeo.jcr.Jcr; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.argeo.jcr.JcrxApi; - -/** Utilities around DocBook. */ -public class DbkUtils { - private final static CmsLog log = CmsLog.getLog(DbkUtils.class); - - /** Get or add a DocBook element. */ - public static Node getOrAddDbk(Node parent, DbkType child) { - try { - if (!parent.hasNode(child.get())) { - return addDbk(parent, child); - } else { - return parent.getNode(child.get()); - } - } catch (RepositoryException e) { - throw new JcrException("Cannot get or add element " + child.get() + " to " + parent, e); - } - } - - /** Add a DocBook element to this node. */ - public static Node addDbk(Node parent, DbkType child) { - try { - Node node = parent.addNode(child.get(), child.get()); - return node; - } catch (RepositoryException e) { - throw new JcrException("Cannot add element " + child.get() + " to " + parent, e); - } - } - - /** Whether this DocBook element is of this type. */ - public static boolean isDbk(Node node, DbkType type) { - return Jcr.getName(node).equals(type.get()); - } - - /** Whether this node is a DocBook type. */ - public static boolean isDbk(Node node) { - String name = Jcr.getName(node); - for (DbkType type : DbkType.values()) { - if (name.equals(type.get())) - return true; - } - return false; - } - - public static String getTitle(Node node) { - return JcrxApi.getXmlValue(node, DbkType.title.get()); - } - - public static void setTitle(Node node, String txt) { - Node titleNode = getOrAddDbk(node, DbkType.title); - JcrxApi.setXmlValue(titleNode, txt); - } - - public static Node getMetadata(Node infoContainer) { - try { - if (!infoContainer.hasNode(DbkType.info.get())) - return null; - Node info = infoContainer.getNode(DbkType.info.get()); - if (!info.hasNode(EntityType.local.get())) - return null; - return info.getNode(EntityType.local.get()); - } catch (RepositoryException e) { - throw new JcrException("Cannot retrieve metadata from " + infoContainer, e); - } - } - - public static Node getChildByRole(Node parent, String role) { - try { - NodeIterator baseSections = parent.getNodes(); - while (baseSections.hasNext()) { - Node n = baseSections.nextNode(); - String r = Jcr.get(n, DbkAttr.role.name()); - if (r != null && r.equals(role)) - return n; - } - return null; - } catch (RepositoryException e) { - throw new JcrException("Cannot get child from " + parent + " with role " + role, e); - } - } - - public static Node addParagraph(Node node, String txt) { - Node p = addDbk(node, para); - JcrxApi.setXmlValue(p, txt); - return p; - } - - /** - * Removes a paragraph if it empty. The sesison is not saved. - * - * @return true if the paragraph was empty and it was removed - */ - public static boolean removeIfEmptyParagraph(Node node) { - try { - if (isDbk(node, DbkType.para)) { - NodeIterator nit = node.getNodes(); - if (!nit.hasNext()) { - node.remove(); - return true; - } - Node first = nit.nextNode(); - if (nit.hasNext()) - return false; - if (first.getName().equals(Jcr.JCR_XMLTEXT)) { - String str = Jcr.get(first, Jcr.JCR_XMLCHARACTERS); - if (str != null && str.trim().equals("")) { - node.remove(); - return true; - } - } else { - return false; - } - } - return false; - } catch (RepositoryException e) { - throw new JcrException("Cannot remove possibly empty paragraph", e); - } - } - - public static Node insertImageAfter(Node sibling) { - try { - - Node parent = sibling.getParent(); - Node mediaNode = addDbk(parent, DbkType.mediaobject); - // TODO optimise? - parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", - sibling.getName() + "[" + sibling.getIndex() + "]"); - parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", - mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); - - Node imageNode = addDbk(mediaNode, DbkType.imageobject); - Node imageDataNode = addDbk(imageNode, DbkType.imagedata); -// Node infoNode = imageNode.addNode(DocBookTypes.INFO, DocBookTypes.INFO); -// Node fileNode = JcrUtils.copyBytesAsFile(mediaFolder, EntityType.box.get(), new byte[0]); -// fileNode.addMixin(EntityType.box.get()); -// fileNode.setProperty(EntityNames.SVG_WIDTH, 0); -// fileNode.setProperty(EntityNames.SVG_LENGTH, 0); -// fileNode.addMixin(NodeType.MIX_MIMETYPE); -// -// // we assume this is a folder next to the main DocBook document -// // TODO make it more robust and generic -// String fileRef = mediaNode.getName(); -// imageDataNode.setProperty(DocBookNames.DBK_FILEREF, fileRef); - return mediaNode; - } catch (RepositoryException e) { - throw new JcrException("Cannot insert empty image after " + sibling, e); - } - } - - public static Node insertVideoAfter(Node sibling) { - try { - - Node parent = sibling.getParent(); - Node mediaNode = addDbk(parent, DbkType.mediaobject); - // TODO optimise? - parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", - sibling.getName() + "[" + sibling.getIndex() + "]"); - parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", - mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); - - Node videoNode = addDbk(mediaNode, DbkType.videoobject); - Node videoDataNode = addDbk(videoNode, DbkType.videodata); - return mediaNode; - } catch (RepositoryException e) { - throw new JcrException("Cannot insert empty image after " + sibling, e); - } - } - - public static String getMediaFileref(Node node) { - try { - Node mediadata; - if (node.hasNode(DbkType.imageobject.get())) { - mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get()); - } else if (node.hasNode(DbkType.videoobject.get())) { - mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get()); - } else { - throw new IllegalArgumentException("Fileref not found in " + node); - } - - if (mediadata.hasProperty(DbkAttr.fileref.name())) { - return mediadata.getProperty(DbkAttr.fileref.name()).getString(); - } else { - return null; - } - } catch (RepositoryException e) { - throw new JcrException("Cannot retrieve file ref from " + node, e); - } - } - - public static void exportXml(Node node, OutputStream out) throws IOException { - try { - node.getSession().exportDocumentView(node.getPath(), out, false, false); - } catch (RepositoryException e) { - throw new JcrException("Cannot export " + node + " to XML", e); - } - } - - public static void exportToFs(Node baseNode, DbkType type, Path directory) { - String fileName = Jcr.getName(baseNode) + ".dbk.xml"; - Path filePath = directory.resolve(fileName); - Node docBookNode = Jcr.getNode(baseNode, type.get()); - if (docBookNode == null) - throw new IllegalArgumentException("No " + type.get() + " under " + baseNode); - try { - Files.createDirectories(directory); - try (OutputStream out = Files.newOutputStream(filePath)) { - exportXml(docBookNode, out); - } - JcrUtils.copyFilesToFs(baseNode, directory, true); - if (log.isDebugEnabled()) - log.debug("DocBook " + baseNode + " exported to " + filePath.toAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static void importXml(Node baseNode, InputStream in) throws IOException { - try { - baseNode.getSession().importXML(baseNode.getPath(), in, - ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } catch (RepositoryException e) { - throw new JcrException("Cannot import XML to " + baseNode, e); - } - - } - - /** Singleton. */ - private DbkUtils() { - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/docbook/db4-upgrade.xsl b/org.argeo.app.core/src/org/argeo/app/docbook/db4-upgrade.xsl deleted file mode 100644 index 00096be..0000000 --- a/org.argeo.app.core/src/org/argeo/app/docbook/db4-upgrade.xsl +++ /dev/null @@ -1,1398 +0,0 @@ - - - - - - - - - - - - - - - - - UNKNOWN - - - - - - - - - - - - Converted by db4-upgrade version - - - - - - - - - - - - - - - - - - Check - - title. - - - - - - - - - - - - - - - Check - - : no title. - - - - - - - - - - - Check - - titleabbrev. - - - - - - - - - - - - - - - - - - - Check - - subtitle. - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check - - title. - - - - - - - - - - - - - - - - - - - - - - Check - - titleabbrev. - - - - - - - - - - - - - - - - - - - Check - - subtitle. - - - - - - - - - - - - - - - - - - - - - - - - - - Discarding title from refentryinfo! - - - - - - - - Discarding titleabbrev from refentryinfo! - - - - - - - - Discarding subtitle from refentryinfo! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dropping class attribute from productname - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Convert equation without title to informal equation. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check conversion of srccredit - (othercredit="srccredit"). - - - - - ??? - - - - - - - - - - - - - - comment - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Converting invpartnumber to biblioid otherclass="invpartnumber". - - - - - - - - - - - - - - Converting contractsponsor to othercredit="contractsponsor". - - - - - - - - - - - - - - - - - - - - - Converting contractnum to othercredit="contractnum". - - - - - ??? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check conversion of collabname - (orgname role="collabname"). - - - - - - - - - - - - Discarding modespec ( - - ). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check conversion of contrib - (othercontrib="contrib"). - - - - ??? - - - - - - - - - - - - - - - - - - - - Converting ulink to link. - - - - - - - - - - - - - - Converting ulink to uri. - - - - - - - - - - - - - - - - - - Discarding linkmode on olink. - - - - - - - - - Converting olink targetdocent to targetdoc. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -01- - - - - - -02- - - - - - -03- - - - - - -04- - - - - - -05- - - - - - -06- - - - - - -07- - - - - - -08- - - - - - -09- - - - - - -10- - - - - - -11- - - - - - -12- - - - - - - - - - - - - -01- - - - - - -02- - - - - - -03- - - - - - -04- - - - - - -05- - - - - - -06- - - - - - -07- - - - - - -08- - - - - - -09- - - - - - -10- - - - - - -11- - - - - - -12- - - - - - - - - - - - - - - - - - - Converted - - into - - for - - - - - - - - - - - - - - Unparseable date: - - in - - (Using default: - - ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Check abstract; moved into info correctly? - - - - - - - - - - - significance - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - beginpage pagenum= - - - Replacing beginpage with comment - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Discarding moreinfo on - - - - - - - - - - - - - - - - - - - - - Discarding float on - - - - - - - Adding floatstyle='normal' on - - - - - normal - - - - - - - Discarding float on - - - - - - - - Discarding float on - - - - - - - Adding floatstyle=' - - ' on - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Converting refmiscinfo@role=type to - @class=other,otherclass=type - - - other - type - - - - - - - - - - - - - - - - - 5.0 - - - - - - - - - 5.0 - - - - - - - - - - - - - - - - - - - ( - - ) - - - -
diff --git a/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java b/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java deleted file mode 100644 index 4d2f521..0000000 --- a/org.argeo.app.core/src/org/argeo/app/odk/OdkUtils.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.argeo.app.odk; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; - -import javax.jcr.ImportUUIDBehavior; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsLog; -import org.argeo.app.api.EntityMimeType; -import org.argeo.app.api.EntityType; -import org.argeo.cms.util.DigestUtils; -import org.argeo.jcr.Jcr; -import org.argeo.jcr.JcrUtils; -import org.argeo.jcr.JcrxApi; - -/** Utilities around ODK. */ -public class OdkUtils { - private final static CmsLog log = CmsLog.getLog(OdkUtils.class); - - public static Node loadOdkForm(Node formBase, String name, InputStream in, InputStream... additionalNodes) - throws RepositoryException, IOException { - if (!formBase.isNodeType(EntityType.formSet.get())) - throw new IllegalArgumentException( - "Parent path " + formBase + " must be of type " + EntityType.formSet.get()); - Node form = JcrUtils.getOrAdd(formBase, name, OrxListName.xform.get(), NodeType.MIX_VERSIONABLE); - - String previousCsum = JcrxApi.getChecksum(form, JcrxApi.MD5); - String previousFormId = Jcr.get(form, OrxListName.formID.get()); - String previousFormVersion = Jcr.get(form, OrxListName.version.get()); - - Session s = formBase.getSession(); - s.importXML(form.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - - for (InputStream additionalIn : additionalNodes) { - s.importXML(form.getPath(), additionalIn, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); - } - s.save(); - - // manage instances - // NodeIterator instances = - // form.getNodes("h:html/h:head/xforms:model/xforms:instance"); - NodeIterator instances = form.getNode("h:html/h:head/xforms:model").getNodes("xforms:instance"); - Node primaryInstance = null; - while (instances.hasNext()) { - Node instance = instances.nextNode(); - if (primaryInstance == null) { - primaryInstance = instance; - } else {// secondary instances - String instanceId = instance.getProperty("id").getString(); - URI instanceUri = null; - if (instance.hasProperty("src")) - try { - instanceUri = new URI(instance.getProperty("src").getString()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Instance " + instanceId + " has a badly formatted URI", e); - } - if (instanceUri != null) { - if ("jr".equals(instanceUri.getScheme())) { - String uuid; - String mimeType; - String encoding = StandardCharsets.UTF_8.name(); - String type = instanceUri.getHost(); - String path = instanceUri.getPath(); - if ("file".equals(type)) { - if (!path.endsWith(".xml")) - throw new IllegalArgumentException("File uri " + instanceUri + " must end with .xml"); - // Work around bug in ODK Collect not supporting paths - // path = path.substring(0, path.length() - ".xml".length()); - // Node target = file.getSession().getNode(path); - uuid = path.substring(1, path.length() - ".xml".length()); - mimeType = EntityMimeType.XML.getMimeType(); - } else if ("file-csv".equals(type)) { - if (!path.endsWith(".csv")) - throw new IllegalArgumentException("File uri " + instanceUri + " must end with .csv"); - // Work around bug in ODK Collect not supporting paths - // path = path.substring(0, path.length() - ".csv".length()); - // Node target = file.getSession().getNode(path); - uuid = path.substring(1, path.length() - ".csv".length()); - mimeType = EntityMimeType.CSV.getMimeType(); - } else { - throw new IllegalArgumentException("Unsupported instance type " + type); - } - Node manifest = JcrUtils.getOrAdd(form, OrxManifestName.manifest.name(), - OrxManifestName.manifest.get()); - Node file = JcrUtils.getOrAdd(manifest, instanceId); - file.addMixin(NodeType.MIX_MIMETYPE); - file.setProperty(Property.JCR_MIMETYPE, mimeType); - file.setProperty(Property.JCR_ENCODING, encoding); - Node target = file.getSession().getNodeByIdentifier(uuid); - -// if (target.isNodeType(NodeType.NT_QUERY)) { -// Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target); -// query.setLimit(10); -// QueryResult queryResult = query.execute(); -// RowIterator rit = queryResult.getRows(); -// while (rit.hasNext()) { -// Row row = rit.nextRow(); -// for (Value value : row.getValues()) { -// System.out.print(value.getString()); -// System.out.print(','); -// } -// System.out.print('\n'); -// } -// -// } - - if (target.isNodeType(NodeType.MIX_REFERENCEABLE)) { - file.setProperty(Property.JCR_ID, target); - if (file.hasProperty(Property.JCR_PATH)) - file.getProperty(Property.JCR_PATH).remove(); - } else { - file.setProperty(Property.JCR_PATH, target.getPath()); - if (file.hasProperty(Property.JCR_ID)) - file.getProperty(Property.JCR_ID).remove(); - } - } - } - } - } - - if (primaryInstance == null) - throw new IllegalArgumentException("No primary instance found in " + form); - if (!primaryInstance.hasNodes()) - throw new IllegalArgumentException("No data found in primary instance of " + form); - NodeIterator primaryInstanceChildren = primaryInstance.getNodes(); - Node data = primaryInstanceChildren.nextNode(); - if (primaryInstanceChildren.hasNext()) - throw new IllegalArgumentException("More than one data found in primary instance of " + form); - String formId = data.getProperty("id").getString(); - if (previousFormId != null && !formId.equals(previousFormId)) - log.warn("Form id of " + form + " changed from " + previousFormId + " to " + formId); - form.setProperty(OrxListName.formID.get(), formId); - String formVersion = data.getProperty("version").getString(); - - if (previousCsum == null)// save before checksuming - s.save(); - String newCsum; - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - s.exportDocumentView(form.getPath() + "/" + OdkNames.H_HTML, out, true, false); - newCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray()); - } - if (previousCsum == null) { - JcrxApi.addChecksum(form, newCsum); - JcrUtils.updateLastModified(form); - form.setProperty(OrxListName.version.get(), formVersion); - s.save(); - s.getWorkspace().getVersionManager().checkpoint(form.getPath()); - if (log.isDebugEnabled()) - log.debug("New form " + form); - } else { - if (newCsum.equals(previousCsum)) { - // discard - s.refresh(false); - if (log.isDebugEnabled()) - log.debug("Unmodified form " + form); - return form; - } else { - if (formVersion.equals(previousFormVersion)) { - s.refresh(false); - throw new IllegalArgumentException("Form " + form + " has been changed but version " + formVersion - + " has not been changed, discarding changes..."); - } - form.setProperty(OrxListName.version.get(), formVersion); - JcrxApi.addChecksum(form, newCsum); - JcrUtils.updateLastModified(form); - s.save(); - s.getWorkspace().getVersionManager().checkpoint(form.getPath()); - if (log.isDebugEnabled()) { - log.debug("Updated form " + form); - log.debug("Previous csum " + previousCsum); - log.debug("New csum " + newCsum); - } - } - } - return form; - } - - /** Singleton. */ - private OdkUtils() { - - } - -} diff --git a/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java b/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java index 0dff64c..6fee2f9 100644 --- a/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java +++ b/org.argeo.app.core/src/org/argeo/app/xforms/FormSubmissionListener.java @@ -1,7 +1,6 @@ package org.argeo.app.xforms; -import javax.jcr.Node; -import javax.jcr.RepositoryException; +import org.argeo.api.acr.Content; /** Called when a user has received a new form submission. */ public interface FormSubmissionListener { @@ -9,5 +8,5 @@ public interface FormSubmissionListener { * Called after a form submission has been stored in the user area. The * submission will be deleted if any exception is thrown. */ - void formSubmissionReceived(Node node) throws RepositoryException; + void formSubmissionReceived(Content content); } diff --git a/org.argeo.app.core/src/org/argeo/internal/app/core/AppUserStateImpl.java b/org.argeo.app.core/src/org/argeo/internal/app/core/AppUserStateImpl.java deleted file mode 100644 index b7201a1..0000000 --- a/org.argeo.app.core/src/org/argeo/internal/app/core/AppUserStateImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.internal.app.core; - -import javax.jcr.Node; - -import org.argeo.api.acr.Content; -import org.argeo.api.acr.ContentSession; -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsSession; -import org.argeo.app.api.AppUserState; -import org.argeo.app.core.SuiteUtils; -import org.argeo.cms.acr.ContentUtils; -import org.argeo.cms.jcr.acr.JcrContentProvider; -import org.argeo.jcr.Jcr; - -public class AppUserStateImpl implements AppUserState { - private JcrContentProvider jcrContentProvider; - - @Override - public Content getOrCreateSessionDir(ContentSession contentSession, CmsSession session) { - Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> { - Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, session); - return node; - }); - Content userDir = contentSession - .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + Jcr.getPath(userDirNode)); - return userDir; - } - - public void setJcrContentProvider(JcrContentProvider jcrContentProvider) { - this.jcrContentProvider = jcrContentProvider; - } - -} diff --git a/org.argeo.app.jcr/.classpath b/org.argeo.app.jcr/.classpath new file mode 100644 index 0000000..81fe078 --- /dev/null +++ b/org.argeo.app.jcr/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.app.jcr/.project b/org.argeo.app.jcr/.project new file mode 100644 index 0000000..f688a62 --- /dev/null +++ b/org.argeo.app.jcr/.project @@ -0,0 +1,28 @@ + + + org.argeo.app.jcr + + + + + + 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/org.argeo.app.jcr/OSGI-INF/appUserState.xml b/org.argeo.app.jcr/OSGI-INF/appUserState.xml new file mode 100644 index 0000000..5dc205e --- /dev/null +++ b/org.argeo.app.jcr/OSGI-INF/appUserState.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.app.jcr/OSGI-INF/maintenanceService.xml b/org.argeo.app.jcr/OSGI-INF/maintenanceService.xml new file mode 100644 index 0000000..7167ff6 --- /dev/null +++ b/org.argeo.app.jcr/OSGI-INF/maintenanceService.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.app.jcr/OSGI-INF/termsManager.xml b/org.argeo.app.jcr/OSGI-INF/termsManager.xml new file mode 100644 index 0000000..e6a2abd --- /dev/null +++ b/org.argeo.app.jcr/OSGI-INF/termsManager.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.argeo.app.jcr/bnd.bnd b/org.argeo.app.jcr/bnd.bnd new file mode 100644 index 0000000..b190cf4 --- /dev/null +++ b/org.argeo.app.jcr/bnd.bnd @@ -0,0 +1,11 @@ +Service-Component:\ +OSGI-INF/termsManager.xml,\ +OSGI-INF/maintenanceService.xml,\ +OSGI-INF/appUserState.xml,\ + +Import-Package:\ +javax.jcr.nodetype,\ +javax.jcr.security,\ +org.apache.jackrabbit.*;version="[1,4)",\ +org.argeo.cms.acr,\ +* \ No newline at end of file diff --git a/org.argeo.app.jcr/build.properties b/org.argeo.app.jcr/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/org.argeo.app.jcr/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/CustomMaintenanceService.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/CustomMaintenanceService.java new file mode 100644 index 0000000..9bb72de --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/CustomMaintenanceService.java @@ -0,0 +1,75 @@ +package org.argeo.app.jcr; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.ItemExistsException; +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.app.api.EntityType; +import org.argeo.jcr.JcrUtils; +import org.argeo.maintenance.AbstractMaintenanceService; + +/** Base for custom initialisations. */ +public abstract class CustomMaintenanceService extends AbstractMaintenanceService { + private final static CmsLog log = CmsLog.getLog(AbstractMaintenanceService.class); + + protected List getTypologies() { + return new ArrayList<>(); + } + + protected String getTypologiesLoadBase() { + return ""; + } + + protected void loadTypologies(Node customBaseNode) throws RepositoryException, IOException { + List typologies = getTypologies(); + if (!typologies.isEmpty()) { + Node termsBase = JcrUtils.getOrAdd(customBaseNode, EntityType.terms.name(), EntityType.typologies.get()); + for (String terms : typologies) { + loadTerms(termsBase, terms); + } + // TODO do not save here, so that upper layers can decide when to save + termsBase.getSession().save(); + } + } + + protected void loadTerms(Node termsBase, String name) throws IOException, RepositoryException { + try { +// if (termsBase.hasNode(name)) +// return; + String typologiesLoadBase = getTypologiesLoadBase(); + if (typologiesLoadBase.contains("/") && !typologiesLoadBase.endsWith("/")) + typologiesLoadBase = typologiesLoadBase + "/"; + String termsLoadPath = typologiesLoadBase + name + ".xml"; + URL termsUrl = getClass().getResource(termsLoadPath); + if (termsUrl == null) + throw new IllegalArgumentException("Terms '" + name + "' not found."); + try (InputStream in = termsUrl.openStream()) { + termsBase.getSession().importXML(termsBase.getPath(), in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } catch (ItemExistsException e) { + log.warn("Terms " + name + " exists with another UUID, removing it..."); + termsBase.getNode(name).remove(); + try (InputStream in = termsUrl.openStream()) { + termsBase.getSession().importXML(termsBase.getPath(), in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } + } + if (log.isDebugEnabled()) + log.debug("Terms '" + name + "' loaded."); + // TODO do not save here, so that upper layers can decide when to save + termsBase.getSession().save(); + } catch (RepositoryException | IOException e) { + log.error("Cannot load terms '" + name + "': " + e.getMessage()); + throw e; + } + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/JcrEntityDefinition.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/JcrEntityDefinition.java new file mode 100644 index 0000000..e76315c --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/JcrEntityDefinition.java @@ -0,0 +1,73 @@ +package org.argeo.app.jcr; + +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.app.api.EntityConstants; +import org.argeo.app.api.EntityDefinition; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.jcr.Jcr; +import org.osgi.framework.BundleContext; + +/** An entity definition based on a JCR data structure. */ +public class JcrEntityDefinition implements EntityDefinition { + private Repository repository; + + private String type; + private String defaultEditorId; + + public void init(BundleContext bundleContext, Map properties) throws RepositoryException { + Session adminSession = CmsJcrUtils.openDataAdminSession(repository, null); + try { + type = properties.get(EntityConstants.TYPE); + if (type == null) + throw new IllegalArgumentException("Entity type property " + EntityConstants.TYPE + " must be set."); + defaultEditorId = properties.get(EntityConstants.DEFAULT_EDITOR_ID); +// String definitionPath = EntityNames.ENTITY_DEFINITIONS_PATH + '/' + type; +// if (!adminSession.itemExists(definitionPath)) { +// Node entityDefinition = JcrUtils.mkdirs(adminSession, definitionPath, EntityTypes.ENTITY_DEFINITION); +//// entityDefinition.addMixin(EntityTypes.ENTITY_DEFINITION); +// adminSession.save(); +// } + initJcr(adminSession); + } finally { + Jcr.logout(adminSession); + } + } + + /** To be overridden in order to perform additional initialisations. */ + protected void initJcr(Session adminSession) throws RepositoryException { + + } + + public void destroy(BundleContext bundleContext, Map properties) throws RepositoryException { + + } + + @Override + public String getEditorId(Node entity) { + return defaultEditorId; + } + + @Override + public String getType() { + return type; + } + + protected Repository getRepository() { + return repository; + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public String toString() { + return "Entity Definition " + getType(); + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/SuiteJcrUtils.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/SuiteJcrUtils.java new file mode 100644 index 0000000..49b7ede --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/SuiteJcrUtils.java @@ -0,0 +1,114 @@ +package org.argeo.app.jcr; + +import static org.argeo.app.core.SuiteUtils.USER_DEVICES_NODE_NAME; +import static org.argeo.app.core.SuiteUtils.USER_SESSIONS_NODE_NAME; +import static org.argeo.app.core.SuiteUtils.USER_STATE_NODE_NAME; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.security.Privilege; +import javax.security.auth.x500.X500Principal; + +import org.argeo.api.acr.ldap.LdapAttr; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsSession; +import org.argeo.app.api.AppUserState; +import org.argeo.app.api.EntityType; +import org.argeo.app.core.SuiteUtils; +import org.argeo.cms.RoleNameUtils; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; + +/** JCR utilities. */ +public class SuiteJcrUtils { + /** @deprecated Use {@link AppUserState} instead. */ + @Deprecated + public static Node getOrCreateUserNode(Session adminSession, String userDn) { + try { + Node usersBase = adminSession.getNode(EntityType.user.basePath()); + String uid = RoleNameUtils.getLastRdnValue(userDn); + Node userNode; + if (!usersBase.hasNode(uid)) { + userNode = usersBase.addNode(uid, NodeType.NT_UNSTRUCTURED); + userNode.addMixin(EntityType.user.get()); + userNode.addMixin(NodeType.MIX_CREATED); + userNode.setProperty(LdapAttr.distinguishedName.get(), userDn.toString()); + userNode.setProperty(LdapAttr.uid.get(), uid); + } else { + userNode = usersBase.getNode(uid); + } + + if (!userNode.hasNode(USER_SESSIONS_NODE_NAME)) { + // Migrate existing user node + Node sessionsNode = userNode.addNode(USER_SESSIONS_NODE_NAME, NodeType.NT_UNSTRUCTURED); + oldSessions: for (NodeIterator nit = userNode.getNodes(); nit.hasNext();) { + Node child = nit.nextNode(); + if (USER_SESSIONS_NODE_NAME.equals(child.getName()) || child.getName().startsWith("rep:") + || child.getName().startsWith("jcr:")) + continue oldSessions; + Node target = sessionsNode.addNode(child.getName()); + JcrUtils.copy(child, target); + } + + Node userStateNode = userNode.addNode(USER_STATE_NODE_NAME, NodeType.NT_UNSTRUCTURED); + Node userDevicesNode = userNode.addNode(USER_DEVICES_NODE_NAME, NodeType.NT_UNSTRUCTURED); + + adminSession.save(); +// JackrabbitSecurityUtils.denyPrivilege(adminSession, userNode.getPath(), SuiteRole.coworker.dn(), +// Privilege.JCR_READ); + JcrUtils.addPrivilege(adminSession, userNode.getPath(), new X500Principal(userDn.toString()).getName(), + Privilege.JCR_READ); + JcrUtils.addPrivilege(adminSession, userNode.getPath(), CmsConstants.ROLE_USER_ADMIN, + Privilege.JCR_ALL); + + JcrUtils.addPrivilege(adminSession, userStateNode.getPath(), userDn, Privilege.JCR_ALL); + JcrUtils.addPrivilege(adminSession, userDevicesNode.getPath(), userDn, Privilege.JCR_ALL); + } + return userNode; + } catch (RepositoryException e) { + throw new JcrException("Cannot create user node for " + userDn, e); + } + } + + /** @deprecated Use {@link AppUserState} instead. */ + @Deprecated + public static Node getCmsSessionNode(Session session, CmsSession cmsSession) { + try { + return session.getNode(SuiteUtils.getUserNodePath(cmsSession.getUserDn()) + '/' + USER_SESSIONS_NODE_NAME + '/' + + cmsSession.getUuid().toString()); + } catch (RepositoryException e) { + throw new JcrException("Cannot get session dir for " + cmsSession, e); + } + } + + /** @deprecated Use {@link AppUserState} instead. */ + @Deprecated + public static Node getOrCreateCmsSessionNode(Session adminSession, CmsSession cmsSession) { + try { + String userDn = cmsSession.getUserDn(); + Node userNode = getOrCreateUserNode(adminSession, userDn); + Node sessionsNode = userNode.getNode(USER_SESSIONS_NODE_NAME); + String cmsSessionUuid = cmsSession.getUuid().toString(); + Node cmsSessionNode; + if (!sessionsNode.hasNode(cmsSessionUuid)) { + cmsSessionNode = sessionsNode.addNode(cmsSessionUuid, NodeType.NT_UNSTRUCTURED); + cmsSessionNode.addMixin(NodeType.MIX_CREATED); + adminSession.save(); + JcrUtils.addPrivilege(adminSession, cmsSessionNode.getPath(), cmsSession.getUserRole(), + Privilege.JCR_ALL); + } else { + cmsSessionNode = sessionsNode.getNode(cmsSessionUuid); + } + return cmsSessionNode; + } catch (RepositoryException e) { + throw new JcrException("Cannot create session dir for " + cmsSession, e); + } + } + + /** singleton */ + private SuiteJcrUtils() { + } +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/XPathUtils.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/XPathUtils.java new file mode 100644 index 0000000..2c3babe --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/XPathUtils.java @@ -0,0 +1,175 @@ +package org.argeo.app.jcr; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.QueryManager; + +import org.apache.jackrabbit.util.ISO9075; +import org.argeo.api.cms.CmsLog; + +/** Ease XPath generation for JCR requests */ +public class XPathUtils { + private final static CmsLog log = CmsLog.getLog(XPathUtils.class); + + private final static String QUERY_XPATH = "xpath"; + + public static String descendantFrom(String parentPath) { + if (notEmpty(parentPath)) { + if ("/".equals(parentPath)) + parentPath = ""; + // Hardcoded dependency to Jackrabbit. Remove + String result = "/jcr:root" + ISO9075.encodePath(parentPath); + if (log.isTraceEnabled()) { + String result2 = "/jcr:root" + parentPath; + if (!result2.equals(result)) + log.warn("Encoded Path " + result2 + " --> " + result); + } + return result; + } else + return ""; + } + + public static String localAnd(String... conditions) { + StringBuilder builder = new StringBuilder(); + for (String condition : conditions) { + if (notEmpty(condition)) { + builder.append(" ").append(condition).append(" and "); + } + } + if (builder.length() > 3) + return builder.substring(0, builder.length() - 4); + else + return ""; + } + + public static String xPathNot(String condition) { + if (notEmpty(condition)) + return "not(" + condition + ")"; + else + return ""; + } + + public static String getFreeTextConstraint(String filter) throws RepositoryException { + StringBuilder builder = new StringBuilder(); + if (notEmpty(filter)) { + String[] strs = filter.trim().split(" "); + for (String token : strs) { + builder.append("jcr:contains(.,'*" + encodeXPathStringValue(token) + "*') and "); + } + return builder.substring(0, builder.length() - 4); + } + return ""; + } + + public static String getPropertyContains(String propertyName, String filter) throws RepositoryException { + if (notEmpty(filter)) + return "jcr:contains(@" + propertyName + ",'*" + encodeXPathStringValue(filter) + "*')"; + return ""; + } + + private final static DateFormat jcrRefFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'+02:00'"); + + /** + * @param propertyName + * @param calendar the reference date + * @param lowerOrGreater "<", ">" TODO validate ">=" + * @throws RepositoryException + */ + public static String getPropertyDateComparaison(String propertyName, Calendar cal, String lowerOrGreater) + throws RepositoryException { + if (cal != null) { + String jcrDateStr = jcrRefFormatter.format(cal.getTime()); + + // jcrDateStr = "2015-08-03T05:00:03:000Z"; + String result = "@" + propertyName + " " + lowerOrGreater + " xs:dateTime('" + jcrDateStr + "')"; + return result; + } + return ""; + } + + public static String getPropertyEquals(String propertyName, String value) { + if (notEmpty(value)) + return "@" + propertyName + "='" + encodeXPathStringValue(value) + "'"; + return ""; + } + + public static String encodeXPathStringValue(String propertyValue) { + // TODO implement safer mechanism to escape invalid characters + // Also check why we have used this regex in ResourceSerrviceImpl l 474 + // String cleanedKey = key.replaceAll("(?:')", "''"); + String result = propertyValue.replaceAll("'", "''"); + return result; + } + + public static void andAppend(StringBuilder builder, String condition) { + if (notEmpty(condition)) { + builder.append(condition); + builder.append(" and "); + } + } + + public static void appendOrderByProperties(StringBuilder builder, boolean ascending, String... propertyNames) { + if (propertyNames.length > 0) { + builder.append(" order by "); + for (String propName : propertyNames) + builder.append("@").append(propName).append(", "); + builder = builder.delete(builder.length() - 2, builder.length()); + if (ascending) + builder.append(" ascending "); + else + builder.append(" descending "); + } + } + + public static void appendAndPropStringCondition(StringBuilder builder, String propertyName, String filter) + throws RepositoryException { + if (notEmpty(filter)) { + andAppend(builder, getPropertyContains(propertyName, filter)); + } + } + + public static void appendAndNotPropStringCondition(StringBuilder builder, String propertyName, String filter) + throws RepositoryException { + if (notEmpty(filter)) { + String cond = getPropertyContains(propertyName, filter); + builder.append(xPathNot(cond)); + builder.append(" and "); + } + } + + public static Query createQuery(Session session, String queryString) throws RepositoryException { + QueryManager queryManager = session.getWorkspace().getQueryManager(); + // Localise JCR properties for XPATH + queryString = localiseJcrItemNames(queryString); + return queryManager.createQuery(queryString, QUERY_XPATH); + } + + private final static String NS_JCR = "\\{http://www.jcp.org/jcr/1.0\\}"; + private final static String NS_NT = "\\{http://www.jcp.org/jcr/nt/1.0\\}"; + private final static String NS_MIX = "\\{http://www.jcp.org/jcr/mix/1.0\\}"; + + /** + * Replace the generic namespace with the local "jcr:", "nt:", "mix:" values. It + * is a workaround that must be later cleaned + */ + public static String localiseJcrItemNames(String name) { + name = name.replaceAll(NS_JCR, "jcr:"); + name = name.replaceAll(NS_NT, "nt:"); + name = name.replaceAll(NS_MIX, "mix:"); + return name; + } + + private static boolean notEmpty(String stringToTest) { + return !(stringToTest == null || "".equals(stringToTest.trim())); + } + + /** Singleton. */ + private XPathUtils() { + + } +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/Dbk4Converter.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/Dbk4Converter.java new file mode 100644 index 0000000..60f2928 --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/Dbk4Converter.java @@ -0,0 +1,103 @@ +package org.argeo.app.jcr.docbook; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.argeo.jcr.JcrException; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import net.sf.saxon.BasicTransformerFactory; +import net.sf.saxon.TransformerFactoryImpl; + +/** Convert from DocBook v4 to DocBook v5, using the official XSL. */ +public class Dbk4Converter { + private final Templates templates; + + public Dbk4Converter() { + try (InputStream in = getClass().getResourceAsStream("db4-upgrade.xsl")) { + Source xsl = new StreamSource(in); + TransformerFactory transformerFactory = new BasicTransformerFactory(); +// TransformerFactory transformerFactory = new TransformerFactoryImpl(); + templates = transformerFactory.newTemplates(xsl); + } catch (IOException | TransformerConfigurationException e) { + throw new RuntimeException("Cannot initialise DocBook v4 converter", e); + } + } + + public void importXml(Node baseNode, InputStream in) throws IOException { + try (ByteArrayOutputStream out = new ByteArrayOutputStream();) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setXIncludeAware(true); + factory.setNamespaceAware(true); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + Document doc = docBuilder.parse(new InputSource(in)); + Source xmlInput = new DOMSource(doc); + +// ContentHandler contentHandler = baseNode.getSession().getImportContentHandler(baseNode.getPath(), +// ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + + Transformer transformer = templates.newTransformer(); + Result xmlOutput = new StreamResult(out); + transformer.transform(xmlInput, xmlOutput); + try (InputStream dbk5in = new ByteArrayInputStream(out.toByteArray())) { + baseNode.getSession().importXML(baseNode.getPath(), dbk5in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot import XML to " + baseNode, e); + } catch (TransformerException | SAXException | ParserConfigurationException e) { + throw new RuntimeException("Cannot import DocBook v4 to " + baseNode, e); + } + + } + + public static void main(String[] args) { + try { + + Source xsl = new StreamSource(new File("/usr/share/xml/docbook5/stylesheet/upgrade/db4-upgrade.xsl")); + TransformerFactory transformerFactory = new TransformerFactoryImpl(); + Templates templates = transformerFactory.newTemplates(xsl); + + File inputDir = new File(args[0]); + File outputDir = new File(args[1]); + + for (File inputFile : inputDir.listFiles()) { + Result xmlOutput = new StreamResult(new File(outputDir, inputFile.getName())); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setXIncludeAware(true); + factory.setNamespaceAware(true); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + Document doc = docBuilder.parse(inputFile); + Source xmlInput = new DOMSource(doc); + Transformer transformer = templates.newTransformer(); + transformer.transform(xmlInput, xmlOutput); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/DbkJcrUtils.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/DbkJcrUtils.java new file mode 100644 index 0000000..4454905 --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/DbkJcrUtils.java @@ -0,0 +1,253 @@ +package org.argeo.app.jcr.docbook; + +import static org.argeo.app.docbook.DbkType.para; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.app.api.EntityType; +import org.argeo.app.docbook.DbkAttr; +import org.argeo.app.docbook.DbkType; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.JcrxApi; + +/** JCR utilities around DocBook. */ +public class DbkJcrUtils { + private final static CmsLog log = CmsLog.getLog(DbkJcrUtils.class); + + /** Get or add a DocBook element. */ + public static Node getOrAddDbk(Node parent, DbkType child) { + try { + if (!parent.hasNode(child.get())) { + return addDbk(parent, child); + } else { + return parent.getNode(child.get()); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot get or add element " + child.get() + " to " + parent, e); + } + } + + /** Add a DocBook element to this node. */ + public static Node addDbk(Node parent, DbkType child) { + try { + Node node = parent.addNode(child.get(), child.get()); + return node; + } catch (RepositoryException e) { + throw new JcrException("Cannot add element " + child.get() + " to " + parent, e); + } + } + + /** Whether this DocBook element is of this type. */ + public static boolean isDbk(Node node, DbkType type) { + return Jcr.getName(node).equals(type.get()); + } + + /** Whether this node is a DocBook type. */ + public static boolean isDbk(Node node) { + String name = Jcr.getName(node); + for (DbkType type : DbkType.values()) { + if (name.equals(type.get())) + return true; + } + return false; + } + + public static String getTitle(Node node) { + return JcrxApi.getXmlValue(node, DbkType.title.get()); + } + + public static void setTitle(Node node, String txt) { + Node titleNode = getOrAddDbk(node, DbkType.title); + JcrxApi.setXmlValue(titleNode, txt); + } + + public static Node getMetadata(Node infoContainer) { + try { + if (!infoContainer.hasNode(DbkType.info.get())) + return null; + Node info = infoContainer.getNode(DbkType.info.get()); + if (!info.hasNode(EntityType.local.get())) + return null; + return info.getNode(EntityType.local.get()); + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve metadata from " + infoContainer, e); + } + } + + public static Node getChildByRole(Node parent, String role) { + try { + NodeIterator baseSections = parent.getNodes(); + while (baseSections.hasNext()) { + Node n = baseSections.nextNode(); + String r = Jcr.get(n, DbkAttr.role.name()); + if (r != null && r.equals(role)) + return n; + } + return null; + } catch (RepositoryException e) { + throw new JcrException("Cannot get child from " + parent + " with role " + role, e); + } + } + + public static Node addParagraph(Node node, String txt) { + Node p = addDbk(node, para); + JcrxApi.setXmlValue(p, txt); + return p; + } + + /** + * Removes a paragraph if it empty. The sesison is not saved. + * + * @return true if the paragraph was empty and it was removed + */ + public static boolean removeIfEmptyParagraph(Node node) { + try { + if (isDbk(node, DbkType.para)) { + NodeIterator nit = node.getNodes(); + if (!nit.hasNext()) { + node.remove(); + return true; + } + Node first = nit.nextNode(); + if (nit.hasNext()) + return false; + if (first.getName().equals(Jcr.JCR_XMLTEXT)) { + String str = Jcr.get(first, Jcr.JCR_XMLCHARACTERS); + if (str != null && str.trim().equals("")) { + node.remove(); + return true; + } + } else { + return false; + } + } + return false; + } catch (RepositoryException e) { + throw new JcrException("Cannot remove possibly empty paragraph", e); + } + } + + public static Node insertImageAfter(Node sibling) { + try { + + Node parent = sibling.getParent(); + Node mediaNode = addDbk(parent, DbkType.mediaobject); + // TODO optimise? + parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", + sibling.getName() + "[" + sibling.getIndex() + "]"); + parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", + mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); + + Node imageNode = addDbk(mediaNode, DbkType.imageobject); + Node imageDataNode = addDbk(imageNode, DbkType.imagedata); +// Node infoNode = imageNode.addNode(DocBookTypes.INFO, DocBookTypes.INFO); +// Node fileNode = JcrUtils.copyBytesAsFile(mediaFolder, EntityType.box.get(), new byte[0]); +// fileNode.addMixin(EntityType.box.get()); +// fileNode.setProperty(EntityNames.SVG_WIDTH, 0); +// fileNode.setProperty(EntityNames.SVG_LENGTH, 0); +// fileNode.addMixin(NodeType.MIX_MIMETYPE); +// +// // we assume this is a folder next to the main DocBook document +// // TODO make it more robust and generic +// String fileRef = mediaNode.getName(); +// imageDataNode.setProperty(DocBookNames.DBK_FILEREF, fileRef); + return mediaNode; + } catch (RepositoryException e) { + throw new JcrException("Cannot insert empty image after " + sibling, e); + } + } + + public static Node insertVideoAfter(Node sibling) { + try { + + Node parent = sibling.getParent(); + Node mediaNode = addDbk(parent, DbkType.mediaobject); + // TODO optimise? + parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", + sibling.getName() + "[" + sibling.getIndex() + "]"); + parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", + mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); + + Node videoNode = addDbk(mediaNode, DbkType.videoobject); + Node videoDataNode = addDbk(videoNode, DbkType.videodata); + return mediaNode; + } catch (RepositoryException e) { + throw new JcrException("Cannot insert empty image after " + sibling, e); + } + } + + public static String getMediaFileref(Node node) { + try { + Node mediadata; + if (node.hasNode(DbkType.imageobject.get())) { + mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get()); + } else if (node.hasNode(DbkType.videoobject.get())) { + mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get()); + } else { + throw new IllegalArgumentException("Fileref not found in " + node); + } + + if (mediadata.hasProperty(DbkAttr.fileref.name())) { + return mediadata.getProperty(DbkAttr.fileref.name()).getString(); + } else { + return null; + } + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve file ref from " + node, e); + } + } + + public static void exportXml(Node node, OutputStream out) throws IOException { + try { + node.getSession().exportDocumentView(node.getPath(), out, false, false); + } catch (RepositoryException e) { + throw new JcrException("Cannot export " + node + " to XML", e); + } + } + + public static void exportToFs(Node baseNode, DbkType type, Path directory) { + String fileName = Jcr.getName(baseNode) + ".dbk.xml"; + Path filePath = directory.resolve(fileName); + Node docBookNode = Jcr.getNode(baseNode, type.get()); + if (docBookNode == null) + throw new IllegalArgumentException("No " + type.get() + " under " + baseNode); + try { + Files.createDirectories(directory); + try (OutputStream out = Files.newOutputStream(filePath)) { + exportXml(docBookNode, out); + } + JcrUtils.copyFilesToFs(baseNode, directory, true); + if (log.isDebugEnabled()) + log.debug("DocBook " + baseNode + " exported to " + filePath.toAbsolutePath()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void importXml(Node baseNode, InputStream in) throws IOException { + try { + baseNode.getSession().importXML(baseNode.getPath(), in, + ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } catch (RepositoryException e) { + throw new JcrException("Cannot import XML to " + baseNode, e); + } + + } + + /** Singleton. */ + private DbkJcrUtils() { + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/db4-upgrade.xsl b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/db4-upgrade.xsl new file mode 100644 index 0000000..00096be --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/docbook/db4-upgrade.xsl @@ -0,0 +1,1398 @@ + + + + + + + + + + + + + + + + + UNKNOWN + + + + + + + + + + + + Converted by db4-upgrade version + + + + + + + + + + + + + + + + + + Check + + title. + + + + + + + + + + + + + + + Check + + : no title. + + + + + + + + + + + Check + + titleabbrev. + + + + + + + + + + + + + + + + + + + Check + + subtitle. + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check + + title. + + + + + + + + + + + + + + + + + + + + + + Check + + titleabbrev. + + + + + + + + + + + + + + + + + + + Check + + subtitle. + + + + + + + + + + + + + + + + + + + + + + + + + + Discarding title from refentryinfo! + + + + + + + + Discarding titleabbrev from refentryinfo! + + + + + + + + Discarding subtitle from refentryinfo! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dropping class attribute from productname + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Convert equation without title to informal equation. + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check conversion of srccredit + (othercredit="srccredit"). + + + + + ??? + + + + + + + + + + + + + + comment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converting invpartnumber to biblioid otherclass="invpartnumber". + + + + + + + + + + + + + + Converting contractsponsor to othercredit="contractsponsor". + + + + + + + + + + + + + + + + + + + + + Converting contractnum to othercredit="contractnum". + + + + + ??? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check conversion of collabname + (orgname role="collabname"). + + + + + + + + + + + + Discarding modespec ( + + ). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check conversion of contrib + (othercontrib="contrib"). + + + + ??? + + + + + + + + + + + + + + + + + + + + Converting ulink to link. + + + + + + + + + + + + + + Converting ulink to uri. + + + + + + + + + + + + + + + + + + Discarding linkmode on olink. + + + + + + + + + Converting olink targetdocent to targetdoc. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -01- + + + + + -02- + + + + + -03- + + + + + -04- + + + + + -05- + + + + + -06- + + + + + -07- + + + + + -08- + + + + + -09- + + + + + -10- + + + + + -11- + + + + + -12- + + + + + + + + + + + + -01- + + + + + -02- + + + + + -03- + + + + + -04- + + + + + -05- + + + + + -06- + + + + + -07- + + + + + -08- + + + + + -09- + + + + + -10- + + + + + -11- + + + + + -12- + + + + + + + + + + + + + + + + + + Converted + + into + + for + + + + + + + + + + + + + + Unparseable date: + + in + + (Using default: + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Check abstract; moved into info correctly? + + + + + + + + + + + significance + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + beginpage pagenum= + + + Replacing beginpage with comment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Discarding moreinfo on + + + + + + + + + + + + + + + + + + + + + Discarding float on + + + + + + + Adding floatstyle='normal' on + + + + + normal + + + + + + + Discarding float on + + + + + + + + Discarding float on + + + + + + + Adding floatstyle=' + + ' on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converting refmiscinfo@role=type to + @class=other,otherclass=type + + + other + type + + + + + + + + + + + + + + + + + 5.0 + + + + + + + + + 5.0 + + + + + + + + + + + + + + + + + + + ( + + ) + + + +
diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java new file mode 100644 index 0000000..881523f --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java @@ -0,0 +1,196 @@ +package org.argeo.app.jcr.odk; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsLog; +import org.argeo.app.api.EntityMimeType; +import org.argeo.app.api.EntityType; +import org.argeo.app.odk.OdkNames; +import org.argeo.app.odk.OrxListName; +import org.argeo.app.odk.OrxManifestName; +import org.argeo.cms.util.DigestUtils; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.JcrxApi; + +/** Utilities around ODK. */ +public class OdkJcrUtils { + private final static CmsLog log = CmsLog.getLog(OdkJcrUtils.class); + + public static Node loadOdkForm(Node formBase, String name, InputStream in, InputStream... additionalNodes) + throws RepositoryException, IOException { + if (!formBase.isNodeType(EntityType.formSet.get())) + throw new IllegalArgumentException( + "Parent path " + formBase + " must be of type " + EntityType.formSet.get()); + Node form = JcrUtils.getOrAdd(formBase, name, OrxListName.xform.get(), NodeType.MIX_VERSIONABLE); + + String previousCsum = JcrxApi.getChecksum(form, JcrxApi.MD5); + String previousFormId = Jcr.get(form, OrxListName.formID.get()); + String previousFormVersion = Jcr.get(form, OrxListName.version.get()); + + Session s = formBase.getSession(); + s.importXML(form.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + + for (InputStream additionalIn : additionalNodes) { + s.importXML(form.getPath(), additionalIn, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } + s.save(); + + // manage instances + // NodeIterator instances = + // form.getNodes("h:html/h:head/xforms:model/xforms:instance"); + NodeIterator instances = form.getNode("h:html/h:head/xforms:model").getNodes("xforms:instance"); + Node primaryInstance = null; + while (instances.hasNext()) { + Node instance = instances.nextNode(); + if (primaryInstance == null) { + primaryInstance = instance; + } else {// secondary instances + String instanceId = instance.getProperty("id").getString(); + URI instanceUri = null; + if (instance.hasProperty("src")) + try { + instanceUri = new URI(instance.getProperty("src").getString()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Instance " + instanceId + " has a badly formatted URI", e); + } + if (instanceUri != null) { + if ("jr".equals(instanceUri.getScheme())) { + String uuid; + String mimeType; + String encoding = StandardCharsets.UTF_8.name(); + String type = instanceUri.getHost(); + String path = instanceUri.getPath(); + if ("file".equals(type)) { + if (!path.endsWith(".xml")) + throw new IllegalArgumentException("File uri " + instanceUri + " must end with .xml"); + // Work around bug in ODK Collect not supporting paths + // path = path.substring(0, path.length() - ".xml".length()); + // Node target = file.getSession().getNode(path); + uuid = path.substring(1, path.length() - ".xml".length()); + mimeType = EntityMimeType.XML.getMimeType(); + } else if ("file-csv".equals(type)) { + if (!path.endsWith(".csv")) + throw new IllegalArgumentException("File uri " + instanceUri + " must end with .csv"); + // Work around bug in ODK Collect not supporting paths + // path = path.substring(0, path.length() - ".csv".length()); + // Node target = file.getSession().getNode(path); + uuid = path.substring(1, path.length() - ".csv".length()); + mimeType = EntityMimeType.CSV.getMimeType(); + } else { + throw new IllegalArgumentException("Unsupported instance type " + type); + } + Node manifest = JcrUtils.getOrAdd(form, OrxManifestName.manifest.name(), + OrxManifestName.manifest.get()); + Node file = JcrUtils.getOrAdd(manifest, instanceId); + file.addMixin(NodeType.MIX_MIMETYPE); + file.setProperty(Property.JCR_MIMETYPE, mimeType); + file.setProperty(Property.JCR_ENCODING, encoding); + Node target = file.getSession().getNodeByIdentifier(uuid); + +// if (target.isNodeType(NodeType.NT_QUERY)) { +// Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target); +// query.setLimit(10); +// QueryResult queryResult = query.execute(); +// RowIterator rit = queryResult.getRows(); +// while (rit.hasNext()) { +// Row row = rit.nextRow(); +// for (Value value : row.getValues()) { +// System.out.print(value.getString()); +// System.out.print(','); +// } +// System.out.print('\n'); +// } +// +// } + + if (target.isNodeType(NodeType.MIX_REFERENCEABLE)) { + file.setProperty(Property.JCR_ID, target); + if (file.hasProperty(Property.JCR_PATH)) + file.getProperty(Property.JCR_PATH).remove(); + } else { + file.setProperty(Property.JCR_PATH, target.getPath()); + if (file.hasProperty(Property.JCR_ID)) + file.getProperty(Property.JCR_ID).remove(); + } + } + } + } + } + + if (primaryInstance == null) + throw new IllegalArgumentException("No primary instance found in " + form); + if (!primaryInstance.hasNodes()) + throw new IllegalArgumentException("No data found in primary instance of " + form); + NodeIterator primaryInstanceChildren = primaryInstance.getNodes(); + Node data = primaryInstanceChildren.nextNode(); + if (primaryInstanceChildren.hasNext()) + throw new IllegalArgumentException("More than one data found in primary instance of " + form); + String formId = data.getProperty("id").getString(); + if (previousFormId != null && !formId.equals(previousFormId)) + log.warn("Form id of " + form + " changed from " + previousFormId + " to " + formId); + form.setProperty(OrxListName.formID.get(), formId); + String formVersion = data.getProperty("version").getString(); + + if (previousCsum == null)// save before checksuming + s.save(); + String newCsum; + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + s.exportDocumentView(form.getPath() + "/" + OdkNames.H_HTML, out, true, false); + newCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray()); + } + if (previousCsum == null) { + JcrxApi.addChecksum(form, newCsum); + JcrUtils.updateLastModified(form); + form.setProperty(OrxListName.version.get(), formVersion); + s.save(); + s.getWorkspace().getVersionManager().checkpoint(form.getPath()); + if (log.isDebugEnabled()) + log.debug("New form " + form); + } else { + if (newCsum.equals(previousCsum)) { + // discard + s.refresh(false); + if (log.isDebugEnabled()) + log.debug("Unmodified form " + form); + return form; + } else { + if (formVersion.equals(previousFormVersion)) { + s.refresh(false); + throw new IllegalArgumentException("Form " + form + " has been changed but version " + formVersion + + " has not been changed, discarding changes..."); + } + form.setProperty(OrxListName.version.get(), formVersion); + JcrxApi.addChecksum(form, newCsum); + JcrUtils.updateLastModified(form); + s.save(); + s.getWorkspace().getVersionManager().checkpoint(form.getPath()); + if (log.isDebugEnabled()) { + log.debug("Updated form " + form); + log.debug("Previous csum " + previousCsum); + log.debug("New csum " + newCsum); + } + } + } + return form; + } + + /** Singleton. */ + private OdkJcrUtils() { + + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTerm.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTerm.java new file mode 100644 index 0000000..db4e4a6 --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTerm.java @@ -0,0 +1,62 @@ +package org.argeo.app.jcr.terms; + +import java.util.ArrayList; +import java.util.List; + +import org.argeo.app.api.Term; + +/** + * A single term. Helper to optimise {@link SuiteTermsManager} implementation. + */ +class SuiteTerm implements Term { + private final String name; + private final String relativePath; + private final SuiteTypology typology; + private final String id; + + private final SuiteTerm parentTerm; + private final List subTerms = new ArrayList<>(); + + SuiteTerm(SuiteTypology typology, String relativePath, SuiteTerm parentTerm) { + this.typology = typology; + this.parentTerm = parentTerm; + this.relativePath = relativePath; + int index = relativePath.lastIndexOf('/'); + if (index > 0) { + this.name = relativePath.substring(index + 1); + } else { + this.name = relativePath; + } + id = typology.getName() + '/' + relativePath; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + public String getRelativePath() { + return relativePath; + } + + @Override + public SuiteTypology getTypology() { + return typology; + } + + @Override + public List getSubTerms() { + return subTerms; + } + + @Override + public SuiteTerm getParentTerm() { + return parentTerm; + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTermsManager.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTermsManager.java new file mode 100644 index 0000000..6327a55 --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTermsManager.java @@ -0,0 +1,103 @@ +package org.argeo.app.jcr.terms; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.app.api.EntityNames; +import org.argeo.app.api.EntityType; +import org.argeo.app.api.Term; +import org.argeo.app.api.TermsManager; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; + +/** Argeo Suite implementation of terms manager. */ +public class SuiteTermsManager implements TermsManager { + private final Map terms = new HashMap<>(); + private final Map typologies = new HashMap<>(); + + // JCR + private Repository repository; + private Session adminSession; + + public void init() { + adminSession = CmsJcrUtils.openDataAdminSession(repository, CmsConstants.SYS_WORKSPACE); + } + + @Override + public List listAllTerms(String typology) { + List res = new ArrayList<>(); + SuiteTypology t = getTypology(typology); + for (SuiteTerm term : t.getAllTerms()) { + res.add(term); + } + return res; + } + + @Override + public SuiteTerm getTerm(String termId) { + return terms.get(termId); + } + + @Override + public SuiteTypology getTypology(String typology) { + SuiteTypology t = typologies.get(typology); + if (t == null) { + Node termsNode = Jcr.getNode(adminSession, "SELECT * FROM [{0}] WHERE NAME()=\"{1}\"", + EntityType.terms.get(), typology); + if (termsNode == null) + throw new IllegalArgumentException("Typology " + typology + " not found."); + t = loadTypology(termsNode); + } + return t; + } + + SuiteTypology loadTypology(Node termsNode) { + try { + SuiteTypology typology = new SuiteTypology(termsNode); + for (Node termNode : Jcr.iterate(termsNode.getNodes())) { + if (termNode.isNodeType(EntityType.term.get())) { + SuiteTerm term = loadTerm(typology, termNode, null); + if (!term.getSubTerms().isEmpty()) + typology.markNotFlat(); + typology.getSubTerms().add(term); + } + } + typologies.put(typology.getName(), typology); + return typology; + } catch (RepositoryException e) { + throw new JcrException("Cannot load typology from " + termsNode, e); + } + } + + SuiteTerm loadTerm(SuiteTypology typology, Node termNode, SuiteTerm parentTerm) throws RepositoryException { + String name = termNode.getProperty(EntityNames.NAME).getString(); + String relativePath = parentTerm == null ? name : parentTerm.getRelativePath() + '/' + name; + SuiteTerm term = new SuiteTerm(typology, relativePath, parentTerm); + terms.put(term.getId(), term); + for (Node subTermNode : Jcr.iterate(termNode.getNodes())) { + if (termNode.isNodeType(EntityType.term.get())) { + SuiteTerm subTerm = loadTerm(typology, subTermNode, term); + term.getSubTerms().add(subTerm); + } + } + return term; + } + + public void destroy() { + Jcr.logout(adminSession); + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTypology.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTypology.java new file mode 100644 index 0000000..0040abb --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/terms/SuiteTypology.java @@ -0,0 +1,95 @@ +package org.argeo.app.jcr.terms; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; + +import org.argeo.app.api.Term; +import org.argeo.app.api.Typology; +import org.argeo.jcr.Jcr; + +/** A typology. Helper to optimise {@link SuiteTermsManager} implementation. */ +class SuiteTypology implements Typology { + private final String name; + private final Node node; + private boolean isFlat = true; + + private final List subTerms = new ArrayList<>(); + + public SuiteTypology(Node node) { + this.node = node; + this.name = Jcr.getName(this.node); + } + + @Override + public String getId() { + return name; + } + + public String getName() { + return name; + } + + public Node getNode() { + return node; + } + + void markNotFlat() { + if (isFlat) + isFlat = false; + } + + @Override + public boolean isFlat() { + return isFlat; + } + + @Override + public List getSubTerms() { + return subTerms; + } + + public List getAllTerms() { + if (isFlat) + return subTerms; + else { + List terms = new ArrayList<>(); + for (SuiteTerm subTerm : subTerms) { + terms.add(subTerm); + collectSubTerms(terms, subTerm); + } + return terms; + } + } + + public Term findTermByName(String name) { + List collected = new ArrayList<>(); + for (SuiteTerm subTerm : subTerms) { + collectTermsByName(subTerm, name, collected); + } + if (collected.isEmpty()) + return null; + if (collected.size() == 1) + return collected.get(0); + throw new IllegalArgumentException( + "There are " + collected.size() + " terms with name " + name + " in typology " + getId()); + } + + private void collectTermsByName(SuiteTerm term, String name, List collected) { + if (term.getName().equals(name)) { + collected.add(term); + } + for (SuiteTerm subTerm : term.getSubTerms()) { + collectTermsByName(subTerm, name, collected); + } + } + + private void collectSubTerms(List terms, SuiteTerm term) { + for (SuiteTerm subTerm : term.getSubTerms()) { + terms.add(subTerm); + collectSubTerms(terms, subTerm); + } + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/AppUserStateImpl.java b/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/AppUserStateImpl.java new file mode 100644 index 0000000..faf217a --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/AppUserStateImpl.java @@ -0,0 +1,34 @@ +package org.argeo.internal.app.jcr; + +import javax.jcr.Node; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentSession; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsSession; +import org.argeo.app.api.AppUserState; +import org.argeo.app.jcr.SuiteJcrUtils; +import org.argeo.cms.acr.ContentUtils; +import org.argeo.cms.jcr.acr.JcrContentProvider; +import org.argeo.jcr.Jcr; + +public class AppUserStateImpl implements AppUserState { + private JcrContentProvider jcrContentProvider; + + @SuppressWarnings("deprecation") + @Override + public Content getOrCreateSessionDir(ContentSession contentSession, CmsSession session) { + Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> { + Node node = SuiteJcrUtils.getOrCreateCmsSessionNode(adminSession, session); + return node; + }); + Content userDir = contentSession + .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + Jcr.getPath(userDirNode)); + return userDir; + } + + public void setJcrContentProvider(JcrContentProvider jcrContentProvider) { + this.jcrContentProvider = jcrContentProvider; + } + +} diff --git a/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/SuiteMaintenanceService.java b/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/SuiteMaintenanceService.java new file mode 100644 index 0000000..ceeb4f5 --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/internal/app/jcr/SuiteMaintenanceService.java @@ -0,0 +1,39 @@ +package org.argeo.internal.app.jcr; + +import java.io.IOException; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.security.Privilege; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.app.api.EntityType; +import org.argeo.jcr.JcrUtils; +import org.argeo.maintenance.AbstractMaintenanceService; + +/** Initialises JCR in an Argeo Suite backend. */ +public class SuiteMaintenanceService extends AbstractMaintenanceService { + @Override + public boolean prepareJcrTree(Session adminSession) throws RepositoryException, IOException { + boolean modified = false; + Node rootNode = adminSession.getRootNode(); + if (!rootNode.hasNode(EntityType.user.name())) { + rootNode.addNode(EntityType.user.name(), NodeType.NT_UNSTRUCTURED); + modified = true; + } + if (modified) + adminSession.save(); + return modified; + } + + @Override + 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); + } + +} diff --git a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java index 9392587..3740805 100644 --- a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java +++ b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkSubmissionServlet.java @@ -24,13 +24,14 @@ import javax.servlet.http.Part; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsSession; -import org.argeo.app.core.SuiteUtils; import org.argeo.app.image.ImageProcessor; +import org.argeo.app.jcr.SuiteJcrUtils; import org.argeo.app.odk.OrxType; import org.argeo.app.xforms.FormSubmissionListener; import org.argeo.cms.auth.RemoteAuthRequest; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.jcr.acr.JcrContent; import org.argeo.cms.servlet.ServletHttpRequest; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrUtils; @@ -54,9 +55,9 @@ public class OdkSubmissionServlet extends HttpServlet { resp.setContentType("text/xml; charset=utf-8"); resp.setHeader("X-OpenRosa-Version", "1.0"); resp.setDateHeader("Date", System.currentTimeMillis()); - + // should be set in HEAD? Let's rather use defaults. - //resp.setIntHeader("X-OpenRosa-Accept-Content-Length", 1024 * 1024); + // resp.setIntHeader("X-OpenRosa-Accept-Content-Length", 1024 * 1024); RemoteAuthRequest request = new ServletHttpRequest(req); Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), request); @@ -67,13 +68,13 @@ public class OdkSubmissionServlet extends HttpServlet { try { // TODO centralise at a deeper level adminSession = CmsJcrUtils.openDataAdminSession(repository, null); - SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); + SuiteJcrUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); } finally { Jcr.logout(adminSession); } try { - Node cmsSessionNode = SuiteUtils.getCmsSessionNode(session, cmsSession); + Node cmsSessionNode = SuiteJcrUtils.getCmsSessionNode(session, cmsSession); Node submission = cmsSessionNode.addNode(submissionNameFormatter.format(Instant.now()), OrxType.submission.get()); for (Part part : req.getParts()) { @@ -117,7 +118,7 @@ public class OdkSubmissionServlet extends HttpServlet { session.save(); try { for (FormSubmissionListener submissionListener : submissionListeners) { - submissionListener.formSubmissionReceived(submission); + submissionListener.formSubmissionReceived(JcrContent.nodeToContent(submission)); } } catch (Exception e) { log.error("Cannot save submision, cancelling...", e); diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java index 152df6e..246a0c2 100644 --- a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/DbkServlet.java @@ -45,7 +45,7 @@ import org.apache.fop.apps.FopFactory; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.ux.CmsTheme; import org.argeo.app.docbook.DbkType; -import org.argeo.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.auth.RemoteAuthUtils; import org.argeo.cms.servlet.ServletHttpRequest; import org.argeo.jcr.Jcr; @@ -116,7 +116,7 @@ public class DbkServlet extends HttpServlet { if (node.hasNode(DbkType.article.get())) { Node dbkNode = node.getNode(DbkType.article.get()); - if (DbkUtils.isDbk(dbkNode)) { + if (DbkJcrUtils.isDbk(dbkNode)) { CmsTheme cmsTheme = null; String themeId = req.getParameter("themeId"); if (themeId != null) { diff --git a/org.argeo.product.knowledge/.classpath b/org.argeo.product.knowledge/.classpath new file mode 100644 index 0000000..81fe078 --- /dev/null +++ b/org.argeo.product.knowledge/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.product.knowledge/.project b/org.argeo.product.knowledge/.project new file mode 100644 index 0000000..54f9ad4 --- /dev/null +++ b/org.argeo.product.knowledge/.project @@ -0,0 +1,33 @@ + + + org.argeo.product.knowledge + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.product.knowledge/OSGI-INF/l10n/bundle.properties b/org.argeo.product.knowledge/OSGI-INF/l10n/bundle.properties new file mode 100644 index 0000000..a750a7a --- /dev/null +++ b/org.argeo.product.knowledge/OSGI-INF/l10n/bundle.properties @@ -0,0 +1 @@ +appTitle=Argeo Knowledge diff --git a/org.argeo.product.knowledge/OSGI-INF/swtArgeoApp.xml b/org.argeo.product.knowledge/OSGI-INF/swtArgeoApp.xml new file mode 100644 index 0000000..ad18dd9 --- /dev/null +++ b/org.argeo.product.knowledge/OSGI-INF/swtArgeoApp.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.argeo.product.knowledge/OSGI-INF/termsEntryArea.xml b/org.argeo.product.knowledge/OSGI-INF/termsEntryArea.xml new file mode 100644 index 0000000..e241b11 --- /dev/null +++ b/org.argeo.product.knowledge/OSGI-INF/termsEntryArea.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/org.argeo.product.knowledge/OSGI-INF/termsLayer.xml b/org.argeo.product.knowledge/OSGI-INF/termsLayer.xml new file mode 100644 index 0000000..ecfea87 --- /dev/null +++ b/org.argeo.product.knowledge/OSGI-INF/termsLayer.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/org.argeo.product.knowledge/bnd.bnd b/org.argeo.product.knowledge/bnd.bnd new file mode 100644 index 0000000..d942709 --- /dev/null +++ b/org.argeo.product.knowledge/bnd.bnd @@ -0,0 +1,9 @@ +Service-Component:\ +OSGI-INF/swtArgeoApp.xml,\ +OSGI-INF/termsEntryArea.xml,\ +OSGI-INF/termsLayer.xml,\ + +Import-Package:\ +org.argeo.app.swt.ux,\ +org.argeo.app.swt.terms,\ +* \ No newline at end of file diff --git a/org.argeo.product.knowledge/build.properties b/org.argeo.product.knowledge/build.properties new file mode 100644 index 0000000..fde2b82 --- /dev/null +++ b/org.argeo.product.knowledge/build.properties @@ -0,0 +1,5 @@ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/swtArgeoApp.xml +source.. = src/ +output.. = bin/ diff --git a/org.argeo.product.knowledge/config/swtArgeoApp.properties b/org.argeo.product.knowledge/config/swtArgeoApp.properties new file mode 100644 index 0000000..96e1d31 --- /dev/null +++ b/org.argeo.product.knowledge/config/swtArgeoApp.properties @@ -0,0 +1,5 @@ +service.pid=argeo.product.knowledge.swtArgeoApp + +event.topics=argeo/suite/* + +argeo.cms.app.contextName=argeo/knowledge \ No newline at end of file diff --git a/org.argeo.product.knowledge/config/termsEntryArea.properties b/org.argeo.product.knowledge/config/termsEntryArea.properties new file mode 100644 index 0000000..cd31517 --- /dev/null +++ b/org.argeo.product.knowledge/config/termsEntryArea.properties @@ -0,0 +1 @@ +service.pid=argeo.suite.ui.termsEntryArea diff --git a/org.argeo.product.knowledge/config/termsLayer.properties b/org.argeo.product.knowledge/config/termsLayer.properties new file mode 100644 index 0000000..2c0532e --- /dev/null +++ b/org.argeo.product.knowledge/config/termsLayer.properties @@ -0,0 +1,5 @@ +service.pid=argeo.suite.ui.termsLayer +title=Terms +icon=dashboard + +entity.type=entity:terms,entity:term \ No newline at end of file diff --git a/sdk/argeo-suite-server.properties b/sdk/argeo-suite-server.properties index 82576bf..6407f79 100644 --- a/sdk/argeo-suite-server.properties +++ b/sdk/argeo-suite-server.properties @@ -19,10 +19,12 @@ org.argeo.cms.jcr argeo.osgi.start.5=\ org.argeo.app.profile.acr.fs,\ org.argeo.app.core,\ +org.argeo.app.jcr,\ org.argeo.app.ui,\ org.argeo.app.theme.default,\ org.argeo.app.servlet.publish,\ -org.argeo.app.servlet.odk +org.argeo.app.servlet.odk,\ +org.argeo.product.knowledge,\ # Local diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/terms/TermsEntryArea.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/terms/TermsEntryArea.java new file mode 100644 index 0000000..2193a3c --- /dev/null +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/terms/TermsEntryArea.java @@ -0,0 +1,22 @@ +package org.argeo.app.swt.terms; + +import org.argeo.api.acr.Content; +import org.argeo.cms.swt.acr.SwtUiProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** Entry area for managing the typologies. */ +public class TermsEntryArea implements SwtUiProvider { + + @Override + public Control createUiPart(Composite parent, Content content) { + parent.setLayout(new GridLayout()); + Label lbl = new Label(parent, SWT.NONE); + lbl.setText("Typologies"); + return lbl; + } + +} diff --git a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties index 8a9cc95..afa3f49 100644 --- a/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties +++ b/swt/org.argeo.app.ui/OSGI-INF/l10n/bundle.properties @@ -1 +1,3 @@ +appTitle=Argeo Suite + people=People diff --git a/swt/org.argeo.app.ui/OSGI-INF/termsEntryArea.xml b/swt/org.argeo.app.ui/OSGI-INF/termsEntryArea.xml deleted file mode 100644 index 6387f1a..0000000 --- a/swt/org.argeo.app.ui/OSGI-INF/termsEntryArea.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/swt/org.argeo.app.ui/OSGI-INF/termsLayer.xml b/swt/org.argeo.app.ui/OSGI-INF/termsLayer.xml deleted file mode 100644 index ecfea87..0000000 --- a/swt/org.argeo.app.ui/OSGI-INF/termsLayer.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/swt/org.argeo.app.ui/bnd.bnd b/swt/org.argeo.app.ui/bnd.bnd index fb2e3f7..5bc2eea 100644 --- a/swt/org.argeo.app.ui/bnd.bnd +++ b/swt/org.argeo.app.ui/bnd.bnd @@ -7,8 +7,6 @@ OSGI-INF/leadPane.xml,\ OSGI-INF/loginScreen.xml,\ OSGI-INF/recentItems.xml,\ OSGI-INF/adminLeadPane.xml,\ -OSGI-INF/termsEntryArea.xml,\ -OSGI-INF/termsLayer.xml,\ OSGI-INF/dashboard.xml,\ OSGI-INF/dashboardLayer.xml,\ OSGI-INF/peopleEntryArea.xml,\ diff --git a/swt/org.argeo.app.ui/config/termsEntryArea.properties b/swt/org.argeo.app.ui/config/termsEntryArea.properties deleted file mode 100644 index cd31517..0000000 --- a/swt/org.argeo.app.ui/config/termsEntryArea.properties +++ /dev/null @@ -1 +0,0 @@ -service.pid=argeo.suite.ui.termsEntryArea diff --git a/swt/org.argeo.app.ui/config/termsLayer.properties b/swt/org.argeo.app.ui/config/termsLayer.properties deleted file mode 100644 index 2c0532e..0000000 --- a/swt/org.argeo.app.ui/config/termsLayer.properties +++ /dev/null @@ -1,5 +0,0 @@ -service.pid=argeo.suite.ui.termsLayer -title=Terms -icon=dashboard - -entity.type=entity:terms,entity:term \ No newline at end of file diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java index e16b7d6..3cc6210 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java @@ -16,7 +16,7 @@ import javax.jcr.query.Query; import javax.jcr.query.QueryResult; import org.argeo.app.api.EntityType; -import org.argeo.app.core.XPathUtils; +import org.argeo.app.jcr.XPathUtils; import org.argeo.app.ui.widgets.DelayedText; import org.argeo.app.ux.SuiteIcon; import org.argeo.app.ux.SuiteUxEvent; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/TermsEntryArea.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/TermsEntryArea.java deleted file mode 100644 index 97d8c1f..0000000 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/TermsEntryArea.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.argeo.app.ui; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -/** Entry area for managing th etypologies. */ -public class TermsEntryArea implements CmsUiProvider { - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - parent.setLayout(new GridLayout()); - Label lbl = new Label(parent, SWT.NONE); - lbl.setText("Typologies"); - return lbl; - } - -} diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/AbstractDbkViewer.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/AbstractDbkViewer.java index 98b9c6c..16b3f42 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/AbstractDbkViewer.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/AbstractDbkViewer.java @@ -1,8 +1,8 @@ package org.argeo.app.ui.docbook; import static org.argeo.app.docbook.DbkType.para; -import static org.argeo.app.docbook.DbkUtils.addDbk; -import static org.argeo.app.docbook.DbkUtils.isDbk; +import static org.argeo.app.jcr.docbook.DbkJcrUtils.addDbk; +import static org.argeo.app.jcr.docbook.DbkJcrUtils.isDbk; import java.util.ArrayList; import java.util.Iterator; @@ -21,7 +21,7 @@ import org.argeo.api.cms.ux.Cms2DSize; import org.argeo.api.cms.ux.CmsEditable; import org.argeo.app.docbook.DbkAttr; import org.argeo.app.docbook.DbkType; -import org.argeo.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.SwtEditablePart; import org.argeo.cms.ui.viewers.AbstractPageViewer; @@ -265,7 +265,7 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke protected DbkSectionTitle prepareSectionTitle(Section newSection, String titleText) throws RepositoryException { Node sectionNode = newSection.getNode(); - Node titleNode = DbkUtils.getOrAddDbk(sectionNode, DbkType.title); + Node titleNode = DbkJcrUtils.getOrAddDbk(sectionNode, DbkType.title); getTextInterpreter().write(titleNode, titleText); if (newSection.getHeader() == null) newSection.createHeader(); @@ -685,7 +685,7 @@ public abstract class AbstractDbkViewer extends AbstractPageViewer implements Ke ((Control) sp).dispose(); } // create title - Node titleNode = DbkUtils.addDbk(newSectionNode, DbkType.title); + Node titleNode = DbkJcrUtils.addDbk(newSectionNode, DbkType.title); // newSectionNode.addNode(DocBookType.TITLE, DocBookType.TITLE); getTextInterpreter().write(titleNode, txt); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkContextMenu.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkContextMenu.java index 1673bd8..89b5493 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkContextMenu.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkContextMenu.java @@ -7,7 +7,7 @@ import javax.jcr.Node; import org.argeo.api.cms.ux.CmsEditable; import org.argeo.app.docbook.DbkMsg; -import org.argeo.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.SwtEditablePart; import org.argeo.cms.swt.MouseDown; @@ -136,7 +136,7 @@ class DbkContextMenu { Label insertPictureB = new Label(parent, SWT.NONE); insertPictureB.setText(DbkMsg.insertPicture.lead()); insertPictureB.addMouseListener((MouseDown) (e) -> { - Node newNode = DbkUtils.insertImageAfter(nodePart.getNode()); + Node newNode = DbkJcrUtils.insertImageAfter(nodePart.getNode()); Jcr.save(newNode); textViewer.insertPart(section, newNode); hide(); @@ -144,7 +144,7 @@ class DbkContextMenu { Label insertVideoB = new Label(parent, SWT.NONE); insertVideoB.setText(DbkMsg.insertVideo.lead()); insertVideoB.addMouseListener((MouseDown) (e) -> { - Node newNode = DbkUtils.insertVideoAfter(nodePart.getNode()); + Node newNode = DbkJcrUtils.insertVideoAfter(nodePart.getNode()); Jcr.save(newNode); textViewer.insertPart(section, newNode); hide(); diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkImageManager.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkImageManager.java index 1493223..fef7a02 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkImageManager.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkImageManager.java @@ -23,7 +23,7 @@ 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.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.cms.ui.util.DefaultImageManager; import org.argeo.jcr.JcrException; @@ -55,7 +55,7 @@ public class DbkImageManager extends DefaultImageManager { @Override public Binary getImageBinary(Node node) { Node fileNode = null; - if (DbkUtils.isDbk(node, DbkType.mediaobject)) { + if (DbkJcrUtils.isDbk(node, DbkType.mediaobject)) { Node imageDataNode = getImageDataNode(node); fileNode = getFileNode(imageDataNode); } diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkTextInterpreter.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkTextInterpreter.java index 80f768c..4875f7d 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkTextInterpreter.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkTextInterpreter.java @@ -2,7 +2,7 @@ package org.argeo.app.ui.docbook; import static org.argeo.app.docbook.DbkType.para; import static org.argeo.app.docbook.DbkType.title; -import static org.argeo.app.docbook.DbkUtils.isDbk; +import static org.argeo.app.jcr.docbook.DbkJcrUtils.isDbk; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java index c911700..a0944d5 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkVideo.java @@ -12,7 +12,7 @@ import javax.jcr.RepositoryException; import org.argeo.api.acr.ldap.NamingUtils; import org.argeo.app.docbook.DbkAttr; import org.argeo.app.docbook.DbkType; -import org.argeo.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.Selected; import org.argeo.cms.ui.viewers.NodePart; @@ -70,7 +70,7 @@ public class DbkVideo extends StyledControl implements SectionPart, NodePart { 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 = DbkUtils.getMediaFileref(mediaobject); + String fileref = DbkJcrUtils.getMediaFileref(mediaobject); Text text = new Text(editor, SWT.SINGLE); if (fileref != null) text.setText(fileref); @@ -171,7 +171,7 @@ public class DbkVideo extends StyledControl implements SectionPart, NodePart { if (control instanceof Browser) { Browser browser = (Browser) control; getNode().getSession(); - String fileref = DbkUtils.getMediaFileref(getNode()); + String fileref = DbkJcrUtils.getMediaFileref(getNode()); if (fileref != null) { // TODO manage self-hosted videos // TODO for YouTube videos, check whether the URL starts with diff --git a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DocumentTextEditor.java b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DocumentTextEditor.java index 7d41117..330abd2 100644 --- a/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DocumentTextEditor.java +++ b/swt/org.argeo.app.ui/src/org/argeo/app/ui/docbook/DocumentTextEditor.java @@ -5,7 +5,7 @@ import javax.jcr.RepositoryException; import org.argeo.api.cms.ux.CmsEditable; import org.argeo.app.docbook.DbkType; -import org.argeo.app.docbook.DbkUtils; +import org.argeo.app.jcr.docbook.DbkJcrUtils; import org.argeo.cms.swt.CmsSwtUtils; import org.eclipse.swt.widgets.Composite; @@ -22,7 +22,7 @@ public class DocumentTextEditor extends AbstractDbkViewer { @Override protected void initModel(Node textNode) throws RepositoryException { if (isFlat()) { - DbkUtils.addParagraph(textNode, ""); + DbkJcrUtils.addParagraph(textNode, ""); } // else // textNode.setProperty(DocBookNames.DBK_TITLE, textNode.getName());