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 \
org.argeo.app.profile.acr.jcr \
swt/org.argeo.app.swt \
swt/org.argeo.app.ui \
+org.argeo.suite.knowledge \
DEP_CATEGORIES = \
org.argeo.tp \
org.argeo.tp.publish \
org.argeo.tp.math \
org.argeo.tp.earth \
-osgi/api/org.argeo.tp.osgi \
+osgi/equinox/org.argeo.tp.osgi \
osgi/equinox/org.argeo.tp.eclipse \
swt/rap/org.argeo.tp.swt \
swt/rap/org.argeo.tp.swt.workbench \
--- /dev/null
+package org.argeo.app.api;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsSession;
+
+/** Access to content which is specific to a user and their state. */
+public interface AppUserState {
+ Content getOrCreateSessionDir(CmsSession session);
+}
/** Administrative units. */
final String ADM = "adm";
+ @Deprecated
final String ENTITY_TYPE = "entity:type";
- // final String ENTITY_UID = "entity:uid";
- // final String ENTITY_NAME = "entity:name";
// GENERIC CONCEPTS
- /** The language which is relevant. */
- final String XML_LANG = "xml:lang";
+// /** The language which is relevant. */
+// final String XML_LANG = "xml:lang";
/** The date which is relevant. */
+ @Deprecated
final String ENTITY_DATE = "entity:date";
@Deprecated
final String ENTITY_RELATED_TO = "entity:relatedTo";
final String OU = LdapAttr.ou.property();
// WGS84
- final String GEO_LAT = "geo:lat";
- final String GEO_LONG = "geo:long";
- final String GEO_ALT = "geo:alt";
+ @Deprecated
+ final String GEO_LAT = WGS84PosName.lat.get();
+ @Deprecated
+ final String GEO_LONG = WGS84PosName.lng.get();
+ @Deprecated
+ final String GEO_ALT = WGS84PosName.alt.get();
// SVG
+ @Deprecated
final String SVG_WIDTH = "svg:width";
+ @Deprecated
final String SVG_HEIGHT = "svg:height";
+ @Deprecated
final String SVG_LENGTH = "svg:length";
+ @Deprecated
final String SVG_UNIT = "svg:unit";
+ @Deprecated
final String SVG_DUR = "svg:dur";
+ @Deprecated
final String SVG_DIRECTION = "svg:direction";
}
// ldap
person, user;
+ public final static String ENTITY_NAMESPACE_URI = "http://www.argeo.org/ns/entity";
+ public final static String ENTITY_DEFAULT_PREFIX = "entity";
+
@Override
public String getDefaultPrefix() {
- return "entity";
+ return ENTITY_DEFAULT_PREFIX;
}
-// @Override
-// public String getPrefix() {
-// return getDefaultPrefix();
-// }
-//
-// public static String prefix() {
-// return "entity";
-// }
-
public String basePath() {
return '/' + name();
}
@Override
public String getNamespace() {
- return "http://www.argeo.org/ns/entity";
+ return ENTITY_NAMESPACE_URI;
}
-
-// public static String namespace() {
-// return "http://www.argeo.org/ns/entity";
-// }
-
}
package org.argeo.app.api;
import java.util.List;
+import java.util.Set;
/** Provides optimised access and utilities around terms typologies. */
public interface TermsManager {
Typology getTypology(String typology);
-
+
+ Set<Typology> getTypologies();
+
Term getTerm(String id);
List<Term> listAllTerms(String typology);
--- /dev/null
+package org.argeo.app.api;
+
+import org.argeo.api.acr.QNamed;
+
+/**
+ * Geographical coordinate in WGS84 reference datum.
+ *
+ * @see https://www.w3.org/2003/01/geo/
+ */
+public enum WGS84PosName implements QNamed {
+ lat, lng("long"), alt;
+
+ private final String localName;
+
+ private WGS84PosName() {
+ localName = null;
+ }
+
+ private WGS84PosName(String localName) {
+ this.localName = localName;
+ }
+
+ @Override
+ public String getNamespace() {
+ return "http://www.w3.org/2003/01/geo/wgs84_pos#";
+ }
+
+ @Override
+ public String getDefaultPrefix() {
+ return "geo";
+ }
+
+ @Override
+ public String localName() {
+ if (localName != null)
+ return localName;
+ return QNamed.super.localName();
+ }
+
+}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="DocBook 4 Converter">
- <implementation class="org.argeo.app.docbook.Dbk4Converter"/>
- <service>
- <provide interface="org.argeo.app.docbook.Dbk4Converter"/>
- </service>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="GeoTools Test">
- <implementation class="org.djapps.on.apaf.GeoToolsTest"/>
-</scr:component>
--- /dev/null
+dashboard=dashboard
+#people=contacts
+documents=documents
+locations=locations
+recentItems=recent items
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=person
+user=user
+org=organisation
+group=group
+
+# NewPersonWizard
+firstName=First Name
+lastName=Last Name
+salutation=Salutation
+email=Email
+personWizardWindowTitle=New person
+personWizardPageTitle=Create a contact
+personWizardFeedback=Contact was created
+
+# NewOrgWizard
+legalName=Legal name
+legalForm=Legal form
+vatId=VAT ID
+orgWizardWindowTitle=New organisation
+orgWizardPageTitle=Create an organisation
+orgWizardFeedback=Organisation was created
+
+# Roles
+userAdminRole=Can create users and modify them
+groupAdminRole=Can create groups and organisations and modify them
+publisherRole=Can validate and publish content
+coworkerRole=Is an active user of the organisation
+
+# Group
+chooseAMember=Choose a member
+
+# ContextAddressComposite
+chooseAnOrganisation=Choose an organisation
+street=Street
+streetComplement=Street complement
+zipCode=Zip code
+city=City
+state=State
+country=Country
+geopoint=Geopoint
+
+# FilteredOrderableEntityTable
+filterHelp=Type filter criterion separated by a space
+
+# BankAccountComposite
+accountHolder=Account holder
+bankName=Bank name
+currency=Currency
+accountNumber=Account number
+bankNumber=Bank number
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Role
+chosenItem=Chose item
+department=Department
+isPrimary=Is primary
+searchAndChooseEntity=Search and choose a corresponding entity
+
+# ContactListCTab (e4)
+notes=Notes
+addAContact=Add a contact
+contactValue=Contact value
+linkedCompany=Linked company
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Payment account
+
+# OrgEditor (e4)
+orgDetails=Details
+orgActivityLog=Activity log
+team=Team
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Contact details
+personActivityLog=Activity log
+personOrgs=Organisations
+personSecurity=Security
+
+# PersonSecurityCTab (e4)
+resetPassword=Reset password
+
+# Generic
+label=Label
+aCustomLabel=A custom label
+description=Description
+value=Value
+name=Name
+primary=Primary
+add=Add
+save=Save
+pickUp=Pick up
+
+# Tags
+confirmNewTag=Tag #{0} is not yet registered. Are you sure you want to create it?
+cannotCreateTag=Tag #{0} is not yet registered and you don't have enough rights to create it.
+
+# People
+people=people
+
+# Library
+content=content
+
+# Geo
+map=map
+
+# Feedback messages
+allFieldsMustBeSet=All fields must be set
+
--- /dev/null
+dashboard=dashboard
+people=Kontakte
+documents=Dokumente
+locations=Orte
+recentItems=neulich
+
+appTitle=Argeo Suite
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=Person
+organisation=Organisation
+
+# NewPersonWizard
+firstName=Vorname
+lastName=Nachname
+salutation=Salutation
+email=E-Mail
+personWizardWindowTitle=Neue Person
+personWizardPageTitle=Kontakt erstellen
+
+# NewOrgWizard
+legalName=Name
+legalForm=Geschäftsform
+vatId=Ust ID
+orgWizardWindowTitle=Neue Organisation
+orgWizardPageTitle=Organisation erstellen
+
+
+# ContextAddressComposite
+chooseAnOrganisation=Organisation wählen
+street=Strasse
+streetComplement=Strasse Zusatz
+zipCode=PLZ
+city=Stadt
+state=Bundesland
+country=Land
+geopoint=Geopoint
+
+# FilteredOrderableEntityTable
+filterHelp=Type filter criterion separated by a space
+
+# BankAccountComposite
+accountHolder=Kontoinhaber
+bankName=Name der Bank
+currency=Währung
+accountNumber=Kontonummer
+bankNumber=BLZ
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Rolle
+chosenItem=Auswahl
+department=Abteilung
+isPrimary=Ist Primär
+searchAndChooseEntity=Suche und wähle ein zugehöriges Objekt
+
+# ContactListCTab (e4)
+notes=Bemerkungen
+addAContact=Kontakt hinzufügen
+contactValue=Kontakt value
+linkedCompany=zugehörige Firma
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Geschäftskonto
+
+# OrgEditor (e4)
+orgDetails=Details
+orgActivityLog=Aktivitäten Log
+team=Team
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Kontakt Daten
+personActivityLog=Aktivitäten Log
+personOrgs=Organisationen
+personSecurity=Sicherheit
+
+# PersonSecurityCTab (e4)
+resetPassword=Passwort zurücksetzen
+
+# Generic
+label=Beschriftung
+aCustomLabel=Eine spezifische Beschriftung
+description=Beschreibung
+value=Wert
+name=Name
+primary=Haupt-
+add=Hinzufügen
+save=Speichern
+pickUp=Aussuchen
+
+# Tags
+confirmNewTag=Das Hashtag '{0}' existiert noch nicht. WollenSie es hinzufügen?
+cannotCreateTag=Das Hashtag '{0}' existiert nicht uns Sie haben nicht die Rechte, um es hinzufügen.
--- /dev/null
+dashboard=dashboard
+people=contacts
+documents=documents
+locations=lieux
+recentItems=récent
+
+appTitle=Argeo Suite
+
+#
+# GENERIC
+#
+
+#
+# PEOPLE
+# org.argeo.people.ui.PeopleMsg
+#
+person=personne
+user=utilisateur
+org=organisation
+group=groupe
+
+# NewPersonWizard
+firstName=Prénom
+lastName=Nom
+salutation=Salutation
+email=Email
+personWizardWindowTitle=Nouvelle personne
+personWizardPageTitle=Créer un contact
+personWizardFeedback=Le contact a été créé
+
+# NewOrgWizard
+legalName=Nom
+legalForm=Forme légale
+vatId=ID TVA
+orgWizardWindowTitle=Nouvelle organisation
+orgWizardPageTitle=Créer une organisation
+orgWizardFeedback=L'organisation a été crée
+
+# Roles
+userAdminRole=Peut créer des utilisateurs et les modifier
+groupAdminRole=Peut créer des groupes et des organisations et les modifier
+publisherRole=Peut publier et valider du contenu
+coworkerRole=Est un membre en activité de l'organisation
+
+# Group
+chooseAMember=Choisir un membre
+
+# ContextAddressComposite
+chooseAnOrganisation=Choisir une organisation
+street=Rue
+streetComplement=Complément rue
+zipCode=Code postal
+city=Ville
+state=État
+country=Pays
+geopoint=Géocoordonnées
+
+# FilteredOrderableEntityTable
+filterHelp=Sasir les critères de filtrage séparés par des espaces
+
+# BankAccountComposite
+accountHolder=Propriétaire du compte
+bankName=Nom de la banque
+currency=Devise
+accountNumber=Numéro de compte
+bankNumber=Numéro de banque
+BIC=BIC
+IBAN=IBAN
+
+# EditJobDialog
+position=Rôle
+chosenItem=Choisir une élément
+department=Service
+isPrimary=Principal
+searchAndChooseEntity=Cherhcer et choisir l'entitée correspondante
+
+# ContactListCTab (e4)
+notes=Notes
+addAContact=Ajouter un contact
+contactValue=Valeur
+linkedCompany=Entreprise liée
+
+# OrgAdminInfoCTab (e4)
+paymentAccount=Compte de paiement
+
+# OrgEditor (e4)
+orgDetails=Détails
+orgActivityLog=Activités
+team=Équipe
+orgAdmin=Admin.
+
+# PersonEditor (e4)
+personDetails=Détails du contact
+personActivityLog=Activités
+personOrgs=Organisations
+personSecurity=Accès
+
+# PersonSecurityCTab (e4)
+resetPassword=Force le mot de passe
+
+# Generic
+label=Étiquette
+aCustomLabel=Une étiquette spécifique
+description=Description
+value=Valeur
+name=Nom
+primary=Principal
+add=Ajouter
+save=Sauver
+pickUp=Choisir
+
+# Tags
+confirmNewTag=Le tag #{0} n'existe pas encore. Voulez-vous le créer?
+cannotCreateTag=Le tag #{0} n'existe pas encore et vous n'avez pas les droits pour le créer.
+
+# Feedback messages
+allFieldsMustBeSet=Toutes les données doivent être renseignées
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="true" name="Suite Maintenance Service">
- <implementation class="org.argeo.app.core.SuiteMaintenanceService"/>
- <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
- <reference bind="setUserTransaction" cardinality="1..1" interface="org.argeo.api.cms.transaction.WorkTransaction" name="WorkTransaction" policy="static"/>
- <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
- <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.spi.ProvidedRepository" name="ContentRepository" policy="static"/>
-</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="org.argeo.app.core.suiteMaintenance">
+ <implementation class="org.argeo.app.core.SuiteMaintenance"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.spi.ProvidedRepository" name="ContentRepository" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" name="org.argeo.app.termsContentProvider">
+ <implementation class="org.argeo.app.acr.terms.TermsContentProvider"/>
+ <reference bind="setService" cardinality="1..1" interface="org.argeo.app.api.TermsManager" name="TermsManager" policy="static"/>
+ <service>
+ <provide interface="org.argeo.api.acr.spi.ContentProvider"/>
+ </service>
+ <property name="acr.mount.path" type="String" value="/terms"/>
+</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="true" name="Suite Terms Manager">
- <implementation class="org.argeo.app.core.SuiteTermsManager"/>
- <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
- <service>
- <provide interface="org.argeo.app.api.TermsManager"/>
- </service>
-</scr:component>
Bundle-ActivationPolicy: lazy
Service-Component:\
-OSGI-INF/termsManager.xml,\
-OSGI-INF/maintenanceService.xml,\
-OSGI-INF/dbk4Converter.xml,\
+OSGI-INF/suiteMaintenance.xml,\
+OSGI-INF/termsContentProvider.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,\
*
Require-Capability:\
-output.. = bin/
bin.includes = META-INF/,\
.,\
- OSGI-INF/
-source.. = src/
+ OSGI-INF/,\
+ OSGI-INF/appUserState.xml,\
+ OSGI-INF/suiteMaintenance.xml,\
+ OSGI-INF/termsContentProvider.xml
additional.bundles = org.argeo.init
+source.. = src/
+output.. = bin/
--- /dev/null
+package org.argeo.app.acr.terms;
+
+import java.util.Iterator;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.app.api.Term;
+import org.argeo.cms.acr.AbstractContent;
+
+public class TermContent extends AbstractContent {
+ private TermsContentProvider provider;
+ private Term term;
+
+ public TermContent(ProvidedSession session, TermsContentProvider provider, Term term) {
+ super(session);
+ this.provider = provider;
+ this.term = term;
+ }
+
+ @Override
+ public Iterator<Content> iterator() {
+ return term.getSubTerms().stream().map((t) -> (Content) new TermContent(getSession(), provider, t)).iterator();
+ }
+
+ @Override
+ public ContentProvider getProvider() {
+ return provider;
+ }
+
+ @Override
+ public QName getName() {
+ return NamespaceUtils.unqualified(term.getName());
+ }
+
+ @Override
+ public Content getParent() {
+ Term parentTerm = term.getParentTerm();
+ return parentTerm == null ? new TypologyContent(getSession(), provider, term.getTypology())
+ : new TermContent(getSession(), provider, parentTerm);
+ }
+
+}
--- /dev/null
+package org.argeo.app.acr.terms;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentNotFoundException;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.api.Typology;
+import org.argeo.cms.acr.AbstractSimpleContentProvider;
+import org.argeo.cms.acr.ContentUtils;
+
+public class TermsContentProvider extends AbstractSimpleContentProvider<TermsManager> {
+
+ public TermsContentProvider() {
+ super(EntityType.ENTITY_NAMESPACE_URI, EntityType.ENTITY_DEFAULT_PREFIX);
+ }
+
+ @Override
+ protected Iterator<Content> firstLevel(ProvidedSession session) {
+ return getService().getTypologies().stream().map((t) -> (Content) new TypologyContent(session, this, t))
+ .iterator();
+ }
+
+ @Override
+ public ProvidedContent get(ProvidedSession session, List<String> segments) {
+ String typologyName = segments.get(0);
+ Typology typology = getService().getTypology(typologyName);
+ if (segments.size() == 1)
+ return new TypologyContent(session, this, typology);
+ Term currTerm = null;
+ terms: for (Term term : typology.getSubTerms()) {
+ if (term.getName().equals(segments.get(1))) {
+ currTerm = term;
+ break terms;
+ }
+ }
+ if (currTerm == null)
+ throw new ContentNotFoundException(session,
+ getMountPath() + "/" + ContentUtils.toPath(segments) + " cannot be found");
+ if (segments.size() == 1)
+ return new TermContent(session, this, currTerm);
+
+ for (int i = 2; i < segments.size(); i++) {
+ String termName = segments.get(i);
+ Term nextTerm = null;
+ terms: for (Term term : currTerm.getSubTerms()) {
+ if (term.getName().equals(termName)) {
+ nextTerm = term;
+ break terms;
+ }
+ }
+ if (nextTerm == null)
+ throw new ContentNotFoundException(session,
+ getMountPath() + "/" + ContentUtils.toPath(segments) + " cannot be found");
+ currTerm = nextTerm;
+ }
+ return new TermContent(session, this, currTerm);
+ }
+
+ ServiceContent getRootContent(ProvidedSession session) {
+ return new ServiceContent(session);
+ }
+
+}
--- /dev/null
+package org.argeo.app.acr.terms;
+
+import java.util.Iterator;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.app.api.Typology;
+import org.argeo.cms.acr.AbstractContent;
+
+public class TypologyContent extends AbstractContent {
+ private TermsContentProvider provider;
+ private Typology typology;
+
+ public TypologyContent(ProvidedSession session, TermsContentProvider provider, Typology typology) {
+ super(session);
+ this.provider = provider;
+ this.typology = typology;
+ }
+
+ @Override
+ public ContentProvider getProvider() {
+ return provider;
+ }
+
+ @Override
+ public QName getName() {
+ return NamespaceUtils.unqualified(typology.getId());
+ }
+
+ @Override
+ public Content getParent() {
+ return provider.getRootContent(getSession());
+ }
+
+ @Override
+ public Iterator<Content> iterator() {
+ return typology.getSubTerms().stream().map((t) -> (Content) new TermContent(getSession(), provider, t))
+ .iterator();
+ }
+
+}
+++ /dev/null
-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<String> getTypologies() {
- return new ArrayList<>();
- }
-
- protected String getTypologiesLoadBase() {
- return "";
- }
-
- protected void loadTypologies(Node customBaseNode) throws RepositoryException, IOException {
- List<String> 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;
- }
- }
-
-}
+++ /dev/null
-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<String, String> 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<String, String> 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();
- }
-
-}
--- /dev/null
+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<Area> 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;
+ }
+
+}
+++ /dev/null
-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<Area> 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);
- }
-
-}
+++ /dev/null
-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<SuiteTerm> 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<SuiteTerm> getSubTerms() {
- return subTerms;
- }
-
- @Override
- public SuiteTerm getParentTerm() {
- return parentTerm;
- }
-
-}
+++ /dev/null
-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<String, SuiteTerm> terms = new HashMap<>();
- private final Map<String, SuiteTypology> typologies = new HashMap<>();
-
- // JCR
- private Repository repository;
- private Session adminSession;
-
- public void init() {
- adminSession = CmsJcrUtils.openDataAdminSession(repository, CmsConstants.SYS_WORKSPACE);
- }
-
- @Override
- public List<Term> listAllTerms(String typology) {
- List<Term> 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;
- }
-
-}
+++ /dev/null
-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<SuiteTerm> 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<SuiteTerm> getSubTerms() {
- return subTerms;
- }
-
- public List<SuiteTerm> getAllTerms() {
- if (isFlat)
- return subTerms;
- else {
- List<SuiteTerm> terms = new ArrayList<>();
- for (SuiteTerm subTerm : subTerms) {
- terms.add(subTerm);
- collectSubTerms(terms, subTerm);
- }
- return terms;
- }
- }
-
- public Term findTermByName(String name) {
- List<SuiteTerm> 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<SuiteTerm> collected) {
- if (term.getName().equals(name)) {
- collected.add(term);
- }
- for (SuiteTerm subTerm : term.getSubTerms()) {
- collectTermsByName(subTerm, name, collected);
- }
- }
-
- private void collectSubTerms(List<SuiteTerm> terms, SuiteTerm term) {
- for (SuiteTerm subTerm : term.getSubTerms()) {
- terms.add(subTerm);
- collectSubTerms(terms, subTerm);
- }
- }
-
-}
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 {
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<String> extractRoles(String[] semiColArr) {
Set<String> res = new HashSet<>();
// TODO factorize and make it more robust
+++ /dev/null
-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() {
-
- }
-}
+++ /dev/null
-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();
- }
- }
-
-}
package org.argeo.app.docbook;
import org.argeo.api.acr.Content;
+import org.argeo.app.api.EntityType;
+/** Utilities when using ACR to access DocBook. */
public class DbkAcrUtils {
/** Whether this DocBook element is of this type. */
public static boolean isDbk(Content content, DbkType type) {
}
}
+ public static Content getMetadata(Content infoContainer) {
+ if (!infoContainer.hasChild(DbkType.info))
+ return null;
+ Content info = infoContainer.child(DbkType.info);
+ if (!info.hasChild(EntityType.local))
+ return null;
+ return info.child(EntityType.local);
+ }
+
/** singleton */
private DbkAcrUtils() {
}
+++ /dev/null
-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() {
- }
-
-}
+++ /dev/null
-<?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:exsl="http://exslt.org/common"
- xmlns:db = "http://docbook.org/ns/docbook"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- exclude-result-prefixes="exsl db"
- version="1.0">
-
-<!--
-# ======================================================================
-# This file is part of DocBook V5.0CR5
-#
-# Copyright 2005 Norman Walsh, Sun Microsystems, Inc., and the
-# Organization for the Advancement of Structured Information
-# Standards (OASIS).
-#
-# Release: $Id: db4-upgrade.xsl 7660 2008-02-06 13:48:36Z nwalsh $
-#
-# Permission to use, copy, modify and distribute this stylesheet
-# and its accompanying documentation for any purpose and without fee
-# is hereby granted in perpetuity, provided that the above copyright
-# notice and this paragraph appear in all copies. The copyright
-# holders make no representation about the suitability of the schema
-# for any purpose. It is provided "as is" without expressed or implied
-# warranty.
-#
-# Please direct all questions, bug reports, or suggestions for changes
-# to the docbook@lists.oasis-open.org mailing list. For more
-# information, see http://www.oasis-open.org/docbook/.
-#
-# ======================================================================
--->
-
-<xsl:variable name="version" select="'1.0'"/>
-
-<xsl:output method="xml" encoding="utf-8" indent="no" omit-xml-declaration="yes"/>
-
-<xsl:preserve-space elements="*"/>
-<xsl:param name="rootid">
- <xsl:choose>
- <xsl:when test="/*/@id">
- <xsl:value-of select="/*/@id"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:text>UNKNOWN</xsl:text>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:param>
-
-<xsl:param name="defaultDate" select="''"/>
-
-<xsl:template match="/">
- <xsl:variable name="converted">
- <xsl:apply-templates/>
- </xsl:variable>
- <xsl:comment>
- <xsl:text> Converted by db4-upgrade version </xsl:text>
- <xsl:value-of select="$version"/>
- <xsl:text> </xsl:text>
- </xsl:comment>
- <xsl:text> </xsl:text>
- <xsl:apply-templates select="exsl:node-set($converted)/*" mode="addNS"/>
-</xsl:template>
-
-<xsl:template match="bookinfo|chapterinfo|articleinfo|artheader|appendixinfo
- |blockinfo
- |bibliographyinfo|glossaryinfo|indexinfo|setinfo
- |setindexinfo
- |sect1info|sect2info|sect3info|sect4info|sect5info
- |sectioninfo
- |refsect1info|refsect2info|refsect3info|refsectioninfo
- |referenceinfo|partinfo"
- priority="200">
- <info>
- <xsl:call-template name="copy.attributes"/>
-
- <!-- titles can be inside or outside or both. fix that -->
- <xsl:choose>
- <xsl:when test="title and following-sibling::title">
- <xsl:if test="title != following-sibling::title">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> title.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="title" mode="copy"/>
- </xsl:when>
- <xsl:when test="title">
- <xsl:apply-templates select="title" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::title">
- <xsl:apply-templates select="following-sibling::title" mode="copy"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text>: no title.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:otherwise>
- </xsl:choose>
-
- <xsl:choose>
- <xsl:when test="titleabbrev and following-sibling::titleabbrev">
- <xsl:if test="titleabbrev != following-sibling::titleabbrev">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> titleabbrev.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- </xsl:when>
- <xsl:when test="titleabbrev">
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::titleabbrev">
- <xsl:apply-templates select="following-sibling::titleabbrev" mode="copy"/>
- </xsl:when>
- </xsl:choose>
-
- <xsl:choose>
- <xsl:when test="subtitle and following-sibling::subtitle">
- <xsl:if test="subtitle != following-sibling::subtitle">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> subtitle.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </xsl:when>
- <xsl:when test="subtitle">
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::subtitle">
- <xsl:apply-templates select="following-sibling::subtitle" mode="copy"/>
- </xsl:when>
- </xsl:choose>
-
- <xsl:apply-templates/>
- </info>
-</xsl:template>
-
-<xsl:template match="objectinfo|prefaceinfo|refsynopsisdivinfo
- |screeninfo|sidebarinfo"
- priority="200">
- <info>
- <xsl:call-template name="copy.attributes"/>
-
- <!-- titles can be inside or outside or both. fix that -->
- <xsl:choose>
- <xsl:when test="title and following-sibling::title">
- <xsl:if test="title != following-sibling::title">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> title.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="title" mode="copy"/>
- </xsl:when>
- <xsl:when test="title">
- <xsl:apply-templates select="title" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::title">
- <xsl:apply-templates select="following-sibling::title" mode="copy"/>
- </xsl:when>
- <xsl:otherwise>
- <!-- it's ok if there's no title on these -->
- </xsl:otherwise>
- </xsl:choose>
-
- <xsl:choose>
- <xsl:when test="titleabbrev and following-sibling::titleabbrev">
- <xsl:if test="titleabbrev != following-sibling::titleabbrev">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> titleabbrev.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- </xsl:when>
- <xsl:when test="titleabbrev">
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::titleabbrev">
- <xsl:apply-templates select="following-sibling::titleabbrev" mode="copy"/>
- </xsl:when>
- </xsl:choose>
-
- <xsl:choose>
- <xsl:when test="subtitle and following-sibling::subtitle">
- <xsl:if test="subtitle != following-sibling::subtitle">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check </xsl:text>
- <xsl:value-of select="name(..)"/>
- <xsl:text> subtitle.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </xsl:when>
- <xsl:when test="subtitle">
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </xsl:when>
- <xsl:when test="following-sibling::subtitle">
- <xsl:apply-templates select="following-sibling::subtitle" mode="copy"/>
- </xsl:when>
- </xsl:choose>
-
- <xsl:apply-templates/>
- </info>
-</xsl:template>
-
-<xsl:template match="refentryinfo"
- priority="200">
- <info>
- <xsl:call-template name="copy.attributes"/>
-
- <!-- titles can be inside or outside or both. fix that -->
- <xsl:if test="title">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding title from refentryinfo!</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:if test="titleabbrev">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding titleabbrev from refentryinfo!</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:if test="subtitle">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding subtitle from refentryinfo!</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:apply-templates/>
- </info>
-</xsl:template>
-
-<xsl:template match="refmiscinfo"
- priority="200">
- <refmiscinfo>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'class'"/>
- </xsl:call-template>
- <xsl:if test="@class">
- <xsl:choose>
- <xsl:when test="@class = 'source'
- or @class = 'version'
- or @class = 'manual'
- or @class = 'sectdesc'
- or @class = 'software'">
- <xsl:attribute name="class">
- <xsl:value-of select="@class"/>
- </xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="class">
- <xsl:value-of select="'other'"/>
- </xsl:attribute>
- <xsl:attribute name="otherclass">
- <xsl:value-of select="@class"/>
- </xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:if>
- <xsl:apply-templates/>
- </refmiscinfo>
-</xsl:template>
-
-<xsl:template match="corpauthor" priority="200">
- <author>
- <xsl:call-template name="copy.attributes"/>
- <orgname>
- <xsl:apply-templates/>
- </orgname>
- </author>
-</xsl:template>
-
-<xsl:template match="corpname" priority="200">
- <orgname>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </orgname>
-</xsl:template>
-
-<xsl:template match="author[not(personname)]|editor[not(personname)]|othercredit[not(personname)]" priority="200">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <personname>
- <xsl:apply-templates select="honorific|firstname|surname|othername|lineage"/>
- </personname>
- <xsl:apply-templates select="*[not(self::honorific|self::firstname|self::surname
- |self::othername|self::lineage)]"/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="address|programlisting|screen|funcsynopsisinfo
- |classsynopsisinfo|literallayout" priority="200">
- <xsl:copy>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'format'"/>
- </xsl:call-template>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="productname[@class]" priority="200">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Dropping class attribute from productname</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:copy>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'class'"/>
- </xsl:call-template>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="dedication|preface|chapter|appendix|part|partintro
- |article|bibliography|glossary|glossdiv|index
- |reference[not(referenceinfo)]
- |book" priority="200">
- <xsl:choose>
- <xsl:when test="not(dedicationinfo|prefaceinfo|chapterinfo
- |appendixinfo|partinfo
- |articleinfo|artheader|bibliographyinfo
- |glossaryinfo|indexinfo
- |bookinfo)">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:if test="title|subtitle|titleabbrev">
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- <xsl:apply-templates select="abstract" mode="copy"/>
- </info>
- </xsl:if>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="formalpara|figure|table[tgroup]|example|blockquote
- |caution|important|note|warning|tip
- |bibliodiv|glossarydiv|indexdiv
- |orderedlist|itemizedlist|variablelist|procedure
- |task|tasksummary|taskprerequisites|taskrelated
- |sidebar"
- priority="200">
- <xsl:choose>
- <xsl:when test="blockinfo">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
-
- <xsl:if test="title|titleabbrev|subtitle">
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </info>
- </xsl:if>
-
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="equation" priority="200">
- <xsl:choose>
- <xsl:when test="not(title)">
- <xsl:call-template name="emit-message">
- <xsl:with-param
- name="message"
- >Convert equation without title to informal equation.</xsl:with-param>
- </xsl:call-template>
- <informalequation>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </informalequation>
- </xsl:when>
- <xsl:when test="blockinfo">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- </info>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="sect1|sect2|sect3|sect4|sect5|section"
- priority="200">
- <section>
- <xsl:call-template name="copy.attributes"/>
-
- <xsl:if test="not(sect1info|sect2info|sect3info|sect4info|sect5info|sectioninfo)">
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- <xsl:apply-templates select="abstract" mode="copy"/>
- </info>
- </xsl:if>
- <xsl:apply-templates/>
- </section>
-</xsl:template>
-
-<xsl:template match="simplesect"
- priority="200">
- <simplesect>
- <xsl:call-template name="copy.attributes"/>
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- <xsl:apply-templates select="abstract" mode="copy"/>
- </info>
- <xsl:apply-templates/>
- </simplesect>
-</xsl:template>
-
-<xsl:template match="refsect1|refsect2|refsect3|refsection" priority="200">
- <refsection>
- <xsl:call-template name="copy.attributes"/>
-
- <xsl:if test="not(refsect1info|refsect2info|refsect3info|refsectioninfo)">
- <info>
- <xsl:apply-templates select="title" mode="copy"/>
- <xsl:apply-templates select="titleabbrev" mode="copy"/>
- <xsl:apply-templates select="subtitle" mode="copy"/>
- <xsl:apply-templates select="abstract" mode="copy"/>
- </info>
- </xsl:if>
- <xsl:apply-templates/>
- </refsection>
-</xsl:template>
-
-<xsl:template match="imagedata|videodata|audiodata|textdata" priority="200">
- <xsl:copy>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'srccredit'"/>
- </xsl:call-template>
- <xsl:if test="@srccredit">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check conversion of srccredit </xsl:text>
- <xsl:text>(othercredit="srccredit").</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <info>
- <othercredit class="other" otherclass="srccredit">
- <orgname>???</orgname>
- <contrib>
- <xsl:value-of select="@srccredit"/>
- </contrib>
- </othercredit>
- </info>
- </xsl:if>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="sgmltag" priority="200">
- <tag>
- <xsl:call-template name="copy.attributes"/>
- <xsl:if test="@class = 'sgmlcomment'">
- <xsl:attribute name="class">comment</xsl:attribute>
- </xsl:if>
- <xsl:apply-templates/>
- </tag>
-</xsl:template>
-
-<xsl:template match="inlinegraphic[@format='linespecific']" priority="210">
- <textobject>
- <textdata>
- <xsl:call-template name="copy.attributes"/>
- </textdata>
- </textobject>
-</xsl:template>
-
-<xsl:template match="inlinegraphic" priority="200">
- <inlinemediaobject>
- <imageobject>
- <imagedata>
- <xsl:call-template name="copy.attributes"/>
- </imagedata>
- </imageobject>
- </inlinemediaobject>
-</xsl:template>
-
-<xsl:template match="graphic[@format='linespecific']" priority="210">
- <mediaobject>
- <textobject>
- <textdata>
- <xsl:call-template name="copy.attributes"/>
- </textdata>
- </textobject>
- </mediaobject>
-</xsl:template>
-
-<xsl:template match="graphic" priority="200">
- <mediaobject>
- <imageobject>
- <imagedata>
- <xsl:call-template name="copy.attributes"/>
- </imagedata>
- </imageobject>
- </mediaobject>
-</xsl:template>
-
-<xsl:template match="pubsnumber" priority="200">
- <biblioid class="pubsnumber">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </biblioid>
-</xsl:template>
-
-<xsl:template match="invpartnumber" priority="200">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting invpartnumber to biblioid otherclass="invpartnumber".</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <biblioid class="other" otherclass="invpartnumber">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </biblioid>
-</xsl:template>
-
-<xsl:template match="contractsponsor" priority="200">
- <xsl:variable name="contractnum"
- select="preceding-sibling::contractnum|following-sibling::contractnum"/>
-
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting contractsponsor to othercredit="contractsponsor".</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <othercredit class="other" otherclass="contractsponsor">
- <orgname>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </orgname>
- <xsl:for-each select="$contractnum">
- <contrib role="contractnum">
- <xsl:apply-templates select="node()"/>
- </contrib>
- </xsl:for-each>
- </othercredit>
-</xsl:template>
-
-<xsl:template match="contractnum" priority="200">
- <xsl:if test="not(preceding-sibling::contractsponsor
- |following-sibling::contractsponsor)
- and not(preceding-sibling::contractnum)">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting contractnum to othercredit="contractnum".</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <othercredit class="other" otherclass="contractnum">
- <orgname>???</orgname>
- <xsl:for-each select="self::contractnum
- |preceding-sibling::contractnum
- |following-sibling::contractnum">
- <contrib>
- <xsl:apply-templates select="node()"/>
- </contrib>
- </xsl:for-each>
- </othercredit>
- </xsl:if>
-</xsl:template>
-
-<xsl:template match="isbn|issn" priority="200">
- <biblioid class="{local-name(.)}">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </biblioid>
-</xsl:template>
-
-<xsl:template match="biblioid[count(*) = 1
- and ulink
- and normalize-space(text()) = '']" priority="200">
- <biblioid xlink:href="{ulink/@url}">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates select="ulink/node()"/>
- </biblioid>
-</xsl:template>
-
-<xsl:template match="authorblurb" priority="200">
- <personblurb>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </personblurb>
-</xsl:template>
-
-<xsl:template match="collabname" priority="200">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check conversion of collabname </xsl:text>
- <xsl:text>(orgname role="collabname").</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <orgname role="collabname">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </orgname>
-</xsl:template>
-
-<xsl:template match="modespec" priority="200">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding modespec (</xsl:text>
- <xsl:value-of select="."/>
- <xsl:text>).</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-</xsl:template>
-
-<xsl:template match="mediaobjectco" priority="200">
- <mediaobject>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </mediaobject>
-</xsl:template>
-
-<xsl:template match="remark" priority="200">
- <!-- get rid of any embedded markup -->
- <remark>
- <xsl:copy-of select="@*"/>
- <xsl:value-of select="."/>
- </remark>
-</xsl:template>
-
-<xsl:template match="biblioentry/title
- |bibliomset/title
- |biblioset/title
- |bibliomixed/title" priority="400">
- <citetitle>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </citetitle>
-</xsl:template>
-
-<xsl:template match="biblioentry/titleabbrev|biblioentry/subtitle
- |bibliomset/titleabbrev|bibliomset/subtitle
- |biblioset/titleabbrev|biblioset/subtitle
- |bibliomixed/titleabbrev|bibliomixed/subtitle"
- priority="400">
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="biblioentry/contrib
- |bibliomset/contrib
- |bibliomixed/contrib" priority="200">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check conversion of contrib </xsl:text>
- <xsl:text>(othercontrib="contrib").</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <othercredit class="other" otherclass="contrib">
- <orgname>???</orgname>
- <contrib>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </contrib>
- </othercredit>
-</xsl:template>
-
-<xsl:template match="link" priority="200">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="ulink" priority="200">
- <xsl:choose>
- <xsl:when test="node()">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting ulink to link.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <link xlink:href="{@url}">
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'url'"/>
- </xsl:call-template>
- <xsl:apply-templates/>
- </link>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting ulink to uri.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <uri xlink:href="{@url}">
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'url'"/>
- </xsl:call-template>
- <xsl:value-of select="@url"/>
- </uri>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="olink" priority="200">
- <xsl:if test="@linkmode">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding linkmode on olink.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:choose>
- <xsl:when test="@targetdocent">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting olink targetdocent to targetdoc.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <olink targetdoc="{unparsed-entity-uri(@targetdocent)}">
- <xsl:for-each select="@*">
- <xsl:if test="name(.) != 'targetdocent'
- and name(.) != 'linkmode'">
- <xsl:copy/>
- </xsl:if>
- </xsl:for-each>
- <xsl:apply-templates/>
- </olink>
- </xsl:when>
- <xsl:otherwise>
- <olink>
- <xsl:for-each select="@*">
- <xsl:if test="name(.) != 'linkmode'">
- <xsl:copy/>
- </xsl:if>
- </xsl:for-each>
- <xsl:apply-templates/>
- </olink>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="biblioentry/firstname
- |biblioentry/surname
- |biblioentry/othername
- |biblioentry/lineage
- |biblioentry/honorific
- |bibliomset/firstname
- |bibliomset/surname
- |bibliomset/othername
- |bibliomset/lineage
- |bibliomset/honorific" priority="200">
- <xsl:choose>
- <xsl:when test="preceding-sibling::firstname
- |preceding-sibling::surname
- |preceding-sibling::othername
- |preceding-sibling::lineage
- |preceding-sibling::honorific">
- <!-- nop -->
- </xsl:when>
- <xsl:otherwise>
- <personname>
- <xsl:apply-templates select="../firstname
- |../surname
- |../othername
- |../lineage
- |../honorific" mode="copy"/>
- </personname>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="areaset" priority="200">
- <xsl:copy>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'coords'"/>
- </xsl:call-template>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="date|pubdate" priority="200">
- <xsl:variable name="rp1" select="substring-before(normalize-space(.), ' ')"/>
- <xsl:variable name="rp2"
- select="substring-before(substring-after(normalize-space(.), ' '),
- ' ')"/>
- <xsl:variable name="rp3"
- select="substring-after(substring-after(normalize-space(.), ' '), ' ')"/>
-
- <xsl:variable name="p1">
- <xsl:choose>
- <xsl:when test="contains($rp1, ',')">
- <xsl:value-of select="substring-before($rp1, ',')"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="$rp1"/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
-
- <xsl:variable name="p2">
- <xsl:choose>
- <xsl:when test="contains($rp2, ',')">
- <xsl:value-of select="substring-before($rp2, ',')"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="$rp2"/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
-
- <xsl:variable name="p3">
- <xsl:choose>
- <xsl:when test="contains($rp3, ',')">
- <xsl:value-of select="substring-before($rp3, ',')"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select="$rp3"/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
-
- <xsl:variable name="date">
- <xsl:choose>
- <xsl:when test="string($p1+1) != 'NaN' and string($p3+1) != 'NaN'">
- <xsl:choose>
- <xsl:when test="$p2 = 'Jan' or $p2 = 'January'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-01-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Feb' or $p2 = 'February'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-02-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Mar' or $p2 = 'March'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-03-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Apr' or $p2 = 'April'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-04-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'May'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-05-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Jun' or $p2 = 'June'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-06-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Jul' or $p2 = 'July'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-07-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Aug' or $p2 = 'August'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-08-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Sep' or $p2 = 'September'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-09-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Oct' or $p2 = 'October'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-10-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Nov' or $p2 = 'November'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-11-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:when test="$p2 = 'Dec' or $p2 = 'December'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-12-</xsl:text>
- <xsl:number value="$p1" format="01"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:apply-templates/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:when test="string($p2+1) != 'NaN' and string($p3+1) != 'NaN'">
- <xsl:choose>
- <xsl:when test="$p1 = 'Jan' or $p1 = 'January'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-01-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Feb' or $p1 = 'February'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-02-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Mar' or $p1 = 'March'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-03-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Apr' or $p1 = 'April'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-04-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'May'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-05-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Jun' or $p1 = 'June'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-06-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Jul' or $p1 = 'July'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-07-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Aug' or $p1 = 'August'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-08-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Sep' or $p1 = 'September'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-09-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Oct' or $p1 = 'October'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-10-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Nov' or $p1 = 'November'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-11-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:when test="$p1 = 'Dec' or $p1 = 'December'">
- <xsl:number value="$p3" format="0001"/>
- <xsl:text>-12-</xsl:text>
- <xsl:number value="$p2" format="01"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:apply-templates/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:otherwise>
- <xsl:apply-templates/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
-
- <xsl:choose>
- <xsl:when test="normalize-space($date) != normalize-space(.)">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converted </xsl:text>
- <xsl:value-of select="normalize-space(.)"/>
- <xsl:text> into </xsl:text>
- <xsl:value-of select="$date"/>
- <xsl:text> for </xsl:text>
- <xsl:value-of select="name(.)"/>
- </xsl:with-param>
- </xsl:call-template>
-
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:value-of select="$date"/>
- </xsl:copy>
- </xsl:when>
-
- <xsl:when test="$defaultDate != ''">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Unparseable date: </xsl:text>
- <xsl:value-of select="normalize-space(.)"/>
- <xsl:text> in </xsl:text>
- <xsl:value-of select="name(.)"/>
- <xsl:text> (Using default: </xsl:text>
- <xsl:value-of select="$defaultDate"/>
- <xsl:text>)</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:copy-of select="$defaultDate"/>
- <xsl:comment>
- <xsl:value-of select="."/>
- </xsl:comment>
- </xsl:copy>
- </xsl:when>
-
- <xsl:otherwise>
- <!-- these don't really matter anymore
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Unparseable date: </xsl:text>
- <xsl:value-of select="normalize-space(.)"/>
- <xsl:text> in </xsl:text>
- <xsl:value-of select="name(.)"/>
- </xsl:with-param>
- </xsl:call-template>
- -->
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="title|subtitle|titleabbrev" priority="300">
- <!-- nop -->
-</xsl:template>
-
-<xsl:template match="abstract" priority="300">
- <xsl:if test="not(contains(name(parent::*),'info'))">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Check abstract; moved into info correctly?</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:if>
-</xsl:template>
-
-<xsl:template match="indexterm">
- <!-- don't copy the defaulted significance='normal' attribute -->
- <indexterm>
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress">
- <xsl:if test="@significance = 'normal'">significance</xsl:if>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:apply-templates/>
- </indexterm>
-</xsl:template>
-
-<xsl:template match="ackno" priority="200">
- <acknowledgements>
- <xsl:copy-of select="@*"/>
- <para>
- <xsl:apply-templates/>
- </para>
- </acknowledgements>
-</xsl:template>
-
-<xsl:template match="lot|lotentry|tocback|tocchap|tocfront|toclevel1|
- toclevel2|toclevel3|toclevel4|toclevel5|tocpart" priority="200">
- <tocdiv>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </tocdiv>
-</xsl:template>
-
-<xsl:template match="action" priority="200">
- <phrase remap="action">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </phrase>
-</xsl:template>
-
-<xsl:template match="beginpage" priority="200">
- <xsl:comment> beginpage pagenum=<xsl:value-of select="@pagenum"/> </xsl:comment>
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Replacing beginpage with comment</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-</xsl:template>
-
-<xsl:template match="structname|structfield" priority="200">
- <varname remap="{local-name(.)}">
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </varname>
-</xsl:template>
-
-<!-- ====================================================================== -->
-
-<!-- 6 Feb 2008, ndw changed mode=copy so that it only copies the first level,
- then it switches back to "normal" mode so that other rewriting templates
- catch embedded fixes -->
-
-<!--
-<xsl:template match="ulink" priority="200" mode="copy">
- <xsl:choose>
- <xsl:when test="node()">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting ulink to phrase.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <phrase xlink:href="{@url}">
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'url'"/>
- </xsl:call-template>
- <xsl:apply-templates/>
- </phrase>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting ulink to uri.</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
-
- <uri xlink:href="{@url}">
- <xsl:call-template name="copy.attributes">
- <xsl:with-param name="suppress" select="'url'"/>
- </xsl:call-template>
- <xsl:value-of select="@url"/>
- </uri>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="sgmltag" priority="200" mode="copy">
- <tag>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </tag>
-</xsl:template>
--->
-
-<xsl:template match="*" mode="copy">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<!--
-<xsl:template match="comment()|processing-instruction()|text()" mode="copy">
- <xsl:copy/>
-</xsl:template>
--->
-
-<!-- ====================================================================== -->
-
-<xsl:template match="*">
- <xsl:copy>
- <xsl:call-template name="copy.attributes"/>
- <xsl:apply-templates/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="comment()|processing-instruction()|text()">
- <xsl:copy/>
-</xsl:template>
-
-<!-- ====================================================================== -->
-
-<xsl:template name="copy.attributes">
- <xsl:param name="src" select="."/>
- <xsl:param name="suppress" select="''"/>
-
- <xsl:for-each select="$src/@*">
- <xsl:choose>
- <xsl:when test="local-name(.) = 'moreinfo'">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding moreinfo on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:when>
- <xsl:when test="local-name(.) = 'lang'">
- <xsl:attribute name="xml:lang">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:when>
- <xsl:when test="local-name(.) = 'id'">
- <xsl:attribute name="xml:id">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:when>
- <xsl:when test="$suppress = local-name(.)"/>
- <xsl:when test="local-name(.) = 'float'">
- <xsl:choose>
- <xsl:when test=". = '1'">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding float on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:if test="not($src/@floatstyle)">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Adding floatstyle='normal' on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:attribute name="floatstyle">
- <xsl:text>normal</xsl:text>
- </xsl:attribute>
- </xsl:if>
- </xsl:when>
- <xsl:when test=". = '0'">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding float on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Discarding float on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:if test="not($src/@floatstyle)">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Adding floatstyle='</xsl:text>
- <xsl:value-of select="."/>
- <xsl:text>' on </xsl:text>
- <xsl:value-of select="local-name($src)"/>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:attribute name="floatstyle">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:if>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:when test="local-name(.) = 'entityref'">
- <xsl:attribute name="fileref">
- <xsl:value-of select="unparsed-entity-uri(@entityref)"/>
- </xsl:attribute>
- </xsl:when>
-
- <xsl:when test="local-name($src) = 'simplemsgentry'
- and local-name(.) = 'audience'">
- <xsl:attribute name="msgaud">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:when>
- <xsl:when test="local-name($src) = 'simplemsgentry'
- and local-name(.) = 'origin'">
- <xsl:attribute name="msgorig">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:when>
- <xsl:when test="local-name($src) = 'simplemsgentry'
- and local-name(.) = 'level'">
- <xsl:attribute name="msglevel">
- <xsl:value-of select="."/>
- </xsl:attribute>
- </xsl:when>
-
- <!-- * for upgrading XSL litprog params documentation -->
- <xsl:when test="local-name($src) = 'refmiscinfo'
- and local-name(.) = 'role'
- and . = 'type'
- ">
- <xsl:call-template name="emit-message">
- <xsl:with-param name="message">
- <xsl:text>Converting refmiscinfo@role=type to </xsl:text>
- <xsl:text>@class=other,otherclass=type</xsl:text>
- </xsl:with-param>
- </xsl:call-template>
- <xsl:attribute name="class">other</xsl:attribute>
- <xsl:attribute name="otherclass">type</xsl:attribute>
- </xsl:when>
-
- <xsl:otherwise>
- <xsl:copy/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:for-each>
-</xsl:template>
-
-<!-- ====================================================================== -->
-
-<xsl:template match="*" mode="addNS">
- <xsl:choose>
- <xsl:when test="namespace-uri(.) = ''">
- <xsl:element name="{local-name(.)}"
- namespace="http://docbook.org/ns/docbook">
- <xsl:if test="not(parent::*)">
- <xsl:attribute name="version">5.0</xsl:attribute>
- </xsl:if>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates mode="addNS"/>
- </xsl:element>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy>
- <xsl:if test="not(parent::*)">
- <xsl:attribute name="version">5.0</xsl:attribute>
- </xsl:if>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates mode="addNS"/>
- </xsl:copy>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template match="comment()|processing-instruction()|text()" mode="addNS">
- <xsl:copy/>
-</xsl:template>
-
-<!-- ====================================================================== -->
-
-<xsl:template name="emit-message">
- <xsl:param name="message"/>
- <xsl:message>
- <xsl:value-of select="$message"/>
- <xsl:text> (</xsl:text>
- <xsl:value-of select="$rootid"/>
- <xsl:text>)</xsl:text>
- </xsl:message>
-</xsl:template>
-
-</xsl:stylesheet>
+++ /dev/null
-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() {
-
- }
-
-}
--- /dev/null
+package org.argeo.app.ux;
+
+import org.argeo.cms.AbstractCmsApp;
+
+public abstract class AbstractArgeoApp extends AbstractCmsApp {
+
+}
--- /dev/null
+package org.argeo.app.ux;
+
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.cms.Localized;
+
+public interface AppUi extends CmsUi {
+ Localized getTitle();
+
+ boolean isLoginScreen();
+
+}
--- /dev/null
+package org.argeo.app.ux;
+
+import org.argeo.api.cms.ux.CmsIcon;
+
+/** Icon names used by Argeo Suite. */
+public enum SuiteIcon implements CmsIcon {
+ add, save, close, closeAll, search, delete, logout, dashboard,
+ // people
+ people, group, person, organisation, addressBook, users, organisationContact,
+ // library
+ documents, document, folder,
+ // management
+ report,
+ // admin and settings
+ settings, user,
+ // misc
+ task, tag, location, inbox, map, todo,
+ // actions
+ openUserMenu,
+ //
+ ;
+}
--- /dev/null
+package org.argeo.app.ux;
+
+import org.argeo.cms.Localized;
+
+/** Localized messages. */
+public enum SuiteMsg implements Localized {
+ // Entities
+ user, org, person, group,
+ // UI parts
+ dashboard, people, documents, locations, recentItems,
+ // NewPersonWizard
+ firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle, personWizardFeedback,
+ // NewOrgWizard
+ orgWizardWindowTitle, orgWizardPageTitle, orgWizardFeedback, legalName, legalForm, vatId,
+ // Roles
+ userAdminRole, groupAdminRole, publisherRole, coworkerRole,
+ // Group
+ chooseAMember,
+ // ContextAddressComposite
+ chooseAnOrganisation, street, streetComplement, zipCode, city, state, country, geopoint,
+ // FilteredOrderableEntityTable
+ filterHelp,
+ // BankAccountComposite
+ accountHolder, bankName, currency, accountNumber, bankNumber, BIC, IBAN,
+ // EditJobDialog
+ position, chosenItem, department, isPrimary, searchAndChooseEntity,
+ // ContactListCTab (e4)
+ notes, addAContact, contactValue, linkedCompany,
+ // OrgAdminInfoCTab (e4)
+ paymentAccount,
+ // OrgEditor (e4)
+ orgDetails, orgActivityLog, team, orgAdmin,
+ // PersonEditor (e4)
+ personDetails, personActivityLog, personOrgs, personSecurity,
+ // PersonSecurityCTab (e4)
+ resetPassword,
+ // Generic
+ label, aCustomLabel, description, value, name, primary, add, save, pickup,
+ // Tag
+ confirmNewTag, cannotCreateTag,
+ // Feedback messages
+ allFieldsMustBeSet,
+ //
+ ;
+}
--- /dev/null
+package org.argeo.app.ux;
+
+import org.argeo.api.cms.ux.CmsStyle;
+
+/** Styles used by Argeo Suite work UI. */
+public enum SuiteStyle implements CmsStyle {
+ // header
+ header, headerTitle, headerMenu, headerMenuItem,
+ // footer
+ footer,
+ // recent items
+ recentItems,
+ // lead pane
+ leadPane, leadPaneItem, leadPaneSectionTitle, leadPaneSubSectionTitle,
+ // entry area
+ entryArea,
+ // group composite
+ titleContainer, titleLabel, subTitleLabel, formLine, formColumn, navigationBar, navigationTitle, navigationButton,
+ // forms elements
+ simpleLabel, simpleText, simpleInput,
+ // table
+ titleCell,
+ // layers
+ workArea,
+ // tabbed area
+ mainTabBody, mainTabSelected, mainTab,
+ // buttons
+ inlineButton;
+
+ @Override
+ public String getClassPrefix() {
+ return "argeo-suite";
+ }
+
+}
--- /dev/null
+package org.argeo.app.ux;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsEvent;
+
+/** Events specific to Argeo Suite UX. */
+public enum SuiteUxEvent implements CmsEvent {
+ openNewPart, refreshPart, switchLayer;
+
+ public final static String LAYER = "layer";
+// public final static String USERNAME = "username";
+
+ // ACR
+ public final static String CONTENT_PATH = "contentPath";
+
+ public String getTopicBase() {
+ return "argeo.suite.ui";
+ }
+
+ public static Map<String, Object> eventProperties(Content content) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(CONTENT_PATH, content.getPath());
+ return properties;
+ }
+
+// public static Map<String, Object> eventProperties(User user) {
+// Map<String, Object> properties = new HashMap<>();
+// properties.put(USERNAME, user.getName());
+// return properties;
+// }
+}
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 {
* 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);
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.app.jcr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.argeo.internal.app.jcr.appUserState">
+ <implementation class="org.argeo.internal.app.jcr.AppUserStateImpl"/>
+ <service>
+ <provide interface="org.argeo.app.api.AppUserState"/>
+ </service>
+ <reference bind="setJcrContentProvider" cardinality="1..1" interface="org.argeo.cms.jcr.acr.JcrContentProvider" name="JcrContentProvider" policy="static"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="true" name="Suite Maintenance Service">
+ <implementation class="org.argeo.internal.app.jcr.SuiteMaintenanceService"/>
+ <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
+ <reference bind="setUserTransaction" cardinality="1..1" interface="org.argeo.api.cms.transaction.WorkTransaction" name="WorkTransaction" policy="static"/>
+ <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.spi.ProvidedRepository" name="ContentRepository" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="true" name="Suite Terms Manager">
+ <implementation class="org.argeo.app.jcr.terms.SuiteTermsManager"/>
+ <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
+ <service>
+ <provide interface="org.argeo.app.api.TermsManager"/>
+ </service>
+</scr:component>
--- /dev/null
+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
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
--- /dev/null
+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<String> getTypologies() {
+ return new ArrayList<>();
+ }
+
+ protected String getTypologiesLoadBase() {
+ return "";
+ }
+
+ protected void loadTypologies(Node customBaseNode) throws RepositoryException, IOException {
+ List<String> 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...");
+ if (termsBase.hasNode(name))
+ 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;
+ }
+ }
+
+}
--- /dev/null
+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<String, String> 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<String, String> 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();
+ }
+
+}
--- /dev/null
+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.uuid().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.uuid().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() {
+ }
+}
--- /dev/null
+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() {
+
+ }
+}
--- /dev/null
+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();
+ }
+ }
+
+}
--- /dev/null
+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() {
+ }
+
+}
--- /dev/null
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:db = "http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ exclude-result-prefixes="exsl db"
+ version="1.0">
+
+<!--
+# ======================================================================
+# This file is part of DocBook V5.0CR5
+#
+# Copyright 2005 Norman Walsh, Sun Microsystems, Inc., and the
+# Organization for the Advancement of Structured Information
+# Standards (OASIS).
+#
+# Release: $Id: db4-upgrade.xsl 7660 2008-02-06 13:48:36Z nwalsh $
+#
+# Permission to use, copy, modify and distribute this stylesheet
+# and its accompanying documentation for any purpose and without fee
+# is hereby granted in perpetuity, provided that the above copyright
+# notice and this paragraph appear in all copies. The copyright
+# holders make no representation about the suitability of the schema
+# for any purpose. It is provided "as is" without expressed or implied
+# warranty.
+#
+# Please direct all questions, bug reports, or suggestions for changes
+# to the docbook@lists.oasis-open.org mailing list. For more
+# information, see http://www.oasis-open.org/docbook/.
+#
+# ======================================================================
+-->
+
+<xsl:variable name="version" select="'1.0'"/>
+
+<xsl:output method="xml" encoding="utf-8" indent="no" omit-xml-declaration="yes"/>
+
+<xsl:preserve-space elements="*"/>
+<xsl:param name="rootid">
+ <xsl:choose>
+ <xsl:when test="/*/@id">
+ <xsl:value-of select="/*/@id"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>UNKNOWN</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:param>
+
+<xsl:param name="defaultDate" select="''"/>
+
+<xsl:template match="/">
+ <xsl:variable name="converted">
+ <xsl:apply-templates/>
+ </xsl:variable>
+ <xsl:comment>
+ <xsl:text> Converted by db4-upgrade version </xsl:text>
+ <xsl:value-of select="$version"/>
+ <xsl:text> </xsl:text>
+ </xsl:comment>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates select="exsl:node-set($converted)/*" mode="addNS"/>
+</xsl:template>
+
+<xsl:template match="bookinfo|chapterinfo|articleinfo|artheader|appendixinfo
+ |blockinfo
+ |bibliographyinfo|glossaryinfo|indexinfo|setinfo
+ |setindexinfo
+ |sect1info|sect2info|sect3info|sect4info|sect5info
+ |sectioninfo
+ |refsect1info|refsect2info|refsect3info|refsectioninfo
+ |referenceinfo|partinfo"
+ priority="200">
+ <info>
+ <xsl:call-template name="copy.attributes"/>
+
+ <!-- titles can be inside or outside or both. fix that -->
+ <xsl:choose>
+ <xsl:when test="title and following-sibling::title">
+ <xsl:if test="title != following-sibling::title">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> title.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="title" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="title">
+ <xsl:apply-templates select="title" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::title">
+ <xsl:apply-templates select="following-sibling::title" mode="copy"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text>: no title.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="titleabbrev and following-sibling::titleabbrev">
+ <xsl:if test="titleabbrev != following-sibling::titleabbrev">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> titleabbrev.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="titleabbrev">
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::titleabbrev">
+ <xsl:apply-templates select="following-sibling::titleabbrev" mode="copy"/>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="subtitle and following-sibling::subtitle">
+ <xsl:if test="subtitle != following-sibling::subtitle">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> subtitle.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="subtitle">
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::subtitle">
+ <xsl:apply-templates select="following-sibling::subtitle" mode="copy"/>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:apply-templates/>
+ </info>
+</xsl:template>
+
+<xsl:template match="objectinfo|prefaceinfo|refsynopsisdivinfo
+ |screeninfo|sidebarinfo"
+ priority="200">
+ <info>
+ <xsl:call-template name="copy.attributes"/>
+
+ <!-- titles can be inside or outside or both. fix that -->
+ <xsl:choose>
+ <xsl:when test="title and following-sibling::title">
+ <xsl:if test="title != following-sibling::title">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> title.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="title" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="title">
+ <xsl:apply-templates select="title" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::title">
+ <xsl:apply-templates select="following-sibling::title" mode="copy"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- it's ok if there's no title on these -->
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="titleabbrev and following-sibling::titleabbrev">
+ <xsl:if test="titleabbrev != following-sibling::titleabbrev">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> titleabbrev.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="titleabbrev">
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::titleabbrev">
+ <xsl:apply-templates select="following-sibling::titleabbrev" mode="copy"/>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="subtitle and following-sibling::subtitle">
+ <xsl:if test="subtitle != following-sibling::subtitle">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check </xsl:text>
+ <xsl:value-of select="name(..)"/>
+ <xsl:text> subtitle.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="subtitle">
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </xsl:when>
+ <xsl:when test="following-sibling::subtitle">
+ <xsl:apply-templates select="following-sibling::subtitle" mode="copy"/>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:apply-templates/>
+ </info>
+</xsl:template>
+
+<xsl:template match="refentryinfo"
+ priority="200">
+ <info>
+ <xsl:call-template name="copy.attributes"/>
+
+ <!-- titles can be inside or outside or both. fix that -->
+ <xsl:if test="title">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding title from refentryinfo!</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="titleabbrev">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding titleabbrev from refentryinfo!</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="subtitle">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding subtitle from refentryinfo!</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:apply-templates/>
+ </info>
+</xsl:template>
+
+<xsl:template match="refmiscinfo"
+ priority="200">
+ <refmiscinfo>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'class'"/>
+ </xsl:call-template>
+ <xsl:if test="@class">
+ <xsl:choose>
+ <xsl:when test="@class = 'source'
+ or @class = 'version'
+ or @class = 'manual'
+ or @class = 'sectdesc'
+ or @class = 'software'">
+ <xsl:attribute name="class">
+ <xsl:value-of select="@class"/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:attribute name="class">
+ <xsl:value-of select="'other'"/>
+ </xsl:attribute>
+ <xsl:attribute name="otherclass">
+ <xsl:value-of select="@class"/>
+ </xsl:attribute>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </refmiscinfo>
+</xsl:template>
+
+<xsl:template match="corpauthor" priority="200">
+ <author>
+ <xsl:call-template name="copy.attributes"/>
+ <orgname>
+ <xsl:apply-templates/>
+ </orgname>
+ </author>
+</xsl:template>
+
+<xsl:template match="corpname" priority="200">
+ <orgname>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </orgname>
+</xsl:template>
+
+<xsl:template match="author[not(personname)]|editor[not(personname)]|othercredit[not(personname)]" priority="200">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <personname>
+ <xsl:apply-templates select="honorific|firstname|surname|othername|lineage"/>
+ </personname>
+ <xsl:apply-templates select="*[not(self::honorific|self::firstname|self::surname
+ |self::othername|self::lineage)]"/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="address|programlisting|screen|funcsynopsisinfo
+ |classsynopsisinfo|literallayout" priority="200">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'format'"/>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="productname[@class]" priority="200">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Dropping class attribute from productname</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'class'"/>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="dedication|preface|chapter|appendix|part|partintro
+ |article|bibliography|glossary|glossdiv|index
+ |reference[not(referenceinfo)]
+ |book" priority="200">
+ <xsl:choose>
+ <xsl:when test="not(dedicationinfo|prefaceinfo|chapterinfo
+ |appendixinfo|partinfo
+ |articleinfo|artheader|bibliographyinfo
+ |glossaryinfo|indexinfo
+ |bookinfo)">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:if test="title|subtitle|titleabbrev">
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ <xsl:apply-templates select="abstract" mode="copy"/>
+ </info>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="formalpara|figure|table[tgroup]|example|blockquote
+ |caution|important|note|warning|tip
+ |bibliodiv|glossarydiv|indexdiv
+ |orderedlist|itemizedlist|variablelist|procedure
+ |task|tasksummary|taskprerequisites|taskrelated
+ |sidebar"
+ priority="200">
+ <xsl:choose>
+ <xsl:when test="blockinfo">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+
+ <xsl:if test="title|titleabbrev|subtitle">
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </info>
+ </xsl:if>
+
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="equation" priority="200">
+ <xsl:choose>
+ <xsl:when test="not(title)">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param
+ name="message"
+ >Convert equation without title to informal equation.</xsl:with-param>
+ </xsl:call-template>
+ <informalequation>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </informalequation>
+ </xsl:when>
+ <xsl:when test="blockinfo">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ </info>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="sect1|sect2|sect3|sect4|sect5|section"
+ priority="200">
+ <section>
+ <xsl:call-template name="copy.attributes"/>
+
+ <xsl:if test="not(sect1info|sect2info|sect3info|sect4info|sect5info|sectioninfo)">
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ <xsl:apply-templates select="abstract" mode="copy"/>
+ </info>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </section>
+</xsl:template>
+
+<xsl:template match="simplesect"
+ priority="200">
+ <simplesect>
+ <xsl:call-template name="copy.attributes"/>
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ <xsl:apply-templates select="abstract" mode="copy"/>
+ </info>
+ <xsl:apply-templates/>
+ </simplesect>
+</xsl:template>
+
+<xsl:template match="refsect1|refsect2|refsect3|refsection" priority="200">
+ <refsection>
+ <xsl:call-template name="copy.attributes"/>
+
+ <xsl:if test="not(refsect1info|refsect2info|refsect3info|refsectioninfo)">
+ <info>
+ <xsl:apply-templates select="title" mode="copy"/>
+ <xsl:apply-templates select="titleabbrev" mode="copy"/>
+ <xsl:apply-templates select="subtitle" mode="copy"/>
+ <xsl:apply-templates select="abstract" mode="copy"/>
+ </info>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </refsection>
+</xsl:template>
+
+<xsl:template match="imagedata|videodata|audiodata|textdata" priority="200">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'srccredit'"/>
+ </xsl:call-template>
+ <xsl:if test="@srccredit">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check conversion of srccredit </xsl:text>
+ <xsl:text>(othercredit="srccredit").</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <info>
+ <othercredit class="other" otherclass="srccredit">
+ <orgname>???</orgname>
+ <contrib>
+ <xsl:value-of select="@srccredit"/>
+ </contrib>
+ </othercredit>
+ </info>
+ </xsl:if>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="sgmltag" priority="200">
+ <tag>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:if test="@class = 'sgmlcomment'">
+ <xsl:attribute name="class">comment</xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </tag>
+</xsl:template>
+
+<xsl:template match="inlinegraphic[@format='linespecific']" priority="210">
+ <textobject>
+ <textdata>
+ <xsl:call-template name="copy.attributes"/>
+ </textdata>
+ </textobject>
+</xsl:template>
+
+<xsl:template match="inlinegraphic" priority="200">
+ <inlinemediaobject>
+ <imageobject>
+ <imagedata>
+ <xsl:call-template name="copy.attributes"/>
+ </imagedata>
+ </imageobject>
+ </inlinemediaobject>
+</xsl:template>
+
+<xsl:template match="graphic[@format='linespecific']" priority="210">
+ <mediaobject>
+ <textobject>
+ <textdata>
+ <xsl:call-template name="copy.attributes"/>
+ </textdata>
+ </textobject>
+ </mediaobject>
+</xsl:template>
+
+<xsl:template match="graphic" priority="200">
+ <mediaobject>
+ <imageobject>
+ <imagedata>
+ <xsl:call-template name="copy.attributes"/>
+ </imagedata>
+ </imageobject>
+ </mediaobject>
+</xsl:template>
+
+<xsl:template match="pubsnumber" priority="200">
+ <biblioid class="pubsnumber">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </biblioid>
+</xsl:template>
+
+<xsl:template match="invpartnumber" priority="200">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting invpartnumber to biblioid otherclass="invpartnumber".</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <biblioid class="other" otherclass="invpartnumber">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </biblioid>
+</xsl:template>
+
+<xsl:template match="contractsponsor" priority="200">
+ <xsl:variable name="contractnum"
+ select="preceding-sibling::contractnum|following-sibling::contractnum"/>
+
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting contractsponsor to othercredit="contractsponsor".</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <othercredit class="other" otherclass="contractsponsor">
+ <orgname>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </orgname>
+ <xsl:for-each select="$contractnum">
+ <contrib role="contractnum">
+ <xsl:apply-templates select="node()"/>
+ </contrib>
+ </xsl:for-each>
+ </othercredit>
+</xsl:template>
+
+<xsl:template match="contractnum" priority="200">
+ <xsl:if test="not(preceding-sibling::contractsponsor
+ |following-sibling::contractsponsor)
+ and not(preceding-sibling::contractnum)">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting contractnum to othercredit="contractnum".</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <othercredit class="other" otherclass="contractnum">
+ <orgname>???</orgname>
+ <xsl:for-each select="self::contractnum
+ |preceding-sibling::contractnum
+ |following-sibling::contractnum">
+ <contrib>
+ <xsl:apply-templates select="node()"/>
+ </contrib>
+ </xsl:for-each>
+ </othercredit>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="isbn|issn" priority="200">
+ <biblioid class="{local-name(.)}">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </biblioid>
+</xsl:template>
+
+<xsl:template match="biblioid[count(*) = 1
+ and ulink
+ and normalize-space(text()) = '']" priority="200">
+ <biblioid xlink:href="{ulink/@url}">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates select="ulink/node()"/>
+ </biblioid>
+</xsl:template>
+
+<xsl:template match="authorblurb" priority="200">
+ <personblurb>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </personblurb>
+</xsl:template>
+
+<xsl:template match="collabname" priority="200">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check conversion of collabname </xsl:text>
+ <xsl:text>(orgname role="collabname").</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <orgname role="collabname">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </orgname>
+</xsl:template>
+
+<xsl:template match="modespec" priority="200">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding modespec (</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>).</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="mediaobjectco" priority="200">
+ <mediaobject>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </mediaobject>
+</xsl:template>
+
+<xsl:template match="remark" priority="200">
+ <!-- get rid of any embedded markup -->
+ <remark>
+ <xsl:copy-of select="@*"/>
+ <xsl:value-of select="."/>
+ </remark>
+</xsl:template>
+
+<xsl:template match="biblioentry/title
+ |bibliomset/title
+ |biblioset/title
+ |bibliomixed/title" priority="400">
+ <citetitle>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </citetitle>
+</xsl:template>
+
+<xsl:template match="biblioentry/titleabbrev|biblioentry/subtitle
+ |bibliomset/titleabbrev|bibliomset/subtitle
+ |biblioset/titleabbrev|biblioset/subtitle
+ |bibliomixed/titleabbrev|bibliomixed/subtitle"
+ priority="400">
+ <xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="biblioentry/contrib
+ |bibliomset/contrib
+ |bibliomixed/contrib" priority="200">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check conversion of contrib </xsl:text>
+ <xsl:text>(othercontrib="contrib").</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <othercredit class="other" otherclass="contrib">
+ <orgname>???</orgname>
+ <contrib>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </contrib>
+ </othercredit>
+</xsl:template>
+
+<xsl:template match="link" priority="200">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="ulink" priority="200">
+ <xsl:choose>
+ <xsl:when test="node()">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting ulink to link.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <link xlink:href="{@url}">
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'url'"/>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </link>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting ulink to uri.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <uri xlink:href="{@url}">
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'url'"/>
+ </xsl:call-template>
+ <xsl:value-of select="@url"/>
+ </uri>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="olink" priority="200">
+ <xsl:if test="@linkmode">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding linkmode on olink.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="@targetdocent">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting olink targetdocent to targetdoc.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <olink targetdoc="{unparsed-entity-uri(@targetdocent)}">
+ <xsl:for-each select="@*">
+ <xsl:if test="name(.) != 'targetdocent'
+ and name(.) != 'linkmode'">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </olink>
+ </xsl:when>
+ <xsl:otherwise>
+ <olink>
+ <xsl:for-each select="@*">
+ <xsl:if test="name(.) != 'linkmode'">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </olink>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="biblioentry/firstname
+ |biblioentry/surname
+ |biblioentry/othername
+ |biblioentry/lineage
+ |biblioentry/honorific
+ |bibliomset/firstname
+ |bibliomset/surname
+ |bibliomset/othername
+ |bibliomset/lineage
+ |bibliomset/honorific" priority="200">
+ <xsl:choose>
+ <xsl:when test="preceding-sibling::firstname
+ |preceding-sibling::surname
+ |preceding-sibling::othername
+ |preceding-sibling::lineage
+ |preceding-sibling::honorific">
+ <!-- nop -->
+ </xsl:when>
+ <xsl:otherwise>
+ <personname>
+ <xsl:apply-templates select="../firstname
+ |../surname
+ |../othername
+ |../lineage
+ |../honorific" mode="copy"/>
+ </personname>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="areaset" priority="200">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'coords'"/>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="date|pubdate" priority="200">
+ <xsl:variable name="rp1" select="substring-before(normalize-space(.), ' ')"/>
+ <xsl:variable name="rp2"
+ select="substring-before(substring-after(normalize-space(.), ' '),
+ ' ')"/>
+ <xsl:variable name="rp3"
+ select="substring-after(substring-after(normalize-space(.), ' '), ' ')"/>
+
+ <xsl:variable name="p1">
+ <xsl:choose>
+ <xsl:when test="contains($rp1, ',')">
+ <xsl:value-of select="substring-before($rp1, ',')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$rp1"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="p2">
+ <xsl:choose>
+ <xsl:when test="contains($rp2, ',')">
+ <xsl:value-of select="substring-before($rp2, ',')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$rp2"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="p3">
+ <xsl:choose>
+ <xsl:when test="contains($rp3, ',')">
+ <xsl:value-of select="substring-before($rp3, ',')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$rp3"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="date">
+ <xsl:choose>
+ <xsl:when test="string($p1+1) != 'NaN' and string($p3+1) != 'NaN'">
+ <xsl:choose>
+ <xsl:when test="$p2 = 'Jan' or $p2 = 'January'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-01-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Feb' or $p2 = 'February'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-02-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Mar' or $p2 = 'March'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-03-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Apr' or $p2 = 'April'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-04-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'May'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-05-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Jun' or $p2 = 'June'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-06-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Jul' or $p2 = 'July'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-07-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Aug' or $p2 = 'August'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-08-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Sep' or $p2 = 'September'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-09-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Oct' or $p2 = 'October'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-10-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Nov' or $p2 = 'November'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-11-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p2 = 'Dec' or $p2 = 'December'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-12-</xsl:text>
+ <xsl:number value="$p1" format="01"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="string($p2+1) != 'NaN' and string($p3+1) != 'NaN'">
+ <xsl:choose>
+ <xsl:when test="$p1 = 'Jan' or $p1 = 'January'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-01-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Feb' or $p1 = 'February'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-02-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Mar' or $p1 = 'March'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-03-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Apr' or $p1 = 'April'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-04-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'May'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-05-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Jun' or $p1 = 'June'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-06-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Jul' or $p1 = 'July'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-07-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Aug' or $p1 = 'August'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-08-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Sep' or $p1 = 'September'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-09-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Oct' or $p1 = 'October'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-10-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Nov' or $p1 = 'November'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-11-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:when test="$p1 = 'Dec' or $p1 = 'December'">
+ <xsl:number value="$p3" format="0001"/>
+ <xsl:text>-12-</xsl:text>
+ <xsl:number value="$p2" format="01"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="normalize-space($date) != normalize-space(.)">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converted </xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text> into </xsl:text>
+ <xsl:value-of select="$date"/>
+ <xsl:text> for </xsl:text>
+ <xsl:value-of select="name(.)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:value-of select="$date"/>
+ </xsl:copy>
+ </xsl:when>
+
+ <xsl:when test="$defaultDate != ''">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Unparseable date: </xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text> in </xsl:text>
+ <xsl:value-of select="name(.)"/>
+ <xsl:text> (Using default: </xsl:text>
+ <xsl:value-of select="$defaultDate"/>
+ <xsl:text>)</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:copy-of select="$defaultDate"/>
+ <xsl:comment>
+ <xsl:value-of select="."/>
+ </xsl:comment>
+ </xsl:copy>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- these don't really matter anymore
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Unparseable date: </xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text> in </xsl:text>
+ <xsl:value-of select="name(.)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ -->
+ <xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="title|subtitle|titleabbrev" priority="300">
+ <!-- nop -->
+</xsl:template>
+
+<xsl:template match="abstract" priority="300">
+ <xsl:if test="not(contains(name(parent::*),'info'))">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Check abstract; moved into info correctly?</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="indexterm">
+ <!-- don't copy the defaulted significance='normal' attribute -->
+ <indexterm>
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress">
+ <xsl:if test="@significance = 'normal'">significance</xsl:if>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </indexterm>
+</xsl:template>
+
+<xsl:template match="ackno" priority="200">
+ <acknowledgements>
+ <xsl:copy-of select="@*"/>
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </acknowledgements>
+</xsl:template>
+
+<xsl:template match="lot|lotentry|tocback|tocchap|tocfront|toclevel1|
+ toclevel2|toclevel3|toclevel4|toclevel5|tocpart" priority="200">
+ <tocdiv>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+ </tocdiv>
+</xsl:template>
+
+<xsl:template match="action" priority="200">
+ <phrase remap="action">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </phrase>
+</xsl:template>
+
+<xsl:template match="beginpage" priority="200">
+ <xsl:comment> beginpage pagenum=<xsl:value-of select="@pagenum"/> </xsl:comment>
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Replacing beginpage with comment</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="structname|structfield" priority="200">
+ <varname remap="{local-name(.)}">
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </varname>
+</xsl:template>
+
+<!-- ====================================================================== -->
+
+<!-- 6 Feb 2008, ndw changed mode=copy so that it only copies the first level,
+ then it switches back to "normal" mode so that other rewriting templates
+ catch embedded fixes -->
+
+<!--
+<xsl:template match="ulink" priority="200" mode="copy">
+ <xsl:choose>
+ <xsl:when test="node()">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting ulink to phrase.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <phrase xlink:href="{@url}">
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'url'"/>
+ </xsl:call-template>
+ <xsl:apply-templates/>
+ </phrase>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting ulink to uri.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <uri xlink:href="{@url}">
+ <xsl:call-template name="copy.attributes">
+ <xsl:with-param name="suppress" select="'url'"/>
+ </xsl:call-template>
+ <xsl:value-of select="@url"/>
+ </uri>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="sgmltag" priority="200" mode="copy">
+ <tag>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </tag>
+</xsl:template>
+-->
+
+<xsl:template match="*" mode="copy">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<!--
+<xsl:template match="comment()|processing-instruction()|text()" mode="copy">
+ <xsl:copy/>
+</xsl:template>
+-->
+
+<!-- ====================================================================== -->
+
+<xsl:template match="*">
+ <xsl:copy>
+ <xsl:call-template name="copy.attributes"/>
+ <xsl:apply-templates/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="comment()|processing-instruction()|text()">
+ <xsl:copy/>
+</xsl:template>
+
+<!-- ====================================================================== -->
+
+<xsl:template name="copy.attributes">
+ <xsl:param name="src" select="."/>
+ <xsl:param name="suppress" select="''"/>
+
+ <xsl:for-each select="$src/@*">
+ <xsl:choose>
+ <xsl:when test="local-name(.) = 'moreinfo'">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding moreinfo on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="local-name(.) = 'lang'">
+ <xsl:attribute name="xml:lang">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="local-name(.) = 'id'">
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$suppress = local-name(.)"/>
+ <xsl:when test="local-name(.) = 'float'">
+ <xsl:choose>
+ <xsl:when test=". = '1'">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding float on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:if test="not($src/@floatstyle)">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Adding floatstyle='normal' on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:attribute name="floatstyle">
+ <xsl:text>normal</xsl:text>
+ </xsl:attribute>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test=". = '0'">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding float on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Discarding float on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:if test="not($src/@floatstyle)">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Adding floatstyle='</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text>' on </xsl:text>
+ <xsl:value-of select="local-name($src)"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:attribute name="floatstyle">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="local-name(.) = 'entityref'">
+ <xsl:attribute name="fileref">
+ <xsl:value-of select="unparsed-entity-uri(@entityref)"/>
+ </xsl:attribute>
+ </xsl:when>
+
+ <xsl:when test="local-name($src) = 'simplemsgentry'
+ and local-name(.) = 'audience'">
+ <xsl:attribute name="msgaud">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="local-name($src) = 'simplemsgentry'
+ and local-name(.) = 'origin'">
+ <xsl:attribute name="msgorig">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="local-name($src) = 'simplemsgentry'
+ and local-name(.) = 'level'">
+ <xsl:attribute name="msglevel">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:when>
+
+ <!-- * for upgrading XSL litprog params documentation -->
+ <xsl:when test="local-name($src) = 'refmiscinfo'
+ and local-name(.) = 'role'
+ and . = 'type'
+ ">
+ <xsl:call-template name="emit-message">
+ <xsl:with-param name="message">
+ <xsl:text>Converting refmiscinfo@role=type to </xsl:text>
+ <xsl:text>@class=other,otherclass=type</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:attribute name="class">other</xsl:attribute>
+ <xsl:attribute name="otherclass">type</xsl:attribute>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:copy/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+</xsl:template>
+
+<!-- ====================================================================== -->
+
+<xsl:template match="*" mode="addNS">
+ <xsl:choose>
+ <xsl:when test="namespace-uri(.) = ''">
+ <xsl:element name="{local-name(.)}"
+ namespace="http://docbook.org/ns/docbook">
+ <xsl:if test="not(parent::*)">
+ <xsl:attribute name="version">5.0</xsl:attribute>
+ </xsl:if>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates mode="addNS"/>
+ </xsl:element>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy>
+ <xsl:if test="not(parent::*)">
+ <xsl:attribute name="version">5.0</xsl:attribute>
+ </xsl:if>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates mode="addNS"/>
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="comment()|processing-instruction()|text()" mode="addNS">
+ <xsl:copy/>
+</xsl:template>
+
+<!-- ====================================================================== -->
+
+<xsl:template name="emit-message">
+ <xsl:param name="message"/>
+ <xsl:message>
+ <xsl:value-of select="$message"/>
+ <xsl:text> (</xsl:text>
+ <xsl:value-of select="$rootid"/>
+ <xsl:text>)</xsl:text>
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
--- /dev/null
+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() {
+
+ }
+
+}
--- /dev/null
+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<SuiteTerm> 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<SuiteTerm> getSubTerms() {
+ return subTerms;
+ }
+
+ @Override
+ public SuiteTerm getParentTerm() {
+ return parentTerm;
+ }
+
+}
--- /dev/null
+package org.argeo.app.jcr.terms;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+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.app.api.Typology;
+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<String, SuiteTerm> terms = new HashMap<>();
+ private final Map<String, SuiteTypology> typologies = new HashMap<>();
+
+ // JCR
+ private Repository repository;
+ private Session adminSession;
+
+ public void init() {
+ adminSession = CmsJcrUtils.openDataAdminSession(repository, CmsConstants.SYS_WORKSPACE);
+ }
+
+ @Override
+ public List<Term> listAllTerms(String typology) {
+ List<Term> 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;
+ }
+
+ @Override
+ public Set<Typology> getTypologies() {
+ Set<Typology> res = new TreeSet<>((o1, o2) -> o1.getId().compareTo(o2.getId()));
+ NodeIterator termsNodes = Jcr.executeQuery(adminSession, "SELECT * FROM [{0}]", EntityType.terms.get());
+ for (Node termsNode : Jcr.iterate(termsNodes)) {
+ res.add(loadTypology(termsNode));
+ }
+ return res;
+ }
+
+ 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;
+ }
+
+}
--- /dev/null
+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<SuiteTerm> 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<SuiteTerm> getSubTerms() {
+ return subTerms;
+ }
+
+ public List<SuiteTerm> getAllTerms() {
+ if (isFlat)
+ return subTerms;
+ else {
+ List<SuiteTerm> terms = new ArrayList<>();
+ for (SuiteTerm subTerm : subTerms) {
+ terms.add(subTerm);
+ collectSubTerms(terms, subTerm);
+ }
+ return terms;
+ }
+ }
+
+ public Term findTermByName(String name) {
+ List<SuiteTerm> 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<SuiteTerm> collected) {
+ if (term.getName().equals(name)) {
+ collected.add(term);
+ }
+ for (SuiteTerm subTerm : term.getSubTerms()) {
+ collectTermsByName(subTerm, name, collected);
+ }
+ }
+
+ private void collectSubTerms(List<SuiteTerm> terms, SuiteTerm term) {
+ for (SuiteTerm subTerm : term.getSubTerms()) {
+ terms.add(subTerm);
+ collectSubTerms(terms, subTerm);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.internal.app.jcr;
+
+import javax.jcr.Node;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+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 ContentRepository contentRepository;
+ private JcrContentProvider jcrContentProvider;
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public Content getOrCreateSessionDir(CmsSession session) {
+ Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
+ Node node = SuiteJcrUtils.getOrCreateCmsSessionNode(adminSession, session);
+ return node;
+ });
+ ContentSession contentSession = ContentUtils.openSession(contentRepository, session);
+ Content userDir = contentSession.get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + Jcr.getPath(userDirNode));
+ return userDir;
+ }
+
+ public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
+ this.jcrContentProvider = jcrContentProvider;
+ }
+
+ public void setContentRepository(ContentRepository contentRepository) {
+ this.contentRepository = contentRepository;
+ }
+
+}
--- /dev/null
+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);
+ }
+
+}
<property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/submission"/>
<property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
<property name="osgi.http.whiteboard.servlet.multipart.enabled" type="String" value="true"/>
- <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=ego)"/>
<reference bind="addSubmissionListener" cardinality="0..n" interface="org.argeo.app.xforms.FormSubmissionListener" name="FormSubmissionListener" policy="dynamic" unbind="removeSubmissionListener"/>
+ <reference bind="setAppUserState" cardinality="1..1" interface="org.argeo.app.api.AppUserState" name="AppUserState" policy="static"/>
</scr:component>
if (target.isNodeType(NodeType.NT_QUERY)) {
Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target);
QueryResult queryResult = query.execute();
- String[] columnNames = queryResult.getColumnNames();
+ List<String> columnNames = new ArrayList<>();
+ for (String c : queryResult.getColumnNames()) {
+ columnNames.add(c);
+ }
+ // TODO make it more configurable
+ columnNames.add("display");
+
if (EntityMimeType.XML.equals(mimeType)) {
} else if (EntityMimeType.CSV.equals(mimeType)) {
CsvWriter csvWriter = new CsvWriter(out, charset);
for (Value value : values) {
lst.add(value.getString());
}
+ // display
+ lst.add(row.getValue("name").getString() + " (" + row.getValue("label").getString() + ")");
csvWriter.writeLine(lst);
}
} else {
// corner case of an empty initial database
List<String> lst = new ArrayList<>();
- for (int i = 0; i < columnNames.length; i++)
+ for (int i = 0; i < columnNames.size(); i++)
lst.add("-");
csvWriter.writeLine(lst);
}
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.Node;
import javax.jcr.Property;
-import javax.jcr.Repository;
-import javax.jcr.Session;
import javax.jcr.nodetype.NodeType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
+import org.argeo.api.acr.Content;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsSession;
-import org.argeo.app.core.SuiteUtils;
+import org.argeo.app.api.AppUserState;
import org.argeo.app.image.ImageProcessor;
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;
/** Receives a form submission. */
private DateTimeFormatter submissionNameFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmmssSSS")
.withZone(ZoneId.from(ZoneOffset.UTC));
- private Repository repository;
+// private Repository repository;
+// private ContentRepository contentRepository;
private Set<FormSubmissionListener> submissionListeners = new HashSet<>();
+ private AppUserState appUserState;
+
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
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);
-
+// Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), request);
+//
CmsSession cmsSession = RemoteAuthUtils.getCmsSession(request);
- Session adminSession = null;
- try {
- // TODO centralise at a deeper level
- adminSession = CmsJcrUtils.openDataAdminSession(repository, null);
- SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
- } finally {
- Jcr.logout(adminSession);
- }
+// Session adminSession = null;
+// try {
+// // TODO centralise at a deeper level
+// adminSession = CmsJcrUtils.openDataAdminSession(repository, null);
+// SuiteJcrUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
+// } finally {
+// Jcr.logout(adminSession);
+// }
try {
- Node cmsSessionNode = SuiteUtils.getCmsSessionNode(session, cmsSession);
+ Content sessionDir = appUserState.getOrCreateSessionDir(cmsSession);
+ Node cmsSessionNode = sessionDir.adapt(Node.class);
+ // Node cmsSessionNode = SuiteJcrUtils.getCmsSessionNode(session, cmsSession);
Node submission = cmsSessionNode.addNode(submissionNameFormatter.format(Instant.now()),
OrxType.submission.get());
for (Part part : req.getParts()) {
if (part.getName().equals(XML_SUBMISSION_FILE)) {
Node xml = submission.addNode(XML_SUBMISSION_FILE, NodeType.NT_UNSTRUCTURED);
- session.importXML(xml.getPath(), part.getInputStream(),
+ cmsSessionNode.getSession().importXML(xml.getPath(), part.getInputStream(),
ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
} else {
}
}
- session.save();
+ cmsSessionNode.getSession().save();
try {
for (FormSubmissionListener submissionListener : submissionListeners) {
- submissionListener.formSubmissionReceived(submission);
+ submissionListener.formSubmissionReceived(JcrContent.nodeToContent(submission));
}
} catch (Exception e) {
log.error("Cannot save submision, cancelling...", e);
submission.remove();
- session.save();
+ cmsSessionNode.getSession().save();
resp.setStatus(503);
return;
}
log.error("Cannot save submision", e);
resp.setStatus(503);
return;
- } finally {
- Jcr.logout(session);
+// } finally {
+// Jcr.logout(session);
}
resp.setStatus(201);
}
- public void setRepository(Repository repository) {
- this.repository = repository;
- }
+// public void setRepository(Repository repository) {
+// this.repository = repository;
+// }
public synchronized void addSubmissionListener(FormSubmissionListener listener) {
submissionListeners.add(listener);
public synchronized void removeSubmissionListener(FormSubmissionListener listener) {
submissionListeners.remove(listener);
}
+
+// public void setContentRepository(ContentRepository contentRepository) {
+// this.contentRepository = contentRepository;
+// }
+
+ public void setAppUserState(AppUserState appUserState) {
+ this.appUserState = appUserState;
+ }
+
}
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;
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) {
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.suite.knowledge</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+appTitle=Argeo Knowledge
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="argeo.product.knowledge.leadPane">
+ <implementation class="org.argeo.app.swt.ux.DefaultLeadPane"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/leadPane.properties"/>
+ <property name="defaultLayers" type="String">argeo.product.knowledge.structureLayer
+argeo.product.knowledge.termsLayer
+ </property>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.swt.ux.SwtAppLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="argeo.product.knowledge.spaceEntryArea">
+ <implementation class="org.argeo.app.swt.space.SpaceEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/spaceEntryArea.properties"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="argeo.product.knowledge.structureLayer">
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
+ <service>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/structureLayer.properties"/>
+ <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.product.knowledge.spaceEntryArea)"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="argeo.product.knowledge.swtArgeoApp">
+ <implementation class="org.argeo.app.swt.ux.SwtArgeoApp"/>
+ <service>
+ <provide interface="org.argeo.api.cms.CmsApp"/>
+ </service>
+ <properties entry="config/swtArgeoApp.properties"/>
+ <reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" unbind="removeUiProvider"/>
+ <reference bind="addTheme" cardinality="1..n" interface="org.argeo.api.cms.ux.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.swt.ux.SwtAppLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+ <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
+ <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+ <reference bind="setAppUserState" cardinality="1..1" interface="org.argeo.app.api.AppUserState" name="AppUserState" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="argeo.product.knowledge.termsEntryArea">
+ <implementation class="org.argeo.app.swt.terms.TermsEntryArea"/>
+ <service>
+ <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/termsEntryArea.properties"/>
+ <reference bind="setTermsManager" cardinality="1..1" interface="org.argeo.app.api.TermsManager" name="TermsManager" policy="static"/>
+</scr:component>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="argeo.product.knowledge.termsLayer">
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
+ <service>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
+ </service>
+ <property name="service.ranking" type="Integer" value="-1000"/>
+ <properties entry="config/termsLayer.properties"/>
+ <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.product.knowledge.termsEntryArea)"/>
+</scr:component>
--- /dev/null
+Service-Component:\
+OSGI-INF/swtArgeoApp.xml,\
+OSGI-INF/leadPane.xml,\
+OSGI-INF/spaceEntryArea.xml,\
+OSGI-INF/structureLayer.xml,\
+OSGI-INF/termsEntryArea.xml,\
+OSGI-INF/termsLayer.xml,\
+
+Import-Package:\
+org.argeo.app.api,\
+org.argeo.cms.swt.acr;resolution:=optional,\
+org.argeo.app.swt.ux;resolution:=optional,\
+org.argeo.app.swt.terms;resolution:=optional,\
+org.argeo.app.swt.space;resolution:=optional,\
+*
\ No newline at end of file
--- /dev/null
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/swtArgeoApp.xml
+source.. = src/
+output.. = bin/
--- /dev/null
+service.pid=argeo.product.knowledge.leadPane
--- /dev/null
+service.pid=argeo.product.knowledge.spaceEntryArea
--- /dev/null
+service.pid=argeo.product.knowledge.structureLayer
+
+title=Structure
+icon=folder
+
+#entity.type=entity:space
\ No newline at end of file
--- /dev/null
+service.pid=argeo.product.knowledge.swtArgeoApp
+
+sharedPidPrefix=argeo.suite.ui
+
+event.topics=argeo/suite/*
+
+argeo.cms.app.contextName=argeo/knowledge
\ No newline at end of file
--- /dev/null
+service.pid=argeo.product.knowledge.termsEntryArea
--- /dev/null
+service.pid=argeo.product.knowledge.termsLayer
+
+title=Terms
+icon=dashboard
+
+entity.type=entity:terms,entity:term,entity:typologies
\ No newline at end of file
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.suite.knowledge,\
# Local
minor=1
micro=27
qualifier=.next
+
+Bundle-Copyright= \
+Copyright 2014-2023 Argeo GmbH, \
+Copyright 2017-2023 Mathieu Baudier
+
+SPDX-License-Identifier= \
+GPL-2.0-or-later \
+OR LicenseRef-argeo2-GPL-2.0-or-later-with-EPL-and-JCR-permissions
major=2
minor=3
-micro=14
-qualifier=.next
+micro=15
+qualifier=
Bundle-Copyright= \
Copyright 2014-2023 Argeo GmbH, \
String fileref = imageDataNode.get(DbkAttr.fileref, String.class).orElse(null);
if (fileref == null)
return null;
- return ((ProvidedContent) baseFolder).getContent(fileref);
+ return ((ProvidedContent) baseFolder).getContent(fileref).get();
}
protected Content getMediaFolder() {
super(parent, style);
editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
this.section = section;
- setStyle(DbkType.videoobject.name());
+ // set data before setting style since it creates the control
setData(node);
+ setStyle(DbkType.videoobject.name());
}
@Override
protected Control createControl(Composite box, String style) {
- Content mediaobject = getNode();
+ Content mediaobject = getContent();
Composite wrapper = new Composite(box, SWT.NONE);
wrapper.setLayout(CmsSwtUtils.noSpaceGridLayout());
updateB.setText("Update");
updateB.addSelectionListener(new Selected() {
+ private static final long serialVersionUID = -8234047511858456222L;
+
@Override
public void widgetSelected(SelectionEvent e) {
Content videodata = mediaobject.child(DbkType.videoobject).child(DbkType.videodata);
deleteB.setText("Delete");
deleteB.addSelectionListener(new Selected() {
+ private static final long serialVersionUID = -7552456185687361642L;
+
@Override
public void widgetSelected(SelectionEvent e) {
mediaobject.remove();
import static org.argeo.app.docbook.DbkAcrUtils.isDbk;
import static org.argeo.app.docbook.DbkType.para;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import org.argeo.api.acr.Content;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
+/** Displays DocBook content. */
public class DocBookViewer extends AbstractPageViewer {
private TextInterpreter textInterpreter = new DbkTextInterpreter();
protected void refresh(Control control) {
if (!(control instanceof SwtSection))
return;
- long begin = System.currentTimeMillis();
+// long begin = System.currentTimeMillis();
SwtSection section = (SwtSection) control;
if (section instanceof TextSection) {
CmsSwtUtils.clear(mainSection);
refreshTextSection(mainSection);
}
- long duration = System.currentTimeMillis() - begin;
+// long duration = System.currentTimeMillis() - begin;
// System.out.println(duration + " ms - " + DbkUtils.getTitle(section.getNode()));
}
for (Content child : section.getContent()) {
if (child.hasContentClass(DbkType.section)) {
processingSubSections = true;
- TextSection childSection = new TextSection(section, 0, child);
+ TextSection childSection = newTextSection(section, child); // new TextSection(section, 0, child);
childSection.setLayoutData(CmsSwtUtils.fillWidth());
refreshTextSection(childSection);
} else {
} else {
throw new IllegalArgumentException("Unsupported media object " + child);
}
+ } else if (isDbk(child, DbkType.info)) {
+ // TODO enrich UI based on info
} else if (isDbk(child, DbkType.title)) {
// already managed
// TODO check that it is first?
} else if (part instanceof DbkImg) {
DbkImg editableImage = (DbkImg) part;
-// imageManager.load(partContent, part.getControl(), editableImage.getPreferredImageSize());
+ imageManager.load(partContent, part.getControl(), editableImage.getPreferredImageSize(), null);
} else if (part instanceof DbkVideo) {
DbkVideo video = (DbkVideo) part;
video.load(part.getControl());
}
}
+ /** To be overridden in order to provide additional SectionPart types */
+ protected TextSection newTextSection(SwtSection section, Content node) {
+ return new TextSection(section, SWT.NONE, node);
+ }
+
protected Paragraph newParagraph(TextSection parent, Content node) {
Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
updateContent(paragraph);
* level.
*
* @return the parent to use for the {@link DbkSectionTitle}, by default
- * {@link Section#getHeader()}
+ * {@link SwtSection#getHeader()}
*/
protected Composite newSectionHeader(TextSection section) {
return section.getHeader();
}
+ protected List<String> getAvailableStyles(SwtEditablePart editablePart) {
+ return new ArrayList<>();
+ }
+
public TextSection getMainSection() {
return mainSection;
}
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable String that displays a browsable link when read-only */
+public class EditableLink extends EditablePropertyString implements SwtEditablePart {
+ private static final long serialVersionUID = 5055000749992803591L;
+
+ private String type;
+ private String message;
+ private boolean readOnly;
+
+ public EditableLink(Composite parent, int style, Content node, QName propertyName, String type, String message) {
+ super(parent, style, node, propertyName, message);
+ this.message = message;
+ this.type = type;
+
+ readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
+ if (node.containsKey(propertyName)) {
+ this.setStyle(FormStyle.propertyText.style());
+ this.setText(node.attr(propertyName));
+ } else {
+ this.setStyle(FormStyle.propertyMessage.style());
+ this.setText("");
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message);
+ else if (readOnly)
+ setLinkValue(lbl, text);
+ else
+ // if canEdit() we put only the value with no link
+ // to avoid glitches of the edition life cycle
+ lbl.setText(text);
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ txt.setMessage(message);
+ } else
+ txt.setText(text);
+ }
+ }
+
+ private void setLinkValue(Label lbl, String text) {
+ if (FormStyle.email.style().equals(type))
+ lbl.setText(FormUtils.getMailLink(text));
+ else if (FormStyle.phone.style().equals(type))
+ lbl.setText(FormUtils.getPhoneLink(text));
+ else if (FormStyle.website.style().equals(type))
+ lbl.setText(FormUtils.getUrlLink(text));
+ else if (FormStyle.facebook.style().equals(type) || FormStyle.instagram.style().equals(type)
+ || FormStyle.linkedIn.style().equals(type) || FormStyle.twitter.style().equals(type))
+ lbl.setText(FormUtils.getUrlLink(text));
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.acr.ContentStyledControl;
+import org.argeo.cms.swt.dialogs.CmsMessageDialog;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Display, add or remove values from a list in a CMS context */
+public class EditableMultiStringProperty extends ContentStyledControl implements SwtEditablePart {
+ private static final long serialVersionUID = -7044614381252178595L;
+
+ private QName propertyName;
+ private String message;
+ // TODO implement the ability to provide a list of possible values
+// private String[] possibleValues;
+ private boolean canEdit;
+ private SelectionListener removeValueSL;
+ private List<String> values;
+
+ // TODO manage within the CSS
+ private int rowSpacing = 5;
+ private int rowMarging = 0;
+ private int oneValueMargingRight = 5;
+ private int btnWidth = 16;
+ private int btnHeight = 16;
+ private int btnHorizontalIndent = 3;
+
+ public EditableMultiStringProperty(Composite parent, int style, Content node, QName propertyName,
+ List<String> values, String[] possibleValues, String addValueMsg,
+ SelectionListener removeValueSelectionListener) {
+ super(parent, style, node);
+
+ this.propertyName = propertyName;
+ this.values = values;
+// this.possibleValues = new String[] { "Un", "Deux", "Trois" };
+ this.message = addValueMsg;
+ this.canEdit = removeValueSelectionListener != null;
+ this.removeValueSL = removeValueSelectionListener;
+ }
+
+ public List<String> getValues() {
+ return values;
+ }
+
+ public void setValues(List<String> values) {
+ this.values = values;
+ }
+
+ // Row layout items do not need explicit layout data
+ protected void setControlLayoutData(Control control) {
+ }
+
+ /** To be overridden */
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+ @Override
+ public Control getControl() {
+ return super.getControl();
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Composite row = new Composite(box, SWT.NO_FOCUS);
+ row.setLayoutData(EclipseUiUtils.fillAll());
+
+ RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+ rl.wrap = true;
+ rl.spacing = rowSpacing;
+ rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging;
+ row.setLayout(rl);
+
+ if (values != null) {
+ for (final String value : values) {
+ if (canEdit)
+ createRemovableValue(row, SWT.SINGLE, value);
+ else
+ createValueLabel(row, SWT.SINGLE, value);
+ }
+ }
+
+ if (!canEdit)
+ return row;
+ else if (isEditing())
+ return createText(row, style);
+ else
+ return createLabel(row, style);
+ }
+
+ /**
+ * Override to provide specific layout for the existing values, typically adding
+ * a pound (#) char for tags or anchor info for browsable links. We assume the
+ * parent composite already has a layout and it is the caller responsibility to
+ * apply corresponding layout data
+ */
+ protected Label createValueLabel(Composite parent, int style, String value) {
+ Label label = new Label(parent, style);
+ label.setText("#" + value);
+ CmsSwtUtils.markup(label);
+ CmsSwtUtils.style(label, FormStyle.propertyText.style());
+ return label;
+ }
+
+ private Composite createRemovableValue(Composite parent, int style, String value) {
+ Composite valCmp = new Composite(parent, SWT.NO_FOCUS);
+ GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false));
+ gl.marginRight = oneValueMargingRight;
+ valCmp.setLayout(gl);
+
+ createValueLabel(valCmp, SWT.WRAP, value);
+
+ Button deleteBtn = new Button(valCmp, SWT.FLAT);
+ deleteBtn.setData(FormConstants.LINKED_VALUE, value);
+ deleteBtn.addSelectionListener(removeValueSL);
+ CmsSwtUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX);
+ GridData gd = new GridData();
+ gd.heightHint = btnHeight;
+ gd.widthHint = btnWidth;
+ gd.horizontalIndent = btnHorizontalIndent;
+ deleteBtn.setLayoutData(gd);
+
+ return valCmp;
+ }
+
+ protected Text createText(Composite box, String style) {
+ final Text text = new Text(box, getStyle());
+ // The "add new value" text is not meant to change, so we can set it on
+ // creation
+ text.setMessage(message);
+ CmsSwtUtils.style(text, style);
+ text.setFocus();
+
+ text.addTraverseListener(new TraverseListener() {
+ private static final long serialVersionUID = 1L;
+
+ public void keyTraversed(TraverseEvent e) {
+ if (e.keyCode == SWT.CR) {
+ addValue(text);
+ e.doit = false;
+ }
+ }
+ });
+
+ // The OK button does not work with the focusOut listener
+ // because focus out is called before the OK button is pressed
+
+ // // we must call layout() now so that the row data can compute the
+ // height
+ // // of the other controls.
+ // text.getParent().layout();
+ // int height = text.getSize().y;
+ //
+ // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM);
+ // okBtn.setText("OK");
+ // RowData rd = new RowData(SWT.DEFAULT, height - 2);
+ // okBtn.setLayoutData(rd);
+ //
+ // okBtn.addSelectionListener(new SelectionAdapter() {
+ // private static final long serialVersionUID = 2780819012423622369L;
+ //
+ // @Override
+ // public void widgetSelected(SelectionEvent e) {
+ // addValue(text);
+ // }
+ // });
+
+ return text;
+ }
+
+ /** Performs the real addition, overwrite to make further sanity checks */
+ protected void addValue(Text text) {
+ String value = text.getText();
+ String errMsg = null;
+
+ if (EclipseUiUtils.isEmpty(value))
+ return;
+
+ if (values.contains(value))
+ errMsg = "Dupplicated value: " + value + ", please correct and try again";
+ if (errMsg != null)
+ CmsMessageDialog.openError("Addition not allowed: " + errMsg);
+ else {
+ values.add(value);
+ Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value);
+ newCmp.moveAbove(text);
+ text.setText("");
+ newCmp.getParent().layout();
+ }
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ if (canEdit) {
+ Label lbl = new Label(box, getStyle());
+ lbl.setText(message);
+ CmsSwtUtils.style(lbl, style);
+ CmsSwtUtils.markup(lbl);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ return lbl;
+ }
+ return null;
+ }
+
+ protected void clear(boolean deep) {
+ Control child = getControl();
+ if (deep)
+ super.clear(deep);
+ else {
+ child.getParent().dispose();
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (canEdit)
+ lbl.setText(text);
+ else
+ lbl.setText("");
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ txt.setText(text);
+ }
+ }
+
+ public synchronized void startEditing() {
+ CmsSwtUtils.style(getControl(), FormStyle.propertyText.style());
+// getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ CmsSwtUtils.style(getControl(), FormStyle.propertyMessage.style());
+// getControl().setData(STYLE, FormStyle.propertyMessage.style());
+ super.stopEditing();
+ }
+
+ public QName getPropertyName() {
+ return propertyName;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import java.text.DateFormat;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Optional;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.acr.ContentStyledControl;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** CMS form part to display and edit a date */
+public class EditablePropertyDate extends ContentStyledControl implements SwtEditablePart {
+ private static final long serialVersionUID = 2500215515778162468L;
+
+ // Context
+ private QName propertyName;
+ private String message;
+ private DateTimeFormatter dateFormat;
+
+ // UI Objects
+ private Text dateTxt;
+ private Button openCalBtn;
+
+ // TODO manage within the CSS
+ private int fieldBtnSpacing = 5;
+
+ /**
+ *
+ * @param parent
+ * @param style
+ * @param node
+ * @param propertyName
+ * @param message
+ * @param dateFormat provide a {@link DateFormat} as contract to be able to
+ * read/write dates as strings
+ * @throws RepositoryException
+ */
+ public EditablePropertyDate(Composite parent, int style, Content node, QName propertyName, String message,
+ DateTimeFormatter dateFormat) {
+ super(parent, style, node);
+
+ this.propertyName = propertyName;
+ this.message = message;
+ this.dateFormat = dateFormat;
+
+ Optional<Instant> instant = node.get(propertyName, Instant.class);
+ if (instant.isPresent()) {
+ this.setStyle(FormStyle.propertyText.style());
+ this.setText(dateFormat.format(instant.get()));
+ } else {
+ this.setStyle(FormStyle.propertyMessage.style());
+ this.setText(message);
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message);
+ else
+ lbl.setText(text);
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ } else
+ txt.setText(text);
+ }
+ }
+
+ public synchronized void startEditing() {
+ // if (dateTxt != null && !dateTxt.isDisposed())
+ CmsSwtUtils.style(getControl(), FormStyle.propertyText);
+// getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ if (EclipseUiUtils.isEmpty(dateTxt.getText()))
+ CmsSwtUtils.style(getControl(), FormStyle.propertyMessage);
+// getControl().setData(STYLE, FormStyle.propertyMessage.style());
+ else
+ CmsSwtUtils.style(getControl(), FormStyle.propertyText);
+// getControl().setData(STYLE, FormStyle.propertyText.style());
+ super.stopEditing();
+ }
+
+ public QName getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ return createCustomEditableControl(box, style);
+ } else
+ return createLabel(box, style);
+ }
+
+ protected Label createLabel(Composite box, String style) {
+ Label lbl = new Label(box, getStyle() | SWT.WRAP);
+ lbl.setLayoutData(CmsSwtUtils.fillWidth());
+ CmsSwtUtils.style(lbl, style);
+ CmsSwtUtils.markup(lbl);
+ if (mouseListener != null)
+ lbl.addMouseListener(mouseListener);
+ return lbl;
+ }
+
+ private Control createCustomEditableControl(Composite box, String style) {
+ box.setLayoutData(CmsSwtUtils.fillWidth());
+ Composite dateComposite = new Composite(box, SWT.NONE);
+ GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false));
+ gl.horizontalSpacing = fieldBtnSpacing;
+ dateComposite.setLayout(gl);
+ dateTxt = new Text(dateComposite, SWT.BORDER);
+ CmsSwtUtils.style(dateTxt, style);
+ dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT));
+ dateTxt.setToolTipText(
+ "Enter a date with form \"" + FormUtils.DEFAULT_SHORT_DATE_FORMAT + "\" or use the calendar");
+ openCalBtn = new Button(dateComposite, SWT.FLAT);
+ CmsSwtUtils.style(openCalBtn, FormStyle.calendar.style() + FormStyle.BUTTON_SUFFIX);
+ GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ gd.heightHint = 17;
+ openCalBtn.setLayoutData(gd);
+ // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN);
+
+ openCalBtn.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 1L;
+
+ public void widgetSelected(SelectionEvent event) {
+ CalendarPopup popup = new CalendarPopup(dateTxt);
+ popup.open();
+ }
+ });
+
+ // dateTxt.addFocusListener(new FocusListener() {
+ // private static final long serialVersionUID = 1L;
+ //
+ // @Override
+ // public void focusLost(FocusEvent event) {
+ // String newVal = dateTxt.getText();
+ // // Enable reset of the field
+ // if (FormUtils.notNull(newVal))
+ // calendar = null;
+ // else {
+ // try {
+ // Calendar newCal = parseDate(newVal);
+ // // DateText.this.setText(newCal);
+ // calendar = newCal;
+ // } catch (ParseException pe) {
+ // // Silent. Manage error popup?
+ // if (calendar != null)
+ // EditablePropertyDate.this.setText(calendar);
+ // }
+ // }
+ // }
+ //
+ // @Override
+ // public void focusGained(FocusEvent event) {
+ // }
+ // });
+ return dateTxt;
+ }
+
+ protected void clear(boolean deep) {
+ Control child = getControl();
+ if (deep || child instanceof Label)
+ super.clear(deep);
+ else {
+ child.getParent().dispose();
+ }
+ }
+
+// /** Enable setting a custom tooltip on the underlying text */
+// @Deprecated
+// public void setToolTipText(String toolTipText) {
+// dateTxt.setToolTipText(toolTipText);
+// }
+//
+// @Deprecated
+// /** Enable setting a custom message on the underlying text */
+// public void setMessage(String message) {
+// dateTxt.setMessage(message);
+// }
+//
+// @Deprecated
+// public void setText(Calendar cal) {
+// String newValueStr = "";
+// if (cal != null)
+// newValueStr = dateFormat.format(cal.getTime());
+// if (!newValueStr.equals(dateTxt.getText()))
+// dateTxt.setText(newValueStr);
+// }
+
+ // UTILITIES TO MANAGE THE CALENDAR POPUP
+ // TODO manage the popup shell in a cleaner way
+ private class CalendarPopup extends Shell {
+ private static final long serialVersionUID = 1L;
+ private DateTime dateTimeCtl;
+
+ public CalendarPopup(Control source) {
+ super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+ populate();
+ // Add border and shadow style
+ CmsSwtUtils.markup(CalendarPopup.this);
+ CmsSwtUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style());
+ pack();
+ layout();
+ setLocation(source.toDisplay((source.getLocation().x - 2), (source.getSize().y) + 3));
+
+ addShellListener(new ShellAdapter() {
+ private static final long serialVersionUID = 5178980294808435833L;
+
+ @Override
+ public void shellDeactivated(ShellEvent e) {
+ close();
+ dispose();
+ }
+ });
+ open();
+ }
+
+ private void setProperty() {
+ // Direct set does not seems to work. investigate
+ // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(),
+ // dateTimeCtl.getDay(), 12, 0);
+ // TODO use modern time API
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.YEAR, dateTimeCtl.getYear());
+ cal.set(Calendar.MONTH, dateTimeCtl.getMonth());
+ cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay());
+ String dateStr = dateFormat.format(cal.toInstant());
+ dateTxt.setText(dateStr);
+ }
+
+ protected void populate() {
+ setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+ dateTimeCtl = new DateTime(this, SWT.CALENDAR);
+ dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll());
+
+ TemporalAccessor calendar = FormUtils.parseDate(dateFormat, dateTxt.getText());
+
+ if (calendar != null)
+ dateTimeCtl.setDate(calendar.get(ChronoField.YEAR), calendar.get(ChronoField.MONTH_OF_YEAR),
+ calendar.get(ChronoField.DAY_OF_MONTH));
+
+ dateTimeCtl.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = -8414377364434281112L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setProperty();
+ }
+ });
+
+ dateTimeCtl.addMouseListener(new MouseListener() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ setProperty();
+ close();
+ dispose();
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import static org.argeo.app.swt.forms.FormStyle.propertyMessage;
+import static org.argeo.app.swt.forms.FormStyle.propertyText;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.argeo.cms.ux.acr.ContentPart;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable String in a CMS context */
+public class EditablePropertyString extends EditableText implements SwtEditablePart, ContentPart {
+ private static final long serialVersionUID = 5055000749992803591L;
+
+ private QName propertyName;
+ private String message;
+
+ // encode the '&' character in rap
+ private final static String AMPERSAND = "&";
+ private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)";
+
+ public EditablePropertyString(Composite parent, int style, Content node, QName propertyName, String message) {
+ super(parent, style);
+ // setUseTextAsLabel(true);
+ this.propertyName = propertyName;
+ this.message = message;
+ setData(node);
+
+ if (node.containsKey(propertyName)) {
+ this.setStyle(propertyText.style());
+ this.setText(node.attr(propertyName));
+ } else {
+ this.setStyle(propertyMessage.style());
+ this.setText(message + " ");
+ }
+ }
+
+ public void setText(String text) {
+ Control child = getControl();
+ if (child instanceof Label) {
+ Label lbl = (Label) child;
+ if (EclipseUiUtils.isEmpty(text))
+ lbl.setText(message + " ");
+ else
+ // TODO enhance this
+ lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND));
+ } else if (child instanceof Text) {
+ Text txt = (Text) child;
+ if (EclipseUiUtils.isEmpty(text)) {
+ txt.setText("");
+ txt.setMessage(message + " ");
+ } else
+ txt.setText(text.replaceAll("<br/>", "\n"));
+ }
+ }
+
+ public synchronized void startEditing() {
+ CmsSwtUtils.style(getControl(), FormStyle.propertyText);
+ super.startEditing();
+ }
+
+ public synchronized void stopEditing() {
+ if (EclipseUiUtils.isEmpty(((Text) getControl()).getText()))
+ CmsSwtUtils.style(getControl(), FormStyle.propertyMessage);
+ else
+ CmsSwtUtils.style(getControl(), FormStyle.propertyText);
+ super.stopEditing();
+ }
+
+ public QName getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.forms;
+
+/** Constants used in the various CMS Forms */
+public interface FormConstants {
+ // DATAKEYS
+ public final static String LINKED_VALUE = "LinkedValue";
+}
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.acr.AbstractPageViewer;
+import org.argeo.cms.swt.acr.Img;
+import org.argeo.cms.swt.acr.SwtSection;
+import org.argeo.cms.swt.acr.SwtSectionPart;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadEvent;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadListener;
+import org.eclipse.rap.fileupload.FileUploadReceiver;
+import org.eclipse.rap.rwt.service.ServerPushSession;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Manage life cycle of a form page that is linked to a given node */
+public class FormPageViewer extends AbstractPageViewer {
+ private final static CmsLog log = CmsLog.getLog(FormPageViewer.class);
+
+ private final SwtSection mainSection;
+
+ // TODO manage within the CSS
+ private Integer labelColWidth = null;
+ private int rowLayoutHSpacing = 8;
+
+ // Context cached in the viewer
+ // The reference to translate from text to calendar and reverse
+ private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(FormUtils.DEFAULT_SHORT_DATE_FORMAT);
+ // new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT);
+ private CmsImageManager<Control, Content> imageManager;
+ private FileUploadListener fileUploadListener;
+
+ public FormPageViewer(SwtSection mainSection, int style, CmsEditable cmsEditable) {
+ super(mainSection, style, cmsEditable);
+ this.mainSection = mainSection;
+
+ if (getCmsEditable().canEdit()) {
+ fileUploadListener = new FUL();
+ }
+ }
+
+ @Override
+ protected void prepare(SwtEditablePart part, Object caretPosition) {
+ if (part instanceof Img) {
+ // ((Img) part).setFileUploadListener(fileUploadListener);
+ }
+ }
+
+ /** To be overridden.Save the edited part. */
+ protected void save(SwtEditablePart part) {
+ Content node = null;
+ if (part instanceof EditableMultiStringProperty ept) {
+ List<String> values = ept.getValues();
+ node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ if (values.isEmpty()) {
+ if (node.containsKey(propName))
+ node.remove(propName);
+ } else {
+ node.put(propName, values);
+// node.setProperty(propName, values.toArray(new String[0]));
+ }
+ // => Viewer : Controller
+ } else if (part instanceof EditablePropertyString ept) {
+ String txt = ((Text) ept.getControl()).getText();
+ node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ if (EclipseUiUtils.isEmpty(txt)) {
+ node.remove(propName);
+ } else {
+ setPropertySilently(node, propName, txt);
+ // node.setProperty(propName, txt);
+ }
+ // node.getSession().save();
+ // => Viewer : Controller
+ } else if (part instanceof EditablePropertyDate) {
+ EditablePropertyDate ept = (EditablePropertyDate) part;
+ // FIXME deal with no value set
+ TemporalAccessor cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText());
+ node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ if (cal == null) {
+ node.remove(propName);
+ } else {
+ node.put(propName, cal);
+ }
+ // node.getSession().save();
+ // => Viewer : Controller
+ }
+ // TODO: make this configurable, sometimes we do not want to save the
+ // current session at this stage
+// if (node != null && node.getSession().hasPendingChanges()) {
+// JcrUtils.updateLastModified(node, true);
+// node.getSession().save();
+// }
+ }
+
+ @Override
+ protected void updateContent(SwtEditablePart part) {
+ if (part instanceof EditableMultiStringProperty ept) {
+ Content node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ List<String> valStrings = new ArrayList<String>();
+ if (node.containsKey(propName)) {
+ for (String val : node.getMultiple(propName, String.class))
+ valStrings.add(val);
+ }
+ ept.setValues(valStrings);
+ } else if (part instanceof EditablePropertyString ept) {
+ // || part instanceof EditableLink
+ Content node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ ept.setText(node.get(propName, String.class).orElse(""));
+ } else if (part instanceof EditablePropertyDate ept) {
+ Content node = ept.getContent();
+ QName propName = ept.getPropertyName();
+ if (node.containsKey(propName))
+ ept.setText(dateFormat.format(node.get(propName, Instant.class).get()));
+ else
+ ept.setText("");
+ } else if (part instanceof SwtSectionPart sectionPart) {
+ Content partNode = sectionPart.getContent();
+ // use control AFTER setting style, since it may have been reset
+ if (part instanceof Img) {
+ Img editableImage = (Img) part;
+ imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize(), null);
+ }
+ }
+ }
+
+ // FILE UPLOAD LISTENER
+ protected class FUL implements FileUploadListener {
+
+ public FUL() {
+ }
+
+ public void uploadProgress(FileUploadEvent event) {
+ // TODO Monitor upload progress
+ }
+
+ public void uploadFailed(FileUploadEvent event) {
+ throw new IllegalStateException("Upload failed " + event, event.getException());
+ }
+
+ public void uploadFinished(FileUploadEvent event) {
+ for (FileDetails file : event.getFileDetails()) {
+ if (log.isDebugEnabled())
+ log.debug("Received: " + file.getFileName());
+ }
+ mainSection.getDisplay().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ saveEdit();
+ }
+ });
+ FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource();
+ uploadHandler.dispose();
+ }
+ }
+
+ // FOCUS OUT LISTENER
+ protected FocusListener createFocusListener() {
+ return new FocusOutListener();
+ }
+
+ private class FocusOutListener implements FocusListener {
+ private static final long serialVersionUID = -6069205786732354186L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+ saveEdit();
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // does nothing;
+ }
+ }
+
+ // MOUSE LISTENER
+ @Override
+ protected MouseListener createMouseListener() {
+ return new ML();
+ }
+
+ private class ML extends MouseAdapter {
+ private static final long serialVersionUID = 8526890859876770905L;
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ if (getCmsEditable().canEdit()) {
+ if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) {
+ if (source == mainSection)
+ return;
+ SwtEditablePart part = findDataParent(source);
+ upload(part);
+ } else {
+ getCmsEditable().startEditing();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ if (getCmsEditable().isEditing()) {
+ if (e.button == 1) {
+ Control source = (Control) e.getSource();
+ SwtEditablePart composite = findDataParent(source);
+ Point point = new Point(e.x, e.y);
+ if (!(composite instanceof Img))
+ edit(composite, source.toDisplay(point));
+ } else if (e.button == 3) {
+ // EditablePart composite = findDataParent((Control) e
+ // .getSource());
+ // if (styledTools != null)
+ // styledTools.show(composite, new Point(e.x, e.y));
+ }
+ }
+ }
+
+ protected synchronized void upload(SwtEditablePart part) {
+ if (part instanceof SwtSectionPart) {
+ if (part instanceof Img) {
+ if (getEdited() == part)
+ return;
+ edit(part, null);
+ layout(part.getControl());
+ }
+ }
+ }
+ }
+
+ @Override
+ public Control getControl() {
+ return mainSection;
+ }
+
+ protected CmsImageManager<Control, Content> imageManager() {
+ if (imageManager == null)
+ imageManager = CmsSwtUtils.getCmsView(mainSection).getImageManager();
+ return imageManager;
+ }
+
+ // LOCAL UI HELPERS
+ protected SwtSection createSectionIfNeeded(Composite body, Content node) {
+ SwtSection section = null;
+ if (node != null) {
+ section = new SwtSection(body, SWT.NO_FOCUS, node);
+ section.setLayoutData(CmsSwtUtils.fillWidth());
+ section.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ }
+ return section;
+ }
+
+ protected void createSimpleLT(Composite bodyRow, Content node, QName propName, String label, String msg) {
+ if (getCmsEditable().canEdit() || node.containsKey(propName)) {
+ createPropertyLbl(bodyRow, label);
+ EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg);
+ eps.setMouseListener(getMouseListener());
+ eps.setFocusListener(getFocusListener());
+ eps.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+ }
+
+ protected void createMultiStringLT(Composite bodyRow, Content node, QName propName, String label, String msg) {
+ boolean canEdit = getCmsEditable().canEdit();
+ if (canEdit || node.containsKey(propName)) {
+ createPropertyLbl(bodyRow, label);
+
+ List<String> valueStrings = new ArrayList<String>();
+
+ if (node.containsKey(propName)) {
+ for (String value : node.getMultiple(propName, String.class))
+ valueStrings.add(value);
+ }
+
+ // TODO use a drop down to display possible values to the end user
+ EditableMultiStringProperty emsp = new EditableMultiStringProperty(bodyRow, SWT.SINGLE | SWT.LEAD, node,
+ propName, valueStrings, new String[] { "Implement this" }, msg,
+ canEdit ? getRemoveValueSelListener() : null);
+ addListeners(emsp);
+ // emsp.setMouseListener(getMouseListener());
+ emsp.setStyle(FormStyle.propertyMessage.style());
+ emsp.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+ }
+
+ protected Label createPropertyLbl(Composite parent, String value) {
+ return createPropertyLbl(parent, value, SWT.NONE);
+ }
+
+ protected Label createPropertyLbl(Composite parent, String value, int vAlign) {
+ // boolean isSmall = CmsView.getCmsView(parent).getUxContext().isSmall();
+ Label label = new Label(parent, SWT.LEAD | SWT.WRAP);
+ label.setText(value + " ");
+ CmsSwtUtils.style(label, FormStyle.propertyLabel.style());
+ GridData gd = new GridData(SWT.LEAD, vAlign, false, false);
+ if (labelColWidth != null)
+ gd.widthHint = labelColWidth;
+ label.setLayoutData(gd);
+ return label;
+ }
+
+ protected Label newStyledLabel(Composite parent, String style, String value) {
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(value);
+ CmsSwtUtils.style(label, style);
+ return label;
+ }
+
+ protected Composite createRowLayoutComposite(Composite parent) {
+ Composite bodyRow = new Composite(parent, SWT.NO_FOCUS);
+ bodyRow.setLayoutData(CmsSwtUtils.fillWidth());
+ RowLayout rl = new RowLayout(SWT.WRAP);
+ rl.type = SWT.HORIZONTAL;
+ rl.spacing = rowLayoutHSpacing;
+ rl.marginHeight = rl.marginWidth = 0;
+ rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0;
+ bodyRow.setLayout(rl);
+ return bodyRow;
+ }
+
+ protected Composite createAddImgComposite(SwtSection section, Composite parent, Content parentNode) {
+
+ Composite body = new Composite(parent, SWT.NO_FOCUS);
+ body.setLayout(new GridLayout());
+
+ FormFileUploadReceiver receiver = new FormFileUploadReceiver(section, parentNode, null);
+ final FileUploadHandler currentUploadHandler = new FileUploadHandler(receiver);
+ if (fileUploadListener != null)
+ currentUploadHandler.addUploadListener(fileUploadListener);
+
+ // Button creation
+ final FileUpload fileUpload = new FileUpload(body, SWT.BORDER);
+ fileUpload.setText("Import an image");
+ fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ fileUpload.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 4869523412991968759L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ServerPushSession pushSession = new ServerPushSession();
+ pushSession.start();
+ String uploadURL = currentUploadHandler.getUploadUrl();
+ fileUpload.submit(uploadURL);
+ }
+ });
+
+ return body;
+ }
+
+ protected class FormFileUploadReceiver extends FileUploadReceiver {
+
+ private Content context;
+ private SwtSection section;
+ private String name;
+
+ public FormFileUploadReceiver(SwtSection section, Content context, String name) {
+ this.context = context;
+ this.section = section;
+ this.name = name;
+ }
+
+ @Override
+ public void receive(InputStream stream, FileDetails details) throws IOException {
+
+ if (name == null)
+ name = details.getFileName();
+
+ // TODO clean image name more carefully
+ String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", "");
+ // We add a unique prefix to workaround the cache issue: when
+ // deleting and re-adding a new image with same name, the end user
+ // browser will use the cache and the image will remain unchanged
+ // for a while
+ cleanedName = System.currentTimeMillis() % 100000 + "_" + cleanedName;
+
+ imageManager().uploadImage(context, context, cleanedName, stream, details.getContentType());
+ // TODO clean refresh strategy
+ section.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ FormPageViewer.this.refresh(section);
+ section.layout();
+ section.getParent().layout();
+ }
+ });
+ }
+ }
+
+ protected void addListeners(StyledControl control) {
+ control.setMouseListener(getMouseListener());
+ control.setFocusListener(getFocusListener());
+ }
+
+ protected Img createImgComposite(Composite parent, Content node, Point preferredSize) {
+ Img img = new Img(parent, SWT.NONE, node, new Cms2DSize(preferredSize.x, preferredSize.y)) {
+ private static final long serialVersionUID = 1297900641952417540L;
+
+ @Override
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+
+ @Override
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ }
+ };
+ img.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT));
+ updateContent(img);
+ addListeners(img);
+ return img;
+ }
+
+ protected Composite addDeleteAbility(final SwtSection section, Content sessionNode, int topWeight,
+ int rightWeight) {
+ Composite comp = new Composite(section, SWT.NONE);
+ comp.setLayoutData(CmsSwtUtils.fillAll());
+ comp.setLayout(new FormLayout());
+
+ // The body to be populated
+ Composite body = new Composite(comp, SWT.NO_FOCUS);
+ body.setLayoutData(EclipseUiUtils.fillFormData());
+
+ if (getCmsEditable().canEdit()) {
+ // the delete button
+ Button deleteBtn = new Button(comp, SWT.FLAT);
+ CmsSwtUtils.style(deleteBtn, FormStyle.deleteOverlay.style());
+ FormData formData = new FormData();
+ formData.right = new FormAttachment(rightWeight, 0);
+ formData.top = new FormAttachment(topWeight, 0);
+ deleteBtn.setLayoutData(formData);
+ deleteBtn.moveAbove(body);
+
+ deleteBtn.addSelectionListener(new SelectionAdapter() {
+ private static final long serialVersionUID = 4304223543657238462L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+// if (MessageDialog.openConfirm(section.getShell(), "Confirm deletion",
+// "Are you really you want to remove this?")) {
+// Session session;
+// try {
+// session = sessionNode.getSession();
+// SwtSection parSection = section.getParentSection();
+// sessionNode.remove();
+// session.save();
+// refresh(parSection);
+// layout(parSection);
+// } catch (RepositoryException re) {
+// throw new JcrException("Unable to delete " + sessionNode, re);
+// }
+//
+// }
+
+ }
+ });
+ }
+ return body;
+ }
+
+// // LOCAL HELPERS FOR NODE MANAGEMENT
+// private Node getOrCreateNode(Node parent, String nodeName, String nodeType) throws RepositoryException {
+// Node node = null;
+// if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) {
+// node = JcrUtils.mkdirs(parent, nodeName, nodeType);
+// parent.getSession().save();
+// }
+//
+// if (getCmsEditable().canEdit() || parent.hasNode(nodeName))
+// node = parent.getNode(nodeName);
+//
+// return node;
+// }
+
+ private SelectionListener getRemoveValueSelListener() {
+ return new SelectionAdapter() {
+ private static final long serialVersionUID = 9022259089907445195L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Object source = e.getSource();
+ if (source instanceof Button) {
+ Button btn = (Button) source;
+ Object obj = btn.getData(FormConstants.LINKED_VALUE);
+ SwtEditablePart ep = findDataParent(btn);
+ if (ep != null && ep instanceof EditableMultiStringProperty) {
+ EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep;
+ List<String> values = emsp.getValues();
+ if (values.contains(obj)) {
+ values.remove(values.indexOf(obj));
+ emsp.setValues(values);
+ save(emsp);
+ // TODO workaround to force refresh
+ edit(emsp, 0);
+ cancelEdit();
+ layout(emsp);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ protected void setPropertySilently(Content node, QName propName, String value) {
+ // TODO Clean this:
+ // Format strings to replace \n
+ value = value.replaceAll("\n", "<br/>");
+ // Do not make the update if validation fails
+// try {
+// MarkupValidatorCopy.getInstance().validate(value);
+// } catch (Exception e) {
+// log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node
+// + ", String cannot be validated - " + e.getMessage());
+// return;
+// }
+ // TODO check if the newly created property is of the correct type,
+ // otherwise the property will be silently created with a STRING
+ // property type.
+ node.put(propName, value);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import org.argeo.api.cms.ux.CmsStyle;
+
+/** Syles used */
+public enum FormStyle implements CmsStyle {
+ // Main
+ form, title,
+ // main part
+ header, headerBtn, headerCombo, section, sectionHeader,
+ // Property fields
+ propertyLabel, propertyText, propertyMessage, errorMessage,
+ // Date
+ popupCalendar,
+ // Buttons
+ starred, unstarred, starOverlay, editOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete,
+ // Contacts
+ email, address, phone, website,
+ // Social Media
+ facebook, twitter, linkedIn, instagram;
+
+ @Override
+ public String getClassPrefix() {
+ return "argeo-form";
+ }
+
+ // TODO clean button style management
+ public final static String BUTTON_SUFFIX = "_btn";
+}
--- /dev/null
+package org.argeo.app.swt.forms;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalAccessor;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+
+/** Utilitary methods to ease implementation of CMS forms */
+public class FormUtils {
+ private final static CmsLog log = CmsLog.getLog(FormUtils.class);
+
+ public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy";
+
+ /** Best effort to convert a String to a calendar. Fails silently */
+ public static TemporalAccessor parseDate(DateTimeFormatter dateFormat, String calStr) {
+ if (EclipseUiUtils.notEmpty(calStr)) {
+ try {
+ return dateFormat.parse(calStr);
+// cal = new GregorianCalendar();
+// cal.setTime(date);
+ } catch (DateTimeParseException pe) {
+ // Silent
+ log.warn("Unable to parse date: " + calStr + " - msg: " + pe.getMessage());
+ throw pe;
+ }
+ }
+ return null;
+ }
+
+// /** Add a double click listener on tables that display a JCR node list */
+// public static void addCanonicalDoubleClickListener(final TableViewer v) {
+// v.addDoubleClickListener(new IDoubleClickListener() {
+//
+// @Override
+// public void doubleClick(DoubleClickEvent event) {
+// CmsView cmsView = CmsUiUtils.getCmsView();
+// Node node = (Node) ((IStructuredSelection) event.getSelection())
+// .getFirstElement();
+// try {
+// cmsView.navigateTo(node.getPath());
+// } catch (RepositoryException e) {
+// throw new CmsException("Unable to get path for node "
+// + node + " before calling navigateTo(path)", e);
+// }
+// }
+// });
+// }
+
+ // MANAGE ERROR DECORATION
+
+// public static ControlDecoration addDecoration(final Text text) {
+// final ControlDecoration dynDecoration = new ControlDecoration(text,
+// SWT.LEFT);
+// Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR);
+// dynDecoration.setImage(icon);
+// dynDecoration.setMarginWidth(3);
+// dynDecoration.hide();
+// return dynDecoration;
+// }
+//
+// public static void refreshDecoration(Text text, ControlDecoration deco,
+// boolean isValid, boolean clean) {
+// if (isValid || clean) {
+// text.setBackground(null);
+// deco.hide();
+// } else {
+// text.setBackground(new Color(text.getDisplay(), 250, 200, 150));
+// deco.show();
+// }
+// }
+//
+// public static Image getDecorationImage(String image) {
+// FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault();
+// return registry.getFieldDecoration(image).getImage();
+// }
+//
+// public static void addCompulsoryDecoration(Label label) {
+// final ControlDecoration dynDecoration = new ControlDecoration(label,
+// SWT.RIGHT | SWT.TOP);
+// Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED);
+// dynDecoration.setImage(icon);
+// dynDecoration.setMarginWidth(3);
+// }
+
+ // TODO the read only generation of read only links for various contact type
+ // should be factorised in the cms Utils.
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able phone number
+ */
+ public static String getPhoneLink(String value) {
+ return getPhoneLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able phone number
+ *
+ * @param value
+ * @param label a potentially distinct label
+ * @return the link
+ */
+ public static String getPhoneLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<a href=\"tel:");
+ builder.append(value).append("\" target=\"_blank\" >").append(label).append("</a>");
+ return builder.toString();
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able mail
+ */
+ public static String getMailLink(String value) {
+ return getMailLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able mail
+ *
+ * @param value
+ * @param label a potentially distinct label
+ * @return the link
+ */
+ public static String getMailLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ value = replaceAmpersand(value);
+ builder.append("<a href=\"mailto:");
+ builder.append(value).append("\" >").append(label).append("</a>");
+ return builder.toString();
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able link
+ */
+ public static String getUrlLink(String value) {
+ return getUrlLink(value, value);
+ }
+
+ /**
+ * Creates the read-only HTML snippet to display in a label with styling enabled
+ * in order to provide a click-able link
+ */
+ public static String getUrlLink(String value, String label) {
+ StringBuilder builder = new StringBuilder();
+ value = replaceAmpersand(value);
+ label = replaceAmpersand(label);
+ if (!(value.startsWith("http://") || value.startsWith("https://")))
+ value = "http://" + value;
+ builder.append("<a href=\"");
+ builder.append(value + "\" target=\"_blank\" >" + label + "</a>");
+ return builder.toString();
+ }
+
+ private static String AMPERSAND = "&";
+
+ /**
+ * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to
+ * avoid <code>SAXParseException</code> while rendering HTML with RWT
+ */
+ public static String replaceAmpersand(String value) {
+ value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
+ return value;
+ }
+
+ // Prevents instantiation
+ private FormUtils() {
+ }
+}
--- /dev/null
+package org.argeo.app.swt.space;
+
+import java.net.URI;
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.EntityType;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.SwtTreeView;
+import org.argeo.cms.ux.acr.ContentHierarchicalPart;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Entry area for managing the typologies. */
+public class SpaceEntryArea implements SwtUiProvider {
+ @Override
+ public Control createUiPart(Composite parent, Content content) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+
+ parent.setLayout(new GridLayout());
+
+ ContentHierarchicalPart contentPart = new ContentHierarchicalPart() {
+
+ @Override
+ public List<Content> getChildren(Content parent) {
+ if (parent != null)
+ return super.getChildren(parent);
+ List<Content> res = ((ProvidedContent) content).getSession().search((bs) -> {
+ bs.from(URI.create("/sys")).where((f) -> f.isContentClass(EntityType.space));
+ }).filter((c) -> noSpaceParent((ProvidedContent) c)).toList();
+ return res;
+ }
+
+ };
+ contentPart.addColumn((c) -> NamespaceUtils.toPrefixedName(c.getName()));
+// contentPart.setInput(content);
+
+ SwtTreeView<Content> view = new SwtTreeView<>(parent, 0, contentPart);
+ view.setLayoutData(CmsSwtUtils.fillAll());
+
+ contentPart.setInput(null);
+ return view;
+
+ }
+
+ private static boolean noSpaceParent(ProvidedContent content) {
+ if (content.isRoot() || !content.isParentAccessible())// end condition
+ return true;
+ ProvidedContent parent = (ProvidedContent) content.getParent();
+ if (parent.hasContentClass(EntityType.space))
+ return false;
+ return noSpaceParent(parent);
+ }
+}
--- /dev/null
+package org.argeo.app.swt.terms;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.api.Typology;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.argeo.cms.swt.widgets.StyledControl;
+import org.argeo.cms.ux.acr.ContentPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** Common logic between single and mutliple terms editable part. */
+public abstract class AbstractTermsPart extends StyledControl implements SwtEditablePart, ContentPart {
+ private static final long serialVersionUID = -5497097995341927710L;
+ protected final TermsManager termsManager;
+ protected final Typology typology;
+
+ private final boolean editable;
+
+ private CmsIcon deleteIcon;
+ private CmsIcon addIcon;
+ private CmsIcon cancelIcon;
+
+ private Color highlightColor;
+ private Composite highlight;
+
+ private boolean canDelete = true;
+
+ protected final CmsSwtTheme theme;
+
+ private int iconsSize = 12;
+
+ private String message;
+
+ @SuppressWarnings("rawtypes")
+ private Class<? extends Enum> localized;
+
+ public AbstractTermsPart(Composite parent, int style, Content item, TermsManager termsManager, String typology) {
+ super(parent, style);
+ if (item == null)
+ throw new IllegalArgumentException("Item cannot be null");
+ setData(item);
+ this.termsManager = termsManager;
+ this.typology = termsManager.getTypology(typology);
+ this.theme = CmsSwtUtils.getCmsTheme(parent);
+ editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
+ highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY);
+ }
+
+ public boolean isEditable() {
+ return editable;
+ }
+
+ protected void createHighlight(Composite block) {
+ highlight = new Composite(block, SWT.NONE);
+ highlight.setBackground(highlightColor);
+ GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
+ highlightGd.widthHint = 5;
+ highlightGd.heightHint = 3;
+ highlight.setLayoutData(highlightGd);
+
+ }
+
+ protected String getTermLabel(Term term) {
+ if (term instanceof Localized) {
+ return ((Localized) term).lead();
+ } else {
+ if (localized != null) {
+ @SuppressWarnings("unchecked")
+ String msg = ((Localized) Enum.valueOf(localized, term.getName())).lead();
+ return msg;
+ } else {
+ return term.getName();
+ }
+ }
+
+ }
+
+ protected abstract void refresh(ContextOverlay contextArea, String filter, Text txt);
+
+ protected boolean isTermSelectable(Term term) {
+ return true;
+ }
+
+ protected void processTermListLabel(Term term, Text label) {
+
+ }
+
+ protected void setControlLayoutData(Control control) {
+ control.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ protected void setContainerLayoutData(Composite composite) {
+ composite.setLayoutData(CmsSwtUtils.fillAll());
+ }
+
+ //
+ // STYLING
+ //
+ public void setDeleteIcon(CmsIcon deleteIcon) {
+ this.deleteIcon = deleteIcon;
+ }
+
+ public void setAddIcon(CmsIcon addIcon) {
+ this.addIcon = addIcon;
+ }
+
+ public void setCancelIcon(CmsIcon cancelIcon) {
+ this.cancelIcon = cancelIcon;
+ }
+
+ protected TermsManager getTermsManager() {
+ return termsManager;
+ }
+
+ protected void styleDelete(ToolItem deleteItem) {
+ if (deleteIcon != null)
+ deleteItem.setImage(theme.getSmallIcon(deleteIcon));
+ else
+ deleteItem.setText("-");
+ }
+
+ protected void styleCancel(ToolItem cancelItem) {
+ if (cancelIcon != null) {
+ // cancelItem.setImage(theme.getSmallIcon(cancelIcon));
+ cancelItem.setImage(theme.getIcon(cancelIcon.name(), iconsSize));
+
+ } else {
+ cancelItem.setText("X");
+ }
+ }
+
+ protected void styleAdd(ToolItem addItem) {
+ if (addIcon != null) {
+// addItem.setImage(theme.getSmallIcon(addIcon));
+ addItem.setImage(theme.getIcon(addIcon.name(), iconsSize));
+ } else {
+ addItem.setText("+");
+ }
+ }
+
+ @Override
+ public Content getContent() {
+ return (Content) getData();
+ }
+
+ public void setCanDelete(boolean canDelete) {
+ this.canDelete = canDelete;
+ }
+
+ public boolean isCanDelete() {
+ return canDelete;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void setLocalized(Class<? extends Enum> localized) {
+ this.localized = localized;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.terms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.MouseDoubleClick;
+import org.argeo.cms.swt.MouseDown;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** {@link SwtEditablePart} for multiple terms. */
+public class MultiTermsPart extends AbstractTermsPart {
+ private static final long serialVersionUID = -4961135649177920808L;
+ private final static CmsLog log = CmsLog.getLog(MultiTermsPart.class);
+
+ public MultiTermsPart(Composite parent, int style, Content item, TermsManager termsManager, String typology) {
+ super(parent, style, item, termsManager, typology);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ Composite placeholder = new Composite(box, SWT.NONE);
+
+ boolean vertical = SWT.VERTICAL == (getStyle() & SWT.VERTICAL);
+ RowLayout rl = new RowLayout(vertical ? SWT.VERTICAL : SWT.HORIZONTAL);
+ rl = CmsSwtUtils.noMarginsRowLayout(rl);
+// rl.wrap = true;
+// rl.justify = true;
+ placeholder.setLayout(rl);
+ List<Term> currentValue = getValue();
+ if (currentValue != null && !currentValue.isEmpty()) {
+ for (Term value : currentValue) {
+ Composite block = new Composite(placeholder, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+ Text lbl = new Text(block, SWT.NONE);
+ lbl.setEditable(false);
+ String display = getTermLabel(value);
+ lbl.setText(display);
+ CmsSwtUtils.style(lbl, style == null ? SuiteStyle.simpleInput.style() : style);
+ processTermListLabel(value, lbl);
+ if (isEditable())
+ lbl.addMouseListener((MouseDoubleClick) (e) -> {
+ startEditing();
+ });
+ if (isEditing()) {
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem deleteItem = new ToolItem(toolBar, SWT.FLAT);
+ styleDelete(deleteItem);
+ deleteItem.addSelectionListener((Selected) (e) -> {
+ // we retrieve them again here because they may have changed
+ List<Term> curr = getValue();
+ List<Term> newValue = new ArrayList<>();
+ for (Term v : curr) {
+ if (!v.equals(value))
+ newValue.add(v);
+ }
+ setValue(newValue);
+ block.dispose();
+ layout(true, true);
+ });
+
+ }
+ }
+ } else {// empty
+ if (isEditable() && !isEditing()) {
+ ToolBar toolBar = new ToolBar(placeholder, SWT.HORIZONTAL);
+ ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
+ styleAdd(addItem);
+ addItem.addSelectionListener((Selected) (e) -> {
+ startEditing();
+ });
+ }
+ }
+
+ if (isEditing()) {
+ Composite block = new Composite(placeholder, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ createHighlight(block);
+
+ Text txt = new Text(block, SWT.SINGLE | SWT.BORDER);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+// txt.setMessage("[new]");
+
+ CmsSwtUtils.style(txt, style == null ? SuiteStyle.simpleInput.style() : style);
+
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem cancelItem = new ToolItem(toolBar, SWT.FLAT);
+ styleCancel(cancelItem);
+ cancelItem.addSelectionListener((Selected) (e) -> {
+ stopEditing();
+ });
+
+ ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
+ private static final long serialVersionUID = -7980078594405384874L;
+
+ @Override
+ protected void onHide() {
+ stopEditing();
+ }
+ };
+ contextOverlay.setLayout(new GridLayout());
+ // filter
+ txt.addModifyListener((e) -> {
+ String filter = txt.getText().toLowerCase();
+ if ("".equals(filter.trim()))
+ filter = null;
+ refresh(contextOverlay, filter, txt);
+ });
+ txt.addFocusListener(new FocusListener() {
+ private static final long serialVersionUID = -6024501573409619949L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
+// getDisplay().asyncExec(() -> stopEditing());
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // txt.setText("");
+ if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
+ refresh(contextOverlay, null, txt);
+ }
+ });
+ layout(new Control[] { txt });
+ // getDisplay().asyncExec(() -> txt.setFocus());
+ }
+ return placeholder;
+ }
+
+ @Override
+ protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
+ CmsSwtUtils.clear(contextArea);
+ List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
+ List<Term> currentValue = getValue();
+ terms: for (Term term : terms) {
+ if (currentValue != null && currentValue.contains(term))
+ continue terms;
+ String display = getTermLabel(term);
+ if (filter != null && !display.toLowerCase().contains(filter))
+ continue terms;
+ Text termL = new Text(contextArea, SWT.WRAP);
+ termL.setEditable(false);
+ termL.setText(display);
+ processTermListLabel(term, termL);
+ if (isTermSelectable(term))
+ termL.addMouseListener((MouseDown) (e) -> {
+ List<Term> newValue = new ArrayList<>();
+ List<Term> curr = getValue();
+ if (currentValue != null)
+ newValue.addAll(curr);
+ newValue.add(term);
+ setValue(newValue);
+ contextArea.hide();
+ stopEditing();
+ });
+ }
+ contextArea.show();
+ }
+
+ protected List<Term> getValue() {
+ String property = typology.getId();
+ List<String> curr = getContent().getMultiple(NamespaceUtils.unqualified(property));
+// List<String> curr = Jcr.getMultiple(getNode(), property);
+ List<Term> res = new ArrayList<>();
+ if (curr != null)
+ terms: for (String str : curr) {
+ Term term = termsManager.getTerm(str);
+ if (term == null) {
+ log.warn("Ignoring term " + str + " for " + getContent() + ", as it was not found.");
+ continue terms;
+ }
+ res.add(term);
+ }
+ return res;
+ }
+
+ protected void setValue(List<Term> value) {
+ String property = typology.getId();
+ List<String> ids = new ArrayList<>();
+ for (Term term : value) {
+ ids.add(term.getId());
+ }
+ getContent().put(property, ids);
+// Jcr.set(getNode(), property, ids);
+// Jcr.save(getNode());
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.terms;
+
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.app.api.Term;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.MouseDoubleClick;
+import org.argeo.cms.swt.MouseDown;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.cms.swt.widgets.ContextOverlay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** {@link SwtEditablePart} for terms. */
+public class SingleTermPart extends AbstractTermsPart {
+ private static final long serialVersionUID = -4961135649177920808L;
+
+ public SingleTermPart(Composite parent, int style, Content item, TermsManager termsManager, String typology) {
+ super(parent, style, item, termsManager, typology);
+ }
+
+ @Override
+ protected Control createControl(Composite box, String style) {
+ if (isEditing()) {
+ Composite block = new Composite(box, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ createHighlight(block);
+
+ Text txt = new Text(block, SWT.SINGLE);
+ CmsSwtUtils.style(txt, style == null ? SuiteStyle.simpleInput.style() : style);
+
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ if (isCanDelete()) {
+ ToolItem deleteItem = new ToolItem(toolBar, SWT.PUSH);
+ styleDelete(deleteItem);
+ deleteItem.addSelectionListener((Selected) (e) -> {
+ setValue(null);
+ stopEditing();
+ });
+ }
+ ToolItem cancelItem = new ToolItem(toolBar, SWT.PUSH);
+ styleCancel(cancelItem);
+ cancelItem.addSelectionListener((Selected) (e) -> {
+ stopEditing();
+ });
+
+ ContextOverlay contextOverlay = new ContextOverlay(txt, SWT.NONE) {
+ private static final long serialVersionUID = -7980078594405384874L;
+
+ @Override
+ protected void onHide() {
+ stopEditing();
+ }
+ };
+ contextOverlay.setLayout(new GridLayout());
+ // filter
+ txt.addModifyListener((e) -> {
+ String filter = txt.getText().toLowerCase();
+ if ("".equals(filter.trim()))
+ filter = null;
+ refresh(contextOverlay, filter, txt);
+ });
+ txt.addFocusListener(new FocusListener() {
+ private static final long serialVersionUID = -6024501573409619949L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+// if (!contextOverlay.isDisposed() && contextOverlay.isShellVisible())
+// getDisplay().asyncExec(() -> stopEditing());
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ // txt.setText("");
+ if (!contextOverlay.isDisposed() && !contextOverlay.isShellVisible())
+ refresh(contextOverlay, null, txt);
+ }
+ });
+ layout(new Control[] { block });
+ getDisplay().asyncExec(() -> txt.setFocus());
+ return block;
+ } else {
+ Composite block = new Composite(box, SWT.NONE);
+ block.setLayout(CmsSwtUtils.noSpaceGridLayout(2));
+ Term currentValue = getValue();
+ if (currentValue != null) {
+ Text lbl = new Text(block, SWT.SINGLE);
+ lbl.setEditable(false);
+ String display = getTermLabel(currentValue);
+ lbl.setText(display);
+ CmsSwtUtils.style(lbl, style == null ? SuiteStyle.simpleInput.style() : style);
+ processTermListLabel(currentValue, lbl);
+ if (isEditable()) {
+ lbl.addMouseListener((MouseDoubleClick) (e) -> {
+ startEditing();
+ });
+ }
+ } else {
+ if (isEditable()) {
+
+ ToolBar toolBar = new ToolBar(block, SWT.HORIZONTAL);
+ ToolItem addItem = new ToolItem(toolBar, SWT.FLAT);
+ styleAdd(addItem);
+ addItem.addSelectionListener((Selected) (e) -> {
+ startEditing();
+ });
+ }
+ // add dummy text so that height wont's move afterwards
+ Text lbl = new Text(block, SWT.SINGLE);
+ lbl.setEditable(false);
+ if (!isEditable()) {// empty, non editable
+ if (getMessage() != null)
+ lbl.setMessage(getMessage());
+ }
+ }
+ return block;
+ }
+ }
+
+ @Override
+ protected void refresh(ContextOverlay contextArea, String filter, Text txt) {
+ CmsSwtUtils.clear(contextArea);
+ List<? extends Term> terms = termsManager.listAllTerms(typology.getId());
+ terms: for (Term term : terms) {
+ String display = getTermLabel(term);
+ if (filter != null && !display.toLowerCase().contains(filter))
+ continue terms;
+ Text termL = new Text(contextArea, SWT.WRAP);
+ termL.setEditable(false);
+ termL.setText(display);
+ processTermListLabel(term, termL);
+ if (isTermSelectable(term))
+ termL.addMouseListener((MouseDown) (e) -> {
+ setValue(term);
+ contextArea.hide();
+ stopEditing();
+ });
+ }
+ contextArea.show();
+ // txt.setFocus();
+ }
+
+ protected Term getValue() {
+ String property = typology.getId();
+ String id = getContent().attr(property);
+ Term term = termsManager.getTerm(id);
+
+ return term;
+ }
+
+ protected void setValue(Term value) {
+ String property = typology.getId();
+ if (value == null)
+ getContent().remove(property);
+ else
+ getContent().put(property, value.getId());
+// Jcr.set(getNode(), property, value != null ? value.getId() : null);
+// Jcr.save(getNode());
+ }
+}
--- /dev/null
+package org.argeo.app.swt.terms;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.TermsManager;
+import org.argeo.app.ux.SuiteUxEvent;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.widgets.SwtTreeView;
+import org.argeo.cms.ux.acr.ContentHierarchicalPart;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Entry area for managing the typologies. */
+public class TermsEntryArea implements SwtUiProvider {
+ private TermsManager termsManager;
+
+ @Override
+ public Control createUiPart(Composite parent, Content content) {
+// parent.setLayout(new GridLayout());
+// Label lbl = new Label(parent, SWT.NONE);
+// lbl.setText("Typologies");
+//
+// Set<Typology> typologies = termsManager.getTypologies();
+// for (Typology typology : typologies) {
+// new Label(parent, 0).setText(typology.getId());
+// }
+//
+//
+// return lbl;
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+
+ parent.setLayout(new GridLayout());
+ Content rootContent = ((ProvidedContent) content).getSession().getRepository().get().get("/terms");
+
+ ContentHierarchicalPart contentPart = new ContentHierarchicalPart() {
+
+ @Override
+ protected boolean isLeaf(Content content) {
+ if (content.hasContentClass(EntityType.document.qName()))
+ return true;
+ return super.isLeaf(content);
+ }
+
+ };
+ contentPart.addColumn((c) -> NamespaceUtils.toPrefixedName(c.getName()));
+// contentPart.setInput(rootContent);
+
+ SwtTreeView<Content> view = new SwtTreeView<>(parent, 0, contentPart);
+ view.setLayoutData(CmsSwtUtils.fillAll());
+
+ contentPart.setInput(rootContent);
+// contentPart.onSelected((o) -> {
+// Content c = (Content) o;
+//// log.debug(c.getPath());
+// cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(c));
+// });
+ return view;
+
+ }
+
+ public void setTermsManager(TermsManager termsManager) {
+ this.termsManager = termsManager;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.argeo.api.acr.Content;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtTabbedArea;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.util.LangUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** An app layer based on an entry area and an editor area. */
+public class DefaultEditionLayer implements SwtAppLayer {
+ private String id;
+ private SwtUiProvider entryArea;
+ private SwtUiProvider defaultView;
+ private SwtUiProvider workArea;
+ private List<String> weights = new ArrayList<>();
+ private boolean startMaximized = false;
+ private boolean fixedEntryArea = false;
+ private boolean singleTab = false;
+ private Localized title = null;
+ private Localized singleTabTitle = null;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ // TODO Factorize more, or split into more specialised classes?
+ if (entryArea != null) {
+ if (fixedEntryArea) {
+ FixedEditionArea editionArea = new FixedEditionArea(parent, parent.getStyle());
+ Control entryAreaC = entryArea.createUiPart(editionArea.getEntryArea(), context);
+ CmsSwtUtils.style(entryAreaC, SuiteStyle.entryArea);
+ if (this.defaultView != null) {
+ editionArea.getTabbedArea().view(defaultView, context);
+ }
+ return editionArea;
+ } else {
+ SashFormEditionArea editionArea = new SashFormEditionArea(parent, parent.getStyle());
+ entryArea.createUiPart(editionArea.getEntryArea(), context);
+ if (this.defaultView != null) {
+ editionArea.getTabbedArea().view(defaultView, context);
+ }
+ return editionArea;
+ }
+ } else {
+ if (this.workArea != null) {
+ Composite area = new Composite(parent, SWT.NONE);
+ this.workArea.createUiPart(area, context);
+ return area;
+ }
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+ SwtTabbedArea tabbedArea = createTabbedArea(parent, theme);
+ return tabbedArea;
+ }
+ }
+
+ @Override
+ public void view(SwtUiProvider uiProvider, Composite workAreaC, Content context) {
+ if (workArea != null) {
+ CmsSwtUtils.clear(workAreaC);
+ workArea.createUiPart(workAreaC, context);
+ workAreaC.layout(true, true);
+ return;
+ }
+
+ // tabbed area
+ SwtTabbedArea tabbedArea = findTabbedArea(workAreaC);
+ if (tabbedArea == null)
+ throw new IllegalArgumentException("Unsupported work area " + workAreaC.getClass().getName());
+ if (uiProvider == null) {
+ // reset
+ tabbedArea.closeAllTabs();
+ if (this.defaultView != null) {
+ tabbedArea.view(defaultView, context);
+ }
+ } else {
+ tabbedArea.view(uiProvider, context);
+ }
+ }
+
+ @Override
+ public Content getCurrentContext(Composite workArea) {
+ SwtTabbedArea tabbedArea = findTabbedArea(workArea);
+ if (tabbedArea == null)
+ return null;
+ return tabbedArea.getCurrentContext();
+ }
+
+ private SwtTabbedArea findTabbedArea(Composite workArea) {
+ SwtTabbedArea tabbedArea = null;
+ if (workArea instanceof SashFormEditionArea) {
+ tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
+ } else if (workArea instanceof FixedEditionArea) {
+ tabbedArea = ((FixedEditionArea) workArea).getTabbedArea();
+ } else if (workArea instanceof SwtTabbedArea) {
+ tabbedArea = (SwtTabbedArea) workArea;
+ }
+ return tabbedArea;
+ }
+
+ @Override
+ public void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
+ SwtTabbedArea tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
+ tabbedArea.open(uiProvider, context);
+ }
+
+ @Override
+ public Localized getTitle() {
+ return title;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, Object> properties) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ id = pid;
+ Objects.requireNonNull(id, "Layer id must be set.");
+
+ weights = LangUtils.toStringList(properties.get(Property.weights.name()));
+ startMaximized = properties.containsKey(Property.startMaximized.name())
+ && "true".equals(properties.get(Property.startMaximized.name()));
+ fixedEntryArea = properties.containsKey(Property.fixedEntryArea.name())
+ && "true".equals(properties.get(Property.fixedEntryArea.name()));
+ if (fixedEntryArea && weights.size() != 0) {
+ throw new IllegalArgumentException("Property " + Property.weights.name() + " should not be set if property "
+ + Property.fixedEntryArea.name() + " is set.");
+ }
+ singleTab = properties.containsKey(Property.singleTab.name())
+ && "true".equals(properties.get(Property.singleTab.name()));
+
+ String titleStr = (String) properties.get(SwtAppLayer.Property.title.name());
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ title = new Localized() {
+
+ @Override
+ public String name() {
+ return titleStr;
+ }
+
+ @Override
+ public ClassLoader getL10nClassLoader() {
+ return bundleContext != null
+ ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+ }
+ };
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+
+ String singleTabTitleStr = (String) properties.get(SwtAppLayer.Property.singleTabTitle.name());
+ if (singleTabTitleStr != null) {
+ if (singleTabTitleStr.startsWith("%")) {
+ singleTabTitle = new Localized() {
+
+ @Override
+ public String name() {
+ return singleTabTitleStr;
+ }
+
+ @Override
+ public ClassLoader getL10nClassLoader() {
+ return bundleContext != null
+ ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+ }
+ };
+ } else {
+ singleTabTitle = new Localized.Untranslated(singleTabTitleStr);
+ }
+ }
+
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public void setEntryArea(SwtUiProvider entryArea) {
+ this.entryArea = entryArea;
+ }
+
+ public void setWorkArea(SwtUiProvider workArea) {
+ this.workArea = workArea;
+ }
+
+ public void setDefaultView(SwtUiProvider defaultView) {
+ this.defaultView = defaultView;
+ }
+
+ SwtTabbedArea createTabbedArea(Composite parent, CmsSwtTheme theme) {
+ SwtTabbedArea tabbedArea = new SwtTabbedArea(parent, SWT.NONE);
+ tabbedArea.setSingleTab(singleTab);
+ if (singleTabTitle != null)
+ tabbedArea.setSingleTabTitle(singleTabTitle.lead());
+ tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.style());
+ tabbedArea.setTabStyle(SuiteStyle.mainTab.style());
+ tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.style());
+ tabbedArea.setCloseIcon(theme.getSmallIcon(SuiteIcon.close));
+ tabbedArea.setLayoutData(CmsSwtUtils.fillAll());
+ return tabbedArea;
+ }
+
+// /** A work area based on an entry area and and a tabbed area. */
+ class SashFormEditionArea extends SashForm {
+ private static final long serialVersionUID = 2219125778722702618L;
+ private SwtTabbedArea tabbedArea;
+ private Composite entryC;
+
+ SashFormEditionArea(Composite parent, int style) {
+ super(parent, SWT.HORIZONTAL);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ Composite editorC;
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ editorC = new Composite(this, SWT.BORDER);
+ entryC = new Composite(this, SWT.BORDER);
+ } else {
+ entryC = new Composite(this, SWT.NONE);
+ editorC = new Composite(this, SWT.NONE);
+ }
+
+ // sash form specific
+ if (weights.size() != 0) {
+ int[] actualWeight = new int[weights.size()];
+ for (int i = 0; i < weights.size(); i++) {
+ actualWeight[i] = Integer.parseInt(weights.get(i));
+ }
+ setWeights(actualWeight);
+ } else {
+ int[] actualWeights = new int[] { 3000, 7000 };
+ setWeights(actualWeights);
+ }
+ if (startMaximized)
+ setMaximizedControl(editorC);
+
+ GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
+// editorAreaLayout.verticalSpacing = 0;
+// editorAreaLayout.marginBottom = 0;
+// editorAreaLayout.marginHeight = 0;
+// editorAreaLayout.marginLeft = 0;
+// editorAreaLayout.marginRight = 0;
+ editorC.setLayout(editorAreaLayout);
+
+ tabbedArea = createTabbedArea(editorC, theme);
+ }
+
+ SwtTabbedArea getTabbedArea() {
+ return tabbedArea;
+ }
+
+ Composite getEntryArea() {
+ return entryC;
+ }
+
+ }
+
+ class FixedEditionArea extends Composite {
+ private static final long serialVersionUID = -5525672639277322465L;
+ private SwtTabbedArea tabbedArea;
+ private Composite entryC;
+
+ public FixedEditionArea(Composite parent, int style) {
+ super(parent, style);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ setLayout(CmsSwtUtils.noSpaceGridLayout(2));
+
+ Composite editorC;
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ editorC = new Composite(this, SWT.NONE);
+ entryC = new Composite(this, SWT.NONE);
+ } else {
+ entryC = new Composite(this, SWT.NONE);
+ editorC = new Composite(this, SWT.NONE);
+ }
+ entryC.setLayoutData(CmsSwtUtils.fillHeight());
+
+ GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
+// editorAreaLayout.verticalSpacing = 0;
+// editorAreaLayout.marginBottom = 0;
+// editorAreaLayout.marginHeight = 0;
+// editorAreaLayout.marginLeft = 0;
+// editorAreaLayout.marginRight = 0;
+ editorC.setLayout(editorAreaLayout);
+ editorC.setLayoutData(CmsSwtUtils.fillAll());
+
+ tabbedArea = createTabbedArea(editorC, theme);
+ }
+
+ SwtTabbedArea getTabbedArea() {
+ return tabbedArea;
+ }
+
+ Composite getEntryArea() {
+ return entryC;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+
+/** Footer of a standard Argeo Suite application. */
+public class DefaultFooter implements SwtUiProvider {
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ Composite content = new Composite(parent, SWT.NONE);
+ content.setLayoutData(new GridData(0, 0));
+ Control contentControl = createContent(content, context);
+
+ // TODO support and guarantee
+
+ return contentControl;
+ }
+
+ protected Control createContent(Composite parent, Content context) {
+ return parent;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Header of a standard Argeo Suite application. */
+public class DefaultHeader implements SwtUiProvider {
+ public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
+ private Localized title = null;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
+
+ // TODO right to left
+ Composite lead = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(lead, SuiteStyle.header);
+ lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
+ lead.setLayout(new GridLayout());
+ Label lbl = new Label(lead, SWT.NONE);
+// String title = properties.get(TITLE_PROPERTY);
+// // TODO expose the localized
+// lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
+// : title);
+ lbl.setText(title.lead());
+ CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
+ lbl.setLayoutData(CmsSwtUtils.fillWidth());
+
+ Composite middle = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(middle, SuiteStyle.header);
+ middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+ middle.setLayout(new GridLayout());
+
+ Composite end = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(end, SuiteStyle.header);
+ end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
+
+ if (!cmsView.isAnonymous()) {
+ end.setLayout(new GridLayout(2, false));
+ Label userL = new Label(end, SWT.NONE);
+ CmsSwtUtils.style(userL, SuiteStyle.header);
+ userL.setText(CurrentUser.getDisplayName());
+// Button logoutB = new Button(end, SWT.FLAT);
+// logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
+// logoutB.addSelectionListener(new SelectionAdapter() {
+// private static final long serialVersionUID = 7116760083964201233L;
+//
+// @Override
+// public void widgetSelected(SelectionEvent e) {
+// cmsView.logout();
+// }
+//
+// });
+ Label logOutL = new Label(end, 0);
+ logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
+ logOutL.addMouseListener(new MouseAdapter() {
+ private static final long serialVersionUID = 6908266850511460799L;
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ cmsView.logout();
+ }
+
+ });
+ } else {
+ end.setLayout(new GridLayout(1, false));
+ // required in order to avoid wrong height after logout
+ new Label(end, SWT.NONE).setText("");
+
+ }
+ return lbl;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, String> properties) {
+ String titleStr = (String) properties.get(TITLE_PROPERTY);
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ title = new Localized() {
+
+ @Override
+ public String name() {
+ return titleStr;
+ }
+
+ @Override
+ public ClassLoader getL10nClassLoader() {
+ return bundleContext != null
+ ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+ }
+ };
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public Localized getTitle() {
+ return title;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.RankedObject;
+import org.argeo.app.core.SuiteUtils;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.app.ux.SuiteUxEvent;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleWiring;
+
+/** Side pane listing various perspectives. */
+public class DefaultLeadPane implements SwtUiProvider {
+ private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
+
+ public static enum Property {
+ defaultLayers, adminLayers;
+ }
+
+ private Map<String, RankedObject<SwtAppLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
+ private List<String> defaultLayers;
+ private List<String> adminLayers = new ArrayList<>();
+
+ private ClassLoader l10nClassLoader;
+
+ @Override
+ public Control createUiPart(Composite parent, Content node) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ Composite appLayersC = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
+ GridLayout layout = new GridLayout();
+ layout.verticalSpacing = 10;
+ layout.marginTop = 10;
+ layout.marginLeft = 10;
+ layout.marginRight = 10;
+ appLayersC.setLayout(layout);
+ appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
+
+ Composite adminLayersC;
+ if (!adminLayers.isEmpty()) {
+ adminLayersC = new Composite(parent, SWT.NONE);
+ CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
+ GridLayout adminLayout = new GridLayout();
+ adminLayout.verticalSpacing = 10;
+ adminLayout.marginBottom = 10;
+ adminLayout.marginLeft = 10;
+ adminLayout.marginRight = 10;
+ adminLayersC.setLayout(adminLayout);
+ adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
+ } else {
+ adminLayersC = null;
+ }
+
+// boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
+ // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
+ Button first = null;
+ layers: for (String layerDef : defaultLayers) {
+ layerDef = layerDef.trim();
+ if ("".equals(layerDef))
+ continue layers;// skip empty lines
+ String[] semiColArr = layerDef.split(";");
+ String layerId = semiColArr[0];
+ Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
+ if (layers.containsKey(layerId)) {
+ if (!layerRoles.isEmpty()) {
+ boolean authorized = false;
+ authorized = cmsView.doAs(() -> {
+ for (String layerRole : layerRoles) {
+ if (CurrentUser.implies(layerRole, null)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ if (!authorized)
+ continue layers;// skip unauthorized layer
+// Set<String> intersection = new HashSet<String>(layerRoles);
+// intersection.retainAll(userRoles);
+// if (intersection.isEmpty())
+// continue layers;// skip unauthorized layer
+ }
+ RankedObject<SwtAppLayer> layerObj = layers.get(layerId);
+
+ Localized title = null;
+ if (!adminLayers.contains(layerId)) {
+ String titleStr = (String) layerObj.getProperties().get(SwtAppLayer.Property.title.name());
+ if (titleStr != null) {
+ if (titleStr.startsWith("%")) {
+ // LocaleUtils.local(titleStr, getClass().getClassLoader());
+ title = () -> titleStr;
+ } else {
+ title = new Localized.Untranslated(titleStr);
+ }
+ }
+ }
+
+ String iconName = (String) layerObj.getProperties().get(SwtAppLayer.Property.icon.name());
+ SuiteIcon icon = null;
+ if (iconName != null)
+ icon = SuiteIcon.valueOf(iconName);
+
+ Composite buttonParent;
+ if (adminLayers.contains(layerId))
+ buttonParent = adminLayersC;
+ else
+ buttonParent = appLayersC;
+ Button b = createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
+ if (first == null)
+ first = b;
+ }
+ }
+ return first;
+ }
+
+ protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
+ ClassLoader l10nClassLoader) {
+ CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
+ Button button = new Button(parent, SWT.PUSH);
+ CmsSwtUtils.style(button, SuiteStyle.leadPane);
+ if (icon != null)
+ button.setImage(theme.getBigIcon(icon));
+ button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+ // button.setToolTipText(msg.lead());
+ if (msg != null) {
+ Label lbl = new Label(parent, SWT.CENTER);
+ CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
+ String txt = LocaleUtils.lead(msg, l10nClassLoader);
+// String txt = msg.lead();
+ lbl.setText(txt);
+ lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+ }
+ CmsSwtUtils.sendEventOnSelect(button, SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.LAYER, layer);
+ return button;
+ }
+
+ public void init(BundleContext bundleContext, Map<String, Object> properties) {
+ l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
+ : getClass().getClassLoader();
+
+ String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
+ if (defaultLayers == null)
+ throw new IllegalArgumentException("Default layers must be set.");
+ this.defaultLayers = Arrays.asList(defaultLayers);
+ if (log.isDebugEnabled())
+ log.debug("Default layers: " + Arrays.asList(defaultLayers));
+ String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
+ if (adminLayers != null) {
+ this.adminLayers = Arrays.asList(adminLayers);
+ if (log.isDebugEnabled())
+ log.debug("Admin layers: " + Arrays.asList(adminLayers));
+ }
+ }
+
+ public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+
+ }
+
+ public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(layers, pid, layer, properties);
+ }
+ }
+
+ public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (layers.containsKey(pid)) {
+ if (layers.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+ layers.remove(pid);
+ }
+ }
+ }
+ }
+
+// protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
+// CmsTheme theme = CmsTheme.getCmsTheme(parent);
+// Button button = new Button(parent, SWT.PUSH);
+// CmsUiUtils.style(button, SuiteStyle.leadPane);
+// if (icon != null)
+// button.setImage(icon.getBigIcon(theme));
+// button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
+// // button.setToolTipText(msg.lead());
+// if (msg != null) {
+// Label lbl = new Label(parent, SWT.CENTER);
+// CmsUiUtils.style(lbl, SuiteStyle.leadPane);
+// // CmsUiUtils.markup(lbl);
+// ClassLoader l10nClassLoader = getClass().getClassLoader();
+// String txt = LocaleUtils.lead(msg, l10nClassLoader);
+//// String txt = msg.lead();
+// lbl.setText(txt);
+// lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
+// }
+// CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
+// return button;
+// }
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.auth.CmsLogin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Provides a login screen. */
+public class DefaultLoginScreen implements SwtUiProvider {
+ private CmsContext cmsContext;
+
+ @Override
+ public Control createUiPart(Composite parent, Content context) {
+ CmsView cmsView = CmsSwtUtils.getCmsView(parent);
+ if (!cmsView.isAnonymous())
+ throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
+
+ parent.setLayout(new GridLayout());
+ Composite loginArea = new Composite(parent, SWT.NONE);
+ loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+
+ CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
+ cmsLogin.createUi(loginArea);
+ return cmsLogin.getCredentialsBlock();
+ }
+
+ public void setCmsContext(CmsContext cmsContext) {
+ this.cmsContext = cmsContext;
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.function.Predicate;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.QNamed;
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.api.cms.ux.CmsEditable;
+import org.argeo.api.cms.ux.CmsStyle;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.Img;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.swt.dialogs.LightweightDialog;
+import org.argeo.cms.swt.widgets.CmsLink;
+import org.argeo.cms.swt.widgets.EditableText;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Static utilities implementing the look and feel of Argeo Suite with SWT. */
+public class SuiteSwtUtils {
+ /** creates a title bar composite with label and optional button */
+ public static Composite addTitleBar(Composite parent, Localized title) {
+ return addTitleBar(parent, title.lead());
+ }
+
+ /** creates a title bar composite with label and optional button */
+ public static Composite addTitleBar(Composite parent, String title) {
+ Composite titleBar = new Composite(parent, SWT.NONE);
+ titleBar.setLayoutData(CmsSwtUtils.fillWidth());
+ CmsSwtUtils.style(titleBar, SuiteStyle.titleContainer);
+
+ titleBar.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
+ Label titleLbl = new Label(titleBar, SWT.NONE);
+ titleLbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+ CmsSwtUtils.style(titleLbl, SuiteStyle.titleLabel);
+ titleLbl.setText(title);
+
+// if (isEditable) {
+// Button editBtn = new Button(titleBar, SWT.PUSH);
+// editBtn.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+// CmsSwtUtils.style(editBtn, SuiteStyle.inlineButton);
+// editBtn.setText("Edit");
+// }
+ return titleBar;
+ }
+
+ public static Label addFormLabel(Composite parent, String label) {
+ Label lbl = new Label(parent, SWT.WRAP);
+ lbl.setText(label);
+ lbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+ CmsSwtUtils.style(lbl, SuiteStyle.simpleLabel);
+ return lbl;
+ }
+
+ public static Label addFormLabel(Composite parent, Localized msg) {
+ return addFormLabel(parent, msg.lead());
+ }
+
+ public static Text addFormTextField(Composite parent, String text, String message, int style) {
+ Text txt = new Text(parent, style);
+ if (text != null)
+ txt.setText(text);
+ if (message != null)
+ txt.setMessage(message);
+ txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+ CmsSwtUtils.style(txt, SuiteStyle.simpleInput);
+ return txt;
+ }
+
+ public static Text addFormTextField(Composite parent, String text, String message) {
+ return addFormTextField(parent, text, message, SWT.NONE);
+ }
+
+// public static Text addFormInputField(Composite parent, String placeholder) {
+// Text txt = new Text(parent, SWT.BORDER);
+//
+// GridData gridData = CmsSwtUtils.fillWidth();
+// txt.setLayoutData(gridData);
+//
+// if (placeholder != null)
+// txt.setText(placeholder);
+//
+// CmsSwtUtils.style(txt, SuiteStyle.simpleInput);
+// return txt;
+// }
+
+ public static Composite addLineComposite(Composite parent, int columns) {
+ Composite lineComposite = new Composite(parent, SWT.NONE);
+ GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ lineComposite.setLayoutData(gd);
+ lineComposite.setLayout(new GridLayout(columns, false));
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ return lineComposite;
+ }
+
+ /** creates a single horizontal-block composite for key:value display */
+ public static Text addFormLine(Composite parent, Localized label, String text) {
+ return addFormLine(parent, label.lead(), text);
+ }
+
+ /** creates a single horizontal-block composite for key:value display */
+ public static Text addFormLine(Composite parent, String label, String text) {
+ Composite lineComposite = addLineComposite(parent, 2);
+ CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
+ addFormLabel(lineComposite, label);
+ Text txt = addFormTextField(lineComposite, text, null);
+ txt.setEditable(false);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+// public static Text addFormInput(Composite parent, String label, String placeholder) {
+// Composite lineComposite = addLineComposite(parent, 2);
+// addFormLabel(lineComposite, label);
+// Text txt = addFormInputField(lineComposite, placeholder);
+// txt.setLayoutData(CmsSwtUtils.fillWidth());
+// return txt;
+// }
+
+ /**
+ * creates a single horizontal-block composite for key:value display, with
+ * offset value
+ */
+ public static Text addFormLine(Composite parent, String label, String text, Integer offset) {
+ Composite lineComposite = addLineComposite(parent, 3);
+ Label offsetLbl = new Label(lineComposite, SWT.NONE);
+ GridData gridData = new GridData();
+ gridData.widthHint = offset;
+ offsetLbl.setLayoutData(gridData);
+ addFormLabel(lineComposite, label);
+ Text txt = addFormTextField(lineComposite, text, null);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ /** creates a single vertical-block composite for key:value display */
+ public static Text addFormColumn(Composite parent, Localized label, String text) {
+ return addFormColumn(parent, label.lead(), text);
+ }
+
+ /** creates a single vertical-block composite for key:value display */
+ public static Text addFormColumn(Composite parent, String label, String text) {
+ // Composite columnComposite = new Composite(parent, SWT.NONE);
+ // columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
+ // false));
+ // columnComposite.setLayout(new GridLayout(1, false));
+ addFormLabel(parent, label);
+ Text txt = addFormTextField(parent, text, null);
+ txt.setEditable(false);
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Label createBoldLabel(Composite parent, Localized localized) {
+ Label label = new Label(parent, SWT.LEAD);
+ label.setText(localized.lead());
+ label.setFont(EclipseUiUtils.getBoldFont(parent));
+ label.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+ return label;
+ }
+
+ /*
+ * CONTENT
+ */
+ public static String toLink(Content content) {
+ return content != null ? "#" + CmsSwtUtils.cleanPathForUrl(content.getPath()) : null;
+ }
+
+ public static Text addFormLine(Composite parent, Localized label, Content content, QNamed property,
+ CmsEditable cmsEditable) {
+ return addFormLine(parent, label.lead(), content, property.qName(), cmsEditable);
+ }
+
+ public static EditableText addTextLine(Composite parent, int style, Localized msg, Content content, QNamed attr,
+ CmsEditable cmsEditable, boolean line, Predicate<String> validator) {
+ Composite parentToUse = line ? SuiteSwtUtils.addLineComposite(parent, 2) : parent;
+ SuiteSwtUtils.addFormLabel(parentToUse, msg.lead());
+ EditableText text = createFormText(parentToUse, style, msg, content, attr, cmsEditable, validator);
+ return text;
+ }
+
+ public static EditableText createFormText(Composite parent, int style, Localized msg, Content content, QNamed attr,
+ CmsEditable cmsEditable, Predicate<String> validator) {
+ EditableText text = new EditableText(parent, style | SWT.FLAT | (cmsEditable.isEditing() ? 0 : SWT.READ_ONLY));
+ text.setMessage("-");
+ text.setLayoutData(CmsSwtUtils.fillWidth());
+ text.setStyle(SuiteStyle.simpleInput);
+ String txt = content.attr(attr);
+ if (txt == null)
+ txt = "";
+ text.setText(txt);
+ if (cmsEditable.isEditing())
+ text.setMouseListener(new MouseAdapter() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ String currentTxt = text.getText();
+ text.startEditing();
+ text.setText(currentTxt);
+
+ Runnable save = () -> {
+ String editedTxt = text.getText();
+ if (validator != null) {
+ if (!validator.test(editedTxt)) {
+ text.stopEditing();
+ text.setText(currentTxt);
+ CmsFeedback.show(editedTxt + " is not properly formatted");
+ return;
+ // throw new IllegalArgumentException(editedTxt + " is not properly formatted");
+ }
+ }
+ content.put(attr, editedTxt);
+ text.stopEditing();
+ text.setText(editedTxt);
+ text.getParent().layout(new Control[] { text.getControl() });
+ };
+ ((Text) text.getControl()).addSelectionListener(new SelectionListener() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ save.run();
+ }
+ });
+ ((Text) text.getControl()).addFocusListener(new FocusListener() {
+
+ private static final long serialVersionUID = 333838002411959302L;
+
+ @Override
+ public void focusLost(FocusEvent event) {
+ save.run();
+ }
+
+ @Override
+ public void focusGained(FocusEvent event) {
+ }
+ });
+
+ }
+
+ });
+ return text;
+ }
+
+ public static Text addFormLine(Composite parent, String label, Content content, QName property,
+ CmsEditable cmsEditable) {
+ Composite lineComposite = SuiteSwtUtils.addLineComposite(parent, 2);
+ SuiteSwtUtils.addFormLabel(lineComposite, label);
+ String text = content.attr(property);
+ Text txt = SuiteSwtUtils.addFormTextField(lineComposite, text, null, SWT.WRAP);
+ if (cmsEditable != null && cmsEditable.isEditing()) {
+ txt.addModifyListener((e) -> {
+ content.put(property, txt.getText());
+ });
+ } else {
+ txt.setEditable(false);
+ }
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ public static Text addFormColumn(Composite parent, Localized label, Content content, QNamed property,
+ CmsEditable cmsEditable) {
+ return addFormColumn(parent, label.lead(), content, property.qName(), cmsEditable);
+ }
+
+ public static Text addFormColumn(Composite parent, String label, Content content, QName property,
+ CmsEditable cmsEditable) {
+ SuiteSwtUtils.addFormLabel(parent, label);
+ String text = content.attr(property);
+ Text txt = SuiteSwtUtils.addFormTextField(parent, text, null, 0);
+ if (cmsEditable != null && cmsEditable.isEditing()) {
+ txt.addModifyListener((e) -> {
+ content.put(property, txt.getText());
+ });
+ } else {
+ txt.setEditable(false);
+ }
+ txt.setLayoutData(CmsSwtUtils.fillWidth());
+ return txt;
+ }
+
+ /*
+ * LINKS
+ */
+
+ /** Add a link to an internal content. */
+ public static Control addLink(Composite parent, String label, Content node, CmsStyle style) {
+ String target = toLink(node);
+ CmsLink link = new CmsLink(label, target, style);
+ return link.createUi(parent);
+ }
+
+ public static Control addExternalLink(Composite parent, String label, String url, String plainCssAnchorClass,
+ boolean newWindow) {
+ Label lbl = new Label(parent, SWT.NONE);
+ CmsSwtUtils.markup(lbl);
+ StringBuilder txt = new StringBuilder();
+ txt.append("<a");
+ if (plainCssAnchorClass != null)
+ txt.append(" class='" + plainCssAnchorClass + "'");
+ txt.append(" href='").append(url).append("'");
+ if (newWindow) {
+ txt.append(" target='blank_'");
+ }
+ txt.append(">");
+ txt.append(label);
+ txt.append("</a>");
+ lbl.setText(txt.toString());
+ return lbl;
+ }
+
+ /*
+ * IMAGES
+ */
+
+ public static Img addPicture(Composite parent, Content file) {
+ return addPicture(parent, file, null);
+ }
+
+ public static Img addPicture(Composite parent, Content file, Integer maxWidth) {
+ return addPicture(parent, file, maxWidth, null);
+ }
+
+ public static Img addPicture(Composite parent, Content file, Integer maxWidth, Content link) {
+ // TODO optimise
+// Integer width;
+// Integer height;
+// if (file.hasContentClass(EntityType.box)) {
+// width = file.get(SvgAttrs.width, Integer.class).get();
+// height = file.get(SvgAttrs.height, Integer.class).get();
+// } else {
+// try (InputStream in = file.open(InputStream.class)) {
+// ImageData imageData = new ImageData(in);
+// width = imageData.width;
+// height = imageData.height;
+// } catch (IOException e) {
+// throw new RuntimeException(e);
+// }
+// }
+//
+// if (maxWidth != null && width > maxWidth) {
+// Double ratio = maxWidth.doubleValue() / width.doubleValue();
+// width = maxWidth;
+// height = (int) Math.rint(ratio * height);
+// }
+// Label img = new Label(parent, SWT.NONE);
+// CmsSwtUtils.markup(img);
+// StringBuffer txt = new StringBuffer();
+// String target = toLink(link);
+// if (target != null)
+// txt.append("<a href='").append(target).append("'>");
+// txt.append(CmsUiUtils.img(fileNode, width.toString(), height.toString()));
+// if (target != null)
+// txt.append("</a>");
+// img.setText(txt.toString());
+// if (parent.getLayout() instanceof GridLayout) {
+// GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+// gd.widthHint = width.intValue();
+// gd.heightHint = height.intValue();
+// img.setLayoutData(gd);
+// }
+
+ Img img = new Img(parent, 0, file, new Cms2DSize(maxWidth != null ? maxWidth : 0, 0));
+ if (link != null)
+ img.setLink(link);
+
+// String target = toLink(link);
+ if (link == null)
+ img.addMouseListener(new MouseListener() {
+ private static final long serialVersionUID = -1362242049325206168L;
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ }
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ LightweightDialog dialog = new LightweightDialog(img.getShell()) {
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ parent.setLayout(new GridLayout());
+ ScrolledComposite scroll = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+ scroll.setLayoutData(CmsSwtUtils.fillAll());
+ scroll.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+ // scroll.setAlwaysShowScrollBars(true);
+
+ Composite c = new Composite(scroll, SWT.NONE);
+ scroll.setContent(c);
+ c.setLayout(new GridLayout());
+ c.setLayoutData(CmsSwtUtils.fillAll());
+ Img bigImg = new Img(c, 0, file);
+// Label bigImg = new Label(c, SWT.NONE);
+// CmsSwtUtils.markup(bigImg);
+// bigImg.setText(CmsUiUtils.img(fileNode, Jcr.get(content, EntityNames.SVG_WIDTH),
+// Jcr.get(content, EntityNames.SVG_HEIGHT)));
+ bigImg.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ return bigImg;
+ }
+
+ @Override
+ protected Point getInitialSize() {
+ Point shellSize = img.getShell().getSize();
+ return new Point(shellSize.x - 100, shellSize.y - 100);
+ }
+
+ };
+ dialog.open();
+ }
+ });
+ img.initControl();
+ return img;
+ }
+
+ /** singleton */
+ private SuiteSwtUtils() {
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.eclipse.swt.widgets.Composite;
+
+/** An UI layer for the main work area. */
+public interface SwtAppLayer extends SwtUiProvider {
+ static enum Property {
+ title, icon, weights, startMaximized, singleTab, singleTabTitle, fixedEntryArea;
+ }
+
+ String getId();
+
+ void view(SwtUiProvider uiProvider, Composite workArea, Content context);
+
+ Content getCurrentContext(Composite workArea);
+
+ default void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
+ view(uiProvider, workArea, context);
+ }
+
+ default Localized getTitle() {
+ return null;
+ }
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.app.ux.AppUi;
+import org.argeo.app.ux.SuiteStyle;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtUi;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** The view for the default UX of Argeo Suite. */
+public class SwtAppUi extends CmsSwtUi implements AppUi {
+ static enum Structural {
+ header, footer, leadPane, sidePane, loginScreen, adminLeadPane;
+ }
+
+ private static final long serialVersionUID = 6207018859086689108L;
+ private final static CmsLog log = CmsLog.getLog(SwtAppUi.class);
+
+ private Localized title;
+ private Composite header;
+ private Composite footer;
+ private Composite belowHeader;
+ private Composite leadPane;
+ private Composite sidePane;
+ private Composite dynamicArea;
+
+ private Content userDir;
+
+ private Map<String, SwtAppLayer> layers = new HashMap<>();
+ private Map<String, Composite> workAreas = new HashMap<>();
+ private String currentLayerId = null;
+
+ private boolean loginScreen = false;
+
+ public SwtAppUi(Composite parent, int style) {
+ super(parent, style);
+ this.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+ header = new Composite(this, SWT.NONE);
+ header.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(header, SuiteStyle.header);
+ header.setLayoutData(CmsSwtUtils.fillWidth());
+
+ belowHeader = new Composite(this, SWT.NONE);
+ belowHeader.setLayoutData(CmsSwtUtils.fillAll());
+
+ footer = new Composite(this, SWT.NONE);
+ footer.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(footer, SuiteStyle.footer);
+ footer.setLayoutData(CmsSwtUtils.fillWidth());
+ }
+
+ public void refreshBelowHeader(boolean initApp) {
+ CmsSwtUtils.clear(belowHeader);
+ int style = getStyle();
+ if (initApp) {
+ belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+
+ if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+ sidePane = new Composite(belowHeader, SWT.NONE);
+ sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ sidePane.setLayoutData(CmsSwtUtils.fillHeight());
+ dynamicArea = new Composite(belowHeader, SWT.NONE);
+ leadPane = new Composite(belowHeader, SWT.NONE);
+ } else {
+ leadPane = new Composite(belowHeader, SWT.NONE);
+ dynamicArea = new Composite(belowHeader, SWT.NONE);
+ sidePane = new Composite(belowHeader, SWT.NONE);
+ sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ sidePane.setLayoutData(CmsSwtUtils.fillHeight());
+ }
+ leadPane.setLayoutData(CmsSwtUtils.fillHeight());
+ leadPane.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ CmsSwtUtils.style(leadPane, SuiteStyle.leadPane);
+
+ dynamicArea.setLayoutData(CmsSwtUtils.fillAll());
+ dynamicArea.setLayout(new FormLayout());
+
+ } else {
+ belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
+ }
+ }
+
+ /*
+ * LAYERS
+ */
+
+ public Composite getCurrentWorkArea() {
+ if (currentLayerId == null)
+ throw new IllegalStateException("No current layer");
+ return workAreas.get(currentLayerId);
+ }
+
+ public String getCurrentLayerId() {
+ return currentLayerId;
+ }
+
+ private Composite getLayer(String id, Content context) {
+ if (!layers.containsKey(id))
+ return null;
+ if (!workAreas.containsKey(id))
+ initLayer(id, layers.get(id), context);
+ return workAreas.get(id);
+ }
+
+ public Composite switchToLayer(String layerId, Content context) {
+ Composite current = null;
+ if (currentLayerId != null) {
+ current = getCurrentWorkArea();
+ if (currentLayerId.equals(layerId))
+ return current;
+ }
+ if (context == null) {
+ if (!getCmsView().isAnonymous())
+ context = getUserDir();
+ }
+ Composite toShow = getLayer(layerId, context);
+ if (toShow != null) {
+ currentLayerId = layerId;
+ if (!isDisposed()) {
+ if (!toShow.isDisposed()) {
+ toShow.moveAbove(null);
+ } else {
+ log.warn("Cannot show work area because it is disposed.");
+ toShow = initLayer(layerId, layers.get(layerId), context);
+ toShow.moveAbove(null);
+ }
+ dynamicArea.layout(true, true);
+ }
+ return toShow;
+ } else {
+ return current;
+ }
+ }
+
+ public void switchToLayer(SwtAppLayer layer, Content context) {
+ // TODO make it more robust
+ for (String layerId : layers.keySet()) {
+ SwtAppLayer l = layers.get(layerId);
+ if (layer.getId().equals(l.getId())) {
+ switchToLayer(layerId, context);
+ return;
+ }
+ }
+ throw new IllegalArgumentException("Layer is not registered.");
+ }
+
+ public void addLayer(String id, SwtAppLayer layer) {
+ if (!id.equals(layer.getId())) {
+ log.error("Layer id as key '" + id + "' is not consistent with layer id '" + layer.getId()
+ + "', ignoring...");
+ return;
+ }
+ layers.put(id, layer);
+ }
+
+ public void removeLayer(String id) {
+ layers.remove(id);
+ if (workAreas.containsKey(id)) {
+ Composite workArea = workAreas.remove(id);
+ if (!workArea.isDisposed())
+ workArea.dispose();
+ }
+ }
+
+ protected Composite initLayer(String id, SwtAppLayer layer, Content context) {
+ Composite workArea = getCmsView().doAs(() -> (Composite) layer.createUiPart(dynamicArea, context));
+ CmsSwtUtils.style(workArea, SuiteStyle.workArea);
+ workArea.setLayoutData(CmsSwtUtils.coverAll());
+ workAreas.put(id, workArea);
+ return workArea;
+ }
+
+ public synchronized void logout() {
+ userDir = null;
+ currentLayerId = null;
+ workAreas.clear();
+ }
+
+ /*
+ * GETTERS / SETTERS
+ */
+
+ public Composite getHeader() {
+ return header;
+ }
+
+ public Composite getFooter() {
+ return footer;
+ }
+
+ public Composite getLeadPane() {
+ return leadPane;
+ }
+
+ public Composite getSidePane() {
+ return sidePane;
+ }
+
+ public Composite getBelowHeader() {
+ return belowHeader;
+ }
+
+ public Content getUserDir() {
+ return userDir;
+ }
+
+ public void setUserDir(Content userDir) {
+ this.userDir = userDir;
+ }
+
+// @Override
+ public Localized getTitle() {
+ return title;
+ }
+
+ public void setTitle(Localized title) {
+ this.title = title;
+ }
+
+ @Override
+ public boolean isLoginScreen() {
+ return loginScreen;
+ }
+
+ public void setLoginScreen(boolean loginScreen) {
+ this.loginScreen = loginScreen;
+ }
+}
--- /dev/null
+package org.argeo.app.swt.ux;
+
+import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsEvent;
+import org.argeo.api.cms.CmsEventSubscriber;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.app.api.AppUserState;
+import org.argeo.app.api.EntityConstants;
+import org.argeo.app.api.EntityName;
+import org.argeo.app.api.EntityType;
+import org.argeo.app.api.RankedObject;
+import org.argeo.app.ux.AbstractArgeoApp;
+import org.argeo.app.ux.AppUi;
+import org.argeo.app.ux.SuiteUxEvent;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.Localized;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.acr.SwtUiProvider;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.util.LangUtils;
+import org.argeo.cms.ux.CmsUxUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Constants;
+
+/** The Argeo Suite App. */
+public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber {
+ private final static CmsLog log = CmsLog.getLog(SwtArgeoApp.class);
+
+ public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
+ public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
+ public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
+ public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
+ public final static String SHARED_PID_PREFIX_PROPERTY = "sharedPidPrefix";
+
+ private final static String LOGIN = "login";
+ private final static String HOME_STATE = "~";
+
+ private String publicBasePath = null;
+
+ private String appPid;
+ private String pidPrefix;
+ private String sharedPidPrefix;
+
+// private String headerPid;
+// private String footerPid;
+// private String leadPanePid;
+// private String adminLeadPanePid;
+// private String loginScreenPid;
+
+ private String defaultUiName = "app";
+ private String adminUiName = "admin";
+
+ // FIXME such default names make refactoring more dangerous
+ @Deprecated
+ private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
+ @Deprecated
+ private String defaultThemeId = "org.argeo.app.theme.default";
+
+ // TODO use QName as key for byType
+ private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
+ private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
+ private Map<String, RankedObject<SwtAppLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
+ private Map<String, RankedObject<SwtAppLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
+
+// private CmsUserManager cmsUserManager;
+
+ // TODO make more optimal or via CmsSession/CmsView
+ private Map<String, SwtAppUi> managedUis = Collections.synchronizedMap(new HashMap<>());
+
+ // ACR
+ private ContentRepository contentRepository;
+ private AppUserState appUserState;
+ // JCR
+// private Repository repository;
+
+ public void start(Map<String, Object> properties) {
+ for (SuiteUxEvent event : SuiteUxEvent.values()) {
+ getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
+ }
+
+ if (log.isDebugEnabled())
+ log.info("Argeo Suite App started");
+
+ if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
+ defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
+ if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
+ defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
+ if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
+ defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
+ sharedPidPrefix = LangUtils.get(properties, SHARED_PID_PREFIX_PROPERTY);
+ publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
+
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ appPid = properties.get(Constants.SERVICE_PID).toString();
+ int lastDotIndex = appPid.lastIndexOf('.');
+ if (lastDotIndex >= 0) {
+ pidPrefix = appPid.substring(0, lastDotIndex);
+ }
+ } else {
+ // TODO doe it make sense to accept that?
+ appPid = "<unknown>";
+ }
+
+ Objects.requireNonNull(contentRepository, "Content repository must be provided");
+ Objects.requireNonNull(appUserState, "App user state must be provided");
+// if (pidPrefix == null)
+// throw new IllegalArgumentException("PID prefix must be set.");
+
+// headerPid = pidPrefix + "header";
+// footerPid = pidPrefix + "footer";
+// leadPanePid = pidPrefix + "leadPane";
+// adminLeadPanePid = pidPrefix + "adminLeadPane";
+// loginScreenPid = pidPrefix + "loginScreen";
+ }
+
+ public void stop(Map<String, Object> properties) {
+ for (SwtAppUi ui : managedUis.values())
+ if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) {
+ ui.getDisplay().syncExec(() -> ui.dispose());
+ }
+ managedUis.clear();
+ if (log.isDebugEnabled())
+ log.info("Argeo Suite App stopped");
+
+ }
+
+ @Override
+ public Set<String> getUiNames() {
+ HashSet<String> uiNames = new HashSet<>();
+ uiNames.add(defaultUiName);
+ uiNames.add(adminUiName);
+ return uiNames;
+ }
+
+ @Override
+ public CmsUi initUi(Object parent) {
+ Composite uiParent = (Composite) parent;
+ String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
+ : null;
+ CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
+ if (cmsView == null)
+ throw new IllegalStateException("No CMS view is registered.");
+ CmsTheme theme = getTheme(uiName);
+ if (theme != null)
+ CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
+ SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT);
+ String uid = cmsView.getUid();
+ managedUis.put(uid, argeoSuiteUi);
+ argeoSuiteUi.addDisposeListener(new CleanUpUi(uid));
+// argeoSuiteUi.addDisposeListener((e) -> {
+// managedUis.remove(uid);
+// if (log.isDebugEnabled())
+// log.debug("Suite UI " + uid + " has been disposed.");
+// });
+// Display.getCurrent().disposeExec(() -> {
+// if (managedUis.containsKey(uid)) {
+// managedUis.remove(uid);
+// if (log.isDebugEnabled())
+// log.debug("Suite UI " + uid + " has been disposed from Display#disposeExec().");
+// }
+// });
+ return argeoSuiteUi;
+ }
+
+ @Override
+ public String getThemeId(String uiName) {
+ String themeId = System.getProperty("org.argeo.app.theme.default");
+ if (themeId != null)
+ return themeId;
+ return defaultThemeId;
+ }
+
+ @Override
+ public void refreshUi(CmsUi cmsUi, String state) {
+ try {
+ Content context = null;
+ SwtAppUi ui = (SwtAppUi) cmsUi;
+
+ String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
+ if (uiName == null)
+ throw new IllegalStateException("UI name should not be null");
+ CmsView cmsView = CmsSwtUtils.getCmsView(ui);
+
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
+
+ SwtUiProvider headerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.header.name());
+ SwtUiProvider footerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.footer.name());
+ SwtUiProvider leadPaneUiProvider;
+ if (adminUiName.equals(uiName)) {
+ leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.adminLeadPane.name());
+ } else {
+ leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.leadPane.name());
+ }
+
+ Localized appTitle = null;
+ if (headerUiProvider instanceof DefaultHeader) {
+ appTitle = ((DefaultHeader) headerUiProvider).getTitle();
+ }
+ ui.setTitle(appTitle);
+
+ if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
+ ui.logout();
+ ui.setLoginScreen(true);
+ if (headerUiProvider != null)
+ refreshPart(headerUiProvider, ui.getHeader(), context);
+ ui.refreshBelowHeader(false);
+ SwtUiProvider loginScreenUiProvider = findStructuralUiProvider(SwtAppUi.Structural.loginScreen.name());
+ refreshPart(loginScreenUiProvider, ui.getBelowHeader(), context);
+ if (footerUiProvider != null)
+ refreshPart(footerUiProvider, ui.getFooter(), context);
+ ui.layout(true, true);
+ setState(ui, LOGIN);
+ } else {
+ if (LOGIN.equals(state))
+ state = null;
+ if (ui.isLoginScreen()) {
+ ui.setLoginScreen(false);
+ }
+ CmsSession cmsSession = cmsView.getCmsSession();
+ if (ui.getUserDir() == null) {
+ // FIXME NPE on CMSSession when logging in from anonymous
+ if (cmsSession == null || cmsView.isAnonymous()) {
+ assert publicBasePath != null;
+ Content userDir = contentSession
+ .get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + publicBasePath);
+ ui.setUserDir(userDir);
+ } else {
+ Content userDir = appUserState.getOrCreateSessionDir(cmsSession);
+ ui.setUserDir(userDir);
+// Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
+// Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
+// return node;
+// });
+// Content userDir = contentSession
+// .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
+// ui.setUserDir(userDir);
+ }
+ }
+ initLocale(cmsSession);
+ context = stateToNode(ui, state);
+ if (context == null)
+ context = ui.getUserDir();
+
+ if (headerUiProvider != null)
+ refreshPart(headerUiProvider, ui.getHeader(), context);
+ ui.refreshBelowHeader(true);
+ for (String key : layersByPid.keySet()) {
+ SwtAppLayer layer = layersByPid.get(key).get();
+ ui.addLayer(key, layer);
+ }
+
+ if (leadPaneUiProvider != null)
+ refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
+ if (footerUiProvider != null)
+ refreshPart(footerUiProvider, ui.getFooter(), context);
+ ui.layout(true, true);
+ setState(ui, state != null ? state : defaultLayerPid);
+ }
+ } catch (Exception e) {
+ CmsFeedback.error("Unexpected exception", e);
+ }
+ }
+
+ private void initLocale(CmsSession cmsSession) {
+ if (cmsSession == null)
+ return;
+ Locale locale = cmsSession.getLocale();
+ UiContext.setLocale(locale);
+ LocaleUtils.setThreadLocale(locale);
+
+ }
+
+ private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
+ CmsSwtUtils.clear(part);
+ uiProvider.createUiPart(part, context);
+ }
+
+ private SwtUiProvider findStructuralUiProvider(String suffix) {
+ SwtUiProvider res = null;
+ if (pidPrefix != null)
+ res = findUiProvider(pidPrefix + "." + suffix);
+ if (res != null)
+ return res;
+ if (sharedPidPrefix != null)
+ res = findUiProvider(sharedPidPrefix + "." + suffix);
+ return res;
+ }
+
+ private SwtUiProvider findUiProvider(String pid) {
+ if (!uiProvidersByPid.containsKey(pid))
+ return null;
+ return uiProvidersByPid.get(pid).get();
+ }
+
+ private SwtAppLayer findLayer(String pid) {
+ if (!layersByPid.containsKey(pid))
+ return null;
+ return layersByPid.get(pid).get();
+ }
+
+ private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
+ if (content == null)
+ throw new IllegalArgumentException("A node should be provided");
+
+// boolean checkJcr = false;
+// if (checkJcr && content instanceof JcrContent) {
+// Node context = ((JcrContent) content).getJcrNode();
+// try {
+// // mixins
+// Set<String> types = new TreeSet<>();
+// for (NodeType mixinType : context.getMixinNodeTypes()) {
+// String mixinTypeName = mixinType.getName();
+// if (byType.containsKey(mixinTypeName)) {
+// types.add(mixinTypeName);
+// }
+// for (NodeType superType : mixinType.getDeclaredSupertypes()) {
+// if (byType.containsKey(superType.getName())) {
+// types.add(superType.getName());
+// }
+// }
+// }
+// // primary node type
+// NodeType primaryType = context.getPrimaryNodeType();
+// String primaryTypeName = primaryType.getName();
+// if (byType.containsKey(primaryTypeName)) {
+// types.add(primaryTypeName);
+// }
+// for (NodeType superType : primaryType.getDeclaredSupertypes()) {
+// if (byType.containsKey(superType.getName())) {
+// types.add(superType.getName());
+// }
+// }
+// // entity type
+// if (context.isNodeType(EntityType.entity.get())) {
+// if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
+// String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
+// if (byType.containsKey(entityTypeName)) {
+// types.add(entityTypeName);
+// }
+// }
+// }
+//
+// if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
+// types.add("nt:folder");
+// }
+//
+// if (types.size() == 0)
+// throw new IllegalArgumentException(
+// "No type found for " + context + " (" + listTypes(context) + ")");
+// String type = types.iterator().next();
+// if (!byType.containsKey(type))
+// throw new IllegalArgumentException("No component found for " + context + " with type " + type);
+// return byType.get(type).get();
+// } catch (RepositoryException e) {
+// throw new IllegalStateException(e);
+// }
+//
+// } else {
+ Set<String> types = new TreeSet<>();
+ if (content.hasContentClass(EntityType.entity.qName())) {
+ String type = content.attr(EntityName.type.qName());
+ if (type != null && byType.containsKey(type))
+ types.add(type);
+ }
+
+ List<QName> objectClasses = content.getContentClasses();
+ for (QName cc : objectClasses) {
+ String type = cc.getPrefix() + ":" + cc.getLocalPart();
+ if (byType.containsKey(type))
+ types.add(type);
+ }
+ if (types.size() == 0) {
+ throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
+ }
+ String type = types.iterator().next();
+ if (!byType.containsKey(type))
+ throw new IllegalArgumentException("No component found for " + content + " with type " + type);
+ return byType.get(type).get();
+// }
+ }
+
+// private static String listTypes(Node context) {
+// try {
+// StringBuilder sb = new StringBuilder();
+// sb.append(context.getPrimaryNodeType().getName());
+// for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
+// sb.append(' ');
+// sb.append(superType.getName());
+// }
+//
+// for (NodeType nodeType : context.getMixinNodeTypes()) {
+// sb.append(' ');
+// sb.append(nodeType.getName());
+// if (nodeType.getName().equals(EntityType.local.get()))
+// sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
+// for (NodeType superType : nodeType.getDeclaredSupertypes()) {
+// sb.append(' ');
+// sb.append(superType.getName());
+// }
+// }
+// return sb.toString();
+// } catch (RepositoryException e) {
+// throw new JcrException(e);
+// }
+// }
+
+ @Override
+ public void setState(CmsUi cmsUi, String state) {
+ AppUi ui = (AppUi) cmsUi;
+ if (state == null)
+ return;
+ if (!state.startsWith("/")) {
+// if (cmsUi instanceof SwtAppUi) {
+// SwtAppUi ui = (SwtAppUi) cmsUi;
+ if (LOGIN.equals(state)) {
+ String appTitle = "";
+ if (ui.getTitle() != null)
+ appTitle = ui.getTitle().lead();
+ ui.getCmsView().stateChanged(state, appTitle);
+ return;
+ }
+ Map<String, Object> properties = new HashMap<>();
+ String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
+ properties.put(SuiteUxEvent.LAYER, layerId);
+ properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE);
+ ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties);
+// }
+ return;
+ }
+// SwtAppUi suiteUi = (SwtAppUi) cmsUi;
+ if (ui.isLoginScreen()) {
+ return;
+ }
+
+ Content node = stateToNode(ui, state);
+ if (node == null) {
+ ui.getCmsView().navigateTo(HOME_STATE);
+ } else {
+ ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node));
+ ui.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node));
+ }
+ }
+
+ // TODO move it to an internal package?
+ private static String nodeToState(Content node) {
+ return node.getPath();
+ }
+
+ private Content stateToNode(CmsUi suiteUi, String state) {
+ if (suiteUi == null)
+ return null;
+ if (state == null || !state.startsWith("/"))
+ return null;
+
+ String path = state;
+
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+ suiteUi.getCmsView());
+ return contentSession.get(path);
+ }
+
+ /*
+ * Events management
+ */
+
+ @Override
+ public void onEvent(String topic, Map<String, Object> event) {
+
+ // Specific UI related events
+ SwtAppUi ui = getRelatedUi(event);
+ if (ui == null)
+ return;
+ ui.getCmsView().runAs(() -> {
+ try {
+ String appTitle = "";
+ if (ui.getTitle() != null)
+ appTitle = ui.getTitle().lead();
+
+ if (isTopic(topic, SuiteUxEvent.refreshPart)) {
+ Content node = getContentFromEvent(ui, event);
+ if (node == null)
+ return;
+ SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+ SwtAppLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ layer.view(uiProvider, ui.getCurrentWorkArea(), node);
+ ui.getCmsView().stateChanged(nodeToState(node), stateTitle(appTitle, CmsUxUtils.getTitle(node)));
+ } else if (isTopic(topic, SuiteUxEvent.openNewPart)) {
+ Content node = getContentFromEvent(ui, event);
+ if (node == null)
+ return;
+ SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
+ SwtAppLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ layer.open(uiProvider, ui.getCurrentWorkArea(), node);
+ ui.getCmsView().stateChanged(nodeToState(node), stateTitle(appTitle, CmsUxUtils.getTitle(node)));
+ } else if (isTopic(topic, SuiteUxEvent.switchLayer)) {
+ String layerId = get(event, SuiteUxEvent.LAYER);
+ if (layerId != null) {
+ SwtAppLayer suiteLayer = findLayer(layerId);
+ if (suiteLayer == null)
+ throw new IllegalArgumentException("No layer '" + layerId + "' available.");
+ Localized layerTitle = suiteLayer.getTitle();
+ // FIXME make sure we don't rebuild the work area twice
+ Composite workArea = ui.switchToLayer(layerId, ui.getUserDir());
+ String title = null;
+ if (layerTitle != null)
+ title = layerTitle.lead();
+ Content nodeFromState = getContentFromEvent(ui, event);
+ if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) {
+ // default layer view is forced
+ String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
+ ui.getCmsView().stateChanged(state, stateTitle(appTitle, title));
+ suiteLayer.view(null, workArea, nodeFromState);
+ } else {
+ Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
+ if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) {
+ // layer was already showing a context so we set the state to it
+ ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
+ stateTitle(appTitle, CmsUxUtils.getTitle(layerCurrentContext)));
+ } else {
+ // no context was shown
+ ui.getCmsView().stateChanged(layerId, stateTitle(appTitle, title));
+ }
+ }
+ } else {
+ Content node = getContentFromEvent(ui, event);
+ if (node != null) {
+ SwtAppLayer layer = findByType(layersByType, node);
+ ui.switchToLayer(layer, node);
+ }
+ }
+ }
+ } catch (Exception e) {
+ CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
+// log.error("Cannot handle event " + event, e);
+ }
+ });
+ }
+
+ private String stateTitle(String appTitle, String additionalTitle) {
+ return additionalTitle == null ? appTitle : appTitle + " - " + additionalTitle;
+ }
+
+ private boolean isTopic(String topic, CmsEvent cmsEvent) {
+ Objects.requireNonNull(topic);
+ return topic.equals(cmsEvent.topic());
+ }
+
+ protected Content getContentFromEvent(SwtAppUi ui, Map<String, Object> event) {
+ ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
+ ui.getCmsView());
+
+ String path = get(event, SuiteUxEvent.CONTENT_PATH);
+
+ if (path != null && (path.equals(HOME_STATE) || path.equals("")))
+ return ui.getUserDir();
+ Content node;
+ if (path == null) {
+ return null;
+// // look for a user
+// String username = get(event, SuiteUxEvent.USERNAME);
+// if (username == null)
+// return null;
+// User user = cmsUserManager.getUser(username);
+// if (user == null)
+// return null;
+// node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
+ } else {
+ node = contentSession.get(path);
+ }
+ return node;
+ }
+
+ private SwtAppUi getRelatedUi(Map<String, Object> eventProperties) {
+ return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
+ }
+
+ public static String get(Map<String, Object> eventProperties, String key) {
+ Object value = eventProperties.get(key);
+ if (value == null)
+ return null;
+ return value.toString();
+
+ }
+
+ /*
+ * Dependency injection.
+ */
+
+ public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
+ }
+ }
+ }
+
+ public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (uiProvidersByPid.containsKey(pid)) {
+ if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+ uiProvidersByPid.remove(pid);
+ }
+ }
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ if (uiProvidersByType.containsKey(type)) {
+ if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
+ uiProvidersByType.remove(type);
+ }
+ }
+ }
+ }
+ }
+
+ public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types)
+ RankedObject.putIfHigherRank(layersByType, type, layer, properties);
+ }
+ }
+
+ public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
+ if (properties.containsKey(Constants.SERVICE_PID)) {
+ String pid = (String) properties.get(Constants.SERVICE_PID);
+ if (layersByPid.containsKey(pid)) {
+ if (layersByPid.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+ layersByPid.remove(pid);
+ }
+ }
+ }
+ if (properties.containsKey(EntityConstants.TYPE)) {
+ List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
+ for (String type : types) {
+ if (layersByType.containsKey(type)) {
+ if (layersByType.get(type).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
+ layersByType.remove(type);
+ }
+ }
+ }
+ }
+ }
+
+// public void setCmsUserManager(CmsUserManager cmsUserManager) {
+// this.cmsUserManager = cmsUserManager;
+// }
+
+// protected ContentRepository getContentRepository() {
+// return contentRepository;
+// }
+
+ public void setContentRepository(ContentRepository contentRepository) {
+ this.contentRepository = contentRepository;
+ }
+
+ public void setAppUserState(AppUserState appUserState) {
+ this.appUserState = appUserState;
+ }
+
+ /**
+ * Dedicated class to clean up the UI in order to avoid illegal access issues
+ * with lambdas.
+ */
+ private class CleanUpUi implements DisposeListener {
+ private static final long serialVersionUID = 1905900302262082463L;
+ final String uid;
+
+ public CleanUpUi(String uid) {
+ super();
+ this.uid = uid;
+ }
+
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ managedUis.remove(uid);
+ if (log.isDebugEnabled())
+ log.debug("App " + appPid + " - Suite UI " + uid + " has been disposed (" + managedUis.size()
+ + " UIs still being managed).");
+ }
+
+ }
+
+}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Admin Lead Pane">
- <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultLeadPane"/>
<service>
<provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
</service>
<property name="service.ranking" type="Integer" value="-1000"/>
<property name="defaultLayers" type="String">argeo.suite.ui.termsLayer
</property>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.swt.ux.SwtAppLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Argeo Suite App">
- <implementation class="org.argeo.app.ui.SuiteApp"/>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop" name="Argeo Suite App">
+ <implementation class="org.argeo.app.swt.ux.SwtArgeoApp"/>
<service>
<provide interface="org.argeo.api.cms.CmsApp"/>
</service>
<properties entry="config/cmsApp.properties"/>
<reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" unbind="removeUiProvider"/>
<reference bind="addTheme" cardinality="1..n" interface="org.argeo.api.cms.ux.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
- <reference bind="setCmsUserManager" cardinality="1..1" interface="org.argeo.api.cms.directory.CmsUserManager" name="CmsUserManager" policy="static"/>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.swt.ux.SwtAppLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
<reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
<reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
- <reference bind="setJcrContentProvider" cardinality="1..1" interface="org.argeo.cms.jcr.acr.JcrContentProvider" name="JcrContentProvider" policy="static"/>
+ <reference bind="setAppUserState" cardinality="1..1" interface="org.argeo.app.api.AppUserState" name="AppUserState" policy="static"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Content Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Content Layer">
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
<service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
</service>
<reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" policy="dynamic" target="(service.pid=argeo.library.ui.contentEntryArea)"/>
<property name="service.ranking" type="Integer" value="-1000"/>
<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Dashboard Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Dashboard Layer">
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
<service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
</service>
<property name="service.ranking" type="Integer" value="-1000"/>
<properties entry="config/dashboardLayer.properties"/>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Footer">
- <implementation class="org.argeo.app.ui.DefaultFooter"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultFooter"/>
<service>
<provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
</service>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Suite Header">
- <implementation class="org.argeo.app.ui.DefaultHeader"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultHeader"/>
<service>
<provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
</service>
-dashboard=dashboard
-#people=contacts
-documents=documents
-locations=locations
-recentItems=recent items
-
appTitle=Argeo Suite
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=person
-user=user
-org=organisation
-group=group
-
-# NewPersonWizard
-firstName=First Name
-lastName=Last Name
-salutation=Salutation
-email=Email
-personWizardWindowTitle=New person
-personWizardPageTitle=Create a contact
-personWizardFeedback=Contact was created
-
-# NewOrgWizard
-legalName=Legal name
-legalForm=Legal form
-vatId=VAT ID
-orgWizardWindowTitle=New organisation
-orgWizardPageTitle=Create an organisation
-orgWizardFeedback=Organisation was created
-
-# Roles
-userAdminRole=Can create users and modify them
-groupAdminRole=Can create groups and organisations and modify them
-publisherRole=Can validate and publish content
-coworkerRole=Is an active user of the organisation
-
-# Group
-chooseAMember=Choose a member
-
-# ContextAddressComposite
-chooseAnOrganisation=Choose an organisation
-street=Street
-streetComplement=Street complement
-zipCode=Zip code
-city=City
-state=State
-country=Country
-geopoint=Geopoint
-
-# FilteredOrderableEntityTable
-filterHelp=Type filter criterion separated by a space
-
-# BankAccountComposite
-accountHolder=Account holder
-bankName=Bank name
-currency=Currency
-accountNumber=Account number
-bankNumber=Bank number
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Role
-chosenItem=Chose item
-department=Department
-isPrimary=Is primary
-searchAndChooseEntity=Search and choose a corresponding entity
-
-# ContactListCTab (e4)
-notes=Notes
-addAContact=Add a contact
-contactValue=Contact value
-linkedCompany=Linked company
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Payment account
-
-# OrgEditor (e4)
-orgDetails=Details
-orgActivityLog=Activity log
-team=Team
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Contact details
-personActivityLog=Activity log
-personOrgs=Organisations
-personSecurity=Security
-
-# PersonSecurityCTab (e4)
-resetPassword=Reset password
-
-# Generic
-label=Label
-aCustomLabel=A custom label
-description=Description
-value=Value
-name=Name
-primary=Primary
-add=Add
-save=Save
-pickUp=Pick up
-
-# Tags
-confirmNewTag=Tag #{0} is not yet registered. Are you sure you want to create it?
-cannotCreateTag=Tag #{0} is not yet registered and you don't have enough rights to create it.
-
-# People
-people=people
-
-# Library
-content=content
-
-# Geo
-map=map
-
-# Feedback messages
-allFieldsMustBeSet=All fields must be set
-
+people=People
+++ /dev/null
-dashboard=dashboard
-people=Kontakte
-documents=Dokumente
-locations=Orte
-recentItems=neulich
-
-appTitle=Argeo Suite
-
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=Person
-organisation=Organisation
-
-# NewPersonWizard
-firstName=Vorname
-lastName=Nachname
-salutation=Salutation
-email=E-Mail
-personWizardWindowTitle=Neue Person
-personWizardPageTitle=Kontakt erstellen
-
-# NewOrgWizard
-legalName=Name
-legalForm=Geschäftsform
-vatId=Ust ID
-orgWizardWindowTitle=Neue Organisation
-orgWizardPageTitle=Organisation erstellen
-
-
-# ContextAddressComposite
-chooseAnOrganisation=Organisation wählen
-street=Strasse
-streetComplement=Strasse Zusatz
-zipCode=PLZ
-city=Stadt
-state=Bundesland
-country=Land
-geopoint=Geopoint
-
-# FilteredOrderableEntityTable
-filterHelp=Type filter criterion separated by a space
-
-# BankAccountComposite
-accountHolder=Kontoinhaber
-bankName=Name der Bank
-currency=Währung
-accountNumber=Kontonummer
-bankNumber=BLZ
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Rolle
-chosenItem=Auswahl
-department=Abteilung
-isPrimary=Ist Primär
-searchAndChooseEntity=Suche und wähle ein zugehöriges Objekt
-
-# ContactListCTab (e4)
-notes=Bemerkungen
-addAContact=Kontakt hinzufügen
-contactValue=Kontakt value
-linkedCompany=zugehörige Firma
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Geschäftskonto
-
-# OrgEditor (e4)
-orgDetails=Details
-orgActivityLog=Aktivitäten Log
-team=Team
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Kontakt Daten
-personActivityLog=Aktivitäten Log
-personOrgs=Organisationen
-personSecurity=Sicherheit
-
-# PersonSecurityCTab (e4)
-resetPassword=Passwort zurücksetzen
-
-# Generic
-label=Beschriftung
-aCustomLabel=Eine spezifische Beschriftung
-description=Beschreibung
-value=Wert
-name=Name
-primary=Haupt-
-add=Hinzufügen
-save=Speichern
-pickUp=Aussuchen
-
-# Tags
-confirmNewTag=Das Hashtag '{0}' existiert noch nicht. WollenSie es hinzufügen?
-cannotCreateTag=Das Hashtag '{0}' existiert nicht uns Sie haben nicht die Rechte, um es hinzufügen.
+++ /dev/null
-dashboard=dashboard
-people=contacts
-documents=documents
-locations=lieux
-recentItems=récent
-
-appTitle=Argeo Suite
-
-#
-# GENERIC
-#
-
-#
-# PEOPLE
-# org.argeo.people.ui.PeopleMsg
-#
-person=personne
-user=utilisateur
-org=organisation
-group=groupe
-
-# NewPersonWizard
-firstName=Prénom
-lastName=Nom
-salutation=Salutation
-email=Email
-personWizardWindowTitle=Nouvelle personne
-personWizardPageTitle=Créer un contact
-personWizardFeedback=Le contact a été créé
-
-# NewOrgWizard
-legalName=Nom
-legalForm=Forme légale
-vatId=ID TVA
-orgWizardWindowTitle=Nouvelle organisation
-orgWizardPageTitle=Créer une organisation
-orgWizardFeedback=L'organisation a été crée
-
-# Roles
-userAdminRole=Peut créer des utilisateurs et les modifier
-groupAdminRole=Peut créer des groupes et des organisations et les modifier
-publisherRole=Peut publier et valider du contenu
-coworkerRole=Est un membre en activité de l'organisation
-
-# Group
-chooseAMember=Choisir un membre
-
-# ContextAddressComposite
-chooseAnOrganisation=Choisir une organisation
-street=Rue
-streetComplement=Complément rue
-zipCode=Code postal
-city=Ville
-state=État
-country=Pays
-geopoint=Géocoordonnées
-
-# FilteredOrderableEntityTable
-filterHelp=Sasir les critères de filtrage séparés par des espaces
-
-# BankAccountComposite
-accountHolder=Propriétaire du compte
-bankName=Nom de la banque
-currency=Devise
-accountNumber=Numéro de compte
-bankNumber=Numéro de banque
-BIC=BIC
-IBAN=IBAN
-
-# EditJobDialog
-position=Rôle
-chosenItem=Choisir une élément
-department=Service
-isPrimary=Principal
-searchAndChooseEntity=Cherhcer et choisir l'entitée correspondante
-
-# ContactListCTab (e4)
-notes=Notes
-addAContact=Ajouter un contact
-contactValue=Valeur
-linkedCompany=Entreprise liée
-
-# OrgAdminInfoCTab (e4)
-paymentAccount=Compte de paiement
-
-# OrgEditor (e4)
-orgDetails=Détails
-orgActivityLog=Activités
-team=Équipe
-orgAdmin=Admin.
-
-# PersonEditor (e4)
-personDetails=Détails du contact
-personActivityLog=Activités
-personOrgs=Organisations
-personSecurity=Accès
-
-# PersonSecurityCTab (e4)
-resetPassword=Force le mot de passe
-
-# Generic
-label=Étiquette
-aCustomLabel=Une étiquette spécifique
-description=Description
-value=Valeur
-name=Nom
-primary=Principal
-add=Ajouter
-save=Sauver
-pickUp=Choisir
-
-# Tags
-confirmNewTag=Le tag #{0} n'existe pas encore. Voulez-vous le créer?
-cannotCreateTag=Le tag #{0} n'existe pas encore et vous n'avez pas les droits pour le créer.
-
-# Feedback messages
-allFieldsMustBeSet=Toutes les données doivent être renseignées
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" immediate="false" name="Default Lead Pane">
- <implementation class="org.argeo.app.ui.DefaultLeadPane"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultLeadPane"/>
<service>
<provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
</service>
<properties entry="config/leadPane.properties"/>
<property name="defaultLayers" type="String">argeo.suite.ui.dashboardLayer
argeo.library.ui.contentLayer
+argeo.product.knowledge.structureLayer
argeo.people.ui.peopleLayer
argeo.geo.ui.mapLayer
</property>
- <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
+ <reference bind="addLayer" cardinality="1..n" interface="org.argeo.app.swt.ux.SwtAppLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default Login Screen">
- <implementation class="org.argeo.app.ui.DefaultLoginScreen"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultLoginScreen"/>
<properties entry="config/loginScreen.properties"/>
<service>
<provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Map Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
<properties entry="config/mapLayer.properties"/>
<service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
</service>
<property name="service.ranking" type="Integer" value="-1000"/>
<reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.geo.ui.overviewMap)"/>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="People Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
<properties entry="config/peopleLayer.properties"/>
<service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
</service>
<property name="service.ranking" type="Integer" value="-1000"/>
<reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.people.ui.peopleEntryArea)"/>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Entry Area">
- <implementation class="org.argeo.app.ui.TermsEntryArea"/>
- <service>
- <provide interface="org.argeo.cms.swt.acr.SwtUiProvider"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/termsEntryArea.properties"/>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Terms Layer">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
- <service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
- </service>
- <property name="service.ranking" type="Integer" value="-1000"/>
- <properties entry="config/termsLayer.properties"/>
- <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.suite.ui.termsEntryArea)"/>
-</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy">
- <implementation class="org.argeo.app.ui.DefaultEditionLayer"/>
+ <implementation class="org.argeo.app.swt.ux.DefaultEditionLayer"/>
<properties entry="config/wwwLayer.properties"/>
<service>
- <provide interface="org.argeo.app.ui.SuiteLayer"/>
+ <provide interface="org.argeo.app.swt.ux.SwtAppLayer"/>
</service>
<property name="service.ranking" type="Integer" value="-1000"/>
<reference bind="setWorkArea" cardinality="1..1" interface="org.argeo.cms.swt.acr.SwtUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.publishing.ui.documentUiProvider)"/>
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,\
org.eclipse.jface.dialogs,\
org.eclipse.rap.rwt,\
javax.servlet.*;version="[3,5)",\
+javax.jcr.nodetype,\
*
+++ /dev/null
-service.pid=argeo.suite.ui.termsEntryArea
+++ /dev/null
-service.pid=argeo.suite.ui.termsLayer
-title=Terms
-icon=dashboard
-
-entity.type=entity:terms,entity:term
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtTabbedArea;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.util.LangUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** An app layer based on an entry area and an editor area. */
-public class DefaultEditionLayer implements SuiteLayer {
- private String id;
- private SwtUiProvider entryArea;
- private SwtUiProvider defaultView;
- private SwtUiProvider workArea;
- private List<String> weights = new ArrayList<>();
- private boolean startMaximized = false;
- private boolean fixedEntryArea = false;
- private boolean singleTab = false;
- private Localized title = null;
- private Localized singleTabTitle = null;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- // TODO Factorize more, or split into more specialised classes?
- if (entryArea != null) {
- if (fixedEntryArea) {
- FixedEditionArea editionArea = new FixedEditionArea(parent, parent.getStyle());
- Control entryAreaC = entryArea.createUiPart(editionArea.getEntryArea(), context);
- CmsSwtUtils.style(entryAreaC, SuiteStyle.entryArea);
- if (this.defaultView != null) {
- editionArea.getTabbedArea().view(defaultView, context);
- }
- return editionArea;
- } else {
- SashFormEditionArea editionArea = new SashFormEditionArea(parent, parent.getStyle());
- entryArea.createUiPart(editionArea.getEntryArea(), context);
- if (this.defaultView != null) {
- editionArea.getTabbedArea().view(defaultView, context);
- }
- return editionArea;
- }
- } else {
- if (this.workArea != null) {
- Composite area = new Composite(parent, SWT.NONE);
- this.workArea.createUiPart(area, context);
- return area;
- }
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
- SwtTabbedArea tabbedArea = createTabbedArea(parent, theme);
- return tabbedArea;
- }
- }
-
- @Override
- public void view(SwtUiProvider uiProvider, Composite workAreaC, Content context) {
- if (workArea != null) {
- CmsSwtUtils.clear(workAreaC);
- workArea.createUiPart(workAreaC, context);
- workAreaC.layout(true, true);
- return;
- }
-
- // tabbed area
- SwtTabbedArea tabbedArea = findTabbedArea(workAreaC);
- if (tabbedArea == null)
- throw new IllegalArgumentException("Unsupported work area " + workAreaC.getClass().getName());
- if (uiProvider == null) {
- // reset
- tabbedArea.closeAllTabs();
- if (this.defaultView != null) {
- tabbedArea.view(defaultView, context);
- }
- } else {
- tabbedArea.view(uiProvider, context);
- }
- }
-
- @Override
- public Content getCurrentContext(Composite workArea) {
- SwtTabbedArea tabbedArea = findTabbedArea(workArea);
- if (tabbedArea == null)
- return null;
- return tabbedArea.getCurrentContext();
- }
-
- private SwtTabbedArea findTabbedArea(Composite workArea) {
- SwtTabbedArea tabbedArea = null;
- if (workArea instanceof SashFormEditionArea) {
- tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
- } else if (workArea instanceof FixedEditionArea) {
- tabbedArea = ((FixedEditionArea) workArea).getTabbedArea();
- } else if (workArea instanceof SwtTabbedArea) {
- tabbedArea = (SwtTabbedArea) workArea;
- }
- return tabbedArea;
- }
-
- @Override
- public void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
- SwtTabbedArea tabbedArea = ((SashFormEditionArea) workArea).getTabbedArea();
- tabbedArea.open(uiProvider, context);
- }
-
- @Override
- public Localized getTitle() {
- return title;
- }
-
- @Override
- public String getId() {
- return id;
- }
-
- public void init(BundleContext bundleContext, Map<String, Object> properties) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- id = pid;
-
- weights = LangUtils.toStringList(properties.get(Property.weights.name()));
- startMaximized = properties.containsKey(Property.startMaximized.name())
- && "true".equals(properties.get(Property.startMaximized.name()));
- fixedEntryArea = properties.containsKey(Property.fixedEntryArea.name())
- && "true".equals(properties.get(Property.fixedEntryArea.name()));
- if (fixedEntryArea && weights.size() != 0) {
- throw new IllegalArgumentException("Property " + Property.weights.name() + " should not be set if property "
- + Property.fixedEntryArea.name() + " is set.");
- }
- singleTab = properties.containsKey(Property.singleTab.name())
- && "true".equals(properties.get(Property.singleTab.name()));
-
- String titleStr = (String) properties.get(SuiteLayer.Property.title.name());
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- title = new Localized() {
-
- @Override
- public String name() {
- return titleStr;
- }
-
- @Override
- public ClassLoader getL10nClassLoader() {
- return bundleContext != null
- ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
- }
- };
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
-
- String singleTabTitleStr = (String) properties.get(SuiteLayer.Property.singleTabTitle.name());
- if (singleTabTitleStr != null) {
- if (singleTabTitleStr.startsWith("%")) {
- singleTabTitle = new Localized() {
-
- @Override
- public String name() {
- return singleTabTitleStr;
- }
-
- @Override
- public ClassLoader getL10nClassLoader() {
- return bundleContext != null
- ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
- }
- };
- } else {
- singleTabTitle = new Localized.Untranslated(singleTabTitleStr);
- }
- }
-
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public void setEntryArea(SwtUiProvider entryArea) {
- this.entryArea = entryArea;
- }
-
- public void setWorkArea(SwtUiProvider workArea) {
- this.workArea = workArea;
- }
-
- public void setDefaultView(SwtUiProvider defaultView) {
- this.defaultView = defaultView;
- }
-
- SwtTabbedArea createTabbedArea(Composite parent, CmsSwtTheme theme) {
- SwtTabbedArea tabbedArea = new SwtTabbedArea(parent, SWT.NONE);
- tabbedArea.setSingleTab(singleTab);
- if (singleTabTitle != null)
- tabbedArea.setSingleTabTitle(singleTabTitle.lead());
- tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.style());
- tabbedArea.setTabStyle(SuiteStyle.mainTab.style());
- tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.style());
- tabbedArea.setCloseIcon(theme.getSmallIcon(SuiteIcon.close));
- tabbedArea.setLayoutData(CmsSwtUtils.fillAll());
- return tabbedArea;
- }
-
-// /** A work area based on an entry area and and a tabbed area. */
- class SashFormEditionArea extends SashForm {
- private static final long serialVersionUID = 2219125778722702618L;
- private SwtTabbedArea tabbedArea;
- private Composite entryC;
-
- SashFormEditionArea(Composite parent, int style) {
- super(parent, SWT.HORIZONTAL);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- Composite editorC;
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- editorC = new Composite(this, SWT.BORDER);
- entryC = new Composite(this, SWT.BORDER);
- } else {
- entryC = new Composite(this, SWT.NONE);
- editorC = new Composite(this, SWT.NONE);
- }
-
- // sash form specific
- if (weights.size() != 0) {
- int[] actualWeight = new int[weights.size()];
- for (int i = 0; i < weights.size(); i++) {
- actualWeight[i] = Integer.parseInt(weights.get(i));
- }
- setWeights(actualWeight);
- } else {
- int[] actualWeights = new int[] { 3000, 7000 };
- setWeights(actualWeights);
- }
- if (startMaximized)
- setMaximizedControl(editorC);
-
- GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
-// editorAreaLayout.verticalSpacing = 0;
-// editorAreaLayout.marginBottom = 0;
-// editorAreaLayout.marginHeight = 0;
-// editorAreaLayout.marginLeft = 0;
-// editorAreaLayout.marginRight = 0;
- editorC.setLayout(editorAreaLayout);
-
- tabbedArea = createTabbedArea(editorC, theme);
- }
-
- SwtTabbedArea getTabbedArea() {
- return tabbedArea;
- }
-
- Composite getEntryArea() {
- return entryC;
- }
-
- }
-
- class FixedEditionArea extends Composite {
- private static final long serialVersionUID = -5525672639277322465L;
- private SwtTabbedArea tabbedArea;
- private Composite entryC;
-
- public FixedEditionArea(Composite parent, int style) {
- super(parent, style);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- setLayout(CmsSwtUtils.noSpaceGridLayout(2));
-
- Composite editorC;
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- editorC = new Composite(this, SWT.NONE);
- entryC = new Composite(this, SWT.NONE);
- } else {
- entryC = new Composite(this, SWT.NONE);
- editorC = new Composite(this, SWT.NONE);
- }
- entryC.setLayoutData(CmsSwtUtils.fillHeight());
-
- GridLayout editorAreaLayout = CmsSwtUtils.noSpaceGridLayout();
-// editorAreaLayout.verticalSpacing = 0;
-// editorAreaLayout.marginBottom = 0;
-// editorAreaLayout.marginHeight = 0;
-// editorAreaLayout.marginLeft = 0;
-// editorAreaLayout.marginRight = 0;
- editorC.setLayout(editorAreaLayout);
- editorC.setLayoutData(CmsSwtUtils.fillAll());
-
- tabbedArea = createTabbedArea(editorC, theme);
- }
-
- SwtTabbedArea getTabbedArea() {
- return tabbedArea;
- }
-
- Composite getEntryArea() {
- return entryC;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-
-/** Footer of a standard Argeo Suite application. */
-public class DefaultFooter implements CmsUiProvider {
- @Override
- public Control createUiPart(Composite parent, Content context) {
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- Composite content = new Composite(parent, SWT.NONE);
- content.setLayoutData(new GridData(0, 0));
- Control contentControl = createContent(content, context);
-
- // TODO support and guarantee
-
- return contentControl;
- }
-
- protected Control createContent(Composite parent, Content context) {
- return parent;
- }
-
- public void init(BundleContext bundleContext, Map<String, String> properties) {
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Header of a standard Argeo Suite application. */
-public class DefaultHeader implements CmsUiProvider {
- public final static String TITLE_PROPERTY = "argeo.suite.ui.header.title";
- private Localized title = null;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
-
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, true)));
-
- // TODO right to left
- Composite lead = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(lead, SuiteStyle.header);
- lead.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false));
- lead.setLayout(new GridLayout());
- Label lbl = new Label(lead, SWT.NONE);
-// String title = properties.get(TITLE_PROPERTY);
-// // TODO expose the localized
-// lbl.setText(LocaleUtils.isLocaleKey(title) ? LocaleUtils.local(title, getClass().getClassLoader()).toString()
-// : title);
- lbl.setText(title.lead());
- CmsSwtUtils.style(lbl, SuiteStyle.headerTitle);
- lbl.setLayoutData(CmsSwtUtils.fillWidth());
-
- Composite middle = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(middle, SuiteStyle.header);
- middle.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
- middle.setLayout(new GridLayout());
-
- Composite end = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(end, SuiteStyle.header);
- end.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false));
-
- if (!cmsView.isAnonymous()) {
- end.setLayout(new GridLayout(2, false));
- Label userL = new Label(end, SWT.NONE);
- CmsSwtUtils.style(userL, SuiteStyle.header);
- userL.setText(CurrentUser.getDisplayName());
-// Button logoutB = new Button(end, SWT.FLAT);
-// logoutB.setImage(theme.getSmallIcon(SuiteIcon.logout));
-// logoutB.addSelectionListener(new SelectionAdapter() {
-// private static final long serialVersionUID = 7116760083964201233L;
-//
-// @Override
-// public void widgetSelected(SelectionEvent e) {
-// cmsView.logout();
-// }
-//
-// });
- Label logOutL = new Label(end, 0);
- logOutL.setImage(theme.getSmallIcon(SuiteIcon.openUserMenu));
- logOutL.addMouseListener(new MouseAdapter() {
- private static final long serialVersionUID = 6908266850511460799L;
-
- @Override
- public void mouseDown(MouseEvent e) {
- cmsView.logout();
- }
-
- });
- } else {
- end.setLayout(new GridLayout(1, false));
- // required in order to avoid wrong height after logout
- new Label(end, SWT.NONE).setText("");
-
- }
- return lbl;
- }
-
- public void init(BundleContext bundleContext, Map<String, String> properties) {
- String titleStr = (String) properties.get(TITLE_PROPERTY);
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- title = new Localized() {
-
- @Override
- public String name() {
- return titleStr;
- }
-
- @Override
- public ClassLoader getL10nClassLoader() {
- return bundleContext != null
- ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
- }
- };
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public Localized getTitle() {
- return title;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.RankedObject;
-import org.argeo.app.core.SuiteUtils;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.wiring.BundleWiring;
-
-/** Side pane listing various perspectives. */
-public class DefaultLeadPane implements CmsUiProvider {
- private final static CmsLog log = CmsLog.getLog(DefaultLeadPane.class);
-
- public static enum Property {
- defaultLayers, adminLayers;
- }
-
- private Map<String, RankedObject<SuiteLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
- private List<String> defaultLayers;
- private List<String> adminLayers = new ArrayList<>();
-
- private ClassLoader l10nClassLoader;
-
- @Override
- public Control createUiPart(Composite parent, Content node) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
- Composite appLayersC = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(appLayersC, SuiteStyle.leadPane);
- GridLayout layout = new GridLayout();
- layout.verticalSpacing = 10;
- layout.marginTop = 10;
- layout.marginLeft = 10;
- layout.marginRight = 10;
- appLayersC.setLayout(layout);
- appLayersC.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
-
- Composite adminLayersC;
- if (!adminLayers.isEmpty()) {
- adminLayersC = new Composite(parent, SWT.NONE);
- CmsSwtUtils.style(adminLayersC, SuiteStyle.leadPane);
- GridLayout adminLayout = new GridLayout();
- adminLayout.verticalSpacing = 10;
- adminLayout.marginBottom = 10;
- adminLayout.marginLeft = 10;
- adminLayout.marginRight = 10;
- adminLayersC.setLayout(adminLayout);
- adminLayersC.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, true));
- } else {
- adminLayersC = null;
- }
-
-// boolean isAdmin = cmsView.doAs(() -> CurrentUser.isInRole(NodeConstants.ROLE_USER_ADMIN));
- // Set<String> userRoles = cmsView.doAs(() -> CurrentUser.roles());
- Button first = null;
- layers: for (String layerDef : defaultLayers) {
- layerDef = layerDef.trim();
- if ("".equals(layerDef))
- continue layers;// skip empty lines
- String[] semiColArr = layerDef.split(";");
- String layerId = semiColArr[0];
- Set<String> layerRoles = SuiteUtils.extractRoles(semiColArr);
- if (layers.containsKey(layerId)) {
- if (!layerRoles.isEmpty()) {
- boolean authorized = false;
- authorized = cmsView.doAs(() -> {
- for (String layerRole : layerRoles) {
- if (CurrentUser.implies(layerRole, null)) {
- return true;
- }
- }
- return false;
- });
- if (!authorized)
- continue layers;// skip unauthorized layer
-// Set<String> intersection = new HashSet<String>(layerRoles);
-// intersection.retainAll(userRoles);
-// if (intersection.isEmpty())
-// continue layers;// skip unauthorized layer
- }
- RankedObject<SuiteLayer> layerObj = layers.get(layerId);
-
- Localized title = null;
- if (!adminLayers.contains(layerId)) {
- String titleStr = (String) layerObj.getProperties().get(SuiteLayer.Property.title.name());
- if (titleStr != null) {
- if (titleStr.startsWith("%")) {
- // LocaleUtils.local(titleStr, getClass().getClassLoader());
- title = () -> titleStr;
- } else {
- title = new Localized.Untranslated(titleStr);
- }
- }
- }
-
- String iconName = (String) layerObj.getProperties().get(SuiteLayer.Property.icon.name());
- SuiteIcon icon = null;
- if (iconName != null)
- icon = SuiteIcon.valueOf(iconName);
-
- Composite buttonParent;
- if (adminLayers.contains(layerId))
- buttonParent = adminLayersC;
- else
- buttonParent = appLayersC;
- Button b = SuiteUiUtils.createLayerButton(buttonParent, layerId, title, icon, l10nClassLoader);
- if (first == null)
- first = b;
- }
- }
- return first;
- }
-
- public void init(BundleContext bundleContext, Map<String, Object> properties) {
- l10nClassLoader = bundleContext != null ? bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader()
- : getClass().getClassLoader();
-
- String[] defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
- if (defaultLayers == null)
- throw new IllegalArgumentException("Default layers must be set.");
- this.defaultLayers = Arrays.asList(defaultLayers);
- if (log.isDebugEnabled())
- log.debug("Default layers: " + Arrays.asList(defaultLayers));
- String[] adminLayers = (String[]) properties.get(Property.adminLayers.toString());
- if (adminLayers != null) {
- this.adminLayers = Arrays.asList(adminLayers);
- if (log.isDebugEnabled())
- log.debug("Admin layers: " + Arrays.asList(adminLayers));
- }
- }
-
- public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-
- }
-
- public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(layers, pid, layer, properties);
- }
- }
-
- public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (layers.containsKey(pid)) {
- if (layers.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layers.remove(pid);
- }
- }
- }
- }
-
-// protected Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
-// CmsTheme theme = CmsTheme.getCmsTheme(parent);
-// Button button = new Button(parent, SWT.PUSH);
-// CmsUiUtils.style(button, SuiteStyle.leadPane);
-// if (icon != null)
-// button.setImage(icon.getBigIcon(theme));
-// button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
-// // button.setToolTipText(msg.lead());
-// if (msg != null) {
-// Label lbl = new Label(parent, SWT.CENTER);
-// CmsUiUtils.style(lbl, SuiteStyle.leadPane);
-// // CmsUiUtils.markup(lbl);
-// ClassLoader l10nClassLoader = getClass().getClassLoader();
-// String txt = LocaleUtils.lead(msg, l10nClassLoader);
-//// String txt = msg.lead();
-// lbl.setText(txt);
-// lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
-// }
-// CmsUiUtils.sendEventOnSelect(button, SuiteEvent.switchLayer.topic(), SuiteEvent.LAYER, layer);
-// return button;
-// }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.auth.CmsLogin;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Provides a login screen. */
-public class DefaultLoginScreen implements CmsUiProvider {
- private CmsContext cmsContext;
-
- @Override
- public Control createUiPart(Composite parent, Content context) {
- CmsView cmsView = CmsSwtUtils.getCmsView(parent);
- if (!cmsView.isAnonymous())
- throw new IllegalStateException(CurrentUser.getUsername() + " is already logged in");
-
- parent.setLayout(new GridLayout());
- Composite loginArea = new Composite(parent, SWT.NONE);
- loginArea.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
-
- CmsLogin cmsLogin = new CmsLogin(cmsView, cmsContext);
- cmsLogin.createUi(loginArea);
- return cmsLogin.getCredentialsBlock();
- }
-
- public void setCmsContext(CmsContext cmsContext) {
- this.cmsContext = cmsContext;
- }
-
-}
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;
import org.argeo.cms.swt.CmsSwtTheme;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.ui.CmsUiProvider;
Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
if (node != null)
CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.openNewPart.topic(),
- SuiteUxEvent.eventProperties(node));
+ SuiteUiUtils.eventProperties(node));
}
});
Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
if (node != null) {
CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.refreshPart.topic(),
- SuiteUxEvent.eventProperties(node));
+ SuiteUiUtils.eventProperties(node));
deleteItem.setEnabled(true);
} else {
deleteItem.setEnabled(false);
+++ /dev/null
-package org.argeo.app.ui;
-
-import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentRepository;
-import org.argeo.api.acr.spi.ProvidedSession;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsEventSubscriber;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.directory.CmsUserManager;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.api.EntityConstants;
-import org.argeo.app.api.EntityNames;
-import org.argeo.app.api.EntityType;
-import org.argeo.app.api.RankedObject;
-import org.argeo.app.core.SuiteUtils;
-import org.argeo.cms.AbstractCmsApp;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.Localized;
-import org.argeo.cms.acr.ContentUtils;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.jcr.acr.JcrContentProvider;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.util.LangUtils;
-import org.argeo.cms.ux.CmsUxUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.argeo.jcr.JcrException;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Constants;
-import org.osgi.service.useradmin.User;
-
-/** The Argeo Suite App. */
-public class SuiteApp extends AbstractCmsApp implements CmsEventSubscriber {
- private final static CmsLog log = CmsLog.getLog(SuiteApp.class);
-
- public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
- public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
- public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
- public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
- private final static String LOGIN = "login";
- private final static String HOME_STATE = "~";
-
- private String publicBasePath = null;
-
- private String pidPrefix;
- private String headerPid;
- private String footerPid;
- private String leadPanePid;
- private String adminLeadPanePid;
- private String loginScreenPid;
-
- private String defaultUiName = "app";
- private String adminUiName = "admin";
-
- // FIXME such default names make refactoring more dangerous
- @Deprecated
- private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
- @Deprecated
- private String defaultThemeId = "org.argeo.app.theme.default";
-
- // TODO use QName as key for byType
- private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
- private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
- private Map<String, RankedObject<SuiteLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
- private Map<String, RankedObject<SuiteLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
-
- private CmsUserManager cmsUserManager;
-
- // TODO make more optimal or via CmsSession/CmsView
- private Map<String, SuiteUi> managedUis = new HashMap<>();
-
- // ACR
- private ContentRepository contentRepository;
- private JcrContentProvider jcrContentProvider;
-
- // JCR
-// private Repository repository;
-
- public void init(Map<String, Object> properties) {
- for (SuiteUxEvent event : SuiteUxEvent.values()) {
- getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
- }
-
- if (log.isDebugEnabled())
- log.info("Argeo Suite App started");
-
- if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
- defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
- if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
- defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
- if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
- defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
- publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
-
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String servicePid = properties.get(Constants.SERVICE_PID).toString();
- if (servicePid.endsWith(".app")) {
- pidPrefix = servicePid.substring(0, servicePid.length() - "app".length());
- }
- }
-
- if (pidPrefix == null)
- throw new IllegalArgumentException("PID prefix must be set.");
-
- headerPid = pidPrefix + "header";
- footerPid = pidPrefix + "footer";
- leadPanePid = pidPrefix + "leadPane";
- adminLeadPanePid = pidPrefix + "adminLeadPane";
- loginScreenPid = pidPrefix + "loginScreen";
- }
-
- public void destroy(Map<String, Object> properties) {
- for (SuiteUi ui : managedUis.values())
- if (!ui.isDisposed()) {
- ui.getDisplay().syncExec(() -> ui.dispose());
- }
- if (log.isDebugEnabled())
- log.info("Argeo Suite App stopped");
-
- }
-
- @Override
- public Set<String> getUiNames() {
- HashSet<String> uiNames = new HashSet<>();
- uiNames.add(defaultUiName);
- uiNames.add(adminUiName);
- return uiNames;
- }
-
- @Override
- public CmsUi initUi(Object parent) {
- Composite uiParent = (Composite) parent;
- String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
- : null;
- CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
- if (cmsView == null)
- throw new IllegalStateException("No CMS view is registered.");
- CmsTheme theme = getTheme(uiName);
- if (theme != null)
- CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
- SuiteUi argeoSuiteUi = new SuiteUi(uiParent, SWT.INHERIT_DEFAULT);
- String uid = cmsView.getUid();
- managedUis.put(uid, argeoSuiteUi);
- argeoSuiteUi.addDisposeListener((e) -> {
- managedUis.remove(uid);
- if (log.isDebugEnabled())
- log.debug("Suite UI " + uid + " has been disposed.");
- });
- return argeoSuiteUi;
- }
-
- @Override
- public String getThemeId(String uiName) {
- String themeId = System.getProperty("org.argeo.app.theme.default");
- if (themeId != null)
- return themeId;
- return defaultThemeId;
- }
-
- @Override
- public void refreshUi(CmsUi cmsUi, String state) {
- try {
- Content context = null;
- SuiteUi ui = (SuiteUi) cmsUi;
-
- String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
- if (uiName == null)
- throw new IllegalStateException("UI name should not be null");
- CmsView cmsView = CmsSwtUtils.getCmsView(ui);
-
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
-
- SwtUiProvider headerUiProvider = findUiProvider(headerPid);
- SwtUiProvider footerUiProvider = findUiProvider(footerPid);
- SwtUiProvider leadPaneUiProvider;
- if (adminUiName.equals(uiName)) {
- leadPaneUiProvider = findUiProvider(adminLeadPanePid);
- } else {
- leadPaneUiProvider = findUiProvider(leadPanePid);
- }
-
- Localized appTitle = null;
- if (headerUiProvider instanceof DefaultHeader) {
- appTitle = ((DefaultHeader) headerUiProvider).getTitle();
- }
- ui.setTitle(appTitle);
-
- if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
- ui.logout();
- ui.setLoginScreen(true);
- if (headerUiProvider != null)
- refreshPart(headerUiProvider, ui.getHeader(), context);
- ui.refreshBelowHeader(false);
- refreshPart(findUiProvider(loginScreenPid), ui.getBelowHeader(), context);
- if (footerUiProvider != null)
- refreshPart(footerUiProvider, ui.getFooter(), context);
- ui.layout(true, true);
- setState(ui, LOGIN);
- } else {
- if (LOGIN.equals(state))
- state = null;
- if (ui.isLoginScreen()) {
- ui.setLoginScreen(false);
- }
- CmsSession cmsSession = cmsView.getCmsSession();
- if (ui.getUserDir() == null) {
- // FIXME NPE on CMSSession when logging in from anonymous
- if (cmsSession == null || cmsView.isAnonymous()) {
- assert publicBasePath != null;
- Content userDir = contentSession
- .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + publicBasePath);
- ui.setUserDir(userDir);
- } else {
- Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
- Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
- return node;
- });
- Content userDir = contentSession
- .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
- ui.setUserDir(userDir);
-// Content userDir = contentSession.getSessionRunDir();
-// ui.setUserDir(userDir);
- }
- }
- initLocale(cmsSession);
- context = stateToNode(ui, state);
- if (context == null)
- context = ui.getUserDir();
-
- if (headerUiProvider != null)
- refreshPart(headerUiProvider, ui.getHeader(), context);
- ui.refreshBelowHeader(true);
- for (String key : layersByPid.keySet()) {
- SuiteLayer layer = layersByPid.get(key).get();
- ui.addLayer(key, layer);
- }
-
- if (leadPaneUiProvider != null)
- refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
- if (footerUiProvider != null)
- refreshPart(footerUiProvider, ui.getFooter(), context);
- ui.layout(true, true);
- setState(ui, state != null ? state : defaultLayerPid);
- }
- } catch (Exception e) {
- CmsFeedback.error("Unexpected exception", e);
- }
- }
-
- private void initLocale(CmsSession cmsSession) {
- if (cmsSession == null)
- return;
- Locale locale = cmsSession.getLocale();
- UiContext.setLocale(locale);
- LocaleUtils.setThreadLocale(locale);
-
- }
-
- private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
- CmsSwtUtils.clear(part);
- uiProvider.createUiPart(part, context);
- }
-
- private SwtUiProvider findUiProvider(String pid) {
- if (!uiProvidersByPid.containsKey(pid))
- return null;
- return uiProvidersByPid.get(pid).get();
- }
-
- private SuiteLayer findLayer(String pid) {
- if (!layersByPid.containsKey(pid))
- return null;
- return layersByPid.get(pid).get();
- }
-
- private <T> T findByType(Map<String, RankedObject<T>> byType, Content content) {
- if (content == null)
- throw new IllegalArgumentException("A node should be provided");
-
- if (content instanceof JcrContent) {
- Node context = ((JcrContent) content).getJcrNode();
- try {
- // mixins
- Set<String> types = new TreeSet<>();
- for (NodeType mixinType : context.getMixinNodeTypes()) {
- String mixinTypeName = mixinType.getName();
- if (byType.containsKey(mixinTypeName)) {
- types.add(mixinTypeName);
- }
- for (NodeType superType : mixinType.getDeclaredSupertypes()) {
- if (byType.containsKey(superType.getName())) {
- types.add(superType.getName());
- }
- }
- }
- // primary node type
- NodeType primaryType = context.getPrimaryNodeType();
- String primaryTypeName = primaryType.getName();
- if (byType.containsKey(primaryTypeName)) {
- types.add(primaryTypeName);
- }
- for (NodeType superType : primaryType.getDeclaredSupertypes()) {
- if (byType.containsKey(superType.getName())) {
- types.add(superType.getName());
- }
- }
- // entity type
- if (context.isNodeType(EntityType.entity.get())) {
- if (context.hasProperty(EntityNames.ENTITY_TYPE)) {
- String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString();
- if (byType.containsKey(entityTypeName)) {
- types.add(entityTypeName);
- }
- }
- }
-
- if (CmsJcrUtils.isUserHome(context) && byType.containsKey("nt:folder")) {// home node
- types.add("nt:folder");
- }
-
- if (types.size() == 0)
- throw new IllegalArgumentException(
- "No type found for " + context + " (" + listTypes(context) + ")");
- String type = types.iterator().next();
- if (!byType.containsKey(type))
- throw new IllegalArgumentException("No component found for " + context + " with type " + type);
- return byType.get(type).get();
- } catch (RepositoryException e) {
- throw new IllegalStateException(e);
- }
-
- } else {
- List<QName> objectClasses = content.getContentClasses();
- Set<String> types = new TreeSet<>();
- for (QName cc : objectClasses) {
- String type = cc.getPrefix() + ":" + cc.getLocalPart();
- if (byType.containsKey(type))
- types.add(type);
- }
- if (types.size() == 0) {
- throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
- }
- String type = types.iterator().next();
- if (!byType.containsKey(type))
- throw new IllegalArgumentException("No component found for " + content + " with type " + type);
- return byType.get(type).get();
- }
- }
-
- private static String listTypes(Node context) {
- try {
- StringBuilder sb = new StringBuilder();
- sb.append(context.getPrimaryNodeType().getName());
- for (NodeType superType : context.getPrimaryNodeType().getDeclaredSupertypes()) {
- sb.append(' ');
- sb.append(superType.getName());
- }
-
- for (NodeType nodeType : context.getMixinNodeTypes()) {
- sb.append(' ');
- sb.append(nodeType.getName());
- if (nodeType.getName().equals(EntityType.local.get()))
- sb.append('/').append(context.getProperty(EntityNames.ENTITY_TYPE).getString());
- for (NodeType superType : nodeType.getDeclaredSupertypes()) {
- sb.append(' ');
- sb.append(superType.getName());
- }
- }
- return sb.toString();
- } catch (RepositoryException e) {
- throw new JcrException(e);
- }
- }
-
- @Override
- public void setState(CmsUi cmsUi, String state) {
- if (state == null)
- return;
- if (!state.startsWith("/")) {
- if (cmsUi instanceof SuiteUi) {
- SuiteUi ui = (SuiteUi) cmsUi;
- if (LOGIN.equals(state)) {
- String appTitle = "";
- if (ui.getTitle() != null)
- appTitle = ui.getTitle().lead();
- ui.getCmsView().stateChanged(state, appTitle);
- return;
- }
- Map<String, Object> properties = new HashMap<>();
- String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
- properties.put(SuiteUxEvent.LAYER, layerId);
- properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE);
- ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties);
- }
- return;
- }
- SuiteUi suiteUi = (SuiteUi) cmsUi;
- if (suiteUi.isLoginScreen()) {
- return;
- }
-
- Content node = stateToNode(suiteUi, state);
- if (node == null) {
- suiteUi.getCmsView().navigateTo(HOME_STATE);
- } else {
- suiteUi.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node));
- suiteUi.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node));
- }
- }
-
- // TODO move it to an internal package?
- static String nodeToState(Content node) {
- return node.getPath();
- }
-
- private Content stateToNode(SuiteUi suiteUi, String state) {
- if (suiteUi == null)
- return null;
- if (state == null || !state.startsWith("/"))
- return null;
-
- String path = state;
-
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
- suiteUi.getCmsView());
- return contentSession.get(path);
- }
-
- /*
- * Events management
- */
-
- @Override
- public void onEvent(String topic, Map<String, Object> event) {
-
- // Specific UI related events
- SuiteUi ui = getRelatedUi(event);
- if (ui == null)
- return;
- ui.getCmsView().runAs(() -> {
- try {
- String appTitle = "";
- if (ui.getTitle() != null)
- appTitle = ui.getTitle().lead() + " - ";
-
- if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.refreshPart)) {
- Content node = getContentFromEvent(ui, event);
- if (node == null)
- return;
- SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- layer.view(uiProvider, ui.getCurrentWorkArea(), node);
- ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
- } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.openNewPart)) {
- Content node = getContentFromEvent(ui, event);
- if (node == null)
- return;
- SwtUiProvider uiProvider = findByType(uiProvidersByType, node);
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- layer.open(uiProvider, ui.getCurrentWorkArea(), node);
- ui.getCmsView().stateChanged(nodeToState(node), appTitle + CmsUxUtils.getTitle(node));
- } else if (SuiteUiUtils.isTopic(topic, SuiteUxEvent.switchLayer)) {
- String layerId = get(event, SuiteUxEvent.LAYER);
- if (layerId != null) {
- SuiteLayer suiteLayer = findLayer(layerId);
- if (suiteLayer == null)
- throw new IllegalArgumentException("No layer '" + layerId + "' available.");
- Localized layerTitle = suiteLayer.getTitle();
- // FIXME make sure we don't rebuild the work area twice
- Composite workArea = ui.switchToLayer(layerId, ui.getUserDir());
- String title = null;
- if (layerTitle != null)
- title = layerTitle.lead();
- Content nodeFromState = getContentFromEvent(ui, event);
- if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) {
- // default layer view is forced
- String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
- ui.getCmsView().stateChanged(state, appTitle + title);
- suiteLayer.view(null, workArea, nodeFromState);
- } else {
- Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
- if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) {
- // layer was already showing a context so we set the state to it
- ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
- appTitle + CmsUxUtils.getTitle(layerCurrentContext));
- } else {
- // no context was shown
- ui.getCmsView().stateChanged(layerId, appTitle + title);
- }
- }
- } else {
- Content node = getContentFromEvent(ui, event);
- if (node != null) {
- SuiteLayer layer = findByType(layersByType, node);
- ui.switchToLayer(layer, node);
- }
- }
- }
- } catch (Exception e) {
- CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
-// log.error("Cannot handle event " + event, e);
- }
- });
- }
-
- protected Content getContentFromEvent(SuiteUi ui, Map<String, Object> event) {
- ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
- ui.getCmsView());
-
- String path = get(event, SuiteUxEvent.CONTENT_PATH);
-
- if (path != null && (path.equals(HOME_STATE) || path.equals("")))
- return ui.getUserDir();
- Content node;
- if (path == null) {
- // look for a user
- String username = get(event, SuiteUxEvent.USERNAME);
- if (username == null)
- return null;
- User user = cmsUserManager.getUser(username);
- if (user == null)
- return null;
- node = ContentUtils.roleToContent(cmsUserManager, contentSession, user);
- } else {
- node = contentSession.get(path);
- }
- return node;
- }
-
- private SuiteUi getRelatedUi(Map<String, Object> eventProperties) {
- return managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
- }
-
- public static String get(Map<String, Object> eventProperties, String key) {
- Object value = eventProperties.get(key);
- if (value == null)
- return null;
- return value.toString();
-
- }
-
- /*
- * Dependency injection.
- */
-
- public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
- }
- }
- }
-
- public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (uiProvidersByPid.containsKey(pid)) {
- if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
- uiProvidersByPid.remove(pid);
- }
- }
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- if (uiProvidersByType.containsKey(type)) {
- if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
- uiProvidersByType.remove(type);
- }
- }
- }
- }
- }
-
- public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types)
- RankedObject.putIfHigherRank(layersByType, type, layer, properties);
- }
- }
-
- public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
- if (properties.containsKey(Constants.SERVICE_PID)) {
- String pid = (String) properties.get(Constants.SERVICE_PID);
- if (layersByPid.containsKey(pid)) {
- if (layersByPid.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layersByPid.remove(pid);
- }
- }
- }
- if (properties.containsKey(EntityConstants.TYPE)) {
- List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
- for (String type : types) {
- if (layersByType.containsKey(type)) {
- if (layersByType.get(type).equals(new RankedObject<SuiteLayer>(layer, properties))) {
- layersByType.remove(type);
- }
- }
- }
- }
- }
-
- public void setCmsUserManager(CmsUserManager cmsUserManager) {
- this.cmsUserManager = cmsUserManager;
- }
-
- protected ContentRepository getContentRepository() {
- return contentRepository;
- }
-
- public void setContentRepository(ContentRepository contentRepository) {
- this.contentRepository = contentRepository;
- }
-
- public void setJcrContentProvider(JcrContentProvider jcrContentProvider) {
- this.jcrContentProvider = jcrContentProvider;
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.cms.ux.CmsIcon;
-
-/** Icon names used by Argeo Suite. */
-public enum SuiteIcon implements CmsIcon {
- add, save, close, closeAll, search, delete, logout, dashboard,
- // people
- people, group, person, organisation, addressBook, users, organisationContact,
- // library
- documents, document, folder,
- // management
- report,
- // admin and settings
- settings, user,
- // misc
- task, tag, location, inbox, map, todo,
- // actions
- openUserMenu,
- //
- ;
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.acr.SwtUiProvider;
-import org.eclipse.swt.widgets.Composite;
-
-/** An UI layer for the main work area. */
-public interface SuiteLayer extends SwtUiProvider {
- static enum Property {
- title, icon, weights, startMaximized, singleTab, singleTabTitle, fixedEntryArea;
- }
-
- String getId();
-
- void view(SwtUiProvider uiProvider, Composite workArea, Content context);
-
- Content getCurrentContext(Composite workArea);
-
- default void open(SwtUiProvider uiProvider, Composite workArea, Content context) {
- view(uiProvider, workArea, context);
- }
-
- default Localized getTitle() {
- return null;
- }
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.cms.Localized;
-
-/** Localized messages. */
-public enum SuiteMsg implements Localized {
- // Entities
- user, org, person, group,
- // UI parts
- dashboard, people, documents, locations, recentItems,
- // NewPersonWizard
- firstName, lastName, salutation, email, personWizardWindowTitle, personWizardPageTitle, personWizardFeedback,
- // NewOrgWizard
- orgWizardWindowTitle, orgWizardPageTitle, orgWizardFeedback, legalName, legalForm, vatId,
- // Roles
- userAdminRole, groupAdminRole, publisherRole, coworkerRole,
- // Group
- chooseAMember,
- // ContextAddressComposite
- chooseAnOrganisation, street, streetComplement, zipCode, city, state, country, geopoint,
- // FilteredOrderableEntityTable
- filterHelp,
- // BankAccountComposite
- accountHolder, bankName, currency, accountNumber, bankNumber, BIC, IBAN,
- // EditJobDialog
- position, chosenItem, department, isPrimary, searchAndChooseEntity,
- // ContactListCTab (e4)
- notes, addAContact, contactValue, linkedCompany,
- // OrgAdminInfoCTab (e4)
- paymentAccount,
- // OrgEditor (e4)
- orgDetails, orgActivityLog, team, orgAdmin,
- // PersonEditor (e4)
- personDetails, personActivityLog, personOrgs, personSecurity,
- // PersonSecurityCTab (e4)
- resetPassword,
- // Generic
- label, aCustomLabel, description, value, name, primary, add, save, pickup,
- // Tag
- confirmNewTag, cannotCreateTag,
- // Feedback messages
- allFieldsMustBeSet,
- //
- ;
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import org.argeo.api.cms.ux.CmsStyle;
-
-/** Styles used by Argeo Suite work UI. */
-public enum SuiteStyle implements CmsStyle {
- // header
- header, headerTitle, headerMenu, headerMenuItem,
- // footer
- footer,
- // recent items
- recentItems,
- // lead pane
- leadPane, leadPaneItem, leadPaneSectionTitle, leadPaneSubSectionTitle,
- // entry area
- entryArea,
- // group composite
- titleContainer, titleLabel, subTitleLabel, formLine, formColumn, navigationBar, navigationTitle, navigationButton,
- // forms elements
- simpleLabel, simpleText, simpleInput,
- // table
- titleCell,
- // layers
- workArea,
- // tabbed area
- mainTabBody, mainTabSelected, mainTab,
- // buttons
- inlineButton;
-
- @Override
- public String getClassPrefix() {
- return "argeo-suite";
- }
-
-}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.Localized;
-import org.argeo.cms.swt.CmsSwtUi;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** The view for the default UX of Argeo Suite. */
-class SuiteUi extends CmsSwtUi {
- private static final long serialVersionUID = 6207018859086689108L;
- private final static CmsLog log = CmsLog.getLog(SuiteUi.class);
-
- private Localized title;
- private Composite header;
- private Composite footer;
- private Composite belowHeader;
- private Composite leadPane;
- private Composite sidePane;
- private Composite dynamicArea;
-
- private Content userDir;
-
- private Map<String, SuiteLayer> layers = new HashMap<>();
- private Map<String, Composite> workAreas = new HashMap<>();
- private String currentLayerId = null;
-
- private boolean loginScreen = false;
-
- public SuiteUi(Composite parent, int style) {
- super(parent, style);
- this.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
- header = new Composite(this, SWT.NONE);
- header.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(header, SuiteStyle.header);
- header.setLayoutData(CmsSwtUtils.fillWidth());
-
- belowHeader = new Composite(this, SWT.NONE);
- belowHeader.setLayoutData(CmsSwtUtils.fillAll());
-
- footer = new Composite(this, SWT.NONE);
- footer.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(footer, SuiteStyle.footer);
- footer.setLayoutData(CmsSwtUtils.fillWidth());
- }
-
- public void refreshBelowHeader(boolean initApp) {
- CmsSwtUtils.clear(belowHeader);
- int style = getStyle();
- if (initApp) {
- belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
-
- if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
- sidePane = new Composite(belowHeader, SWT.NONE);
- sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- sidePane.setLayoutData(CmsSwtUtils.fillHeight());
- dynamicArea = new Composite(belowHeader, SWT.NONE);
- leadPane = new Composite(belowHeader, SWT.NONE);
- } else {
- leadPane = new Composite(belowHeader, SWT.NONE);
- dynamicArea = new Composite(belowHeader, SWT.NONE);
- sidePane = new Composite(belowHeader, SWT.NONE);
- sidePane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- sidePane.setLayoutData(CmsSwtUtils.fillHeight());
- }
- leadPane.setLayoutData(CmsSwtUtils.fillHeight());
- leadPane.setLayout(CmsSwtUtils.noSpaceGridLayout());
- CmsSwtUtils.style(leadPane, SuiteStyle.leadPane);
-
- dynamicArea.setLayoutData(CmsSwtUtils.fillAll());
- dynamicArea.setLayout(new FormLayout());
-
- } else {
- belowHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
- }
- }
-
- /*
- * LAYERS
- */
-
- Composite getCurrentWorkArea() {
- if (currentLayerId == null)
- throw new IllegalStateException("No current layer");
- return workAreas.get(currentLayerId);
- }
-
- String getCurrentLayerId() {
- return currentLayerId;
- }
-
- private Composite getLayer(String id, Content context) {
- if (!layers.containsKey(id))
- return null;
- if (!workAreas.containsKey(id))
- initLayer(id, layers.get(id), context);
- return workAreas.get(id);
- }
-
- Composite switchToLayer(String layerId, Content context) {
- Composite current = null;
- if (currentLayerId != null) {
- current = getCurrentWorkArea();
- if (currentLayerId.equals(layerId))
- return current;
- }
- if (context == null) {
- if (!getCmsView().isAnonymous())
- context = getUserDir();
- }
- Composite toShow = getLayer(layerId, context);
- if (toShow != null) {
- currentLayerId = layerId;
- if (!isDisposed()) {
- if (!toShow.isDisposed()) {
- toShow.moveAbove(null);
- } else {
- log.warn("Cannot show work area because it is disposed.");
- toShow = initLayer(layerId, layers.get(layerId), context);
- toShow.moveAbove(null);
- }
- dynamicArea.layout(true, true);
- }
- return toShow;
- } else {
- return current;
- }
- }
-
- void switchToLayer(SuiteLayer layer, Content context) {
- // TODO make it more robust
- for (String layerId : layers.keySet()) {
- SuiteLayer l = layers.get(layerId);
- if (layer.getId().equals(l.getId())) {
- switchToLayer(layerId, context);
- return;
- }
- }
- throw new IllegalArgumentException("Layer is not registered.");
- }
-
- void addLayer(String id, SuiteLayer layer) {
- layers.put(id, layer);
- }
-
- void removeLayer(String id) {
- layers.remove(id);
- if (workAreas.containsKey(id)) {
- Composite workArea = workAreas.remove(id);
- if (!workArea.isDisposed())
- workArea.dispose();
- }
- }
-
- protected Composite initLayer(String id, SuiteLayer layer, Content context) {
- Composite workArea = getCmsView().doAs(() -> (Composite) layer.createUiPart(dynamicArea, context));
- CmsSwtUtils.style(workArea, SuiteStyle.workArea);
- workArea.setLayoutData(CmsSwtUtils.coverAll());
- workAreas.put(id, workArea);
- return workArea;
- }
-
- synchronized void logout() {
- userDir = null;
- currentLayerId = null;
- workAreas.clear();
- }
-
- /*
- * GETTERS / SETTERS
- */
-
- Composite getHeader() {
- return header;
- }
-
- Composite getFooter() {
- return footer;
- }
-
- Composite getLeadPane() {
- return leadPane;
- }
-
- Composite getSidePane() {
- return sidePane;
- }
-
- Composite getBelowHeader() {
- return belowHeader;
- }
-
- Content getUserDir() {
- return userDir;
- }
-
- void setUserDir(Content userDir) {
- this.userDir = userDir;
- }
-
- public Localized getTitle() {
- return title;
- }
-
- public void setTitle(Localized title) {
- this.title = title;
- }
-
- public boolean isLoginScreen() {
- return loginScreen;
- }
-
- public void setLoginScreen(boolean loginScreen) {
- this.loginScreen = loginScreen;
- }
-}
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.util.Objects;
+import java.util.HashMap;
+import java.util.Map;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.io.IOUtils;
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsEvent;
import org.argeo.api.cms.ux.CmsEditable;
-import org.argeo.api.cms.ux.CmsIcon;
import org.argeo.api.cms.ux.CmsStyle;
import org.argeo.app.api.EntityNames;
import org.argeo.app.api.EntityType;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.Localized;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.cms.jcr.acr.JcrContent;
-import org.argeo.cms.swt.CmsSwtTheme;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.swt.dialogs.LightweightDialog;
import org.argeo.cms.ui.util.CmsLink;
import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
import org.argeo.jcr.Jcr;
import org.argeo.jcr.JcrUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
-/** UI utilities related to the APAF project. */
+/** UI utilities around SWT and JCR. */
+@Deprecated
public class SuiteUiUtils {
-
- /** Singleton. */
- private SuiteUiUtils() {
- }
-
- /** creates a title bar composite with label and optional button */
- public static void addTitleBar(Composite parent, String title, Boolean isEditable) {
- Composite titleBar = new Composite(parent, SWT.NONE);
- titleBar.setLayoutData(CmsSwtUtils.fillWidth());
- CmsSwtUtils.style(titleBar, SuiteStyle.titleContainer);
-
- titleBar.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
- Label titleLbl = new Label(titleBar, SWT.NONE);
- titleLbl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
- CmsSwtUtils.style(titleLbl, SuiteStyle.titleLabel);
- titleLbl.setText(title);
-
- if (isEditable) {
- Button editBtn = new Button(titleBar, SWT.PUSH);
- editBtn.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
- CmsSwtUtils.style(editBtn, SuiteStyle.inlineButton);
- editBtn.setText("Edit");
- }
- }
-
- public static Label addFormLabel(Composite parent, Localized msg) {
- return addFormLabel(parent, msg.lead());
- }
-
- public static Label addFormLabel(Composite parent, String label) {
- Label lbl = new Label(parent, SWT.WRAP);
- lbl.setText(label);
- // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
- CmsSwtUtils.style(lbl, SuiteStyle.simpleLabel);
- return lbl;
- }
-
- public static Text addFormTextField(Composite parent, String text, String message) {
- return addFormTextField(parent, text, message, SWT.NONE);
- }
-
- public static Text addFormTextField(Composite parent, String text, String message, int style) {
- Text txt = new Text(parent, style);
- if (text != null)
- txt.setText(text);
- if (message != null)
- txt.setMessage(message);
- txt.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, true));
- CmsSwtUtils.style(txt, SuiteStyle.simpleText);
- return txt;
- }
-
- public static Text addFormInputField(Composite parent, String placeholder) {
- Text txt = new Text(parent, SWT.BORDER);
-
- GridData gridData = CmsSwtUtils.fillWidth();
- txt.setLayoutData(gridData);
-
- if (placeholder != null)
- txt.setText(placeholder);
-
- CmsSwtUtils.style(txt, SuiteStyle.simpleInput);
- return txt;
- }
-
- /** creates a single horizontal-block composite for key:value display */
- public static Text addFormLine(Composite parent, String label, String text) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
- Text txt = addFormTextField(lineComposite, text, null);
- txt.setEditable(false);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
public static Text addFormLine(Composite parent, String label, Node node, String property,
CmsEditable cmsEditable) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
+ Composite lineComposite = SuiteSwtUtils.addLineComposite(parent, 2);
+ SuiteSwtUtils.addFormLabel(lineComposite, label);
String text = Jcr.get(node, property);
-// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
- Text txt = addFormTextField(lineComposite, text, null, SWT.WRAP);
+ Text txt = SuiteSwtUtils.addFormTextField(lineComposite, text, null, SWT.WRAP);
if (cmsEditable != null && cmsEditable.isEditing()) {
txt.addModifyListener((e) -> {
Jcr.set(node, property, txt.getText());
return txt;
}
- public static Text addFormInput(Composite parent, String label, String placeholder) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
- Text txt = addFormInputField(lineComposite, placeholder);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- /**
- * creates a single horizontal-block composite for key:value display, with
- * offset value
- */
- public static Text addFormLine(Composite parent, String label, String text, Integer offset) {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(3, false));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- Label offsetLbl = new Label(lineComposite, SWT.NONE);
- GridData gridData = new GridData();
- gridData.widthHint = offset;
- offsetLbl.setLayoutData(gridData);
- addFormLabel(lineComposite, label);
- Text txt = addFormTextField(lineComposite, text, null);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
- /** creates a single vertical-block composite for key:value display */
- public static Text addFormColumn(Composite parent, String label, String text) {
-// Composite columnComposite = new Composite(parent, SWT.NONE);
-// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-// columnComposite.setLayout(new GridLayout(1, false));
- addFormLabel(parent, label);
- Text txt = addFormTextField(parent, text, null);
- txt.setEditable(false);
- txt.setLayoutData(CmsSwtUtils.fillWidth());
- return txt;
- }
-
public static Text addFormColumn(Composite parent, String label, Node node, String property,
CmsEditable cmsEditable) {
-// Composite columnComposite = new Composite(parent, SWT.NONE);
-// columnComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-// columnComposite.setLayout(new GridLayout(1, false));
- addFormLabel(parent, label);
+ SuiteSwtUtils.addFormLabel(parent, label);
String text = Jcr.get(node, property);
-// int style = cmsEditable.isEditing() ? SWT.WRAP : SWT.WRAP;
- Text txt = addFormTextField(parent, text, null, SWT.WRAP);
+ Text txt = SuiteSwtUtils.addFormTextField(parent, text, null, SWT.WRAP);
if (cmsEditable != null && cmsEditable.isEditing()) {
txt.addModifyListener((e) -> {
Jcr.set(node, property, txt.getText());
return txt;
}
- public static Label createBoldLabel(Composite parent, Localized localized) {
- Label label = new Label(parent, SWT.LEAD);
- label.setText(localized.lead());
- label.setFont(EclipseUiUtils.getBoldFont(parent));
- label.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
- return label;
- }
-
- public static Label addFormPicture(Composite parent, String label, Node fileNode) throws RepositoryException {
- Composite lineComposite = new Composite(parent, SWT.NONE);
- lineComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- lineComposite.setLayout(new GridLayout(2, true));
- CmsSwtUtils.style(lineComposite, SuiteStyle.formLine);
- addFormLabel(lineComposite, label);
-
- return addPicture(lineComposite, fileNode);
- }
+// public static Label addFormPicture(Composite parent, String label, Node fileNode) throws RepositoryException {
+// Composite lineComposite = SuiteSwtUtils.addLineComposite(parent, 2);
+// SuiteSwtUtils.addFormLabel(lineComposite, label);
+//
+// return addPicture(lineComposite, fileNode);
+// }
public static Label addPicture(Composite parent, Node fileNode) throws RepositoryException {
return addPicture(parent, fileNode, null);
return img;
}
- public static String toLink(Content node) {
- return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(node)) : null;
- }
-
public static String toLink(Node node) {
- return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(SuiteApp.nodeToState(JcrContent.nodeToContent(node)))
- : null;
+ return node != null ? "#" + CmsSwtUtils.cleanPathForUrl(JcrContent.nodeToContent(node).getPath()) : null;
}
public static Control addLink(Composite parent, String label, Node node, CmsStyle style)
return link.createUi(parent, node);
}
- public static Control addExternalLink(Composite parent, String label, String url, String plainCssAnchorClass,
- boolean newWindow) throws RepositoryException {
- Label lbl = new Label(parent, SWT.NONE);
- CmsSwtUtils.markup(lbl);
- StringBuilder txt = new StringBuilder();
- txt.append("<a");
- if (plainCssAnchorClass != null)
- txt.append(" class='" + plainCssAnchorClass + "'");
- txt.append(" href='").append(url).append("'");
- if (newWindow) {
- txt.append(" target='blank_'");
- }
- txt.append(">");
- txt.append(label);
- txt.append("</a>");
- lbl.setText(txt.toString());
- return lbl;
- }
-
-// public static boolean isCoworker(CmsView cmsView) {
-// boolean coworker = cmsView.doAs(() -> CurrentUser.isInRole(SuiteRole.coworker.dn()));
-// return coworker;
-// }
-
- public static boolean isTopic(String topic, CmsEvent cmsEvent) {
- Objects.requireNonNull(topic);
- return topic.equals(cmsEvent.topic());
+ @Deprecated
+ public static Map<String, Object> eventProperties(Node node) {
+ Map<String, Object> properties = new HashMap<>();
+ String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node);
+ properties.put(SuiteUxEvent.CONTENT_PATH, contentPath);
+ return properties;
}
- public static Button createLayerButton(Composite parent, String layer, Localized msg, CmsIcon icon,
- ClassLoader l10nClassLoader) {
- CmsSwtTheme theme = CmsSwtUtils.getCmsTheme(parent);
- Button button = new Button(parent, SWT.PUSH);
- CmsSwtUtils.style(button, SuiteStyle.leadPane);
- if (icon != null)
- button.setImage(theme.getBigIcon(icon));
- button.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, false));
- // button.setToolTipText(msg.lead());
- if (msg != null) {
- Label lbl = new Label(parent, SWT.CENTER);
- CmsSwtUtils.style(lbl, SuiteStyle.leadPane);
- String txt = LocaleUtils.lead(msg, l10nClassLoader);
-// String txt = msg.lead();
- lbl.setText(txt);
- lbl.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, false));
- }
- CmsSwtUtils.sendEventOnSelect(button, SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.LAYER, layer);
- return button;
+ /** Singleton. */
+ private SuiteUiUtils() {
}
-// public static String createAndConfigureEntity(Shell shell, Session referenceSession, String mainMixin,
-// String... additionnalProps) {
-//
-// Session tmpSession = null;
-// Session mainSession = null;
-// try {
-// // FIXME would not work if home is another physical workspace
-// tmpSession = referenceSession.getRepository().login(NodeConstants.HOME_WORKSPACE);
-// Node draftNode = null;
-// for (int i = 0; i < additionnalProps.length - 1; i += 2) {
-// draftNode.setProperty(additionnalProps[i], additionnalProps[i + 1]);
-// }
-// Wizard wizard = null;
-// CmsWizardDialog dialog = new CmsWizardDialog(shell, wizard);
-// // WizardDialog dialog = new WizardDialog(shell, wizard);
-// if (dialog.open() == Window.OK) {
-// String parentPath = null;// "/" + appService.getBaseRelPath(mainMixin);
-// // FIXME it should be possible to specify the workspace
-// mainSession = referenceSession.getRepository().login();
-// Node parent = mainSession.getNode(parentPath);
-// Node task = null;// appService.publishEntity(parent, mainMixin, draftNode);
-//// task = appService.saveEntity(task, false);
-// referenceSession.refresh(true);
-// return task.getPath();
-// }
-// return null;
-// } catch (RepositoryException e1) {
-// throw new JcrException(
-// "Unable to create " + mainMixin + " entity with session " + referenceSession.toString(), e1);
-// } finally {
-// JcrUtils.logoutQuietly(tmpSession);
-// JcrUtils.logoutQuietly(mainSession);
-// }
-// }
-
}
+++ /dev/null
-package org.argeo.app.ui;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Node;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.cms.CmsEvent;
-import org.argeo.jcr.Jcr;
-import org.osgi.service.useradmin.User;
-
-/** Events specific to Argeo Suite UX. */
-public enum SuiteUxEvent implements CmsEvent {
- openNewPart, refreshPart, switchLayer;
-
- public final static String LAYER = "layer";
- public final static String USERNAME = "username";
-
- // ACR
- public final static String CONTENT_PATH = "contentPath";
-
- public String getTopicBase() {
- return "argeo.suite.ui";
- }
-
- public static Map<String, Object> eventProperties(Content content) {
- Map<String, Object> properties = new HashMap<>();
- properties.put(CONTENT_PATH, content.getPath());
- return properties;
- }
-
- @Deprecated
- public static Map<String, Object> eventProperties(Node node) {
- Map<String, Object> properties = new HashMap<>();
- String contentPath = '/' + Jcr.getWorkspaceName(node) + Jcr.getPath(node);
- properties.put(CONTENT_PATH, contentPath);
- return properties;
- }
-
- public static Map<String, Object> eventProperties(User user) {
- Map<String, Object> properties = new HashMap<>();
- properties.put(USERNAME, user.getName());
- return properties;
- }
-}
+++ /dev/null
-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;
- }
-
-}
package org.argeo.app.ui.dialogs;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteMsg;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
parent.setLayout(new GridLayout(2, false));
// FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.firstName);
firstNameTxt = new Text(parent, SWT.BORDER);
firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.lastName);
lastNameTxt = new Text(parent, SWT.BORDER);
lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.email);
emailTxt = new Text(parent, SWT.BORDER);
emailTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
import javax.jcr.Node;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteMsg;
import org.argeo.eclipse.ui.EclipseUiUtils;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.wizard.Wizard;
parent.setLayout(new GridLayout(2, false));
// FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.firstName);
firstNameTxt = new Text(parent, SWT.BORDER);
// firstNameTxt.setMessage("a first name");
firstNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.lastName);
lastNameTxt = new Text(parent, SWT.BORDER);
// lastNameTxt.setMessage("a last name");
lastNameTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
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;
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;
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();
((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);
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;
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();
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();
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;
@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);
}
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;
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;
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);
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
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;
@Override
protected void initModel(Node textNode) throws RepositoryException {
if (isFlat()) {
- DbkUtils.addParagraph(textNode, "");
+ DbkJcrUtils.addParagraph(textNode, "");
}
// else
// textNode.setProperty(DocBookNames.DBK_TITLE, textNode.getName());
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.ux.CmsView;
import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteUxEvent;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.swt.acr.SwtUiProvider;
import org.argeo.cms.swt.widgets.SwtTreeView;
import javax.jcr.RepositoryException;
import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.ui.SuiteUxEvent;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.cms.fs.CmsFsUtils;
import org.argeo.cms.swt.CmsSwtUtils;
import org.argeo.cms.ui.CmsUiProvider;
protected void externalNavigateTo(Path path) {
Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(Jcr.getSession(context).getRepository(), path));
parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUxEvent.eventProperties(folderNode));
+ cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUiUtils.eventProperties(folderNode));
}
};
dfc.setLayoutData(CmsSwtUtils.fillAll());
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
-import org.argeo.app.ui.SuiteUxEvent;
+import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.ux.CmsView;
import org.argeo.cms.fs.CmsFsUtils;
if (Files.isDirectory(newSelected)) {
Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(folderNode));
+ cmsView.sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUiUtils.eventProperties(folderNode));
}
}
});
if (Files.isDirectory(newSelected)) {
Node folderNode = cmsView.doAs(() -> CmsFsUtils.getNode(repository, newSelected));
parent.addDisposeListener((e1) -> Jcr.logout(folderNode));
- cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUxEvent.eventProperties(folderNode));
+ cmsView.sendEvent(SuiteUxEvent.openNewPart.topic(), SuiteUiUtils.eventProperties(folderNode));
}
}
});
import org.argeo.api.acr.Content;
import org.argeo.api.cms.CmsConstants;
import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteUxEvent;
-import org.argeo.app.ui.SuiteIcon;
+import org.argeo.app.ui.SuiteUiUtils;
import org.argeo.app.ui.widgets.TreeOrSearchArea;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.cms.jcr.acr.JcrContentProvider;
import org.argeo.cms.swt.CmsSwtTheme;
import org.argeo.cms.swt.CmsSwtUtils;
Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
if (user != null) {
CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.openNewPart.topic(),
- SuiteUxEvent.eventProperties(user));
+ SuiteUiUtils.eventProperties(user));
}
}
Node user = (Node) ui.getTreeViewer().getStructuredSelection().getFirstElement();
if (user != null) {
CmsSwtUtils.getCmsView(parent).sendEvent(SuiteUxEvent.refreshPart.topic(),
- SuiteUxEvent.eventProperties(user));
+ SuiteUiUtils.eventProperties(user));
}
}
});
import org.apache.commons.io.IOUtils;
import org.argeo.app.api.EntityNames;
import org.argeo.app.api.EntityType;
-import org.argeo.app.ui.SuiteUxEvent;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.ux.CmsView;
import org.argeo.api.cms.CmsConstants;
import org.argeo.api.cms.directory.CmsUser;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
-import org.argeo.app.ui.SuiteIcon;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteMsg;
import org.argeo.cms.CurrentUser;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.cms.auth.CmsRole;
String title = (context.hasContentClass(LdapObj.organization) ? SuiteMsg.org.lead() : SuiteMsg.group.lead())
+ " " + LdapAcrUtils.getLocalized(context, LdapAttr.cn.qName(), CurrentUser.locale()) + " ("
+ hierarchyUnit.getHierarchyUnitLabel(CurrentUser.locale()) + ")";
- SuiteUiUtils.addFormLabel(area, title);
+ SuiteSwtUtils.addFormLabel(area, title);
// toolbar
ToolBar toolBar = new ToolBar(area, SWT.NONE);
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.UserDirectory;
import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.app.ui.SuiteIcon;
+import org.argeo.app.ux.SuiteIcon;
import org.argeo.cms.CurrentUser;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.cms.auth.CmsRole;
import org.argeo.api.cms.directory.CmsGroup;
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteMsg;
import org.argeo.cms.swt.dialogs.CmsFeedback;
import org.argeo.cms.swt.widgets.SwtGuidedFormPage;
import org.argeo.cms.ux.widgets.AbstractGuidedForm;
parent.setLayout(new GridLayout(2, false));
// FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.org);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.org);
orgNameT = new Text(parent, SWT.BORDER);
// firstNameTxt.setMessage("a first name");
orgNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.app.core.SuiteUtils;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteMsg;
import org.argeo.cms.swt.dialogs.CmsFeedback;
import org.argeo.cms.swt.widgets.SwtGuidedFormPage;
import org.argeo.cms.ux.widgets.AbstractGuidedForm;
parent.setLayout(new GridLayout(2, false));
// FirstName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.firstName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.firstName);
firstNameT = new Text(parent, SWT.BORDER);
// firstNameTxt.setMessage("a first name");
firstNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// LastName
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.lastName);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.lastName);
lastNameT = new Text(parent, SWT.BORDER);
// lastNameTxt.setMessage("a last name");
lastNameT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
- SuiteUiUtils.createBoldLabel(parent, SuiteMsg.email);
+ SuiteSwtUtils.createBoldLabel(parent, SuiteMsg.email);
emailT = new Text(parent, SWT.BORDER);
emailT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
import org.argeo.api.cms.directory.CmsUserManager;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.ux.CmsView;
-import org.argeo.app.ui.SuiteIcon;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteUxEvent;
+import org.argeo.app.ux.SuiteIcon;
+import org.argeo.app.ux.SuiteMsg;
+import org.argeo.app.ux.SuiteUxEvent;
import org.argeo.cms.CurrentUser;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.cms.auth.CmsRole;
import org.argeo.api.cms.directory.HierarchyUnit;
import org.argeo.api.cms.directory.HierarchyUnit.Type;
import org.argeo.app.api.SuiteRole;
-import org.argeo.app.ui.SuiteMsg;
-import org.argeo.app.ui.SuiteStyle;
-import org.argeo.app.ui.SuiteUiUtils;
+import org.argeo.app.swt.ux.SuiteSwtUtils;
+import org.argeo.app.ux.SuiteMsg;
+import org.argeo.app.ux.SuiteStyle;
import org.argeo.cms.CmsMsg;
import org.argeo.cms.CurrentUser;
import org.argeo.cms.Localized;
changePasswordSection.setLayout(new GridLayout(2, false));
// SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.changePassword)
// .setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, 2, 1));
- SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.newPassword);
- Text newPasswordT = SuiteUiUtils.addFormTextField(changePasswordSection, null, null,
+ SuiteSwtUtils.addFormLabel(changePasswordSection, CmsMsg.newPassword);
+ Text newPasswordT = SuiteSwtUtils.addFormTextField(changePasswordSection, null, null,
SWT.PASSWORD | SWT.BORDER);
newPasswordT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- SuiteUiUtils.addFormLabel(changePasswordSection, CmsMsg.repeatNewPassword);
- Text repeatNewPasswordT = SuiteUiUtils.addFormTextField(changePasswordSection, null, null,
+ SuiteSwtUtils.addFormLabel(changePasswordSection, CmsMsg.repeatNewPassword);
+ Text repeatNewPasswordT = SuiteSwtUtils.addFormTextField(changePasswordSection, null, null,
SWT.PASSWORD | SWT.BORDER);
repeatNewPasswordT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
Button apply = new Button(changePasswordSection, SWT.FLAT);
}
private void addFormLine(SwtSection parent, Localized msg, Content content, QNamed attr) {
- SuiteUiUtils.addFormLabel(parent, msg.lead());
+ SuiteSwtUtils.addFormLabel(parent, msg.lead());
EditableText text = new EditableText(parent, SWT.SINGLE | SWT.FLAT);
text.setLayoutData(CmsSwtUtils.fillWidth());
text.setStyle(SuiteStyle.simpleInput);
import org.argeo.api.acr.ldap.LdapAttr;
import org.argeo.api.acr.ldap.LdapObj;
import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.app.ui.SuiteIcon;
+import org.argeo.app.ux.SuiteIcon;
import org.argeo.cms.CurrentUser;
import org.argeo.cms.auth.UserAdminUtils;
import org.argeo.cms.ux.widgets.Column;
package org.argeo.app.ui.publish;
-import static org.argeo.app.ui.SuiteApp.DEFAULT_THEME_ID_PROPERTY;
-import static org.argeo.app.ui.SuiteApp.DEFAULT_UI_NAME_PROPERTY;
+import static org.argeo.app.swt.ux.SwtArgeoApp.DEFAULT_THEME_ID_PROPERTY;
+import static org.argeo.app.swt.ux.SwtArgeoApp.DEFAULT_UI_NAME_PROPERTY;
import java.util.HashSet;
import java.util.Map;
import org.argeo.api.cms.CmsApp;
import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.app.ui.SuiteApp;
+import org.argeo.app.swt.ux.SwtArgeoApp;
import org.argeo.cms.AbstractCmsApp;
import org.argeo.cms.ui.CmsUiProvider;
import org.argeo.cms.util.LangUtils;
defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
- publicBasePath = LangUtils.get(properties, SuiteApp.PUBLIC_BASE_PATH_PROPERTY);
+ publicBasePath = LangUtils.get(properties, SwtArgeoApp.PUBLIC_BASE_PATH_PROPERTY);
pid = properties.get(Constants.SERVICE_PID);
if (log.isDebugEnabled())