From: Mathieu Baudier Date: Thu, 12 Mar 2020 08:48:40 +0000 (+0100) Subject: Rename packages in order to make future stable documentation clearer. X-Git-Tag: argeo-commons-2.1.88~9 X-Git-Url: http://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=5b3108fe285bca50565b58b63fa4feddc96c0765 Rename packages in order to make future stable documentation clearer. --- diff --git a/dep/org.argeo.dep.cms.node/pom.xml b/dep/org.argeo.dep.cms.node/pom.xml index 7d6eb4310..b202c69fe 100644 --- a/dep/org.argeo.dep.cms.node/pom.xml +++ b/dep/org.argeo.dep.cms.node/pom.xml @@ -22,7 +22,7 @@ org.argeo.commons - org.argeo.node.api + org.argeo.api 2.1.88-SNAPSHOT diff --git a/org.argeo.api/.classpath b/org.argeo.api/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/org.argeo.api/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.argeo.api/.gitignore b/org.argeo.api/.gitignore new file mode 100644 index 000000000..09e3bc9b2 --- /dev/null +++ b/org.argeo.api/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/org.argeo.api/.project b/org.argeo.api/.project new file mode 100644 index 000000000..a396aadc8 --- /dev/null +++ b/org.argeo.api/.project @@ -0,0 +1,28 @@ + + + org.argeo.api + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.argeo.api/META-INF/.gitignore b/org.argeo.api/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/org.argeo.api/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/org.argeo.api/bnd.bnd b/org.argeo.api/bnd.bnd new file mode 100644 index 000000000..f0bd0ceaa --- /dev/null +++ b/org.argeo.api/bnd.bnd @@ -0,0 +1 @@ +Provide-Capability: cms.datamodel;name=ldap;cnd=/org/argeo/node/ldap.cnd;abstract=true diff --git a/org.argeo.api/build.properties b/org.argeo.api/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/org.argeo.api/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/org.argeo.api/pom.xml b/org.argeo.api/pom.xml new file mode 100644 index 000000000..74987c761 --- /dev/null +++ b/org.argeo.api/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.argeo.commons + argeo-commons + 2.1.88-SNAPSHOT + .. + + org.argeo.api + Argeo Node API + jar + + + \ No newline at end of file diff --git a/org.argeo.api/src/org/argeo/api/ArgeoLogListener.java b/org.argeo.api/src/org/argeo/api/ArgeoLogListener.java new file mode 100644 index 000000000..925c9b012 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/ArgeoLogListener.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api; + +/** Framework agnostic interface for log notifications */ +public interface ArgeoLogListener { + /** + * Appends a log + * + * @param username + * authentified user, null for anonymous + * @param level + * INFO, DEBUG, WARN, etc. (logging framework specific) + * @param category + * hierarchy (logging framework specific) + * @param thread + * name of the thread which logged this message + * @param msg + * any object as long as its toString() method returns the + * message + * @param exception + * exception in log4j ThrowableStrRep format + */ + public void appendLog(String username, Long timestamp, String level, + String category, String thread, Object msg, String[] exception); +} diff --git a/org.argeo.api/src/org/argeo/api/ArgeoLogger.java b/org.argeo.api/src/org/argeo/api/ArgeoLogger.java new file mode 100644 index 000000000..eed02ac7f --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/ArgeoLogger.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api; + +/** + * Logging framework agnostic identifying a logging service, to which one can + * register + */ +public interface ArgeoLogger { + /** + * Register for events by threads with the same authentication (or all + * threads if admin) + */ + public void register(ArgeoLogListener listener, + Integer numberOfPreviousEvents); + + /** + * For admin use only: register for all users + * + * @param listener + * the log listener + * @param numberOfPreviousEvents + * the number of previous events to notify + * @param everything + * if true even anonymous is logged + */ + public void registerForAll(ArgeoLogListener listener, + Integer numberOfPreviousEvents, boolean everything); + + public void unregister(ArgeoLogListener listener); + + public void unregisterForAll(ArgeoLogListener listener); +} diff --git a/org.argeo.api/src/org/argeo/api/DataAdminLoginModule.java b/org.argeo.api/src/org/argeo/api/DataAdminLoginModule.java new file mode 100644 index 000000000..295196ad4 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/DataAdminLoginModule.java @@ -0,0 +1,48 @@ +package org.argeo.api; + +import java.util.Map; + +import javax.security.auth.AuthPermission; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.argeo.api.security.DataAdminPrincipal; + +/** + * Log-in a system process as data admin. Protection is via + * {@link AuthPermission} on this login module, so if it can be accessed it will + * always succeed. + */ +public class DataAdminLoginModule implements LoginModule { + private Subject subject; + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, + Map options) { + this.subject = subject; + } + + @Override + public boolean login() throws LoginException { + return true; + } + + @Override + public boolean commit() throws LoginException { + subject.getPrincipals().add(new DataAdminPrincipal()); + return true; + } + + @Override + public boolean abort() throws LoginException { + return true; + } + + @Override + public boolean logout() throws LoginException { + subject.getPrincipals().removeAll(subject.getPrincipals(DataAdminPrincipal.class)); + return true; + } +} diff --git a/org.argeo.api/src/org/argeo/api/DataModelNamespace.java b/org.argeo.api/src/org/argeo/api/DataModelNamespace.java new file mode 100644 index 000000000..421b11a4a --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/DataModelNamespace.java @@ -0,0 +1,18 @@ +package org.argeo.api; + +import org.osgi.resource.Namespace; + +/** CMS Data Model capability namespace. */ +public class DataModelNamespace extends Namespace { + + public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel"; + public static final String NAME = "name"; + public static final String CND = "cnd"; + /** If 'true', indicates that no repository should be published */ + public static final String ABSTRACT = "abstract"; + + private DataModelNamespace() { + // empty + } + +} diff --git a/org.argeo.api/src/org/argeo/api/MvcProvider.java b/org.argeo.api/src/org/argeo/api/MvcProvider.java new file mode 100644 index 000000000..9c5c4a06b --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/MvcProvider.java @@ -0,0 +1,38 @@ +package org.argeo.api; + +import java.util.function.BiFunction; + +/** + * Stateless UI part creator. Takes a parent view (V) and a model context (M) in + * order to create a view part (W) which can then be further configured. Such + * object can be used as services and reference other part of the model which + * are relevant for all created UI part. + */ +@FunctionalInterface +public interface MvcProvider extends BiFunction { + /** + * Whether this parent view is supported. + * + * @return true by default. + */ + default boolean isViewSupported(V parent) { + return true; + } + + /** + * Whether this context is supported. + * + * @return true by default. + */ + default boolean isModelSupported(M context) { + return true; + } + + default W createUiPart(V parent, M context) { + if (!isViewSupported(parent)) + throw new IllegalArgumentException("Parent view " + parent + "is not supported."); + if (!isModelSupported(context)) + throw new IllegalArgumentException("Model context " + context + "is not supported."); + return apply(parent, context); + } +} diff --git a/org.argeo.api/src/org/argeo/api/NodeConstants.java b/org.argeo.api/src/org/argeo/api/NodeConstants.java new file mode 100644 index 000000000..d2dfb83fd --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeConstants.java @@ -0,0 +1,129 @@ +package org.argeo.api; + +public interface NodeConstants { + /* + * DN ATTRIBUTES (RFC 4514) + */ + String CN = "cn"; + String L = "l"; + String ST = "st"; + String O = "o"; + String OU = "ou"; + String C = "c"; + String STREET = "street"; + String DC = "dc"; + String UID = "uid"; + + /* + * STANDARD ATTRIBUTES + */ + String LABELED_URI = "labeledUri"; + + /* + * COMMON NAMES + */ + String NODE = "node"; + String EGO = "ego"; + String HOME = "home"; + String SRV = "srv"; + String GUESTS = "guests"; + String PUBLIC = "public"; + + /* + * BASE DNs + */ + String DEPLOY_BASEDN = "ou=deploy,ou=node"; + + /* + * STANDARD VALUES + */ + String DEFAULT = "default"; + + /* + * RESERVED ROLES + */ + String ROLES_BASEDN = "ou=roles,ou=node"; + String TOKENS_BASEDN = "ou=tokens,ou=node"; + String ROLE_ADMIN = "cn=admin," + ROLES_BASEDN; + String ROLE_USER_ADMIN = "cn=userAdmin," + ROLES_BASEDN; + String ROLE_DATA_ADMIN = "cn=dataAdmin," + ROLES_BASEDN; + // Special system groups that cannot be edited: + // user U anonymous = everyone + String ROLE_USER = "cn=user," + ROLES_BASEDN; + String ROLE_ANONYMOUS = "cn=anonymous," + ROLES_BASEDN; + // Account lifecycle + String ROLE_REGISTERING = "cn=registering," + ROLES_BASEDN; + + /* + * LOGIN CONTEXTS + */ + String LOGIN_CONTEXT_NODE = "NODE"; + String LOGIN_CONTEXT_USER = "USER"; + String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS"; + String LOGIN_CONTEXT_DATA_ADMIN = "DATA_ADMIN"; + String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER"; + String LOGIN_CONTEXT_KEYRING = "KEYRING"; + + /* + * PATHS + */ + String PATH_DATA = "/data"; + String PATH_JCR = "/jcr"; + String PATH_FILES = "/files"; + // String PATH_JCR_PUB = "/pub"; + + /* + * FILE SYSTEMS + */ + String SCHEME_NODE = NODE; + + /* + * KERBEROS + */ + String NODE_SERVICE = NODE; + + /* + * INIT FRAMEWORK PROPERTIES + */ + String NODE_INIT = "argeo.node.init"; + String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; + String I18N_LOCALES = "argeo.i18n.locales"; + // Node Security + String ROLES_URI = "argeo.node.roles.uri"; + String TOKENS_URI = "argeo.node.tokens.uri"; + /** URI to an LDIF file or LDAP server used as initialization or backend */ + String USERADMIN_URIS = "argeo.node.useradmin.uris"; + // Transaction manager + String TRANSACTION_MANAGER = "argeo.node.transaction.manager"; + String TRANSACTION_MANAGER_SIMPLE = "simple"; + String TRANSACTION_MANAGER_BITRONIX = "bitronix"; + // Node + /** Properties configuring the node repository */ + String NODE_REPO_PROP_PREFIX = "argeo.node.repo."; + /** Additional standalone repositories, related to data models. */ + String NODE_REPOS_PROP_PREFIX = "argeo.node.repos."; + // HTTP + String HTTP_PORT = "org.osgi.service.http.port"; + String HTTP_PORT_SECURE = "org.osgi.service.http.port.secure"; + /** + * The HTTP header used to convey the DN of a client verified by a reverse + * proxy. Typically SSL_CLIENT_S_DN for Apache. + */ + String HTTP_PROXY_SSL_DN = "argeo.http.proxy.ssl.dn"; + + /* + * PIDs + */ + String NODE_STATE_PID = "org.argeo.api.state"; + String NODE_DEPLOYMENT_PID = "org.argeo.api.deployment"; + String NODE_INSTANCE_PID = "org.argeo.api.instance"; + + String NODE_KEYRING_PID = "org.argeo.api.keyring"; + String NODE_FS_PROVIDER_PID = "org.argeo.api.fsProvider"; + + /* + * FACTORY PIDs + */ + String NODE_REPOS_FACTORY_PID = "org.argeo.api.repos"; + String NODE_USER_ADMIN_PID = "org.argeo.api.userAdmin"; +} diff --git a/org.argeo.api/src/org/argeo/api/NodeDeployment.java b/org.argeo.api/src/org/argeo/api/NodeDeployment.java new file mode 100644 index 000000000..224d244dc --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeDeployment.java @@ -0,0 +1,5 @@ +package org.argeo.api; + +public interface NodeDeployment { + Long getAvailableSince(); +} diff --git a/org.argeo.api/src/org/argeo/api/NodeInstance.java b/org.argeo.api/src/org/argeo/api/NodeInstance.java new file mode 100644 index 000000000..167ba81d5 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeInstance.java @@ -0,0 +1,15 @@ +package org.argeo.api; + +import javax.naming.ldap.LdapName; + +/** The structured data */ +public interface NodeInstance { + /** + * To be used as an identifier of a workgroup, typically as a value for the + * 'businessCategory' attribute in LDAP. + */ + public final static String WORKGROUP = "workgroup"; + + /** Mark this group as a workgroup */ + void createWorkgroup(LdapName groupDn); +} diff --git a/org.argeo.api/src/org/argeo/api/NodeNames.java b/org.argeo.api/src/org/argeo/api/NodeNames.java new file mode 100644 index 000000000..e44f08bf1 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeNames.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api; + +/** JCR types in the http://www.argeo.org/node namespace */ +@Deprecated +public interface NodeNames { + String LDAP_UID = "ldap:"+NodeConstants.UID; + String LDAP_CN = "ldap:"+NodeConstants.CN; +} diff --git a/org.argeo.api/src/org/argeo/api/NodeOID.java b/org.argeo.api/src/org/argeo/api/NodeOID.java new file mode 100644 index 000000000..ade116342 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeOID.java @@ -0,0 +1,11 @@ +package org.argeo.api; + +interface NodeOID { + String BASE = "1.3.6.1.4.1" + ".48308" + ".1"; + + // ATTRIBUTE TYPES + String ATTRIBUTE_TYPES = BASE + ".4"; + + // OBJECT CLASSES + String OBJECT_CLASSES = BASE + ".6"; +} diff --git a/org.argeo.api/src/org/argeo/api/NodeState.java b/org.argeo.api/src/org/argeo/api/NodeState.java new file mode 100644 index 000000000..30de09587 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeState.java @@ -0,0 +1,17 @@ +package org.argeo.api; + +import java.util.List; +import java.util.Locale; + +public interface NodeState { + Locale getDefaultLocale(); + + List getLocales(); + + String getHostname(); + + boolean isClean(); + + Long getAvailableSince(); + +} diff --git a/org.argeo.api/src/org/argeo/api/NodeTypes.java b/org.argeo.api/src/org/argeo/api/NodeTypes.java new file mode 100644 index 000000000..b408664b7 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeTypes.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api; + +/** JCR types in the http://www.argeo.org/node namespace */ +@Deprecated +public interface NodeTypes { + String NODE_USER_HOME = "node:userHome"; + String NODE_GROUP_HOME = "node:groupHome"; +} diff --git a/org.argeo.api/src/org/argeo/api/NodeUtils.java b/org.argeo.api/src/org/argeo/api/NodeUtils.java new file mode 100644 index 000000000..f64cbe6f7 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/NodeUtils.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api; + +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.QueryResult; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.security.auth.AuthPermission; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +/** Utilities related to Argeo model in JCR */ +public class NodeUtils { + /** + * Wraps the call to the repository factory based on parameter + * {@link NodeConstants#CN} in order to simplify it and protect against future + * API changes. + */ + public static Repository getRepositoryByAlias(RepositoryFactory repositoryFactory, String alias) { + try { + Map parameters = new HashMap(); + parameters.put(NodeConstants.CN, alias); + return repositoryFactory.getRepository(parameters); + } catch (RepositoryException e) { + throw new RuntimeException("Unexpected exception when trying to retrieve repository with alias " + alias, + e); + } + } + + /** + * Wraps the call to the repository factory based on parameter + * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against + * future API changes. + */ + public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri) { + return getRepositoryByUri(repositoryFactory, uri, null); + } + + /** + * Wraps the call to the repository factory based on parameter + * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against + * future API changes. + */ + public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri, String alias) { + try { + Map parameters = new HashMap(); + parameters.put(NodeConstants.LABELED_URI, uri); + if (alias != null) + parameters.put(NodeConstants.CN, alias); + return repositoryFactory.getRepository(parameters); + } catch (RepositoryException e) { + throw new RuntimeException("Unexpected exception when trying to retrieve repository with uri " + uri, e); + } + } + + /** + * Returns the home node of the user or null if none was found. + * + * @param session the session to use in order to perform the search, this can + * be a session with a different user ID than the one searched, + * typically when a system or admin session is used. + * @param username the username of the user + */ + public static Node getUserHome(Session session, String username) { +// try { +// QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory(); +// Selector sel = qomf.selector(NodeTypes.NODE_USER_HOME, "sel"); +// DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_UID); +// StaticOperand sop = qomf.literal(session.getValueFactory().createValue(username)); +// Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop); +// Query query = qomf.createQuery(sel, constraint, null, null); +// return querySingleNode(query); +// } catch (RepositoryException e) { +// throw new RuntimeException("Cannot find home for user " + username, e); +// } + + try { + checkUserWorkspace(session, username); + String homePath = getHomePath(username); + if (session.itemExists(homePath)) + return session.getNode(homePath); + // legacy + homePath = "/home/" + username; + if (session.itemExists(homePath)) + return session.getNode(homePath); + return null; + } catch (RepositoryException e) { + throw new RuntimeException("Cannot find home for user " + username, e); + } + } + + private static String getHomePath(String username) { + LdapName dn; + try { + dn = new LdapName(username); + } catch (InvalidNameException e) { + throw new IllegalArgumentException("Invalid name " + username, e); + } + String userId = dn.getRdn(dn.size() - 1).getValue().toString(); + return '/' + userId; + } + + private static void checkUserWorkspace(Session session, String username) { + String workspaceName = session.getWorkspace().getName(); + if (!NodeConstants.HOME.equals(workspaceName)) + throw new IllegalArgumentException(workspaceName + " is not the home workspace for user " + username); + } + + /** + * Returns the home node of the user or null if none was found. + * + * @param session the session to use in order to perform the search, this can + * be a session with a different user ID than the one searched, + * typically when a system or admin session is used. + * @param groupname the name of the group + */ + public static Node getGroupHome(Session session, String groupname) { +// try { +// QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory(); +// Selector sel = qomf.selector(NodeTypes.NODE_GROUP_HOME, "sel"); +// DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_CN); +// StaticOperand sop = qomf.literal(session.getValueFactory().createValue(cn)); +// Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop); +// Query query = qomf.createQuery(sel, constraint, null, null); +// return querySingleNode(query); +// } catch (RepositoryException e) { +// throw new RuntimeException("Cannot find home for group " + cn, e); +// } + + try { + checkGroupWorkspace(session, groupname); + String homePath = getGroupPath(groupname); + if (session.itemExists(homePath)) + return session.getNode(homePath); + // legacy + homePath = "/groups/" + groupname; + if (session.itemExists(homePath)) + return session.getNode(homePath); + return null; + } catch (RepositoryException e) { + throw new RuntimeException("Cannot find home for group " + groupname, e); + } + + } + + private static String getGroupPath(String groupname) { + String cn; + try { + LdapName dn = new LdapName(groupname); + cn = dn.getRdn(dn.size() - 1).getValue().toString(); + } catch (InvalidNameException e) { + cn = groupname; + } + return '/' + cn; + } + + private static void checkGroupWorkspace(Session session, String groupname) { + String workspaceName = session.getWorkspace().getName(); + if (!NodeConstants.SRV.equals(workspaceName)) + throw new IllegalArgumentException(workspaceName + " is not the group workspace for group " + groupname); + } + + /** + * Queries one single node. + * + * @return one single node or null if none was found + * @throws ArgeoJcrException if more than one node was found + */ + private static Node querySingleNode(Query query) { + NodeIterator nodeIterator; + try { + QueryResult queryResult = query.execute(); + nodeIterator = queryResult.getNodes(); + } catch (RepositoryException e) { + throw new RuntimeException("Cannot execute query " + query, e); + } + Node node; + if (nodeIterator.hasNext()) + node = nodeIterator.nextNode(); + else + return null; + + if (nodeIterator.hasNext()) + throw new RuntimeException("Query returned more than one node."); + return node; + } + + /** Returns the home node of the session user or null if none was found. */ + public static Node getUserHome(Session session) { + String userID = session.getUserID(); + return getUserHome(session, userID); + } + + /** + * Translate the path to this node into a path containing the name of the + * repository and the name of the workspace. + */ + public static String getDataPath(String cn, Node node) throws RepositoryException { + assert node != null; + StringBuilder buf = new StringBuilder(NodeConstants.PATH_DATA); + return buf.append('/').append(cn).append('/').append(node.getSession().getWorkspace().getName()) + .append(node.getPath()).toString(); + } + + /** + * Open a JCR session with full read/write rights on the data, as + * {@link NodeConstants#ROLE_USER_ADMIN}, using the + * {@link NodeConstants#LOGIN_CONTEXT_DATA_ADMIN} login context. For security + * hardened deployement, use {@link AuthPermission} on this login context. + */ + public static Session openDataAdminSession(Repository repository, String workspaceName) { + ClassLoader currentCl = Thread.currentThread().getContextClassLoader(); + LoginContext loginContext; + try { + loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_DATA_ADMIN); + loginContext.login(); + } catch (LoginException e1) { + throw new RuntimeException("Could not login as data admin", e1); + } finally { + Thread.currentThread().setContextClassLoader(currentCl); + } + return Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { + + @Override + public Session run() { + try { + return repository.login(workspaceName); + } catch (NoSuchWorkspaceException e) { + throw new IllegalArgumentException("No workspace " + workspaceName + " available", e); + } catch (RepositoryException e) { + throw new RuntimeException("Cannot open data admin session", e); + } + } + + }); + } + + /** Singleton. */ + private NodeUtils() { + } + +} diff --git a/org.argeo.api/src/org/argeo/api/ldap.cnd b/org.argeo.api/src/org/argeo/api/ldap.cnd new file mode 100644 index 000000000..a2306c60e --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/ldap.cnd @@ -0,0 +1 @@ + diff --git a/org.argeo.api/src/org/argeo/api/node.cnd b/org.argeo.api/src/org/argeo/api/node.cnd new file mode 100644 index 000000000..d8a26b64e --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/node.cnd @@ -0,0 +1,9 @@ + + +[node:userHome] +mixin +- ldap:uid (STRING) m + +[node:groupHome] +mixin +- ldap:cn (STRING) m diff --git a/org.argeo.api/src/org/argeo/api/package-info.java b/org.argeo.api/src/org/argeo/api/package-info.java new file mode 100644 index 000000000..1ea483a0c --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/package-info.java @@ -0,0 +1,5 @@ +/** + * Abstractions or constants related to an Argeo Node, an active repository of + * linked data. + */ +package org.argeo.api; \ No newline at end of file diff --git a/org.argeo.api/src/org/argeo/api/security/AnonymousPrincipal.java b/org.argeo.api/src/org/argeo/api/security/AnonymousPrincipal.java new file mode 100644 index 000000000..d07b055ef --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/AnonymousPrincipal.java @@ -0,0 +1,36 @@ +package org.argeo.api.security; + +import java.security.Principal; + +import javax.naming.ldap.LdapName; + +import org.argeo.api.NodeConstants; + +/** Marker for anonymous users. */ +public final class AnonymousPrincipal implements Principal { + private final String name = NodeConstants.ROLE_ANONYMOUS; + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public String toString() { + return name.toString(); + } + + public LdapName getLdapName(){ + return NodeSecurityUtils.ROLE_ANONYMOUS_NAME; + } +} diff --git a/org.argeo.api/src/org/argeo/api/security/CryptoKeyring.java b/org.argeo.api/src/org/argeo/api/security/CryptoKeyring.java new file mode 100644 index 000000000..a6f37a4d6 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/CryptoKeyring.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.security; + +/** + * Marker interface for an advanced keyring based on cryptography. + */ +public interface CryptoKeyring extends Keyring { + public void changePassword(char[] oldPassword, char[] newPassword); + + public void unlock(char[] password); +} diff --git a/org.argeo.api/src/org/argeo/api/security/DataAdminPrincipal.java b/org.argeo.api/src/org/argeo/api/security/DataAdminPrincipal.java new file mode 100644 index 000000000..7581d8db0 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/DataAdminPrincipal.java @@ -0,0 +1,31 @@ +package org.argeo.api.security; + +import java.security.Principal; + +import org.argeo.api.NodeConstants; + +/** Allows to modify any data. */ +public final class DataAdminPrincipal implements Principal { + private final String name = NodeConstants.ROLE_DATA_ADMIN; + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DataAdminPrincipal; + } + + @Override + public String toString() { + return name.toString(); + } + +} diff --git a/org.argeo.api/src/org/argeo/api/security/Keyring.java b/org.argeo.api/src/org/argeo/api/security/Keyring.java new file mode 100644 index 000000000..c9228f074 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/Keyring.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.security; + +import java.io.InputStream; + +/** + * Access to private (typically encrypted) data. The keyring is responsible for + * retrieving the necessary credentials. Experimental. This API may + * change. + */ +public interface Keyring { + /** + * Returns the confidential information as chars. Must ask for it if it is + * not stored. + */ + public char[] getAsChars(String path); + + /** + * Returns the confidential information as a stream. Must ask for it if it + * is not stored. + */ + public InputStream getAsStream(String path); + + public void set(String path, char[] arr); + + public void set(String path, InputStream in); +} diff --git a/org.argeo.api/src/org/argeo/api/security/NodeSecurityUtils.java b/org.argeo.api/src/org/argeo/api/security/NodeSecurityUtils.java new file mode 100644 index 000000000..245851285 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/NodeSecurityUtils.java @@ -0,0 +1,40 @@ +package org.argeo.api.security; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; + +import org.argeo.api.NodeConstants; + +public class NodeSecurityUtils { + public final static LdapName ROLE_ADMIN_NAME, ROLE_DATA_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME, + ROLE_USER_ADMIN_NAME; + public final static List RESERVED_ROLES; + static { + try { + ROLE_ADMIN_NAME = new LdapName(NodeConstants.ROLE_ADMIN); + ROLE_DATA_ADMIN_NAME = new LdapName(NodeConstants.ROLE_DATA_ADMIN); + ROLE_USER_NAME = new LdapName(NodeConstants.ROLE_USER); + ROLE_USER_ADMIN_NAME = new LdapName(NodeConstants.ROLE_USER_ADMIN); + ROLE_ANONYMOUS_NAME = new LdapName(NodeConstants.ROLE_ANONYMOUS); + RESERVED_ROLES = Collections.unmodifiableList(Arrays.asList( + new LdapName[] { ROLE_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME, ROLE_USER_ADMIN_NAME })); + } catch (InvalidNameException e) { + throw new Error("Cannot initialize login module class", e); + } + } + + public static void checkUserName(LdapName name) throws IllegalArgumentException { + if (RESERVED_ROLES.contains(name)) + throw new IllegalArgumentException(name + " is a reserved name"); + } + + public static void checkImpliedPrincipalName(LdapName roleName) throws IllegalArgumentException { +// if (ROLE_USER_NAME.equals(roleName) || ROLE_ANONYMOUS_NAME.equals(roleName)) +// throw new IllegalArgumentException(roleName + " cannot be listed as role"); + } + +} diff --git a/org.argeo.api/src/org/argeo/api/security/PBEKeySpecCallback.java b/org.argeo.api/src/org/argeo/api/security/PBEKeySpecCallback.java new file mode 100644 index 000000000..ba441e20b --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/security/PBEKeySpecCallback.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.security; + +import javax.crypto.spec.PBEKeySpec; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.PasswordCallback; + +/** + * All information required to set up a {@link PBEKeySpec} bar the password + * itself (use a {@link PasswordCallback}) + */ +public class PBEKeySpecCallback implements Callback { + private String secretKeyFactory; + private byte[] salt; + private Integer iterationCount; + /** Can be null for some algorithms */ + private Integer keyLength; + /** Can be null, will trigger secret key encryption if not */ + private String secretKeyEncryption; + + private String encryptedPasswordHashCipher; + private byte[] encryptedPasswordHash; + + public void set(String secretKeyFactory, byte[] salt, + Integer iterationCount, Integer keyLength, + String secretKeyEncryption) { + this.secretKeyFactory = secretKeyFactory; + this.salt = salt; + this.iterationCount = iterationCount; + this.keyLength = keyLength; + this.secretKeyEncryption = secretKeyEncryption; +// this.encryptedPasswordHashCipher = encryptedPasswordHashCipher; +// this.encryptedPasswordHash = encryptedPasswordHash; + } + + public String getSecretKeyFactory() { + return secretKeyFactory; + } + + public byte[] getSalt() { + return salt; + } + + public Integer getIterationCount() { + return iterationCount; + } + + public Integer getKeyLength() { + return keyLength; + } + + public String getSecretKeyEncryption() { + return secretKeyEncryption; + } + + public String getEncryptedPasswordHashCipher() { + return encryptedPasswordHashCipher; + } + + public byte[] getEncryptedPasswordHash() { + return encryptedPasswordHash; + } + +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/ArrayTabularRow.java b/org.argeo.api/src/org/argeo/api/tabular/ArrayTabularRow.java new file mode 100644 index 000000000..c9f77106a --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/ArrayTabularRow.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + +import java.util.List; + +/** Minimal tabular row wrapping an {@link Object} array */ +public class ArrayTabularRow implements TabularRow { + private final Object[] arr; + + public ArrayTabularRow(List objs) { + this.arr = objs.toArray(); + } + + public Object get(Integer col) { + return arr[col]; + } + + public int size() { + return arr.length; + } + + public Object[] toArray() { + return arr; + } + +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/TabularColumn.java b/org.argeo.api/src/org/argeo/api/tabular/TabularColumn.java new file mode 100644 index 000000000..b38c346d3 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/TabularColumn.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + +/** The column in a tabular content */ +public class TabularColumn { + private String name; + /** + * JCR types, see + * http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0/index.html + * ?javax/jcr/PropertyType.html + */ + private Integer type; + + /** column with default type */ + public TabularColumn(String name) { + super(); + this.name = name; + } + + public TabularColumn(String name, Integer type) { + super(); + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/TabularContent.java b/org.argeo.api/src/org/argeo/api/tabular/TabularContent.java new file mode 100644 index 000000000..0ffef217f --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/TabularContent.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + +import java.util.List; + +/** + * Content organized as a table, possibly with headers. Only JCR types are + * supported even though there is not direct dependency on JCR. + */ +public interface TabularContent { + /** The headers of this table or null is none available. */ + public List getColumns(); + + public TabularRowIterator read(); +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/TabularRow.java b/org.argeo.api/src/org/argeo/api/tabular/TabularRow.java new file mode 100644 index 000000000..5437015bf --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/TabularRow.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + +/** A row of tabular data */ +public interface TabularRow { + /** The value at this column index */ + public Object get(Integer col); + + /** The raw objects (direct references) */ + public Object[] toArray(); + + /** Number of columns */ + public int size(); +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/TabularRowIterator.java b/org.argeo.api/src/org/argeo/api/tabular/TabularRowIterator.java new file mode 100644 index 000000000..d48b37b92 --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/TabularRowIterator.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + +import java.util.Iterator; + +/** Navigation of rows */ +public interface TabularRowIterator extends Iterator { + /** + * Current row number, has to be incremented by each call to next() ; starts at 0, will + * therefore be 1 for the first row returned. + */ + public Long getCurrentRowNumber(); +} diff --git a/org.argeo.api/src/org/argeo/api/tabular/TabularWriter.java b/org.argeo.api/src/org/argeo/api/tabular/TabularWriter.java new file mode 100644 index 000000000..1a029472c --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/tabular/TabularWriter.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.api.tabular; + + +/** Write to a tabular content */ +public interface TabularWriter { + /** Append a new row of data */ + public void appendRow(Object[] row); + + /** Finish persisting data and release resources */ + public void close(); +} diff --git a/org.argeo.cms.e4.rap/OSGI-INF/cms-demo-rap.xml b/org.argeo.cms.e4.rap/OSGI-INF/cms-demo-rap.xml deleted file mode 100644 index e23b11b6a..000000000 --- a/org.argeo.cms.e4.rap/OSGI-INF/cms-demo-rap.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/org.argeo.cms.e4.rap/bnd.bnd b/org.argeo.cms.e4.rap/bnd.bnd index da09b3f8b..90dc8d42b 100644 --- a/org.argeo.cms.e4.rap/bnd.bnd +++ b/org.argeo.cms.e4.rap/bnd.bnd @@ -1,10 +1,7 @@ Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/cms-admin-rap.xml -#OSGI-INF/cms-demo-rap.xml -#Bundle-Activator: org.argeo.cms.script.ScriptAppActivator - -Import-Package: org.argeo.node,\ +Import-Package: org.argeo.api,\ org.eclipse.swt,\ org.eclipse.swt.graphics,\ org.eclipse.e4.ui.workbench,\ diff --git a/org.argeo.cms.e4.rap/cms/app.js b/org.argeo.cms.e4.rap/cms/app.js deleted file mode 100644 index cd5e099eb..000000000 --- a/org.argeo.cms.e4.rap/cms/app.js +++ /dev/null @@ -1,12 +0,0 @@ -var AppUi = Java.type('org.argeo.cms.script.AppUi') -var CmsE4EntryPointFactory = Java - .type('org.argeo.cms.e4.rap.CmsE4EntryPointFactory') - -APP.setWebPath('cms') -APP.setPageTitle('Argeo CMS') - -APP.getUi().put( - 'devops', - new AppUi(APP, new CmsE4EntryPointFactory( - 'org.argeo.cms.e4/e4xmi/cms-devops.e4xmi'))) -APP.getUi().get('devops').setPageTitle('Argeo CMS DevOps') diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4DemoApp.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4DemoApp.java deleted file mode 100644 index 2987e4da3..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4DemoApp.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.e4.rap; - -public class CmsE4DemoApp extends AbstractRapE4App { - public CmsE4DemoApp() { - setPageTitle("CMS Demo"); - setE4Xmi("org.argeo.cms.e4.rap/e4xmi/cms-demo-rap.e4xmi"); - setPath("/cms-e4"); - } - -} diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java index a062bea6b..f7a1ed667 100644 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java +++ b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java @@ -8,17 +8,17 @@ import javax.security.auth.login.LoginException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.ui.CmsImageManager; import org.argeo.cms.ui.CmsView; import org.argeo.cms.ui.UxContext; import org.argeo.cms.ui.dialogs.CmsFeedback; -import org.argeo.cms.util.SimpleImageManager; -import org.argeo.cms.util.SimpleUxContext; -import org.argeo.cms.widgets.auth.CmsLoginShell; +import org.argeo.cms.ui.util.SimpleImageManager; +import org.argeo.cms.ui.util.SimpleUxContext; +import org.argeo.cms.ui.widgets.auth.CmsLoginShell; import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.node.NodeConstants; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-removeButtons.js b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-removeButtons.js deleted file mode 100644 index 20e82e34a..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-removeButtons.js +++ /dev/null @@ -1 +0,0 @@ -'Source,Save,Templates,Strike,Subscript,Superscript,CopyFormatting,Outdent,Indent,CreateDiv,JustifyLeft,JustifyCenter,JustifyRight,JustifyBlock,Language,Anchor,Flash,HorizontalRule,Smiley,SpecialChar,PageBreak,Iframe,Format,Font,FontSize,BGColor,TextColor,ShowBlocks,About,Preview,Print,Redo,Replace,Find,Undo,SelectAll,Scayt,Form,Checkbox,Radio,TextField,Textarea,Select,Button,ImageButton,HiddenField,NewPage,PasteFromWord,Blockquote,BidiLtr,BidiRtl' \ No newline at end of file diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbar.js b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbar.js deleted file mode 100644 index 305865546..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbar.js +++ /dev/null @@ -1,19 +0,0 @@ -CKEDITOR.editorConfig = function( config ) { - config.toolbarGroups = [ - { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, - { name: 'styles', groups: [ 'styles' ] }, - { name: 'editing', groups: [ 'find', 'selection', 'spellchecker', 'editing' ] }, - { name: 'forms', groups: [ 'forms' ] }, - { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, - { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi', 'paragraph' ] }, - { name: 'insert', groups: [ 'insert' ] }, - { name: 'links', groups: [ 'links' ] }, - { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, - { name: 'colors', groups: [ 'colors' ] }, - { name: 'tools', groups: [ 'tools' ] }, - { name: 'others', groups: [ 'others' ] }, - { name: 'about', groups: [ 'about' ] } - ]; - - config.removeButtons = 'Source,Save,Templates,Strike,Subscript,Superscript,CopyFormatting,Outdent,Indent,CreateDiv,JustifyLeft,JustifyCenter,JustifyRight,JustifyBlock,BidiLtr,BidiRtl,Language,Anchor,Flash,HorizontalRule,Smiley,SpecialChar,PageBreak,Iframe,Format,Font,FontSize,BGColor,TextColor,Maximize,ShowBlocks,About,Preview,Print,Redo,Replace,Find,Undo,SelectAll,Scayt,Form,Checkbox,Radio,TextField,Textarea,Select,Button,ImageButton,HiddenField,NewPage,PasteFromWord,Blockquote'; -}; \ No newline at end of file diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbarGroups.json b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbarGroups.json deleted file mode 100644 index a886c2791..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbarGroups.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "name": "document", - "groups": [ - "mode", - "document", - "doctools" - ] - }, - { - "name": "styles", - "groups": [ - "styles" - ] - }, - { - "name": "editing", - "groups": [ - "find", - "selection", - "spellchecker", - "editing" - ] - }, - { - "name": "forms", - "groups": [ - "forms" - ] - }, - { - "name": "basicstyles", - "groups": [ - "basicstyles", - "cleanup" - ] - }, - { - "name": "paragraph", - "groups": [ - "list", - "indent", - "blocks", - "align", - "bidi", - "paragraph" - ] - }, - { - "name": "insert", - "groups": [ - "insert" - ] - }, - { - "name": "links", - "groups": [ - "links" - ] - }, - { - "name": "clipboard", - "groups": [ - "clipboard", - "undo" - ] - }, - { - "name": "colors", - "groups": [ - "colors" - ] - }, - { - "name": "tools", - "groups": [ - "tools" - ] - }, - { - "name": "others", - "groups": [ - "others" - ] - }, - { - "name": "about", - "groups": [ - "about" - ] - } -] \ No newline at end of file diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/HtmlEditor.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/HtmlEditor.java deleted file mode 100644 index 68963f9b5..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/HtmlEditor.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.argeo.cms.e4.rap.parts; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import javax.annotation.PostConstruct; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.CmsException; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.nebula.widgets.richtext.RichTextEditor; -import org.eclipse.nebula.widgets.richtext.RichTextEditorConfiguration; -import org.eclipse.swt.SWT; -import org.eclipse.swt.browser.Browser; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Color; -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.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -public class HtmlEditor { - - @PostConstruct - public void createUI(Composite parent) { - String toolbarGroups; - String removeButtons; - try { - toolbarGroups = IOUtils.toString(HtmlEditor.class.getResourceAsStream("CkEditor-toolbarGroups.json"), - StandardCharsets.UTF_8); - removeButtons = IOUtils.toString(HtmlEditor.class.getResourceAsStream("CkEditor-removeButtons.js"), - StandardCharsets.UTF_8); - } catch (IOException e) { - throw new CmsException("Cannot configure toolbar", e); - } -// System.out.println(toolbarGroups); -// System.out.println(removeButtons); - RichTextEditorConfiguration richTextEditorConfig = new RichTextEditorConfiguration(); - richTextEditorConfig.setOption(RichTextEditorConfiguration.TOOLBAR_GROUPS, toolbarGroups); - richTextEditorConfig.setOption(RichTextEditorConfiguration.REMOVE_BUTTONS, removeButtons); -// richTextEditorConfig.setRemoveStyles(false); -// richTextEditorConfig.setRemovePasteFromWord(true); -// richTextEditorConfig.setRemovePasteText(false); - -// richTextEditorConfig.setToolbarCollapsible(true); -// richTextEditorConfig.setToolbarInitialExpanded(false); - - final Display display = parent.getDisplay(); - Composite composite = new Composite(parent, SWT.NONE); -// composite.setLayoutData(new Fill); - composite.setLayout(new GridLayout()); - final RichTextEditor richTextEditor = new RichTextEditor(composite, richTextEditorConfig, SWT.BORDER); - richTextEditor.setText("Google"); - GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); - richTextEditor.setLayoutData(layoutData); - richTextEditor.setBackground(new Color(display, 247, 247, 247)); - Composite toolbar = new Composite(composite, SWT.NONE); - toolbar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - toolbar.setLayout(new GridLayout(3, false)); - Button showContent = new Button(toolbar, SWT.PUSH); - showContent.setText("Show Content"); - showContent.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - showContent(parent, richTextEditor, false); - } - }); - Button showSource = new Button(toolbar, SWT.PUSH); - showSource.setText("Show Source"); - showSource.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - showContent(parent, richTextEditor, true); - } - }); - Button clearBtn = new Button(toolbar, SWT.NONE); - clearBtn.setText("Clear"); - clearBtn.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - richTextEditor.setText(""); - } - }); - - } - - private static void showContent(Composite parent, RichTextEditor editor, boolean source) { - int style = SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL; - final Shell content = new Shell(parent.getShell(), style); - content.setLayout(new GridLayout(1, true)); - String text = editor.getText(); - if (source) { - content.setText("Rich Text Source"); - Text viewer = new Text(content, SWT.MULTI | SWT.WRAP); - viewer.setLayoutData(new GridData(400, 400)); - viewer.setText(text); - viewer.setEditable(false); - } else { - content.setText("Rich Text"); - Browser viewer = new Browser(content, SWT.NONE); - viewer.setLayoutData(new GridData(400, 400)); - viewer.setText(text); - viewer.setEnabled(false); - } - Button ok = new Button(content, SWT.PUSH); - ok.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false, false)); - ok.setText("OK"); - ok.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - content.dispose(); - } - }); - content.setDefaultButton(ok); - content.pack(); - Display display = parent.getDisplay(); - int left = (display.getClientArea().width / 2) - (content.getBounds().width / 2); - content.setLocation(left, 40); - content.open(); - ok.setFocus(); - } - -} diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/test.json b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/test.json deleted file mode 100644 index eed3f0e87..000000000 --- a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/test.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "firstName": "John", - "lastName": "Smith", - "isAlive": true, - "age": 27, - "address": { - "streetAddress": "21 2nd Street", - "city": "New York", - "state": "NY", - "postalCode": "10021-3100" - }, - "phoneNumbers": [ - { - "type": "home", - "number": "212 555-1234" - }, - { - "type": "office", - "number": "646 555-4567" - }, - { - "type": "mobile", - "number": "123 456-7890" - } - ], - "children": [], - "spouse": null -} diff --git a/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml b/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml index 8653f09df..3fd7fdafb 100644 --- a/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml +++ b/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml @@ -1,6 +1,6 @@ - + diff --git a/org.argeo.cms.e4/OSGI-INF/homeRepository.xml b/org.argeo.cms.e4/OSGI-INF/homeRepository.xml index c03e62e76..65690f262 100644 --- a/org.argeo.cms.e4/OSGI-INF/homeRepository.xml +++ b/org.argeo.cms.e4/OSGI-INF/homeRepository.xml @@ -1,6 +1,6 @@ - + diff --git a/org.argeo.cms.e4/bnd.bnd b/org.argeo.cms.e4/bnd.bnd index 5e64fedef..8714dab79 100644 --- a/org.argeo.cms.e4/bnd.bnd +++ b/org.argeo.cms.e4/bnd.bnd @@ -11,5 +11,5 @@ javax.jcr.nodetype,\ org.argeo.cms,\ org.eclipse.core.commands.common,\ org.eclipse.jface.window,\ -org.argeo.cms.widgets.auth,\ +org.argeo.cms.ui.widgets.auth,\ * diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java new file mode 100644 index 000000000..c42a02a14 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java @@ -0,0 +1,33 @@ +package org.argeo.cms.e4; + +import org.argeo.cms.CmsException; +import org.eclipse.e4.core.contexts.ContextFunction; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.core.di.IInjector; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** An Eclipse 4 {@link ContextFunction} based on an OSGi filter. */ +public class OsgiFilterContextFunction extends ContextFunction { + + private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext(); + + @Override + public Object compute(IEclipseContext context, String contextKey) { + ServiceReference[] srs; + try { + srs = bc.getServiceReferences((String) null, contextKey); + } catch (InvalidSyntaxException e) { + throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e); + } + if (srs == null || srs.length == 0) { + return IInjector.NOT_A_VALUE; + } else { + // return the first one + return bc.getService(srs[0]); + } + } + +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/contexts/OsgiFilterContextFunction.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/contexts/OsgiFilterContextFunction.java deleted file mode 100644 index b0fdcc108..000000000 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/contexts/OsgiFilterContextFunction.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.cms.e4.contexts; - -import org.argeo.cms.CmsException; -import org.eclipse.e4.core.contexts.ContextFunction; -import org.eclipse.e4.core.contexts.IEclipseContext; -import org.eclipse.e4.core.di.IInjector; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -@SuppressWarnings("restriction") -public class OsgiFilterContextFunction extends ContextFunction { - - private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext(); - - @Override - public Object compute(IEclipseContext context, String contextKey) { - ServiceReference[] srs; - try { - srs = bc.getServiceReferences((String) null, contextKey); - } catch (InvalidSyntaxException e) { - throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e); - } - if (srs == null || srs.length == 0) { - return IInjector.NOT_A_VALUE; - } else { - // return the first one - return bc.getService(srs[0]); - } - } - -} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java index 59d7ed401..21ea955b4 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java @@ -24,10 +24,10 @@ import java.nio.file.spi.FileSystemProvider; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.eclipse.ui.fs.AdvancedFsBrowser; import org.argeo.eclipse.ui.fs.SimpleFsBrowser; -import org.argeo.node.NodeUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java index 378409399..51528abd4 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java @@ -16,10 +16,10 @@ import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; import javax.transaction.UserTransaction; +import org.argeo.api.security.CryptoKeyring; import org.argeo.cms.CmsException; import org.argeo.cms.ui.dialogs.CmsMessageDialog; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.node.security.CryptoKeyring; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.swt.SWT; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java index 3e328daa3..89a1bf5d1 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java @@ -31,6 +31,9 @@ import javax.jcr.observation.Event; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.CryptoKeyring; +import org.argeo.api.security.Keyring; import org.argeo.cms.CmsException; import org.argeo.cms.ui.jcr.JcrBrowserUtils; import org.argeo.cms.ui.jcr.NodeContentProvider; @@ -38,15 +41,12 @@ import org.argeo.cms.ui.jcr.NodeLabelProvider; import org.argeo.cms.ui.jcr.OsgiRepositoryRegister; import org.argeo.cms.ui.jcr.PropertiesContentProvider; import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.TreeParent; import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; -import org.argeo.eclipse.ui.jcr.utils.NodeViewerComparer; +import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.CryptoKeyring; -import org.argeo.node.security.Keyring; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.ui.services.EMenuService; @@ -112,7 +112,7 @@ public class JcrBrowserView { // Create the tree on top of the view Composite top = new Composite(sashForm, SWT.NONE); // GridLayout gl = new GridLayout(1, false); - top.setLayout(CmsUtils.noSpaceGridLayout()); + top.setLayout(CmsUiUtils.noSpaceGridLayout()); try { this.userSession = this.nodeRepository.login(NodeConstants.HOME); @@ -136,7 +136,7 @@ public class JcrBrowserView { // Create the property viewer on the bottom Composite bottom = new Composite(sashForm, SWT.NONE); - bottom.setLayout(CmsUtils.noSpaceGridLayout()); + bottom.setLayout(CmsUiUtils.noSpaceGridLayout()); propertiesViewer = createPropertiesViewer(bottom); sashForm.setWeights(getWeights()); diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java index 89c44ca83..aefa95dbf 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java @@ -26,15 +26,15 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.SimpleCredentials; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.api.security.Keyring; import org.argeo.cms.ArgeoNames; import org.argeo.cms.ArgeoTypes; import org.argeo.cms.e4.jcr.JcrBrowserView; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; -import org.argeo.node.security.Keyring; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.ui.model.application.ui.basic.MPart; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java new file mode 100644 index 000000000..c83183130 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java @@ -0,0 +1,42 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.Collection; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +abstract class AbstractOsgiComposite extends Composite { + private static final long serialVersionUID = -4097415973477517137L; + protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + protected final Log log = LogFactory.getLog(getClass()); + + public AbstractOsgiComposite(Composite parent, int style) { + super(parent, style); + parent.setLayout(CmsUiUtils.noSpaceGridLayout()); + setLayout(CmsUiUtils.noSpaceGridLayout()); + setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + initUi(style); + } + + protected abstract void initUi(int style); + + protected T getService(Class clazz) { + return bc.getService(bc.getServiceReference(clazz)); + } + + protected Collection> getServiceReferences(Class clazz, String filter) { + try { + return bc.getServiceReferences(clazz, filter); + } catch (InvalidSyntaxException e) { + throw new IllegalArgumentException("Filter " + filter + " is invalid", e); + } + } +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java new file mode 100644 index 000000000..2212b7b05 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java @@ -0,0 +1,575 @@ +package org.argeo.cms.e4.maintenance; + +import static org.eclipse.swt.SWT.RIGHT; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.LinkedHashMap; + +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsLink; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; + +public class Browse implements CmsUiProvider { + + // Some local constants to experiment. should be cleaned + private final static String BROWSE_PREFIX = "browse#"; + private final static int THUMBNAIL_WIDTH = 400; + private final static int COLUMN_WIDTH = 160; + private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); + + // keep a cache of the opened nodes + // Key is the path + private LinkedHashMap browserCols = new LinkedHashMap(); + private Composite nodeDisplayParent; + private Composite colViewer; + private ScrolledComposite scrolledCmp; + private Text parentPathTxt; + private Text filterTxt; + private Node currEdited; + + private String initialPath; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + if (context == null) + // return null; + throw new CmsException("Context cannot be null"); + GridLayout layout = CmsUiUtils.noSpaceGridLayout(); + layout.numColumns = 2; + parent.setLayout(layout); + + // Left + Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); + leftCmp.setLayoutData(CmsUiUtils.fillAll()); + createBrowserPart(leftCmp, context); + + // Right + nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); + GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); + gd.widthHint = THUMBNAIL_WIDTH; + nodeDisplayParent.setLayoutData(gd); + createNodeView(nodeDisplayParent, context); + + // INIT + setEdited(context); + initialPath = context.getPath(); + + // Workaround we don't yet manage the delete to display parent of the + // initial context node + + return null; + } + + private void createBrowserPart(Composite parent, Node context) throws RepositoryException { + GridLayout layout = CmsUiUtils.noSpaceGridLayout(); + parent.setLayout(layout); + Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(CmsUiUtils.fillWidth()); + + // top filter + addFilterPanel(filterCmp); + + // scrolled composite + scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); + scrolledCmp.setLayoutData(CmsUiUtils.fillAll()); + scrolledCmp.setExpandVertical(true); + scrolledCmp.setExpandHorizontal(true); + scrolledCmp.setShowFocusedControl(true); + + colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); + scrolledCmp.setContent(colViewer); + scrolledCmp.addControlListener(new ControlAdapter() { + private static final long serialVersionUID = 6589392045145698201L; + + @Override + public void controlResized(ControlEvent e) { + Rectangle r = scrolledCmp.getClientArea(); + scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); + } + }); + initExplorer(colViewer, context); + } + + private Control initExplorer(Composite parent, Node context) throws RepositoryException { + parent.setLayout(CmsUiUtils.noSpaceGridLayout()); + createBrowserColumn(parent, context); + return null; + } + + private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { + // TODO style is not correctly managed. + FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); + // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style()); + table.filterList("*"); + table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); + browserCols.put(context.getPath(), table); + return null; + } + + public void addFilterPanel(Composite parent) { + + parent.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(2, false))); + + // Text Area for the filter + parentPathTxt = new Text(parent, SWT.NO_FOCUS); + parentPathTxt.setEditable(false); + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Filter current list"); + filterTxt.setLayoutData(CmsUiUtils.fillWidth()); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 7709303319740056286L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -4523394262771183968L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + FilterEntitiesVirtualTable currTable = null; + if (currEdited != null) { + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + currTable = table; + } + + try { + if (e.keyCode == SWT.ARROW_DOWN) + currTable.setFocus(); + else if (e.keyCode == SWT.BS) { + if (filterTxt.getText().equals("") + && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { + setEdited(currEdited.getParent()); + e.doit = false; + filterTxt.setFocus(); + } + } else if (e.keyCode == SWT.TAB && !shiftPressed) { + if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { + setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); + } + filterTxt.setFocus(); + e.doit = false; + } + } catch (RepositoryException e1) { + throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " + + filterTxt.getText(), e1); + } + + } + }); + } + + private void setEdited(Node node) { + try { + currEdited = node; + CmsUiUtils.clear(nodeDisplayParent); + createNodeView(nodeDisplayParent, currEdited); + nodeDisplayParent.layout(); + refreshFilters(node); + refreshBrowser(node); + } catch (RepositoryException re) { + throw new CmsException("Unable to update browser for " + node, re); + } + } + + private void refreshFilters(Node node) throws RepositoryException { + String currNodePath = node.getPath(); + parentPathTxt.setText(currNodePath); + filterTxt.setText(""); + filterTxt.getParent().layout(); + } + + private void refreshBrowser(Node node) throws RepositoryException { + + // Retrieve + String currNodePath = node.getPath(); + String currParPath = ""; + if (!"/".equals(currNodePath)) + currParPath = JcrUtils.parentPath(currNodePath); + if ("".equals(currParPath)) + currParPath = "/"; + + Object[][] colMatrix = new Object[browserCols.size()][2]; + + int i = 0, j = -1, k = -1; + for (String path : browserCols.keySet()) { + colMatrix[i][0] = path; + colMatrix[i][1] = browserCols.get(path); + if (j >= 0 && k < 0 && !currNodePath.equals("/")) { + boolean leaveOpened = path.startsWith(currNodePath); + + // workaround for same name siblings + // fix me weird side effect when we go left or click on anb + // already selected, unfocused node + if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 + || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) + leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); + + if (!leaveOpened) + k = i; + } + if (currParPath.equals(path)) + j = i; + i++; + } + + if (j >= 0 && k >= 0) + // remove useless cols + for (int l = i - 1; l >= k; l--) { + browserCols.remove(colMatrix[l][0]); + ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); + } + + // Remove disposed columns + // TODO investigate and fix the mechanism that leave them there after + // disposal + if (browserCols.containsKey(currNodePath)) { + FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); + if (currCol.isDisposed()) + browserCols.remove(currNodePath); + } + + if (!browserCols.containsKey(currNodePath)) + createBrowserColumn(colViewer, node); + + colViewer.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); + // colViewer.pack(); + colViewer.layout(); + // also resize the scrolled composite + scrolledCmp.layout(); + scrolledCmp.getShowFocusedControl(); + // colViewer.getParent().layout(); + // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { + // } else { + // } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currEdited != null) { + String filter = filterTxt.getText() + "*"; + FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); + if (table != null && !table.isDisposed()) + table.filterList(filter); + } + + } + + private String getPath(Node node) { + try { + return node.getPath(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get path for node " + node, e); + } + } + + private Point imageWidth = new Point(250, 0); + + /** + * Recreates the content of the box that displays information about the current + * selected node. + */ + private Control createNodeView(Composite parent, Node context) throws RepositoryException { + + parent.setLayout(new GridLayout(2, false)); + + if (isImg(context)) { + EditableImage image = new Img(parent, RIGHT, context, imageWidth); + image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); + } + + // Name and primary type + Label contextL = new Label(parent, SWT.NONE); + CmsUiUtils.markup(contextL); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); + + // Children + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); + } + + // Properties + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private boolean isImg(Node node) throws RepositoryException { + // TODO support images + return false; +// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); + } + + private String getPropAsString(Property property) throws RepositoryException { + String result = ""; + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } + + /** Almost canonical implementation of a table that display entities */ + private class FilterEntitiesVirtualTable extends Composite { + private static final long serialVersionUID = 8798147431706283824L; + + // Context + private Node context; + + // UI Objects + private TableViewer entityViewer; + + // enable management of multiple columns + Node getNode() { + return context; + } + + @Override + public boolean setFocus() { + if (entityViewer.getTable().isDisposed()) + return false; + if (entityViewer.getSelection().isEmpty()) { + Object first = entityViewer.getElementAt(0); + if (first != null) { + entityViewer.setSelection(new StructuredSelection(first), true); + } + } + return entityViewer.getTable().setFocus(); + } + + void filterList(String filter) { + try { + NodeIterator nit = context.getNodes(filter); + refreshFilteredList(nit); + } catch (RepositoryException e) { + throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); + } + + } + + public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { + super(parent, SWT.NO_FOCUS); + this.context = context; + populate(); + } + + protected void populate() { + Composite parent = this; + GridLayout layout = CmsUiUtils.noSpaceGridLayout(); + + this.setLayout(layout); + createTableViewer(parent); + } + + private void createTableViewer(final Composite parent) { + // the list + // We must limit the size of the table otherwise the full list is + // loaded + // before the layout happens + Composite listCmp = new Composite(parent, SWT.NO_FOCUS); + GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); + gd.widthHint = COLUMN_WIDTH; + listCmp.setLayoutData(gd); + listCmp.setLayout(CmsUiUtils.noSpaceGridLayout()); + + entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); + Table table = entityViewer.getTable(); + + table.setLayoutData(CmsUiUtils.fillAll()); + table.setLinesVisible(true); + table.setHeaderVisible(false); + CmsUiUtils.markup(table); + + CmsUiUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + + // first column + TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); + TableColumn tcol = column.getColumn(); + tcol.setWidth(COLUMN_WIDTH); + tcol.setResizable(true); + column.setLabelProvider(new SimpleNameLP()); + + entityViewer.setContentProvider(new MyLazyCP(entityViewer)); + entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + if (selection.isEmpty()) + return; + else + setEdited((Node) selection.getFirstElement()); + + } + }); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -330694313896036230L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + + IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); + Node selected = null; + if (!selection.isEmpty()) + selected = ((Node) selection.getFirstElement()); + try { + if (e.keyCode == SWT.ARROW_RIGHT) { + if (selected != null) { + setEdited(selected); + browserCols.get(selected.getPath()).setFocus(); + } + } else if (e.keyCode == SWT.ARROW_LEFT) { + try { + selected = getNode().getParent(); + String newPath = selected.getPath(); // getNode().getParent() + setEdited(selected); + if (browserCols.containsKey(newPath)) + browserCols.get(newPath).setFocus(); + } catch (ItemNotFoundException ie) { + // root silent + } + } + } catch (RepositoryException ie) { + throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, + ie); + } + } + }); + } + + private class MyLazyCP implements ILazyContentProvider { + private static final long serialVersionUID = 1L; + private TableViewer viewer; + private Object[] elements; + + public MyLazyCP(TableViewer viewer) { + this.viewer = viewer; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + public void updateElement(int index) { + viewer.replace(elements[index], index); + } + } + + protected void refreshFilteredList(NodeIterator children) { + Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); + entityViewer.setInput(rows); + entityViewer.setItemCount(rows.length); + entityViewer.refresh(); + } + + public class SimpleNameLP extends ColumnLabelProvider { + private static final long serialVersionUID = 2465059387875338553L; + + @Override + public String getText(Object element) { + if (element instanceof Node) { + Node curr = ((Node) element); + try { + return curr.getName(); + } catch (RepositoryException e) { + throw new CmsException("Unable to get name for" + curr); + } + } + return super.getText(element); + } + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java new file mode 100644 index 000000000..7e8a99196 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java @@ -0,0 +1,48 @@ +package org.argeo.cms.e4.maintenance; + +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.ServiceReference; +import org.osgi.service.http.HttpService; +import org.osgi.service.useradmin.UserAdmin; + +class ConnectivityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public ConnectivityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + StringBuffer text = new StringBuffer(); + text.append("Provided Servers
"); + + ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); + if (userAdminRef != null) { + // FIXME use constants + Object httpPort = userAdminRef.getProperty("http.port"); + Object httpsPort = userAdminRef.getProperty("https.port"); + if (httpPort != null) + text.append("http ").append(httpPort).append("
"); + if (httpsPort != null) + text.append("https ").append(httpsPort).append("
"); + + } + + text.append("
"); + text.append("Referenced Servers
"); + + Label label = new Label(this, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsUiUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java new file mode 100644 index 000000000..a750e9541 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java @@ -0,0 +1,139 @@ +package org.argeo.cms.e4.maintenance; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; + +import org.apache.jackrabbit.core.RepositoryContext; +import org.apache.jackrabbit.core.config.RepositoryConfig; +import org.argeo.api.NodeConstants; +import org.argeo.cms.ui.util.CmsUiUtils; +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.Label; +import org.osgi.framework.ServiceReference; + +class DataDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public DataDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { +// try { +// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); +// Configuration[] confs = confAdmin.listConfigurations( +// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); +// if (confs == null || confs.length == 0) { +// Group buttonGroup = new Group(parent, SWT.NONE); +// buttonGroup.setText("Repository Type"); +// buttonGroup.setLayout(new GridLayout(2, true)); +// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); +// +// SelectionListener selectionListener = new SelectionAdapter() { +// private static final long serialVersionUID = 6247064348421088092L; +// +// public void widgetSelected(SelectionEvent event) { +// Button radio = (Button) event.widget; +// if (!radio.getSelection()) +// return; +// log.debug(event); +// JackrabbitType nodeType = (JackrabbitType) radio.getData(); +// if (log.isDebugEnabled()) +// log.debug(" selected = " + nodeType.name()); +// }; +// }; +// +// for (JackrabbitType nodeType : JackrabbitType.values()) { +// Button radio = new Button(buttonGroup, SWT.RADIO); +// radio.setText(nodeType.name()); +// radio.setData(nodeType); +// if (nodeType.equals(JackrabbitType.localfs)) +// radio.setSelection(true); +// radio.addSelectionListener(selectionListener); +// } +// +// } else if (confs.length == 1) { +// +// } else { +// throw new CmsException("Multiple repos not yet supported"); +// } +// } catch (Exception e) { +// throw new CmsException("Cannot initialize UI", e); +// } + + } + + private void initCurrentUi(Composite parent) { + parent.setLayout(new GridLayout()); + Collection> contexts = getServiceReferences(RepositoryContext.class, + "(" + NodeConstants.CN + "=*)"); + StringBuffer text = new StringBuffer(); + text.append("Jackrabbit Repositories
"); + for (ServiceReference sr : contexts) { + RepositoryContext repositoryContext = bc.getService(sr); + String alias = sr.getProperty(NodeConstants.CN).toString(); + String rootNodeId = repositoryContext.getRootNodeId().toString(); + RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); + Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); + // TODO check data store + + text.append("" + alias + "
"); + text.append("rootNodeId: " + rootNodeId + "
"); + try { + FileStore fileStore = Files.getFileStore(repoHomePath); + text.append("partition: " + fileStore.toString() + "
"); + text.append( + percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); + } catch (IOException e) { + log.error("Cannot check fileStore for " + repoHomePath, e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsUiUtils.markup(label); + label.setText("" + text.toString() + ""); + } + + private String humanReadable(long bytes) { + long mb = bytes / (1024 * 1024); + return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; + } + + private String percentUsed(FileStore fs) throws IOException { + long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); + long percent = used * 100 / fs.getTotalSpace(); + if (log.isTraceEnabled()) { + // output identical to `df -B 1`) + log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); + } + String span; + if (percent < 80) + span = ""; + else if (percent < 95) + span = ""; + else + span = ""; + return span + percent + "%"; + } + + protected boolean isDeployed() { + return bc.getServiceReference(RepositoryContext.class) != null; + } + +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java new file mode 100644 index 000000000..a746b3c46 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java @@ -0,0 +1,95 @@ +package org.argeo.cms.e4.maintenance; + +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeDeployment; +import org.argeo.api.NodeState; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +class DeploymentEntryPoint { + private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); + + protected void createContents(Composite parent) { + // FIXME manage authentication if needed + // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) + // return; + + // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + if (isDesktop()) { + parent.setLayout(new GridLayout(2, true)); + } else { + // TODO add scrolling + parent.setLayout(new GridLayout(1, true)); + } + + initHighLevelSummary(parent); + + Group securityGroup = createHighLevelGroup(parent, "Security"); + securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new SecurityDeploymentUi(securityGroup, SWT.NONE); + + Group dataGroup = createHighLevelGroup(parent, "Data"); + dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + new DataDeploymentUi(dataGroup, SWT.NONE); + + Group logGroup = createHighLevelGroup(parent, "Notifications"); + logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + new LogDeploymentUi(logGroup, SWT.NONE); + + Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); + new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); + connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); + + } + + private void initHighLevelSummary(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); + if (isDesktop()) + gridData.horizontalSpan = 3; + composite.setLayoutData(gridData); + composite.setLayout(new FillLayout()); + + ServiceReference nodeStateRef = bc.getServiceReference(NodeState.class); + if (nodeStateRef == null) + throw new IllegalStateException("No CMS state available"); + NodeState nodeState = bc.getService(nodeStateRef); + ServiceReference nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class); + Label label = new Label(composite, SWT.WRAP); + CmsUiUtils.markup(label); + if (nodeDeploymentRef == null) { + label.setText("Not yet deployed on
" + nodeState.getHostname() + "
, please configure below."); + } else { + Object stateUuid = nodeStateRef.getProperty(NodeConstants.CN); + NodeDeployment nodeDeployment = bc.getService(nodeDeploymentRef); + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); + calendar.setTimeZone(TimeZone.getDefault()); + label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid + + ", available since " + calendar.getTime() + ""); + } + } + + private static Group createHighLevelGroup(Composite parent, String text) { + Group group = new Group(parent, SWT.NONE); + group.setText(text); + CmsUiUtils.markup(group); + return group; + } + + private boolean isDesktop() { + return true; + } +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java new file mode 100644 index 000000000..05923fb47 --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java @@ -0,0 +1,73 @@ +package org.argeo.cms.e4.maintenance; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogReaderService; + +class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { + private static final long serialVersionUID = 590221539553514693L; + + private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); + + private Display display; + private Text logDisplay; + + public LogDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + LogReaderService logReader = getService(LogReaderService.class); + // FIXME use server push + // logReader.addLogListener(this); + this.display = getDisplay(); + this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); + logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + CmsUiUtils.markup(logDisplay); + Enumeration logEntries = (Enumeration) logReader.getLog(); + while (logEntries.hasMoreElements()) + logDisplay.append(printEntry(logEntries.nextElement())); + } + + private String printEntry(LogEntry entry) { + StringBuilder sb = new StringBuilder(); + GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); + calendar.setTimeInMillis(entry.getTime()); + sb.append(dateFormat.format(calendar.getTime())).append(' '); + sb.append(entry.getMessage()); + sb.append('\n'); + return sb.toString(); + } + + @Override + public void logged(LogEntry entry) { + if (display.isDisposed()) + return; + display.asyncExec(() -> { + if (logDisplay.isDisposed()) + return; + logDisplay.append(printEntry(entry)); + }); + display.wake(); + } + + // @Override + // public void dispose() { + // super.dispose(); + // getService(LogReaderService.class).removeLogListener(this); + // } +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java new file mode 100644 index 000000000..df1be51ad --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java @@ -0,0 +1,10 @@ +package org.argeo.cms.e4.maintenance; + +/** Specific styles used by the various maintenance pages . */ +public interface MaintenanceStyles { + // General + public final static String PREFIX = "maintenance_"; + + // Browser + public final static String BROWSER_COLUMN = "browser_column"; + } diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java new file mode 100644 index 000000000..122f5cfaa --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java @@ -0,0 +1,30 @@ +package org.argeo.cms.e4.maintenance; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class NonAdminPage implements CmsUiProvider{ + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayoutData(CmsUiUtils.fillAll()); + body.setLayout(new GridLayout()); + Label label = new Label(body, SWT.NONE); + label.setText("You should be an admin to perform maintenance operations. " + + "Are you sure you are logged in?"); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + return null; + } + +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java new file mode 100644 index 000000000..4edbf565a --- /dev/null +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java @@ -0,0 +1,85 @@ +package org.argeo.cms.e4.maintenance; + +import java.net.URI; + +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.useradmin.Role; +import org.osgi.service.useradmin.UserAdmin; + +class SecurityDeploymentUi extends AbstractOsgiComposite { + private static final long serialVersionUID = 590221539553514693L; + + public SecurityDeploymentUi(Composite parent, int style) { + super(parent, style); + } + + @Override + protected void initUi(int style) { + if (isDeployed()) { + initCurrentUi(this); + } else { + initNewUi(this); + } + } + + private void initNewUi(Composite parent) { + new Label(parent, SWT.NONE).setText("Security is not configured"); + } + + private void initCurrentUi(Composite parent) { + ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); + UserAdmin userAdmin = bc.getService(userAdminRef); + StringBuffer text = new StringBuffer(); + text.append("Domains
"); + domains: for (String key : userAdminRef.getPropertyKeys()) { + if (!key.startsWith("/")) + continue domains; + URI uri; + try { + uri = new URI(key); + } catch (Exception e) { + // ignore non URI keys + continue domains; + } + + String rootDn = uri.getPath().substring(1, uri.getPath().length()); + // FIXME make reading query options more robust, using utils + boolean readOnly = uri.getQuery().equals("readOnly=true"); + if (readOnly) + text.append(""); + else + text.append(""); + + text.append(rootDn); + text.append("
"); + try { + Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); + long userCount = 0; + long groupCount = 0; + for (Role role : roles) { + if (role.getType() == Role.USER) + userCount++; + else + groupCount++; + } + text.append(" " + userCount + " users, " + groupCount +" groups.
"); + } catch (InvalidSyntaxException e) { + log.error("Invalid syntax", e); + } + } + Label label = new Label(parent, SWT.NONE); + label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); + CmsUiUtils.markup(label); + label.setText(text.toString()); + } + + protected boolean isDeployed() { + return bc.getServiceReference(UserAdmin.class) != null; + } +} diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java index 3c9347a71..c957656c6 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java @@ -282,7 +282,7 @@ public abstract class AbstractRoleEditor { Text text = new Text(parent, SWT.BORDER); text.setText(value); text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - // CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); return text; } @@ -295,7 +295,7 @@ public abstract class AbstractRoleEditor { text.setText(value); text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false)); text.setEditable(false); - // CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); return text; } diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java index a2c920aa8..12cc53999 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java @@ -15,10 +15,10 @@ */ package org.argeo.cms.e4.users; +import static org.argeo.api.NodeInstance.WORKGROUP; import static org.argeo.cms.auth.UserAdminUtils.setProperty; import static org.argeo.naming.LdapAttrs.businessCategory; import static org.argeo.naming.LdapAttrs.description; -import static org.argeo.node.NodeInstance.WORKGROUP; import java.util.ArrayList; import java.util.Iterator; @@ -34,6 +34,9 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.transaction.UserTransaction; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeInstance; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.cms.auth.UserAdminUtils; import org.argeo.cms.e4.users.providers.CommonNameLP; @@ -42,15 +45,12 @@ import org.argeo.cms.e4.users.providers.RoleIconLP; import org.argeo.cms.e4.users.providers.UserFilter; import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.jcr.JcrUtils; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeInstance; -import org.argeo.node.NodeUtils; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ToolBarManager; @@ -161,7 +161,7 @@ public class GroupEditor extends AbstractRoleEditor { // GridLayout layout = new GridLayout(5, false); GridLayout layout = new GridLayout(2, false); body.setLayout(layout); - body.setLayoutData(CmsUtils.fillWidth()); + body.setLayoutData(CmsUiUtils.fillWidth()); String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name()); createReadOnlyLT(body, "Name", cn); @@ -302,7 +302,7 @@ public class GroupEditor extends AbstractRoleEditor { ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsUtils.fillWidth()); + toolBar.setLayoutData(CmsUiUtils.fillWidth()); toolBarManager.add(action); toolBarManager.update(true); @@ -568,7 +568,7 @@ public class GroupEditor extends AbstractRoleEditor { // lbl.setFont(EclipseUiUtils.getBoldFont(parent)); // Text text = toolkit.createText(parent, value, SWT.BORDER); // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); - // CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); // return text; // } // @@ -580,7 +580,7 @@ public class GroupEditor extends AbstractRoleEditor { // Text text = toolkit.createText(parent, value, SWT.NONE); // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); // text.setEditable(false); - // CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); + // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT); // return text; // } diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java index e9a4a25f1..6f0828dd4 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java @@ -24,6 +24,7 @@ import javax.inject.Inject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.e4.users.providers.CommonNameLP; @@ -43,7 +44,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.NodeConstants; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java index 5eecaac21..951dd2415 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -12,8 +12,8 @@ import java.util.TreeSet; import javax.transaction.Status; import javax.transaction.UserTransaction; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.UserAdminConf; import org.osgi.service.useradmin.UserAdmin; import org.osgi.service.useradmin.UserAdminEvent; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java index ed797c58a..7513102fe 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java @@ -10,6 +10,7 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.UserAdminUtils; @@ -22,7 +23,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.NodeConstants; import org.eclipse.jface.dialogs.IPageChangeProvider; import org.eclipse.jface.dialogs.IPageChangedListener; import org.eclipse.jface.dialogs.MessageDialog; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java index a35499984..3d67b8c3b 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java @@ -28,6 +28,7 @@ import java.util.List; import javax.inject.Inject; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.UserAdminUtils; import org.argeo.cms.e4.users.providers.CommonNameLP; @@ -37,12 +38,11 @@ import org.argeo.cms.e4.users.providers.UserFilter; import org.argeo.cms.ui.eclipse.forms.AbstractFormPart; //import org.argeo.cms.ui.eclipse.forms.FormToolkit; import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ToolBarManager; @@ -119,7 +119,7 @@ public class UserEditor extends AbstractRoleEditor { // mainLayout.marginRight = 10; body.setLayout(mainLayout); // body.getParent().setLayout(new GridLayout()); - // body.setLayoutData(CmsUtils.fillAll()); + // body.setLayoutData(CmsUiUtils.fillAll()); User user = getDisplayedUser(); appendOverviewPart(body, user); // Remove to ability to force the password for his own user. The user @@ -293,7 +293,7 @@ public class UserEditor extends AbstractRoleEditor { // Composite body= parent; Composite body = new Composite(parent, SWT.BORDER); body.setLayout(new GridLayout()); - body.setLayoutData(CmsUtils.fillAll()); + body.setLayoutData(CmsUiUtils.fillAll()); // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN); @@ -361,7 +361,7 @@ public class UserEditor extends AbstractRoleEditor { Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC); ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT); ToolBar toolBar = toolBarManager.createControl(body); - toolBar.setLayoutData(CmsUtils.fillWidth()); + toolBar.setLayoutData(CmsUiUtils.fillWidth()); toolBarManager.add(action); toolBarManager.update(true); return userViewerCmp; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java index 377605f5d..a47ca2dc3 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java @@ -22,6 +22,7 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.e4.users.providers.CommonNameLP; @@ -34,7 +35,6 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.NodeConstants; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java index 859081e94..d9a75b89e 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java @@ -1,10 +1,10 @@ package org.argeo.cms.e4.users.providers; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeInstance; import org.argeo.cms.auth.UserAdminUtils; import org.argeo.cms.e4.users.SecurityAdminImages; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeInstance; import org.eclipse.swt.graphics.Image; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java index 7be078ac5..e090fe251 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java @@ -2,9 +2,9 @@ package org.argeo.cms.e4.users.providers; import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.UserAdminUtils; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.osgi.service.useradmin.User; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java deleted file mode 100644 index ece0be36b..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.cms.forms; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.viewers.EditablePart; -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 - EditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String type; - private String message; - private boolean readOnly; - - public EditableLink(Composite parent, int style, Node node, - String propertyName, String type, String message) - throws RepositoryException { - super(parent, style, node, propertyName, message); - this.message = message; - this.type = type; - - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } 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 diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java deleted file mode 100644 index 859f64b28..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java +++ /dev/null @@ -1,259 +0,0 @@ -package org.argeo.cms.forms; - -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.viewers.EditablePart; -import org.argeo.cms.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.dialogs.MessageDialog; -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 StyledControl implements EditablePart { - private static final long serialVersionUID = -7044614381252178595L; - - private String 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 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, Node node, String propertyName, List values, - String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) - throws RepositoryException { - super(parent, style, node, true); - - 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 getValues() { - return values; - } - - public void setValues(List 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(CmsUtils.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); - CmsUtils.markup(label); - CmsUtils.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); - CmsUtils.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); - CmsUtils.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) - MessageDialog.openError(this.getShell(), "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); - CmsUtils.style(lbl, style); - CmsUtils.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() { - getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - getControl().setData(STYLE, FormStyle.propertyMessage.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java deleted file mode 100644 index 928a28508..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.argeo.cms.forms; - -import java.text.DateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.viewers.EditablePart; -import org.argeo.cms.widgets.StyledControl; -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 StyledControl implements EditablePart { - private static final long serialVersionUID = 2500215515778162468L; - - // Context - private String propertyName; - private String message; - private DateFormat 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, Node node, - String propertyName, String message, DateFormat dateFormat) - throws RepositoryException { - super(parent, style, node, false); - - this.propertyName = propertyName; - this.message = message; - this.dateFormat = dateFormat; - - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(dateFormat.format(node.getProperty(propertyName) - .getDate().getTime())); - } 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()) - getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(dateTxt.getText())) - getControl().setData(STYLE, FormStyle.propertyMessage.style()); - else - getControl().setData(STYLE, FormStyle.propertyText.style()); - super.stopEditing(); - } - - public String 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(CmsUtils.fillWidth()); - CmsUtils.style(lbl, style); - CmsUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - private Control createCustomEditableControl(Composite box, String style) { - box.setLayoutData(CmsUtils.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); - CmsUtils.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); - CmsUtils.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 - CmsUtils.markup(CalendarPopup.this); - CmsUtils.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); - 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.getTime()); - dateTxt.setText(dateStr); - } - - protected void populate() { - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - dateTimeCtl = new DateTime(this, SWT.CALENDAR); - dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); - - Calendar calendar = FormUtils.parseDate(dateFormat, - dateTxt.getText()); - - if (calendar != null) - dateTimeCtl.setDate(calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.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 diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java deleted file mode 100644 index dd3ff29dc..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.argeo.cms.forms; - -import static org.argeo.cms.forms.FormStyle.propertyMessage; -import static org.argeo.cms.forms.FormStyle.propertyText; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.viewers.EditablePart; -import org.argeo.cms.widgets.EditableText; -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 - EditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String 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, Node node, - String propertyName, String message) throws RepositoryException { - super(parent, style, node, true); - - this.propertyName = propertyName; - this.message = message; - - if (node.hasProperty(propertyName)) { - this.setStyle(propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } 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("
", "\n")); - } - } - - public synchronized void startEditing() { - getControl().setData(STYLE, propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) - getControl().setData(STYLE, propertyMessage.style()); - else - getControl().setData(STYLE, propertyText.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java deleted file mode 100644 index 18df3e47f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.argeo.cms.forms; - -/** Constants used in the various CMS Forms */ -public interface FormConstants { - // DATAKEYS - public final static String LINKED_VALUE = "LinkedValue"; -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java deleted file mode 100644 index 92ce9da0c..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.argeo.cms.forms; - -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; - -import org.argeo.cms.ui.CmsEditable; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; - -/** Add life cycle management abilities to an editable form page */ -public class FormEditorHeader implements SelectionListener, Observer { - private static final long serialVersionUID = 7392898696542484282L; - - // private final Node context; - private final CmsEditable cmsEditable; - private Button publishBtn; - - // Should we provide here the ability to switch from read only to edition - // mode? - // private Button editBtn; - // private boolean readOnly; - - // TODO add information about the current node status, typically if it is - // dirty or not - - private Composite parent; - private Composite display; - private Object layoutData; - - public FormEditorHeader(Composite parent, int style, Node context, - CmsEditable cmsEditable) { - this.cmsEditable = cmsEditable; - this.parent = parent; - // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - // this.context = context; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - refresh(); - } - - public void setLayoutData(Object layoutData) { - this.layoutData = layoutData; - if (display != null && !display.isDisposed()) - display.setLayoutData(layoutData); - } - - protected void refresh() { - if (display != null && !display.isDisposed()) - display.dispose(); - - display = new Composite(parent, SWT.NONE); - display.setLayoutData(layoutData); - - CmsUtils.style(display, FormStyle.header.style()); - display.setBackgroundMode(SWT.INHERIT_FORCE); - - display.setLayout(CmsUtils.noSpaceGridLayout()); - - publishBtn = createSimpleBtn(display, getPublishButtonLabel()); - display.moveAbove(null); - parent.layout(); - } - - private Button createSimpleBtn(Composite parent, String label) { - Button button = new Button(parent, SWT.FLAT | SWT.PUSH); - button.setText(label); - CmsUtils.style(button, FormStyle.header.style()); - button.addSelectionListener(this); - return button; - } - - private String getPublishButtonLabel() { - // Rather check if the current node differs from what has been - // previously committed - // For the time being, we always reach here, the underlying CmsEditable - // is always editing. - if (cmsEditable.isEditing()) - return " Publish "; - else - return " Edit "; - } - - @Override - public void widgetSelected(SelectionEvent e) { - if (e.getSource() == publishBtn) { - // For the time being, the underlying CmsEditable - // is always editing when we reach this point - if (cmsEditable.isEditing()) { - // we always leave the node in a check outed state - cmsEditable.stopEditing(); - cmsEditable.startEditing(); - } else { - cmsEditable.startEditing(); - } - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) { - refresh(); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java deleted file mode 100644 index d2af709a8..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java +++ /dev/null @@ -1,611 +0,0 @@ -package org.argeo.cms.forms; - -import java.io.IOException; -import java.io.InputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.ValueFormatException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsEditable; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.viewers.AbstractPageViewer; -import org.argeo.cms.viewers.EditablePart; -import org.argeo.cms.viewers.Section; -import org.argeo.cms.viewers.SectionPart; -import org.argeo.cms.widgets.EditableImage; -import org.argeo.cms.widgets.Img; -import org.argeo.cms.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.dialogs.MessageDialog; -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 Log log = LogFactory.getLog(FormPageViewer.class); - private static final long serialVersionUID = 5277789504209413500L; - - private final Section mainSection; - - // TODO manage within the CSS - private int labelColWidth = 150; - private int rowLayoutHSpacing = 8; - - // Context cached in the viewer - // The reference to translate from text to calendar and reverse - private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); - private CmsImageManager imageManager; - private FileUploadListener fileUploadListener; - - public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { - super(mainSection, style, cmsEditable); - this.mainSection = mainSection; - - if (getCmsEditable().canEdit()) { - fileUploadListener = new FUL(); - } - } - - @Override - protected void prepare(EditablePart part, Object caretPosition) { - if (part instanceof Img) { - ((Img) part).setFileUploadListener(fileUploadListener); - } - } - - /** To be overridden.Save the edited part. */ - protected void save(EditablePart part) throws RepositoryException { - Node node = null; - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - List values = ept.getValues(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (values.isEmpty()) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(propName, values.toArray(new String[0])); - } - // => Viewer : Controller - } else if (part instanceof EditablePropertyString) { - EditablePropertyString ept = (EditablePropertyString) part; - // SWT : View - String txt = ((Text) ept.getControl()).getText(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (EclipseUiUtils.isEmpty(txt)) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - setPropertySilently(node, propName, txt); - // node.setProperty(propName, txt); - } - // node.getSession().save(); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (cal == null) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(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); - node.getSession().save(); - } - } - - @Override - protected void updateContent(EditablePart part) throws RepositoryException { - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - List valStrings = new ArrayList(); - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value val : values) - valStrings.add(val.getString()); - } - ept.setValues(valStrings); - } else if (part instanceof EditablePropertyString) { - // || part instanceof EditableLink - EditablePropertyString ept = (EditablePropertyString) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) { - String value = node.getProperty(propName).getString(); - ept.setText(value); - } else - ept.setText(""); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) - ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); - else - ept.setText(""); - } else if (part instanceof SectionPart) { - SectionPart sectionPart = (SectionPart) part; - Node partNode = sectionPart.getNode(); - // use control AFTER setting style, since it may have been reset - if (part instanceof EditableImage) { - EditableImage editableImage = (EditableImage) part; - imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); - } - } - } - - // 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 CmsException("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; - EditablePart 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(); - EditablePart 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(EditablePart part) { - if (part instanceof SectionPart) { - if (part instanceof Img) { - if (getEdited() == part) - return; - edit(part, null); - layout(part.getControl()); - } - } - } - } - - @Override - public Control getControl() { - return mainSection; - } - - protected CmsImageManager imageManager() { - if (imageManager == null) - imageManager = CmsUtils.getCmsView().getImageManager(); - return imageManager; - } - - // LOCAL UI HELPERS - protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { - Section section = null; - if (node != null) { - section = new Section(body, SWT.NO_FOCUS, node); - section.setLayoutData(CmsUtils.fillWidth()); - section.setLayout(CmsUtils.noSpaceGridLayout()); - } - return section; - } - - protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - if (getCmsEditable().canEdit() || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); - eps.setMouseListener(getMouseListener()); - eps.setFocusListener(getFocusListener()); - eps.setLayoutData(CmsUtils.fillWidth()); - } - } - - protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - boolean canEdit = getCmsEditable().canEdit(); - if (canEdit || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - - List valueStrings = new ArrayList(); - - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value value : values) - valueStrings.add(value.getString()); - } - - // 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(CmsUtils.fillWidth()); - } - } - - protected Label createPropertyLbl(Composite parent, String value) { - return createPropertyLbl(parent, value, SWT.TOP); - } - - protected Label createPropertyLbl(Composite parent, String value, int vAlign) { - boolean isSmall = CmsUtils.getCmsView().getUxContext().isSmall(); - Label label = new Label(parent, isSmall ? SWT.LEFT : SWT.RIGHT | SWT.WRAP); - label.setText(value + " "); - CmsUtils.style(label, FormStyle.propertyLabel.style()); - GridData gd = new GridData(isSmall ? SWT.LEFT : SWT.RIGHT, vAlign, false, false); - 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); - CmsUtils.style(label, style); - return label; - } - - protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { - Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); - bodyRow.setLayoutData(CmsUtils.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(final Section section, Composite parent, final Node parentNode) - throws RepositoryException { - - 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 Node context; - private Section section; - private String name; - - public FormFileUploadReceiver(Section section, Node 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; - - try { - imageManager().uploadImage(context, cleanedName, stream); - // TODO clean refresh strategy - section.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - try { - FormPageViewer.this.refresh(section); - section.layout(); - section.getParent().layout(); - } catch (RepositoryException re) { - throw new CmsException("unable to refresh " + "image section for " + context); - } - } - }); - } catch (RepositoryException re) { - throw new CmsException("unable to upload image " + name + " at " + context); - } - } - } - - protected void addListeners(StyledControl control) { - control.setMouseListener(getMouseListener()); - control.setFocusListener(getFocusListener()); - } - - protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { - Img img = new Img(parent, SWT.NONE, node, preferredSize) { - private static final long serialVersionUID = 1297900641952417540L; - - @Override - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - - @Override - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - }; - img.setLayoutData(CmsUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - updateContent(img); - addListeners(img); - return img; - } - - protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, - int rightWeight) { - Composite comp = new Composite(section, SWT.NONE); - comp.setLayoutData(CmsUtils.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); - CmsUtils.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(); - Section parSection = section.getParentSection(); - sessionNode.remove(); - session.save(); - refresh(parSection); - layout(parSection); - } catch (RepositoryException re) { - throw new CmsException("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); - EditablePart ep = findDataParent(btn); - if (ep != null && ep instanceof EditableMultiStringProperty) { - EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; - List values = emsp.getValues(); - if (values.contains(obj)) { - values.remove(values.indexOf(obj)); - emsp.setValues(values); - try { - save(emsp); - // TODO workaround to force refresh - edit(emsp, 0); - cancelEdit(); - } catch (RepositoryException e1) { - throw new CmsException("Unable to remove value " + obj, e1); - } - layout(emsp); - } - } - } - } - }; - } - - protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { - try { - // TODO Clean this: - // Format strings to replace \n - value = value.replaceAll("\n", "
"); - // 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.setProperty(propName, value); - } catch (ValueFormatException vfe) { - log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java deleted file mode 100644 index 6a496a25d..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.argeo.cms.forms; - -/** Syles used */ -public enum FormStyle { - // 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; - - public String style() { - return form.name() + '_' + name(); - } - - // TODO clean button style management - public final static String BUTTON_SUFFIX = "_btn"; -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java deleted file mode 100644 index be087027a..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java +++ /dev/null @@ -1,197 +0,0 @@ -package org.argeo.cms.forms; - -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.util.CmsUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.fieldassist.ControlDecoration; -import org.eclipse.jface.fieldassist.FieldDecorationRegistry; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Utilitary methods to ease implementation of CMS forms */ -public class FormUtils { - private final static Log log = LogFactory.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 Calendar parseDate(DateFormat dateFormat, String calStr) { - Calendar cal = null; - if (EclipseUiUtils.notEmpty(calStr)) { - try { - Date date = dateFormat.parse(calStr); - cal = new GregorianCalendar(); - cal.setTime(date); - } catch (ParseException pe) { - // Silent - log.warn("Unable to parse date: " + calStr + " - msg: " - + pe.getMessage()); - } - } - return cal; - } - - /** 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 = CmsUtils.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 - */ - public static String getPhoneLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - builder.append("").append(label) - .append(""); - 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 - */ - public static String getMailLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - value = replaceAmpersand(value); - builder.append("").append(label).append(""); - 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("" + label + ""); - return builder.toString(); - } - - private static String AMPERSAND = "&"; - - /** - * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to - * avoid SAXParseException 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() { - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/forms/MarkupValidatorCopy.java b/org.argeo.cms.ui/src/org/argeo/cms/forms/MarkupValidatorCopy.java deleted file mode 100644 index f1a5a8c41..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/forms/MarkupValidatorCopy.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.argeo.cms.forms; - -import java.io.StringReader; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.eclipse.rap.rwt.SingletonUtil; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.helpers.DefaultHandler; - -/** - * Copy of RAP v2.3 since it is in an internal package. - */ -class MarkupValidatorCopy { - - // Used by Eclipse Scout project - public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; - - private static final String DTD = createDTD(); - private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); - private final SAXParser saxParser; - - public static MarkupValidatorCopy getInstance() { - return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); - } - - public MarkupValidatorCopy() { - saxParser = createSAXParser(); - } - - public void validate(String text) { - StringBuilder markup = new StringBuilder(); - markup.append(DTD); - markup.append(""); - markup.append(text); - markup.append(""); - InputSource inputSource = new InputSource(new StringReader(markup.toString())); - try { - saxParser.parse(inputSource, new MarkupHandler()); - } catch (RuntimeException exception) { - throw exception; - } catch (Exception exception) { - throw new IllegalArgumentException("Failed to parse markup text", exception); - } - } - - public static boolean isValidationDisabledFor(Widget widget) { - return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); - } - - private static SAXParser createSAXParser() { - SAXParser result = null; - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - try { - result = parserFactory.newSAXParser(); - } catch (Exception exception) { - throw new RuntimeException("Failed to create SAX parser", exception); - } - return result; - } - - private static String createDTD() { - StringBuilder result = new StringBuilder(); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append("]>"); - return result.toString(); - } - - private static Map createSupportedElementsMap() { - Map result = new HashMap(); - result.put("html", new String[0]); - result.put("br", new String[0]); - result.put("b", new String[] { "style" }); - result.put("strong", new String[] { "style" }); - result.put("i", new String[] { "style" }); - result.put("em", new String[] { "style" }); - result.put("sub", new String[] { "style" }); - result.put("sup", new String[] { "style" }); - result.put("big", new String[] { "style" }); - result.put("small", new String[] { "style" }); - result.put("del", new String[] { "style" }); - result.put("ins", new String[] { "style" }); - result.put("code", new String[] { "style" }); - result.put("samp", new String[] { "style" }); - result.put("kbd", new String[] { "style" }); - result.put("var", new String[] { "style" }); - result.put("cite", new String[] { "style" }); - result.put("dfn", new String[] { "style" }); - result.put("q", new String[] { "style" }); - result.put("abbr", new String[] { "style", "title" }); - result.put("span", new String[] { "style" }); - result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); - result.put("a", new String[] { "style", "href", "target", "title" }); - return result; - } - - private static class MarkupHandler extends DefaultHandler { - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) { - checkSupportedElements(name, attributes); - checkSupportedAttributes(name, attributes); - checkMandatoryAttributes(name, attributes); - } - - private static void checkSupportedElements(String elementName, Attributes attributes) { - if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { - throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); - } - } - - private static void checkSupportedAttributes(String elementName, Attributes attributes) { - if (attributes.getLength() > 0) { - List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); - int index = 0; - String attributeName = attributes.getQName(index); - while (attributeName != null) { - if (!supportedAttributes.contains(attributeName)) { - String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; - message = MessageFormat.format(message, new Object[] { attributeName, elementName }); - throw new IllegalArgumentException(message); - } - index++; - attributeName = attributes.getQName(index); - } - } - } - - private static void checkMandatoryAttributes(String elementName, Attributes attributes) { - checkIntAttribute(elementName, attributes, "img", "width"); - checkIntAttribute(elementName, attributes, "img", "height"); - } - - private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, - String checkedAttributeName) { - if (checkedElementName.equals(elementName)) { - String attribute = attributes.getValue(checkedAttributeName); - try { - Integer.parseInt(attribute); - } catch (NumberFormatException exception) { - String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; - Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; - message = MessageFormat.format(message, arguments); - throw new IllegalArgumentException(message); - } - } - } - - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java deleted file mode 100644 index 8c893f2f3..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.argeo.cms.maintenance; - -import java.util.Collection; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; - -abstract class AbstractOsgiComposite extends Composite { - private static final long serialVersionUID = -4097415973477517137L; - protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - protected final Log log = LogFactory.getLog(getClass()); - - public AbstractOsgiComposite(Composite parent, int style) { - super(parent, style); - parent.setLayout(CmsUtils.noSpaceGridLayout()); - setLayout(CmsUtils.noSpaceGridLayout()); - setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - initUi(style); - } - - protected abstract void initUi(int style); - - protected T getService(Class clazz) { - return bc.getService(bc.getServiceReference(clazz)); - } - - protected Collection> getServiceReferences(Class clazz, String filter) { - try { - return bc.getServiceReferences(clazz, filter); - } catch (InvalidSyntaxException e) { - throw new IllegalArgumentException("Filter " + filter + " is invalid", e); - } - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java deleted file mode 100644 index 384cd72ef..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java +++ /dev/null @@ -1,576 +0,0 @@ -package org.argeo.cms.maintenance; - -import static org.eclipse.swt.SWT.RIGHT; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.LinkedHashMap; - -import javax.jcr.ItemNotFoundException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.util.CmsLink; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.widgets.EditableImage; -import org.argeo.cms.widgets.Img; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ILazyContentProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Text; - -public class Browse implements CmsUiProvider { - - // Some local constants to experiment. should be cleaned - private final static String BROWSE_PREFIX = "browse#"; - private final static int THUMBNAIL_WIDTH = 400; - private final static int COLUMN_WIDTH = 160; - private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm"); - - // keep a cache of the opened nodes - // Key is the path - private LinkedHashMap browserCols = new LinkedHashMap(); - private Composite nodeDisplayParent; - private Composite colViewer; - private ScrolledComposite scrolledCmp; - private Text parentPathTxt; - private Text filterTxt; - private Node currEdited; - - private String initialPath; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - if (context == null) - // return null; - throw new CmsException("Context cannot be null"); - GridLayout layout = CmsUtils.noSpaceGridLayout(); - layout.numColumns = 2; - parent.setLayout(layout); - - // Left - Composite leftCmp = new Composite(parent, SWT.NO_FOCUS); - leftCmp.setLayoutData(CmsUtils.fillAll()); - createBrowserPart(leftCmp, context); - - // Right - nodeDisplayParent = new Composite(parent, SWT.NO_FOCUS | SWT.BORDER); - GridData gd = new GridData(SWT.RIGHT, SWT.FILL, false, true); - gd.widthHint = THUMBNAIL_WIDTH; - nodeDisplayParent.setLayoutData(gd); - createNodeView(nodeDisplayParent, context); - - // INIT - setEdited(context); - initialPath = context.getPath(); - - // Workaround we don't yet manage the delete to display parent of the - // initial context node - - return null; - } - - private void createBrowserPart(Composite parent, Node context) throws RepositoryException { - GridLayout layout = CmsUtils.noSpaceGridLayout(); - parent.setLayout(layout); - Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(CmsUtils.fillWidth()); - - // top filter - addFilterPanel(filterCmp); - - // scrolled composite - scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); - scrolledCmp.setLayoutData(CmsUtils.fillAll()); - scrolledCmp.setExpandVertical(true); - scrolledCmp.setExpandHorizontal(true); - scrolledCmp.setShowFocusedControl(true); - - colViewer = new Composite(scrolledCmp, SWT.NO_FOCUS); - scrolledCmp.setContent(colViewer); - scrolledCmp.addControlListener(new ControlAdapter() { - private static final long serialVersionUID = 6589392045145698201L; - - @Override - public void controlResized(ControlEvent e) { - Rectangle r = scrolledCmp.getClientArea(); - scrolledCmp.setMinSize(colViewer.computeSize(SWT.DEFAULT, r.height)); - } - }); - initExplorer(colViewer, context); - } - - private Control initExplorer(Composite parent, Node context) throws RepositoryException { - parent.setLayout(CmsUtils.noSpaceGridLayout()); - createBrowserColumn(parent, context); - return null; - } - - private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException { - // TODO style is not correctly managed. - FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); - // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style()); - table.filterList("*"); - table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); - browserCols.put(context.getPath(), table); - return null; - } - - public void addFilterPanel(Composite parent) { - - parent.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(2, false))); - - // Text Area for the filter - parentPathTxt = new Text(parent, SWT.NO_FOCUS); - parentPathTxt.setEditable(false); - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Filter current list"); - filterTxt.setLayoutData(CmsUtils.fillWidth()); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 7709303319740056286L; - - public void modifyText(ModifyEvent event) { - modifyFilter(false); - } - }); - - filterTxt.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -4523394262771183968L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; - // boolean altPressed = (e.stateMask & SWT.ALT) != 0; - FilterEntitiesVirtualTable currTable = null; - if (currEdited != null) { - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - currTable = table; - } - - try { - if (e.keyCode == SWT.ARROW_DOWN) - currTable.setFocus(); - else if (e.keyCode == SWT.BS) { - if (filterTxt.getText().equals("") - && !(getPath(currEdited).equals("/") || getPath(currEdited).equals(initialPath))) { - setEdited(currEdited.getParent()); - e.doit = false; - filterTxt.setFocus(); - } - } else if (e.keyCode == SWT.TAB && !shiftPressed) { - if (currEdited.getNodes(filterTxt.getText() + "*").getSize() == 1) { - setEdited(currEdited.getNodes(filterTxt.getText() + "*").nextNode()); - } - filterTxt.setFocus(); - e.doit = false; - } - } catch (RepositoryException e1) { - throw new CmsException("Unexpected error in key management for " + currEdited + "with filter " - + filterTxt.getText(), e1); - } - - } - }); - } - - private void setEdited(Node node) { - try { - currEdited = node; - CmsUtils.clear(nodeDisplayParent); - createNodeView(nodeDisplayParent, currEdited); - nodeDisplayParent.layout(); - refreshFilters(node); - refreshBrowser(node); - } catch (RepositoryException re) { - throw new CmsException("Unable to update browser for " + node, re); - } - } - - private void refreshFilters(Node node) throws RepositoryException { - String currNodePath = node.getPath(); - parentPathTxt.setText(currNodePath); - filterTxt.setText(""); - filterTxt.getParent().layout(); - } - - private void refreshBrowser(Node node) throws RepositoryException { - - // Retrieve - String currNodePath = node.getPath(); - String currParPath = ""; - if (!"/".equals(currNodePath)) - currParPath = JcrUtils.parentPath(currNodePath); - if ("".equals(currParPath)) - currParPath = "/"; - - Object[][] colMatrix = new Object[browserCols.size()][2]; - - int i = 0, j = -1, k = -1; - for (String path : browserCols.keySet()) { - colMatrix[i][0] = path; - colMatrix[i][1] = browserCols.get(path); - if (j >= 0 && k < 0 && !currNodePath.equals("/")) { - boolean leaveOpened = path.startsWith(currNodePath); - - // workaround for same name siblings - // fix me weird side effect when we go left or click on anb - // already selected, unfocused node - if (leaveOpened && (path.lastIndexOf("/") == 0 && currNodePath.lastIndexOf("/") == 0 - || JcrUtils.parentPath(path).equals(JcrUtils.parentPath(currNodePath)))) - leaveOpened = JcrUtils.lastPathElement(path).equals(JcrUtils.lastPathElement(currNodePath)); - - if (!leaveOpened) - k = i; - } - if (currParPath.equals(path)) - j = i; - i++; - } - - if (j >= 0 && k >= 0) - // remove useless cols - for (int l = i - 1; l >= k; l--) { - browserCols.remove(colMatrix[l][0]); - ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); - } - - // Remove disposed columns - // TODO investigate and fix the mechanism that leave them there after - // disposal - if (browserCols.containsKey(currNodePath)) { - FilterEntitiesVirtualTable currCol = browserCols.get(currNodePath); - if (currCol.isDisposed()) - browserCols.remove(currNodePath); - } - - if (!browserCols.containsKey(currNodePath)) - createBrowserColumn(colViewer, node); - - colViewer.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); - // colViewer.pack(); - colViewer.layout(); - // also resize the scrolled composite - scrolledCmp.layout(); - scrolledCmp.getShowFocusedControl(); - // colViewer.getParent().layout(); - // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) { - // } else { - // } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currEdited != null) { - String filter = filterTxt.getText() + "*"; - FilterEntitiesVirtualTable table = browserCols.get(getPath(currEdited)); - if (table != null && !table.isDisposed()) - table.filterList(filter); - } - - } - - private String getPath(Node node) { - try { - return node.getPath(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get path for node " + node, e); - } - } - - private Point imageWidth = new Point(250, 0); - - /** - * Recreates the content of the box that displays information about the current - * selected node. - */ - private Control createNodeView(Composite parent, Node context) throws RepositoryException { - - parent.setLayout(new GridLayout(2, false)); - - if (isImg(context)) { - EditableImage image = new Img(parent, RIGHT, context, imageWidth); - image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1)); - } - - // Name and primary type - Label contextL = new Label(parent, SWT.NONE); - contextL.setData(RWT.MARKUP_ENABLED, true); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName()); - - // Children - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context); - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName()); - } - - // Properties - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property)); - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private boolean isImg(Node node) throws RepositoryException { - // TODO support images - return false; -// return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE); - } - - private String getPropAsString(Property property) throws RepositoryException { - String result = ""; - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } - - /** Almost canonical implementation of a table that display entities */ - private class FilterEntitiesVirtualTable extends Composite { - private static final long serialVersionUID = 8798147431706283824L; - - // Context - private Node context; - - // UI Objects - private TableViewer entityViewer; - - // enable management of multiple columns - Node getNode() { - return context; - } - - @Override - public boolean setFocus() { - if (entityViewer.getTable().isDisposed()) - return false; - if (entityViewer.getSelection().isEmpty()) { - Object first = entityViewer.getElementAt(0); - if (first != null) { - entityViewer.setSelection(new StructuredSelection(first), true); - } - } - return entityViewer.getTable().setFocus(); - } - - void filterList(String filter) { - try { - NodeIterator nit = context.getNodes(filter); - refreshFilteredList(nit); - } catch (RepositoryException e) { - throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e); - } - - } - - public FilterEntitiesVirtualTable(Composite parent, int style, Node context) { - super(parent, SWT.NO_FOCUS); - this.context = context; - populate(); - } - - protected void populate() { - Composite parent = this; - GridLayout layout = CmsUtils.noSpaceGridLayout(); - - this.setLayout(layout); - createTableViewer(parent); - } - - private void createTableViewer(final Composite parent) { - // the list - // We must limit the size of the table otherwise the full list is - // loaded - // before the layout happens - Composite listCmp = new Composite(parent, SWT.NO_FOCUS); - GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); - gd.widthHint = COLUMN_WIDTH; - listCmp.setLayoutData(gd); - listCmp.setLayout(CmsUtils.noSpaceGridLayout()); - - entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE); - Table table = entityViewer.getTable(); - - table.setLayoutData(CmsUtils.fillAll()); - table.setLinesVisible(true); - table.setHeaderVisible(false); - table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - - CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); - - // first column - TableViewerColumn column = new TableViewerColumn(entityViewer, SWT.NONE); - TableColumn tcol = column.getColumn(); - tcol.setWidth(COLUMN_WIDTH); - tcol.setResizable(true); - column.setLabelProvider(new SimpleNameLP()); - - entityViewer.setContentProvider(new MyLazyCP(entityViewer)); - entityViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - if (selection.isEmpty()) - return; - else - setEdited((Node) selection.getFirstElement()); - - } - }); - - table.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -330694313896036230L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - - IStructuredSelection selection = (IStructuredSelection) entityViewer.getSelection(); - Node selected = null; - if (!selection.isEmpty()) - selected = ((Node) selection.getFirstElement()); - try { - if (e.keyCode == SWT.ARROW_RIGHT) { - if (selected != null) { - setEdited(selected); - browserCols.get(selected.getPath()).setFocus(); - } - } else if (e.keyCode == SWT.ARROW_LEFT) { - try { - selected = getNode().getParent(); - String newPath = selected.getPath(); // getNode().getParent() - setEdited(selected); - if (browserCols.containsKey(newPath)) - browserCols.get(newPath).setFocus(); - } catch (ItemNotFoundException ie) { - // root silent - } - } - } catch (RepositoryException ie) { - throw new CmsException("Error while managing arrow " + "events in the browser for " + selected, - ie); - } - } - }); - } - - private class MyLazyCP implements ILazyContentProvider { - private static final long serialVersionUID = 1L; - private TableViewer viewer; - private Object[] elements; - - public MyLazyCP(TableViewer viewer) { - this.viewer = viewer; - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // IMPORTANT: don't forget this: an exception will be thrown if - // a selected object is not part of the results anymore. - viewer.setSelection(null); - this.elements = (Object[]) newInput; - } - - public void updateElement(int index) { - viewer.replace(elements[index], index); - } - } - - protected void refreshFilteredList(NodeIterator children) { - Object[] rows = JcrUtils.nodeIteratorToList(children).toArray(); - entityViewer.setInput(rows); - entityViewer.setItemCount(rows.length); - entityViewer.refresh(); - } - - public class SimpleNameLP extends ColumnLabelProvider { - private static final long serialVersionUID = 2465059387875338553L; - - @Override - public String getText(Object element) { - if (element instanceof Node) { - Node curr = ((Node) element); - try { - return curr.getName(); - } catch (RepositoryException e) { - throw new CmsException("Unable to get name for" + curr); - } - } - return super.getText(element); - } - } - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java deleted file mode 100644 index f4f3079b6..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.cms.maintenance; - -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.ServiceReference; -import org.osgi.service.http.HttpService; -import org.osgi.service.useradmin.UserAdmin; - -class ConnectivityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public ConnectivityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - StringBuffer text = new StringBuffer(); - text.append("Provided Servers
"); - - ServiceReference userAdminRef = bc.getServiceReference(HttpService.class); - if (userAdminRef != null) { - // FIXME use constants - Object httpPort = userAdminRef.getProperty("http.port"); - Object httpsPort = userAdminRef.getProperty("https.port"); - if (httpPort != null) - text.append("http ").append(httpPort).append("
"); - if (httpsPort != null) - text.append("https ").append(httpsPort).append("
"); - - } - - text.append("
"); - text.append("Referenced Servers
"); - - Label label = new Label(this, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java deleted file mode 100644 index 613e3cb64..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.argeo.cms.maintenance; - -import java.io.File; -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collection; - -import org.apache.jackrabbit.core.RepositoryContext; -import org.apache.jackrabbit.core.config.RepositoryConfig; -import org.argeo.cms.util.CmsUtils; -import org.argeo.node.NodeConstants; -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.Label; -import org.osgi.framework.ServiceReference; - -class DataDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public DataDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { -// try { -// ConfigurationAdmin confAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class)); -// Configuration[] confs = confAdmin.listConfigurations( -// "(" + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + NodeConstants.NODE_REPOS_FACTORY_PID + ")"); -// if (confs == null || confs.length == 0) { -// Group buttonGroup = new Group(parent, SWT.NONE); -// buttonGroup.setText("Repository Type"); -// buttonGroup.setLayout(new GridLayout(2, true)); -// buttonGroup.setLayoutData(new GridData(GridData.FILL_VERTICAL)); -// -// SelectionListener selectionListener = new SelectionAdapter() { -// private static final long serialVersionUID = 6247064348421088092L; -// -// public void widgetSelected(SelectionEvent event) { -// Button radio = (Button) event.widget; -// if (!radio.getSelection()) -// return; -// log.debug(event); -// JackrabbitType nodeType = (JackrabbitType) radio.getData(); -// if (log.isDebugEnabled()) -// log.debug(" selected = " + nodeType.name()); -// }; -// }; -// -// for (JackrabbitType nodeType : JackrabbitType.values()) { -// Button radio = new Button(buttonGroup, SWT.RADIO); -// radio.setText(nodeType.name()); -// radio.setData(nodeType); -// if (nodeType.equals(JackrabbitType.localfs)) -// radio.setSelection(true); -// radio.addSelectionListener(selectionListener); -// } -// -// } else if (confs.length == 1) { -// -// } else { -// throw new CmsException("Multiple repos not yet supported"); -// } -// } catch (Exception e) { -// throw new CmsException("Cannot initialize UI", e); -// } - - } - - private void initCurrentUi(Composite parent) { - parent.setLayout(new GridLayout()); - Collection> contexts = getServiceReferences(RepositoryContext.class, - "(" + NodeConstants.CN + "=*)"); - StringBuffer text = new StringBuffer(); - text.append("Jackrabbit Repositories
"); - for (ServiceReference sr : contexts) { - RepositoryContext repositoryContext = bc.getService(sr); - String alias = sr.getProperty(NodeConstants.CN).toString(); - String rootNodeId = repositoryContext.getRootNodeId().toString(); - RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig(); - Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath(); - // TODO check data store - - text.append("" + alias + "
"); - text.append("rootNodeId: " + rootNodeId + "
"); - try { - FileStore fileStore = Files.getFileStore(repoHomePath); - text.append("partition: " + fileStore.toString() + "
"); - text.append( - percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)
"); - } catch (IOException e) { - log.error("Cannot check fileStore for " + repoHomePath, e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsUtils.markup(label); - label.setText("" + text.toString() + ""); - } - - private String humanReadable(long bytes) { - long mb = bytes / (1024 * 1024); - return mb >= 2048 ? Long.toString(mb / 1024) + " GB" : Long.toString(mb) + " MB"; - } - - private String percentUsed(FileStore fs) throws IOException { - long used = fs.getTotalSpace() - fs.getUnallocatedSpace(); - long percent = used * 100 / fs.getTotalSpace(); - if (log.isTraceEnabled()) { - // output identical to `df -B 1`) - log.trace(fs.getTotalSpace() + "," + used + "," + fs.getUsableSpace()); - } - String span; - if (percent < 80) - span = ""; - else if (percent < 95) - span = ""; - else - span = ""; - return span + percent + "%"; - } - - protected boolean isDeployed() { - return bc.getServiceReference(RepositoryContext.class) != null; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java deleted file mode 100644 index 8dda4c476..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.cms.maintenance; - -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.cms.util.CmsUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeDeployment; -import org.argeo.node.NodeState; -import org.eclipse.rap.rwt.application.AbstractEntryPoint; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Group; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; - -class DeploymentEntryPoint extends AbstractEntryPoint { - private static final long serialVersionUID = -881152502968982437L; - private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext(); - - @Override - protected void createContents(Composite parent) { - // FIXME manage authentication if needed - // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN)) - // return; - - // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - if (isDesktop()) { - parent.setLayout(new GridLayout(2, true)); - } else { - // TODO add scrolling - parent.setLayout(new GridLayout(1, true)); - } - - initHighLevelSummary(parent); - - Group securityGroup = createHighLevelGroup(parent, "Security"); - securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new SecurityDeploymentUi(securityGroup, SWT.NONE); - - Group dataGroup = createHighLevelGroup(parent, "Data"); - dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - new DataDeploymentUi(dataGroup, SWT.NONE); - - Group logGroup = createHighLevelGroup(parent, "Notifications"); - logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - new LogDeploymentUi(logGroup, SWT.NONE); - - Group connectivityGroup = createHighLevelGroup(parent, "Connectivity"); - new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE); - connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); - - } - - private void initHighLevelSummary(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false); - if (isDesktop()) - gridData.horizontalSpan = 3; - composite.setLayoutData(gridData); - composite.setLayout(new FillLayout()); - - ServiceReference nodeStateRef = bc.getServiceReference(NodeState.class); - if (nodeStateRef == null) - throw new IllegalStateException("No CMS state available"); - NodeState nodeState = bc.getService(nodeStateRef); - ServiceReference nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class); - Label label = new Label(composite, SWT.WRAP); - CmsUtils.markup(label); - if (nodeDeploymentRef == null) { - label.setText("Not yet deployed on
" + nodeState.getHostname() + "
, please configure below."); - } else { - Object stateUuid = nodeStateRef.getProperty(NodeConstants.CN); - NodeDeployment nodeDeployment = bc.getService(nodeDeploymentRef); - GregorianCalendar calendar = new GregorianCalendar(); - calendar.setTimeInMillis(nodeDeployment.getAvailableSince()); - calendar.setTimeZone(TimeZone.getDefault()); - label.setText("[" + "" + nodeState.getHostname() + "]# " + "Deployment state " + stateUuid - + ", available since " + calendar.getTime() + ""); - } - } - - private static Group createHighLevelGroup(Composite parent, String text) { - Group group = new Group(parent, SWT.NONE); - group.setText(text); - CmsUtils.markup(group); - return group; - } - - private boolean isDesktop() { - return true; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java deleted file mode 100644 index 8fb96437c..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.cms.maintenance; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Text; -import org.osgi.service.log.LogEntry; -import org.osgi.service.log.LogListener; -import org.osgi.service.log.LogReaderService; - -class LogDeploymentUi extends AbstractOsgiComposite implements LogListener { - private static final long serialVersionUID = 590221539553514693L; - - private DateFormat dateFormat = new SimpleDateFormat("MMdd HH:mm"); - - private Display display; - private Text logDisplay; - - public LogDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - LogReaderService logReader = getService(LogReaderService.class); - // FIXME use server push - // logReader.addLogListener(this); - this.display = getDisplay(); - this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - logDisplay = new Text(this, SWT.WRAP | SWT.MULTI | SWT.READ_ONLY); - logDisplay.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - CmsUtils.markup(logDisplay); - @SuppressWarnings("unchecked") - Enumeration logEntries = (Enumeration) logReader.getLog(); - while (logEntries.hasMoreElements()) - logDisplay.append(printEntry(logEntries.nextElement())); - } - - private String printEntry(LogEntry entry) { - StringBuilder sb = new StringBuilder(); - GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault()); - calendar.setTimeInMillis(entry.getTime()); - sb.append(dateFormat.format(calendar.getTime())).append(' '); - sb.append(entry.getMessage()); - sb.append('\n'); - return sb.toString(); - } - - @Override - public void logged(LogEntry entry) { - if (display.isDisposed()) - return; - display.asyncExec(() -> { - if (logDisplay.isDisposed()) - return; - logDisplay.append(printEntry(entry)); - }); - display.wake(); - } - - // @Override - // public void dispose() { - // super.dispose(); - // getService(LogReaderService.class).removeLogListener(this); - // } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java deleted file mode 100644 index fef25d7ce..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.maintenance; - -/** Specific styles used by the various maintenance pages . */ -public interface MaintenanceStyles { - // General - public final static String PREFIX = "maintenance_"; - - // Browser - public final static String BROWSER_COLUMN = "browser_column"; - } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java deleted file mode 100644 index 11b0b9077..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.argeo.cms.maintenance; - -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; - -public class MaintenanceUi implements ApplicationConfiguration { - - @Override - public void configure(Application application) { - // application.addEntryPoint("/status", DeploymentEntryPoint.class, - // null); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java deleted file mode 100644 index 8a903448f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.argeo.cms.maintenance; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class NonAdminPage implements CmsUiProvider{ - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Composite body = new Composite(parent, SWT.NO_FOCUS); - body.setLayoutData(CmsUtils.fillAll()); - body.setLayout(new GridLayout()); - Label label = new Label(body, SWT.NONE); - label.setText("You should be an admin to perform maintenance operations. " - + "Are you sure you are logged in?"); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - return null; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java b/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java deleted file mode 100644 index 9fcdaf980..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.cms.maintenance; - -import java.net.URI; - -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.service.useradmin.Role; -import org.osgi.service.useradmin.UserAdmin; - -class SecurityDeploymentUi extends AbstractOsgiComposite { - private static final long serialVersionUID = 590221539553514693L; - - public SecurityDeploymentUi(Composite parent, int style) { - super(parent, style); - } - - @Override - protected void initUi(int style) { - if (isDeployed()) { - initCurrentUi(this); - } else { - initNewUi(this); - } - } - - private void initNewUi(Composite parent) { - new Label(parent, SWT.NONE).setText("Security is not configured"); - } - - private void initCurrentUi(Composite parent) { - ServiceReference userAdminRef = bc.getServiceReference(UserAdmin.class); - UserAdmin userAdmin = bc.getService(userAdminRef); - StringBuffer text = new StringBuffer(); - text.append("Domains
"); - domains: for (String key : userAdminRef.getPropertyKeys()) { - if (!key.startsWith("/")) - continue domains; - URI uri; - try { - uri = new URI(key); - } catch (Exception e) { - // ignore non URI keys - continue domains; - } - - String rootDn = uri.getPath().substring(1, uri.getPath().length()); - // FIXME make reading query options more robust, using utils - boolean readOnly = uri.getQuery().equals("readOnly=true"); - if (readOnly) - text.append(""); - else - text.append(""); - - text.append(rootDn); - text.append("
"); - try { - Role[] roles = userAdmin.getRoles("(dn=*," + rootDn + ")"); - long userCount = 0; - long groupCount = 0; - for (Role role : roles) { - if (role.getType() == Role.USER) - userCount++; - else - groupCount++; - } - text.append(" " + userCount + " users, " + groupCount +" groups.
"); - } catch (InvalidSyntaxException e) { - log.error("Invalid syntax", e); - } - } - Label label = new Label(parent, SWT.NONE); - label.setData(new GridData(SWT.FILL, SWT.FILL, false, false)); - CmsUtils.markup(label); - label.setText(text.toString()); - } - - protected boolean isDeployed() { - return bc.getServiceReference(UserAdmin.class) != null; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/AppUi.java b/org.argeo.cms.ui/src/org/argeo/cms/script/AppUi.java deleted file mode 100644 index a2a554736..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/AppUi.java +++ /dev/null @@ -1,266 +0,0 @@ -package org.argeo.cms.script; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptException; - -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.util.CmsPane; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.util.SimpleErgonomics; -import org.argeo.eclipse.ui.Selected; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -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; - -public class AppUi implements CmsUiProvider, Branding { - private final CmsScriptApp app; - - private CmsUiProvider ui; - private String createUi; - private Object impl; - private String script; - // private Branding branding = new Branding(); - - private EntryPointFactory factory; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public AppUi(CmsScriptApp app) { - this.app = app; - } - - public AppUi(CmsScriptApp app, String scriptPath) { - this.app = app; - this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC), app.getScriptEngine(), scriptPath); - } - - public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) { - this.app = app; - this.ui = uiProvider; - } - - public AppUi(CmsScriptApp app, EntryPointFactory factory) { - this.app = app; - this.factory = factory; - } - - public void apply(Repository repository, Application application, Branding appBranding, String path) { - Map factoryProperties = new HashMap<>(); - if (appBranding != null) - appBranding.applyBranding(factoryProperties); - applyBranding(factoryProperties); - if (factory != null) { - application.addEntryPoint("/" + path, factory, factoryProperties); - } else { - EntryPointFactory entryPointFactory = new EntryPointFactory() { - @Override - public EntryPoint create() { - SimpleErgonomics ergonomics = new SimpleErgonomics(repository, "main", "/home/root/argeo:keyring", - AppUi.this, factoryProperties); -// CmsUiProvider header = app.getHeader(); -// if (header != null) -// ergonomics.setHeader(header); - app.applySides(ergonomics); - Integer headerHeight = app.getHeaderHeight(); - if (headerHeight != null) - ergonomics.setHeaderHeight(headerHeight); - return ergonomics; - } - }; - application.addEntryPoint("/" + path, entryPointFactory, factoryProperties); - } - } - - public void setUi(CmsUiProvider uiProvider) { - this.ui = uiProvider; - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - // public Branding getBranding() { - // return branding; - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsPane cmsPane = new CmsPane(parent, SWT.NONE); - - if (false) { - // QA - CmsUtils.style(cmsPane.getQaArea(), "qa"); - Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); - CmsUtils.style(reload, "qa"); - reload.setText("Reload"); - reload.addSelectionListener(new Selected() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - new Thread() { - @Override - public void run() { - app.reload(); - } - }.start(); - RWT.getClient().getService(JavaScriptExecutor.class) - .execute("setTimeout('location.reload()',1000)"); - } - }); - - // Support - CmsUtils.style(cmsPane.getSupportArea(), "support"); - Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); - CmsUtils.style(msg, "support"); - msg.setText("UNSUPPORTED DEVELOPMENT VERSION"); - } - - if (ui != null) { - ui.createUi(cmsPane.getMainArea(), context); - } - if (createUi != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeFunction(createUi, cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - if (impl != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // Invocable invocable = (Invocable) app.getScriptEngine(); - // try { - // invocable.invokeMethod(AppUi.this, "initUi", parent, context); - // - // } catch (NoSuchMethodException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } catch (ScriptException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - - return null; - } - - public void setCreateUi(String createUi) { - this.createUi = createUi; - } - - public void setImpl(Object impl) { - this.impl = impl; - } - - public Object getImpl() { - return impl; - } - - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/Branding.java b/org.argeo.cms.ui/src/org/argeo/cms/script/Branding.java deleted file mode 100644 index 39b5b531e..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/Branding.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.cms.script; - -import java.util.Map; - -public interface Branding { - public void applyBranding(Map properties); - - public String getThemeId(); - - public String getAdditionalHeaders(); - - public String getBodyHtml(); - - public String getPageTitle(); - - public String getPageOverflow(); - - public String getFavicon(); - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptApp.java b/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptApp.java deleted file mode 100644 index e63941210..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptApp.java +++ /dev/null @@ -1,420 +0,0 @@ -package org.argeo.cms.script; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.ScriptEngine; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.util.BundleResourceLoader; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.util.SimpleErgonomics; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.http.HttpContext; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; - -public class CmsScriptApp implements Branding { - public final static String CONTEXT_NAME = "contextName"; - - ServiceRegistration appConfigReg; - - private ScriptEngine scriptEngine; - - private final static Log log = LogFactory.getLog(CmsScriptApp.class); - - private String webPath; - private String repo = "(cn=node)"; - - // private Branding branding = new Branding(); - private Theme theme; - - private List resources = new ArrayList<>(); - - private Map ui = new HashMap<>(); - - private CmsUiProvider header; - private Integer headerHeight = null; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public CmsScriptApp(ScriptEngine scriptEngine) { - super(); - this.scriptEngine = scriptEngine; - } - - public void apply(BundleContext bundleContext, Repository repository, Application application) { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(CmsConstants.LOADING_IMAGE, createResourceLoader(CmsConstants.LOADING_IMAGE)); - // empty image - application.addResource(CmsConstants.NO_IMAGE, createResourceLoader(CmsConstants.NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - if (theme != null) { - theme.apply(application); - String themeHeaders = theme.getAdditionalHeaders(); - if (themeHeaders != null) { - if (additionalHeaders == null) - additionalHeaders = themeHeaders; - else - additionalHeaders = themeHeaders + "\n" + additionalHeaders; - } - themeId = theme.getThemeId(); - } - - // client JavaScript - Bundle appBundle = bundleRL.getBundle(); - BundleContext bc = appBundle.getBundleContext(); - HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class)); - HttpContext httpContext = new BundleHttpContext(bc); - Enumeration themeResources = appBundle.findEntries("/js/", "*", true); - if (themeResources != null) - bundleResources: while (themeResources.hasMoreElements()) { - try { - String name = themeResources.nextElement().getPath(); - if (name.endsWith("/")) - continue bundleResources; - String alias = "/" + getWebPath() + name; - - httpService.registerResources(alias, name, httpContext); - if (log.isDebugEnabled()) - log.debug("Mapped " + name + " to alias " + alias); - - } catch (NamespaceException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // App UIs - for (String appUiName : ui.keySet()) { - AppUi appUi = ui.get(appUiName); - appUi.apply(repository, application, this, appUiName); - - } - - } - - public void applySides(SimpleErgonomics simpleErgonomics) { - simpleErgonomics.setHeader(header); - simpleErgonomics.setLead(lead); - simpleErgonomics.setEnd(end); - simpleErgonomics.setFooter(footer); - } - - public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) { - Hashtable props = new Hashtable<>(); - props.put(CONTEXT_NAME, webPath); - appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props); - } - - public void reload() { - BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext(); - ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference()); - appConfigReg.unregister(); - register(bundleContext, appConfig); - - // BundleContext bundleContext = (BundleContext) - // getScriptEngine().get("bundleContext"); - // try { - // Bundle bundle = bundleContext.getBundle(); - // bundle.stop(); - // bundle.start(); - // } catch (BundleException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - public List getResources() { - return resources; - } - - public AppUi newUi(String name) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - AppUi appUi = new AppUi(this); - // appUi.setApp(this); - ui.put(name, appUi); - return appUi; - } - - public void addUi(String name, AppUi appUi) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - // appUi.setApp(this); - ui.put(name, appUi); - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUtils.getCmsView().exception(throwable); - } - - } - - // public Branding getBranding() { - // return branding; - // } - - ScriptEngine getScriptEngine() { - return scriptEngine; - } - - public static String toJson(Node node) { - try { - StringBuilder sb = new StringBuilder(); - sb.append('{'); - PropertyIterator pit = node.getProperties(); - int count = 0; - while (pit.hasNext()) { - Property p = pit.nextProperty(); - int type = p.getType(); - if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) { - Node ref = p.getNode(); - if (count != 0) - sb.append(','); - // TODO limit depth? - sb.append(toJson(ref)); - count++; - } else if (!p.isMultiple()) { - if (count != 0) - sb.append(','); - sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"'); - count++; - } - } - sb.append('}'); - return sb.toString(); - } catch (RepositoryException e) { - throw new CmsException("Cannot convert " + node + " to JSON", e); - } - } - - public void fromJson(Node node, String json) { - // TODO - } - - public Theme getTheme() { - return theme; - } - - public void setTheme(Theme theme) { - this.theme = theme; - } - - public String getWebPath() { - return webPath; - } - - public void setWebPath(String context) { - this.webPath = context; - } - - public String getRepo() { - return repo; - } - - public void setRepo(String repo) { - this.repo = repo; - } - - public Map getUi() { - return ui; - } - - public void setUi(Map ui) { - this.ui = ui; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public Integer getHeaderHeight() { - return headerHeight; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - static class BundleHttpContext implements HttpContext { - private BundleContext bundleContext; - - public BundleHttpContext(BundleContext bundleContext) { - super(); - this.bundleContext = bundleContext; - } - - @Override - public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO Auto-generated method stub - return true; - } - - @Override - public URL getResource(String name) { - - return bundleContext.getBundle().getEntry(name); - } - - @Override - public String getMimeType(String name) { - return null; - } - - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptRwtApplication.java b/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptRwtApplication.java deleted file mode 100644 index cc5ea7ff5..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptRwtApplication.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.argeo.cms.script; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; - -import javax.jcr.Repository; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.wiring.BundleWiring; - -public class CmsScriptRwtApplication implements ApplicationConfiguration { - public final static String APP = "APP"; - public final static String BC = "BC"; - - private final Log log = LogFactory.getLog(CmsScriptRwtApplication.class); - - BundleContext bundleContext; - Repository repository; - - ScriptEngine engine; - - public void init(BundleContext bundleContext) { - this.bundleContext = bundleContext; - ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); - ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); - try { -// Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager -// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// if (engine == null) {// Nashorn -// Thread.currentThread().setContextClassLoader(originalCcl); -// scriptEngineManager = new ScriptEngineManager(); -// Thread.currentThread().setContextClassLoader(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// } - engine = loadScriptEngine(originalCcl, bundleCl); - - // Load script - URL appUrl = bundleContext.getBundle().getEntry("cms/app.js"); - // System.out.println("Loading " + appUrl); - // System.out.println("Loading " + appUrl.getHost()); - // System.out.println("Loading " + appUrl.getPath()); - - CmsScriptApp app = new CmsScriptApp(engine); - engine.put(APP, app); - engine.put(BC, bundleContext); - try (Reader reader = new InputStreamReader(appUrl.openStream())) { - engine.eval(reader); - } catch (IOException | ScriptException e) { - throw new CmsException("Cannot execute " + appUrl, e); - } - - if (log.isDebugEnabled()) - log.debug("CMS script app initialized from " + appUrl); - - } catch (Exception e) { - e.printStackTrace(); - } finally { - Thread.currentThread().setContextClassLoader(originalCcl); - } - } - - public void destroy(BundleContext bundleContext) { - engine = null; - } - - @Override - public void configure(Application application) { - load(application); - } - - void load(Application application) { - CmsScriptApp app = getApp(); - app.apply(bundleContext, repository, application); - if (log.isDebugEnabled()) - log.debug("CMS script app loaded to " + app.getWebPath()); - } - - CmsScriptApp getApp() { - if (engine == null) - throw new IllegalStateException("CMS script app is not initialized"); - return (CmsScriptApp) engine.get(APP); - } - - void update() { - - try { - bundleContext.getBundle().update(); - } catch (BundleException e) { - e.printStackTrace(); - } - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) { - Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager - ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); - ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); - if (engine == null) {// Nashorn - Thread.currentThread().setContextClassLoader(originalCcl); - scriptEngineManager = new ScriptEngineManager(); - Thread.currentThread().setContextClassLoader(bundleCl); - engine = scriptEngineManager.getEngineByName("JavaScript"); - } - return engine; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptAppActivator.java b/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptAppActivator.java deleted file mode 100644 index edf25bb00..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptAppActivator.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.argeo.cms.script; - -import javax.jcr.Repository; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -public class ScriptAppActivator implements BundleActivator { - private final static Log log = LogFactory.getLog(ScriptAppActivator.class); - - @Override - public void start(BundleContext context) throws Exception { - try { - CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication(); - appConfig.init(context); - CmsScriptApp app = appConfig.getApp(); - ServiceTracker repoSt = new ServiceTracker(context, - FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) { - - @Override - public Repository addingService(ServiceReference reference) { - Repository repository = super.addingService(reference); - appConfig.setRepository(repository); - CmsScriptApp app = appConfig.getApp(); - app.register(context, appConfig); - return repository; - } - - }; - repoSt.open(); - } catch (Exception e) { - log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e); - throw e; - } - } - - @Override - public void stop(BundleContext context) throws Exception { - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptUi.java b/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptUi.java deleted file mode 100644 index fd9aa9d31..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/ScriptUi.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.argeo.cms.script; - -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.osgi.framework.BundleContext; - -class ScriptUi implements CmsUiProvider { - private final static Log log = LogFactory.getLog(ScriptUi.class); - - private boolean development = true; - private ScriptEngine scriptEngine; - - private URL appUrl; - // private BundleContext bundleContext; - // private String path; - - // private Bindings bindings; - // private String script; - - public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) { - this.scriptEngine = scriptEngine; -//// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); -// ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); -// ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); -// try { -//// Thread.currentThread().setContextClassLoader(bundleCl); -//// scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); -//// scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext); -// scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl); -// -// } catch (Exception e) { -// e.printStackTrace(); -// } finally { -// Thread.currentThread().setContextClassLoader(originalCcl); -// } - this.appUrl = bundleContext.getBundle().getEntry(path); - load(); - } - - private void load() { -// try (Reader reader = new InputStreamReader(appUrl.openStream())) { -// scriptEngine.eval(reader); -// } catch (IOException | ScriptException e) { -// log.warn("Cannot execute " + appUrl, e); -// } - - try { - scriptEngine.eval("load('" + appUrl + "')"); - } catch (ScriptException e) { - log.warn("Cannot execute " + appUrl, e); - } - - } - - // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws - // ScriptException { - // super(); - // this.scriptEngine = scriptEngine; - // this.script = script; - // bindings = scriptEngine.createBindings(); - // scriptEngine.eval(script, bindings); - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - long begin = System.currentTimeMillis(); - // if (bindings == null) { - // bindings = scriptEngine.createBindings(); - // try { - // scriptEngine.eval(script, bindings); - // } catch (ScriptException e) { - // log.warn("Cannot evaluate script", e); - // } - // } - // Bindings bindings = scriptEngine.createBindings(); - // bindings.put("parent", parent); - // bindings.put("context", context); - // URL appUrl = bundleContext.getBundle().getEntry(path); - // try (Reader reader = new InputStreamReader(appUrl.openStream())) { - // scriptEngine.eval(reader,bindings); - // } catch (IOException | ScriptException e) { - // log.warn("Cannot execute " + appUrl, e); - // } - - if (development) - load(); - - Invocable invocable = (Invocable) scriptEngine; - try { - invocable.invokeFunction("createUi", parent, context); - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - long duration = System.currentTimeMillis() - begin; - if (log.isTraceEnabled()) - log.trace(appUrl + " UI in " + duration + " ms"); - return null; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/Theme.java b/org.argeo.cms.ui/src/org/argeo/cms/script/Theme.java deleted file mode 100644 index 584f521e0..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/Theme.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.argeo.cms.script; - -import org.argeo.cms.util.CmsTheme; -import org.osgi.framework.BundleContext; - -/** @deprecated Use CmsTheme instead. */ -@Deprecated -public class Theme extends CmsTheme { - - public Theme(BundleContext bundleContext, String symbolicName) { - super(bundleContext, symbolicName); - } - - public Theme(BundleContext bundleContext) { - super(bundleContext); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/script/cms.js b/org.argeo.cms.ui/src/org/argeo/cms/script/cms.js deleted file mode 100644 index ac2eecfba..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/script/cms.js +++ /dev/null @@ -1,90 +0,0 @@ -// CMS -var ScrolledPage = Java.type('org.argeo.cms.widgets.ScrolledPage'); - -var CmsScriptApp = Java.type('org.argeo.cms.script.CmsScriptApp'); -var AppUi = Java.type('org.argeo.cms.script.AppUi'); -var Theme = Java.type('org.argeo.cms.script.Theme'); -var ScriptUi = Java.type('org.argeo.cms.script.ScriptUi'); -var CmsUtils = Java.type('org.argeo.cms.util.CmsUtils'); -var SimpleCmsHeader = Java.type('org.argeo.cms.util.SimpleCmsHeader'); -var CmsLink = Java.type('org.argeo.cms.util.CmsLink'); -var MenuLink = Java.type('org.argeo.cms.util.MenuLink'); -var UserMenuLink = Java.type('org.argeo.cms.util.UserMenuLink'); - -// SWT -var SWT = Java.type('org.eclipse.swt.SWT'); -var Composite = Java.type('org.eclipse.swt.widgets.Composite'); -var Label = Java.type('org.eclipse.swt.widgets.Label'); -var Button = Java.type('org.eclipse.swt.widgets.Button'); -var Text = Java.type('org.eclipse.swt.widgets.Text'); -var Browser = Java.type('org.eclipse.swt.browser.Browser'); - -var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout'); -var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout'); -var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout'); -var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout'); -var GridData = Java.type('org.eclipse.swt.layout.GridData'); - -function loadNode(node) { - var json = CmsScriptApp.toJson(node) - var fromJson = JSON.parse(json) - return fromJson -} - -function newArea(parent, style, layout) { - var control = new Composite(parent, SWT.NONE) - control.setLayout(layout) - CmsUtils.style(control, style) - return control -} - -function newLabel(parent, style, text) { - var control = new Label(parent, SWT.WRAP) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newButton(parent, style, text) { - var control = new Button(parent, SWT.FLAT) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newFormLabel(parent, style, text) { - return newLabel(parent, style, '' + text + '') -} - -function newText(parent, style, msg) { - var control = new Text(parent, SWT.NONE) - control.setMessage(msg) - CmsUtils.style(control, style) - return control -} - -function newScrolledPage(parent) { - var scrolled = new ScrolledPage(parent, SWT.NONE) - scrolled.setLayoutData(CmsUtils.fillAll()) - scrolled.setLayout(CmsUtils.noSpaceGridLayout()) - var page = new Composite(scrolled, SWT.NONE) - page.setLayout(CmsUtils.noSpaceGridLayout()) - page.setBackgroundMode(SWT.INHERIT_NONE) - return page -} - -function gridData(control) { - var gridData = new GridData() - control.setLayoutData(gridData) - return gridData -} - -function gridData(control, hAlign, vAlign) { - var gridData = new GridData(hAlign, vAlign, false, false) - control.setLayoutData(gridData) - return gridData -} - -// print(__FILE__, __LINE__, __DIR__) diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/AbstractCmsEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/AbstractCmsEntryPoint.java index 2cd86ce08..6b61ae396 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/AbstractCmsEntryPoint.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/AbstractCmsEntryPoint.java @@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.HttpRequestCallback; @@ -31,7 +32,6 @@ import org.argeo.eclipse.ui.specific.UiContext; import org.argeo.jcr.JcrUtils; import org.argeo.naming.AuthPassword; import org.argeo.naming.SharedSecret; -import org.argeo.node.NodeConstants; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.AbstractEntryPoint; import org.eclipse.rap.rwt.client.WebClient; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java index 6ef4b315c..8a12c100e 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java @@ -3,7 +3,7 @@ package org.argeo.cms.ui; import javax.jcr.Node; import javax.jcr.RepositoryException; -import org.argeo.node.MvcProvider; +import org.argeo.api.MvcProvider; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java index fa85dff46..eb50ef2f3 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java @@ -1,7 +1,7 @@ package org.argeo.cms.ui.dialogs; import org.argeo.cms.CmsMsg; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.Selected; import org.argeo.eclipse.ui.dialogs.LightweightDialog; @@ -57,7 +57,7 @@ public class CmsMessageDialog extends LightweightDialog { body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label messageLbl = new Label(body, SWT.WRAP); - CmsUtils.markup(messageLbl); + CmsUiUtils.markup(messageLbl); messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); messageLbl.setFont(EclipseUiUtils.getBoldFont(parent)); if (message != null) diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java index de41bbfe1..b3860f780 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java @@ -4,7 +4,7 @@ import java.lang.reflect.InvocationTargetException; import org.argeo.cms.CmsException; import org.argeo.cms.CmsMsg; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.Selected; import org.argeo.eclipse.ui.dialogs.LightweightDialog; @@ -57,7 +57,7 @@ public class CmsWizardDialog extends LightweightDialog implements IWizardContain Composite messageArea = new Composite(parent, SWT.NONE); messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); { - messageArea.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(2, false))); + messageArea.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(2, false))); titleBar = new Label(messageArea, SWT.WRAP); titleBar.setFont(EclipseUiUtils.getBoldFont(parent)); titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false)); @@ -78,7 +78,7 @@ public class CmsWizardDialog extends LightweightDialog implements IWizardContain IWizardPage[] pages = wizard.getPages(); for (int i = 0; i < pages.length; i++) { pageBodies[i] = new Composite(body, SWT.NONE); - pageBodies[i].setLayout(CmsUtils.noSpaceGridLayout()); + pageBodies[i].setLayout(CmsUiUtils.noSpaceGridLayout()); setSwitchingFormData(pageBodies[i]); pages[i].createControl(pageBodies[i]); } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java new file mode 100644 index 000000000..e74de5ee8 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java @@ -0,0 +1,75 @@ +package org.argeo.cms.ui.forms; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.viewers.EditablePart; +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 + EditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String type; + private String message; + private boolean readOnly; + + public EditableLink(Composite parent, int style, Node node, + String propertyName, String type, String message) + throws RepositoryException { + super(parent, style, node, propertyName, message); + this.message = message; + this.type = type; + + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } 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 diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java new file mode 100644 index 000000000..24b1d0c67 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java @@ -0,0 +1,259 @@ +package org.argeo.cms.ui.forms; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.viewers.EditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.dialogs.MessageDialog; +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 StyledControl implements EditablePart { + private static final long serialVersionUID = -7044614381252178595L; + + private String 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 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, Node node, String propertyName, List values, + String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) + throws RepositoryException { + super(parent, style, node, true); + + 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 getValues() { + return values; + } + + public void setValues(List 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(CmsUiUtils.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); + CmsUiUtils.markup(label); + CmsUiUtils.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); + CmsUiUtils.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); + CmsUiUtils.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) + MessageDialog.openError(this.getShell(), "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); + CmsUiUtils.style(lbl, style); + CmsUiUtils.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() { + getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + getControl().setData(STYLE, FormStyle.propertyMessage.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java new file mode 100644 index 000000000..4428d1451 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java @@ -0,0 +1,304 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.viewers.EditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +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 StyledControl implements EditablePart { + private static final long serialVersionUID = 2500215515778162468L; + + // Context + private String propertyName; + private String message; + private DateFormat 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, Node node, + String propertyName, String message, DateFormat dateFormat) + throws RepositoryException { + super(parent, style, node, false); + + this.propertyName = propertyName; + this.message = message; + this.dateFormat = dateFormat; + + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(dateFormat.format(node.getProperty(propertyName) + .getDate().getTime())); + } 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()) + getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(dateTxt.getText())) + getControl().setData(STYLE, FormStyle.propertyMessage.style()); + else + getControl().setData(STYLE, FormStyle.propertyText.style()); + super.stopEditing(); + } + + public String 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(CmsUiUtils.fillWidth()); + CmsUiUtils.style(lbl, style); + CmsUiUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + private Control createCustomEditableControl(Composite box, String style) { + box.setLayoutData(CmsUiUtils.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); + CmsUiUtils.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); + CmsUiUtils.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 + CmsUiUtils.markup(CalendarPopup.this); + CmsUiUtils.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); + 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.getTime()); + dateTxt.setText(dateStr); + } + + protected void populate() { + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + dateTimeCtl = new DateTime(this, SWT.CALENDAR); + dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); + + Calendar calendar = FormUtils.parseDate(dateFormat, + dateTxt.getText()); + + if (calendar != null) + dateTimeCtl.setDate(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.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 diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java new file mode 100644 index 000000000..1ab0338cb --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java @@ -0,0 +1,80 @@ +package org.argeo.cms.ui.forms; + +import static org.argeo.cms.ui.forms.FormStyle.propertyMessage; +import static org.argeo.cms.ui.forms.FormStyle.propertyText; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.viewers.EditablePart; +import org.argeo.cms.ui.widgets.EditableText; +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 + EditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String 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, Node node, + String propertyName, String message) throws RepositoryException { + super(parent, style, node, true); + + this.propertyName = propertyName; + this.message = message; + + if (node.hasProperty(propertyName)) { + this.setStyle(propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } 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("
", "\n")); + } + } + + public synchronized void startEditing() { + getControl().setData(STYLE, propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) + getControl().setData(STYLE, propertyMessage.style()); + else + getControl().setData(STYLE, propertyText.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java new file mode 100644 index 000000000..fe9f7e7d7 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java @@ -0,0 +1,7 @@ +package org.argeo.cms.ui.forms; + +/** Constants used in the various CMS Forms */ +public interface FormConstants { + // DATAKEYS + public final static String LINKED_VALUE = "LinkedValue"; +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java new file mode 100644 index 000000000..7d4612731 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java @@ -0,0 +1,114 @@ +package org.argeo.cms.ui.forms; + +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; + +import org.argeo.cms.ui.CmsEditable; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +/** Add life cycle management abilities to an editable form page */ +public class FormEditorHeader implements SelectionListener, Observer { + private static final long serialVersionUID = 7392898696542484282L; + + // private final Node context; + private final CmsEditable cmsEditable; + private Button publishBtn; + + // Should we provide here the ability to switch from read only to edition + // mode? + // private Button editBtn; + // private boolean readOnly; + + // TODO add information about the current node status, typically if it is + // dirty or not + + private Composite parent; + private Composite display; + private Object layoutData; + + public FormEditorHeader(Composite parent, int style, Node context, + CmsEditable cmsEditable) { + this.cmsEditable = cmsEditable; + this.parent = parent; + // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + // this.context = context; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + refresh(); + } + + public void setLayoutData(Object layoutData) { + this.layoutData = layoutData; + if (display != null && !display.isDisposed()) + display.setLayoutData(layoutData); + } + + protected void refresh() { + if (display != null && !display.isDisposed()) + display.dispose(); + + display = new Composite(parent, SWT.NONE); + display.setLayoutData(layoutData); + + CmsUiUtils.style(display, FormStyle.header.style()); + display.setBackgroundMode(SWT.INHERIT_FORCE); + + display.setLayout(CmsUiUtils.noSpaceGridLayout()); + + publishBtn = createSimpleBtn(display, getPublishButtonLabel()); + display.moveAbove(null); + parent.layout(); + } + + private Button createSimpleBtn(Composite parent, String label) { + Button button = new Button(parent, SWT.FLAT | SWT.PUSH); + button.setText(label); + CmsUiUtils.style(button, FormStyle.header.style()); + button.addSelectionListener(this); + return button; + } + + private String getPublishButtonLabel() { + // Rather check if the current node differs from what has been + // previously committed + // For the time being, we always reach here, the underlying CmsEditable + // is always editing. + if (cmsEditable.isEditing()) + return " Publish "; + else + return " Edit "; + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.getSource() == publishBtn) { + // For the time being, the underlying CmsEditable + // is always editing when we reach this point + if (cmsEditable.isEditing()) { + // we always leave the node in a check outed state + cmsEditable.stopEditing(); + cmsEditable.startEditing(); + } else { + cmsEditable.startEditing(); + } + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) { + refresh(); + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java new file mode 100644 index 000000000..3ae2036ae --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java @@ -0,0 +1,611 @@ +package org.argeo.cms.ui.forms; + +import java.io.IOException; +import java.io.InputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsEditable; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.viewers.AbstractPageViewer; +import org.argeo.cms.ui.viewers.EditablePart; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.dialogs.MessageDialog; +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 Log log = LogFactory.getLog(FormPageViewer.class); + private static final long serialVersionUID = 5277789504209413500L; + + private final Section mainSection; + + // TODO manage within the CSS + private int labelColWidth = 150; + private int rowLayoutHSpacing = 8; + + // Context cached in the viewer + // The reference to translate from text to calendar and reverse + private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); + private CmsImageManager imageManager; + private FileUploadListener fileUploadListener; + + public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { + super(mainSection, style, cmsEditable); + this.mainSection = mainSection; + + if (getCmsEditable().canEdit()) { + fileUploadListener = new FUL(); + } + } + + @Override + protected void prepare(EditablePart part, Object caretPosition) { + if (part instanceof Img) { + ((Img) part).setFileUploadListener(fileUploadListener); + } + } + + /** To be overridden.Save the edited part. */ + protected void save(EditablePart part) throws RepositoryException { + Node node = null; + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + List values = ept.getValues(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (values.isEmpty()) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(propName, values.toArray(new String[0])); + } + // => Viewer : Controller + } else if (part instanceof EditablePropertyString) { + EditablePropertyString ept = (EditablePropertyString) part; + // SWT : View + String txt = ((Text) ept.getControl()).getText(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (EclipseUiUtils.isEmpty(txt)) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + setPropertySilently(node, propName, txt); + // node.setProperty(propName, txt); + } + // node.getSession().save(); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (cal == null) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(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); + node.getSession().save(); + } + } + + @Override + protected void updateContent(EditablePart part) throws RepositoryException { + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + List valStrings = new ArrayList(); + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value val : values) + valStrings.add(val.getString()); + } + ept.setValues(valStrings); + } else if (part instanceof EditablePropertyString) { + // || part instanceof EditableLink + EditablePropertyString ept = (EditablePropertyString) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) { + String value = node.getProperty(propName).getString(); + ept.setText(value); + } else + ept.setText(""); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) + ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); + else + ept.setText(""); + } else if (part instanceof SectionPart) { + SectionPart sectionPart = (SectionPart) part; + Node partNode = sectionPart.getNode(); + // use control AFTER setting style, since it may have been reset + if (part instanceof EditableImage) { + EditableImage editableImage = (EditableImage) part; + imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); + } + } + } + + // 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 CmsException("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; + EditablePart 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(); + EditablePart 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(EditablePart part) { + if (part instanceof SectionPart) { + if (part instanceof Img) { + if (getEdited() == part) + return; + edit(part, null); + layout(part.getControl()); + } + } + } + } + + @Override + public Control getControl() { + return mainSection; + } + + protected CmsImageManager imageManager() { + if (imageManager == null) + imageManager = CmsUiUtils.getCmsView().getImageManager(); + return imageManager; + } + + // LOCAL UI HELPERS + protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { + Section section = null; + if (node != null) { + section = new Section(body, SWT.NO_FOCUS, node); + section.setLayoutData(CmsUiUtils.fillWidth()); + section.setLayout(CmsUiUtils.noSpaceGridLayout()); + } + return section; + } + + protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + if (getCmsEditable().canEdit() || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); + eps.setMouseListener(getMouseListener()); + eps.setFocusListener(getFocusListener()); + eps.setLayoutData(CmsUiUtils.fillWidth()); + } + } + + protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + boolean canEdit = getCmsEditable().canEdit(); + if (canEdit || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + + List valueStrings = new ArrayList(); + + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value value : values) + valueStrings.add(value.getString()); + } + + // 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(CmsUiUtils.fillWidth()); + } + } + + protected Label createPropertyLbl(Composite parent, String value) { + return createPropertyLbl(parent, value, SWT.TOP); + } + + protected Label createPropertyLbl(Composite parent, String value, int vAlign) { + boolean isSmall = CmsUiUtils.getCmsView().getUxContext().isSmall(); + Label label = new Label(parent, isSmall ? SWT.LEFT : SWT.RIGHT | SWT.WRAP); + label.setText(value + " "); + CmsUiUtils.style(label, FormStyle.propertyLabel.style()); + GridData gd = new GridData(isSmall ? SWT.LEFT : SWT.RIGHT, vAlign, false, false); + 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); + CmsUiUtils.style(label, style); + return label; + } + + protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { + Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); + bodyRow.setLayoutData(CmsUiUtils.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(final Section section, Composite parent, final Node parentNode) + throws RepositoryException { + + 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 Node context; + private Section section; + private String name; + + public FormFileUploadReceiver(Section section, Node 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; + + try { + imageManager().uploadImage(context, cleanedName, stream); + // TODO clean refresh strategy + section.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + try { + FormPageViewer.this.refresh(section); + section.layout(); + section.getParent().layout(); + } catch (RepositoryException re) { + throw new CmsException("unable to refresh " + "image section for " + context); + } + } + }); + } catch (RepositoryException re) { + throw new CmsException("unable to upload image " + name + " at " + context); + } + } + } + + protected void addListeners(StyledControl control) { + control.setMouseListener(getMouseListener()); + control.setFocusListener(getFocusListener()); + } + + protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { + Img img = new Img(parent, SWT.NONE, node, preferredSize) { + private static final long serialVersionUID = 1297900641952417540L; + + @Override + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + + @Override + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + }; + img.setLayoutData(CmsUiUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + updateContent(img); + addListeners(img); + return img; + } + + protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, + int rightWeight) { + Composite comp = new Composite(section, SWT.NONE); + comp.setLayoutData(CmsUiUtils.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); + CmsUiUtils.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(); + Section parSection = section.getParentSection(); + sessionNode.remove(); + session.save(); + refresh(parSection); + layout(parSection); + } catch (RepositoryException re) { + throw new CmsException("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); + EditablePart ep = findDataParent(btn); + if (ep != null && ep instanceof EditableMultiStringProperty) { + EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; + List values = emsp.getValues(); + if (values.contains(obj)) { + values.remove(values.indexOf(obj)); + emsp.setValues(values); + try { + save(emsp); + // TODO workaround to force refresh + edit(emsp, 0); + cancelEdit(); + } catch (RepositoryException e1) { + throw new CmsException("Unable to remove value " + obj, e1); + } + layout(emsp); + } + } + } + } + }; + } + + protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { + try { + // TODO Clean this: + // Format strings to replace \n + value = value.replaceAll("\n", "
"); + // 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.setProperty(propName, value); + } catch (ValueFormatException vfe) { + log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); + } + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java new file mode 100644 index 000000000..7e1b8b029 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java @@ -0,0 +1,26 @@ +package org.argeo.cms.ui.forms; + +/** Syles used */ +public enum FormStyle { + // 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; + + public String style() { + return form.name() + '_' + name(); + } + + // TODO clean button style management + public final static String BUTTON_SUFFIX = "_btn"; +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java new file mode 100644 index 000000000..a5475d6e5 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java @@ -0,0 +1,197 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsView; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Utilitary methods to ease implementation of CMS forms */ +public class FormUtils { + private final static Log log = LogFactory.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 Calendar parseDate(DateFormat dateFormat, String calStr) { + Calendar cal = null; + if (EclipseUiUtils.notEmpty(calStr)) { + try { + Date date = dateFormat.parse(calStr); + cal = new GregorianCalendar(); + cal.setTime(date); + } catch (ParseException pe) { + // Silent + log.warn("Unable to parse date: " + calStr + " - msg: " + + pe.getMessage()); + } + } + return cal; + } + + /** 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 + */ + public static String getPhoneLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + builder.append("").append(label) + .append(""); + 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 + */ + public static String getMailLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + value = replaceAmpersand(value); + builder.append("").append(label).append(""); + 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("" + label + ""); + return builder.toString(); + } + + private static String AMPERSAND = "&"; + + /** + * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to + * avoid SAXParseException 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() { + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java new file mode 100644 index 000000000..3f588d1ea --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java @@ -0,0 +1,169 @@ +package org.argeo.cms.ui.forms; + +import java.io.StringReader; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.rap.rwt.SingletonUtil; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Copy of RAP v2.3 since it is in an internal package. + */ +class MarkupValidatorCopy { + + // Used by Eclipse Scout project + public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; + + private static final String DTD = createDTD(); + private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); + private final SAXParser saxParser; + + public static MarkupValidatorCopy getInstance() { + return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); + } + + public MarkupValidatorCopy() { + saxParser = createSAXParser(); + } + + public void validate(String text) { + StringBuilder markup = new StringBuilder(); + markup.append(DTD); + markup.append(""); + markup.append(text); + markup.append(""); + InputSource inputSource = new InputSource(new StringReader(markup.toString())); + try { + saxParser.parse(inputSource, new MarkupHandler()); + } catch (RuntimeException exception) { + throw exception; + } catch (Exception exception) { + throw new IllegalArgumentException("Failed to parse markup text", exception); + } + } + + public static boolean isValidationDisabledFor(Widget widget) { + return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); + } + + private static SAXParser createSAXParser() { + SAXParser result = null; + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + try { + result = parserFactory.newSAXParser(); + } catch (Exception exception) { + throw new RuntimeException("Failed to create SAX parser", exception); + } + return result; + } + + private static String createDTD() { + StringBuilder result = new StringBuilder(); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append("]>"); + return result.toString(); + } + + private static Map createSupportedElementsMap() { + Map result = new HashMap(); + result.put("html", new String[0]); + result.put("br", new String[0]); + result.put("b", new String[] { "style" }); + result.put("strong", new String[] { "style" }); + result.put("i", new String[] { "style" }); + result.put("em", new String[] { "style" }); + result.put("sub", new String[] { "style" }); + result.put("sup", new String[] { "style" }); + result.put("big", new String[] { "style" }); + result.put("small", new String[] { "style" }); + result.put("del", new String[] { "style" }); + result.put("ins", new String[] { "style" }); + result.put("code", new String[] { "style" }); + result.put("samp", new String[] { "style" }); + result.put("kbd", new String[] { "style" }); + result.put("var", new String[] { "style" }); + result.put("cite", new String[] { "style" }); + result.put("dfn", new String[] { "style" }); + result.put("q", new String[] { "style" }); + result.put("abbr", new String[] { "style", "title" }); + result.put("span", new String[] { "style" }); + result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); + result.put("a", new String[] { "style", "href", "target", "title" }); + return result; + } + + private static class MarkupHandler extends DefaultHandler { + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) { + checkSupportedElements(name, attributes); + checkSupportedAttributes(name, attributes); + checkMandatoryAttributes(name, attributes); + } + + private static void checkSupportedElements(String elementName, Attributes attributes) { + if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { + throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); + } + } + + private static void checkSupportedAttributes(String elementName, Attributes attributes) { + if (attributes.getLength() > 0) { + List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); + int index = 0; + String attributeName = attributes.getQName(index); + while (attributeName != null) { + if (!supportedAttributes.contains(attributeName)) { + String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; + message = MessageFormat.format(message, new Object[] { attributeName, elementName }); + throw new IllegalArgumentException(message); + } + index++; + attributeName = attributes.getQName(index); + } + } + } + + private static void checkMandatoryAttributes(String elementName, Attributes attributes) { + checkIntAttribute(elementName, attributes, "img", "width"); + checkIntAttribute(elementName, attributes, "img", "height"); + } + + private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, + String checkedAttributeName) { + if (checkedElementName.equals(elementName)) { + String attribute = attributes.getValue(checkedAttributeName); + try { + Integer.parseInt(attribute); + } catch (NumberFormatException exception) { + String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; + Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; + message = MessageFormat.format(message, arguments); + throw new IllegalArgumentException(message); + } + } + } + + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java index 8aab1ec21..4e0067521 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java @@ -18,9 +18,10 @@ import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.Session; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; @@ -29,7 +30,6 @@ import org.argeo.eclipse.ui.fs.FsUiConstants; import org.argeo.eclipse.ui.fs.FsUiUtils; import org.argeo.eclipse.ui.fs.NioFileLabelProvider; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeUtils; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; @@ -277,7 +277,7 @@ public class CmsFsBrowser extends Composite { else nameStr = path.getFileName().toString(); elemBtn.setText(nameStr + " >> "); - CmsUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); + CmsUiUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); elemBtn.addSelectionListener(new SelectionAdapter() { private static final long serialVersionUID = -4103695476023480651L; @@ -323,7 +323,7 @@ public class CmsFsBrowser extends Composite { } private void populateBookmarks(Composite parent) { - CmsUtils.clear(parent); + CmsUiUtils.clear(parent); parent.setLayout(new GridLayout()); ISelectionChangedListener selList = new BookmarksSelChangeListener(); diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java index ffb5f0a83..f45629b04 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java @@ -18,7 +18,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.cms.CmsException; -import org.argeo.cms.util.CmsUtils; +import org.argeo.cms.ui.util.CmsUiUtils; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.dialogs.SingleValue; import org.eclipse.jface.dialogs.MessageDialog; @@ -70,7 +70,7 @@ public class FsContextMenu extends Shell { Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - CmsUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); + CmsUiUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); createContextMenu(boxCmp); addShellListener(new ActionsShellListener()); @@ -82,8 +82,8 @@ public class FsContextMenu extends Shell { Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); btn.setText(getLabel(actionId)); btn.setLayoutData(EclipseUiUtils.fillWidth()); - CmsUtils.markup(btn); - CmsUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); + CmsUiUtils.markup(btn); + CmsUiUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); btn.setData(KEY_ACTION_ID, actionId); btn.addSelectionListener(asl); actionButtons.put(actionId, btn); diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/img/Dummy.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/img/Dummy.java deleted file mode 100644 index 4c48f7114..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/img/Dummy.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.cms.ui.img; - -public class Dummy { - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java index 6417b25fe..9ffddd8f3 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java @@ -1,6 +1,6 @@ package org.argeo.cms.ui.internal; -import org.argeo.node.NodeState; +import org.argeo.api.NodeState; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java index 3bb1fdf15..5602d991b 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java @@ -2,8 +2,8 @@ package org.argeo.cms.ui.internal; import javax.jcr.RepositoryException; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.widgets.EditableImage; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.widgets.EditableImage; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; @@ -41,16 +41,16 @@ public class SimpleEditableImage extends EditableImage { protected String createImgTag() throws RepositoryException { String imgTag; if (src != null) - imgTag = CmsUtils.img(src, imageSize); + imgTag = CmsUiUtils.img(src, imageSize); else - imgTag = CmsUtils.noImg(imageSize != null ? imageSize + imgTag = CmsUiUtils.noImg(imageSize != null ? imageSize : NO_IMAGE_SIZE); return imgTag; } protected Text createText(Composite box, String style) { Text text = new Text(box, getStyle()); - CmsUtils.style(text, style); + CmsUiUtils.style(text, style); return text; } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java index 88cd17b81..5e21cc428 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java @@ -1,6 +1,6 @@ package org.argeo.cms.ui.internal.rwt; -import org.argeo.cms.util.LoginEntryPoint; +import org.argeo.cms.ui.util.LoginEntryPoint; import org.eclipse.rap.rwt.application.Application; import org.eclipse.rap.rwt.application.Application.OperationMode; import org.eclipse.rap.rwt.application.ApplicationConfiguration; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java index d5e639f62..7a0603eb7 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java @@ -25,7 +25,7 @@ import javax.jcr.RepositoryException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.argeo.node.NodeConstants; +import org.argeo.api.NodeConstants; public class DefaultRepositoryRegister extends Observable implements RepositoryRegister { /** Key for a JCR repository alias */ diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java index b688e6259..8f9ac1c75 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java @@ -24,7 +24,7 @@ import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.utils.JcrItemsComparator; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java index 9cccb52b1..0a662e216 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java @@ -26,12 +26,12 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.api.security.Keyring; import org.argeo.cms.ui.jcr.model.RepositoriesElem; import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; import org.argeo.eclipse.ui.TreeParent; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; -import org.argeo.node.security.Keyring; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java index d67b1330c..5883be5ba 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java @@ -24,7 +24,7 @@ import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.utils.JcrItemsComparator; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java index ad173cf38..58bbc3baf 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java @@ -24,11 +24,11 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.SimpleCredentials; +import org.argeo.api.NodeUtils; +import org.argeo.api.security.Keyring; import org.argeo.cms.ArgeoNames; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.TreeParent; -import org.argeo.node.NodeUtils; -import org.argeo.node.security.Keyring; /** Root of a remote repository */ public class RemoteRepositoryElem extends RepositoryElem { diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java index c772424b7..a5af700b1 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java @@ -24,13 +24,13 @@ import javax.jcr.RepositoryException; import javax.jcr.RepositoryFactory; import javax.jcr.Session; +import org.argeo.api.NodeUtils; +import org.argeo.api.security.Keyring; import org.argeo.cms.ArgeoNames; import org.argeo.cms.ui.jcr.RepositoryRegister; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.TreeParent; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; -import org.argeo.node.NodeUtils; -import org.argeo.node.security.Keyring; /** * UI Tree component that implements the Argeo abstraction of a diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java new file mode 100644 index 000000000..229378674 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java @@ -0,0 +1,266 @@ +package org.argeo.cms.ui.script; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.script.Invocable; +import javax.script.ScriptException; + +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.CmsPane; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.util.SimpleErgonomics; +import org.argeo.eclipse.ui.Selected; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.application.EntryPointFactory; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +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; + +public class AppUi implements CmsUiProvider, Branding { + private final CmsScriptApp app; + + private CmsUiProvider ui; + private String createUi; + private Object impl; + private String script; + // private Branding branding = new Branding(); + + private EntryPointFactory factory; + + // Branding + private String themeId; + private String additionalHeaders; + private String bodyHtml; + private String pageTitle; + private String pageOverflow; + private String favicon; + + public AppUi(CmsScriptApp app) { + this.app = app; + } + + public AppUi(CmsScriptApp app, String scriptPath) { + this.app = app; + this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC), app.getScriptEngine(), scriptPath); + } + + public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) { + this.app = app; + this.ui = uiProvider; + } + + public AppUi(CmsScriptApp app, EntryPointFactory factory) { + this.app = app; + this.factory = factory; + } + + public void apply(Repository repository, Application application, Branding appBranding, String path) { + Map factoryProperties = new HashMap<>(); + if (appBranding != null) + appBranding.applyBranding(factoryProperties); + applyBranding(factoryProperties); + if (factory != null) { + application.addEntryPoint("/" + path, factory, factoryProperties); + } else { + EntryPointFactory entryPointFactory = new EntryPointFactory() { + @Override + public EntryPoint create() { + SimpleErgonomics ergonomics = new SimpleErgonomics(repository, "main", "/home/root/argeo:keyring", + AppUi.this, factoryProperties); +// CmsUiProvider header = app.getHeader(); +// if (header != null) +// ergonomics.setHeader(header); + app.applySides(ergonomics); + Integer headerHeight = app.getHeaderHeight(); + if (headerHeight != null) + ergonomics.setHeaderHeight(headerHeight); + return ergonomics; + } + }; + application.addEntryPoint("/" + path, entryPointFactory, factoryProperties); + } + } + + public void setUi(CmsUiProvider uiProvider) { + this.ui = uiProvider; + } + + public void applyBranding(Map properties) { + if (themeId != null) + properties.put(WebClient.THEME_ID, themeId); + if (additionalHeaders != null) + properties.put(WebClient.HEAD_HTML, additionalHeaders); + if (bodyHtml != null) + properties.put(WebClient.BODY_HTML, bodyHtml); + if (pageTitle != null) + properties.put(WebClient.PAGE_TITLE, pageTitle); + if (pageOverflow != null) + properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); + if (favicon != null) + properties.put(WebClient.FAVICON, favicon); + } + + // public Branding getBranding() { + // return branding; + // } + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + CmsPane cmsPane = new CmsPane(parent, SWT.NONE); + + if (false) { + // QA + CmsUiUtils.style(cmsPane.getQaArea(), "qa"); + Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); + CmsUiUtils.style(reload, "qa"); + reload.setText("Reload"); + reload.addSelectionListener(new Selected() { + private static final long serialVersionUID = 1L; + + @Override + public void widgetSelected(SelectionEvent e) { + new Thread() { + @Override + public void run() { + app.reload(); + } + }.start(); + RWT.getClient().getService(JavaScriptExecutor.class) + .execute("setTimeout('location.reload()',1000)"); + } + }); + + // Support + CmsUiUtils.style(cmsPane.getSupportArea(), "support"); + Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); + CmsUiUtils.style(msg, "support"); + msg.setText("UNSUPPORTED DEVELOPMENT VERSION"); + } + + if (ui != null) { + ui.createUi(cmsPane.getMainArea(), context); + } + if (createUi != null) { + Invocable invocable = (Invocable) app.getScriptEngine(); + try { + invocable.invokeFunction(createUi, cmsPane.getMainArea(), context); + + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if (impl != null) { + Invocable invocable = (Invocable) app.getScriptEngine(); + try { + invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context); + + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // Invocable invocable = (Invocable) app.getScriptEngine(); + // try { + // invocable.invokeMethod(AppUi.this, "initUi", parent, context); + // + // } catch (NoSuchMethodException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } catch (ScriptException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + + return null; + } + + public void setCreateUi(String createUi) { + this.createUi = createUi; + } + + public void setImpl(Object impl) { + this.impl = impl; + } + + public Object getImpl() { + return impl; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + // Branding + public String getThemeId() { + return themeId; + } + + public void setThemeId(String themeId) { + this.themeId = themeId; + } + + public String getAdditionalHeaders() { + return additionalHeaders; + } + + public void setAdditionalHeaders(String additionalHeaders) { + this.additionalHeaders = additionalHeaders; + } + + public String getBodyHtml() { + return bodyHtml; + } + + public void setBodyHtml(String bodyHtml) { + this.bodyHtml = bodyHtml; + } + + public String getPageTitle() { + return pageTitle; + } + + public void setPageTitle(String pageTitle) { + this.pageTitle = pageTitle; + } + + public String getPageOverflow() { + return pageOverflow; + } + + public void setPageOverflow(String pageOverflow) { + this.pageOverflow = pageOverflow; + } + + public String getFavicon() { + return favicon; + } + + public void setFavicon(String favicon) { + this.favicon = favicon; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Branding.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Branding.java new file mode 100644 index 000000000..f72338ef7 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Branding.java @@ -0,0 +1,20 @@ +package org.argeo.cms.ui.script; + +import java.util.Map; + +public interface Branding { + public void applyBranding(Map properties); + + public String getThemeId(); + + public String getAdditionalHeaders(); + + public String getBodyHtml(); + + public String getPageTitle(); + + public String getPageOverflow(); + + public String getFavicon(); + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptApp.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptApp.java new file mode 100644 index 000000000..c3e1a72e6 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptApp.java @@ -0,0 +1,420 @@ +package org.argeo.cms.ui.script; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.script.ScriptEngine; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsConstants; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.util.BundleResourceLoader; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.util.SimpleErgonomics; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.http.HttpContext; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; + +public class CmsScriptApp implements Branding { + public final static String CONTEXT_NAME = "contextName"; + + ServiceRegistration appConfigReg; + + private ScriptEngine scriptEngine; + + private final static Log log = LogFactory.getLog(CmsScriptApp.class); + + private String webPath; + private String repo = "(cn=node)"; + + // private Branding branding = new Branding(); + private Theme theme; + + private List resources = new ArrayList<>(); + + private Map ui = new HashMap<>(); + + private CmsUiProvider header; + private Integer headerHeight = null; + private CmsUiProvider lead; + private CmsUiProvider end; + private CmsUiProvider footer; + + // Branding + private String themeId; + private String additionalHeaders; + private String bodyHtml; + private String pageTitle; + private String pageOverflow; + private String favicon; + + public CmsScriptApp(ScriptEngine scriptEngine) { + super(); + this.scriptEngine = scriptEngine; + } + + public void apply(BundleContext bundleContext, Repository repository, Application application) { + BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); + + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); + + application.setExceptionHandler(new CmsExceptionHandler()); + + // loading animated gif + application.addResource(CmsConstants.LOADING_IMAGE, createResourceLoader(CmsConstants.LOADING_IMAGE)); + // empty image + application.addResource(CmsConstants.NO_IMAGE, createResourceLoader(CmsConstants.NO_IMAGE)); + + for (String resource : resources) { + application.addResource(resource, bundleRL); + if (log.isTraceEnabled()) + log.trace("Resource " + resource); + } + + if (theme != null) { + theme.apply(application); + String themeHeaders = theme.getAdditionalHeaders(); + if (themeHeaders != null) { + if (additionalHeaders == null) + additionalHeaders = themeHeaders; + else + additionalHeaders = themeHeaders + "\n" + additionalHeaders; + } + themeId = theme.getThemeId(); + } + + // client JavaScript + Bundle appBundle = bundleRL.getBundle(); + BundleContext bc = appBundle.getBundleContext(); + HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class)); + HttpContext httpContext = new BundleHttpContext(bc); + Enumeration themeResources = appBundle.findEntries("/js/", "*", true); + if (themeResources != null) + bundleResources: while (themeResources.hasMoreElements()) { + try { + String name = themeResources.nextElement().getPath(); + if (name.endsWith("/")) + continue bundleResources; + String alias = "/" + getWebPath() + name; + + httpService.registerResources(alias, name, httpContext); + if (log.isDebugEnabled()) + log.debug("Mapped " + name + " to alias " + alias); + + } catch (NamespaceException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // App UIs + for (String appUiName : ui.keySet()) { + AppUi appUi = ui.get(appUiName); + appUi.apply(repository, application, this, appUiName); + + } + + } + + public void applySides(SimpleErgonomics simpleErgonomics) { + simpleErgonomics.setHeader(header); + simpleErgonomics.setLead(lead); + simpleErgonomics.setEnd(end); + simpleErgonomics.setFooter(footer); + } + + public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) { + Hashtable props = new Hashtable<>(); + props.put(CONTEXT_NAME, webPath); + appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props); + } + + public void reload() { + BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext(); + ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference()); + appConfigReg.unregister(); + register(bundleContext, appConfig); + + // BundleContext bundleContext = (BundleContext) + // getScriptEngine().get("bundleContext"); + // try { + // Bundle bundle = bundleContext.getBundle(); + // bundle.stop(); + // bundle.start(); + // } catch (BundleException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + } + + private static ResourceLoader createResourceLoader(final String resourceName) { + return new ResourceLoader() { + public InputStream getResourceAsStream(String resourceName) throws IOException { + return getClass().getClassLoader().getResourceAsStream(resourceName); + } + }; + } + + public List getResources() { + return resources; + } + + public AppUi newUi(String name) { + if (ui.containsKey(name)) + throw new IllegalArgumentException("There is already an UI named " + name); + AppUi appUi = new AppUi(this); + // appUi.setApp(this); + ui.put(name, appUi); + return appUi; + } + + public void addUi(String name, AppUi appUi) { + if (ui.containsKey(name)) + throw new IllegalArgumentException("There is already an UI named " + name); + // appUi.setApp(this); + ui.put(name, appUi); + } + + public void applyBranding(Map properties) { + if (themeId != null) + properties.put(WebClient.THEME_ID, themeId); + if (additionalHeaders != null) + properties.put(WebClient.HEAD_HTML, additionalHeaders); + if (bodyHtml != null) + properties.put(WebClient.BODY_HTML, bodyHtml); + if (pageTitle != null) + properties.put(WebClient.PAGE_TITLE, pageTitle); + if (pageOverflow != null) + properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); + if (favicon != null) + properties.put(WebClient.FAVICON, favicon); + } + + class CmsExceptionHandler implements ExceptionHandler { + + @Override + public void handleException(Throwable throwable) { + // TODO be smarter + CmsUiUtils.getCmsView().exception(throwable); + } + + } + + // public Branding getBranding() { + // return branding; + // } + + ScriptEngine getScriptEngine() { + return scriptEngine; + } + + public static String toJson(Node node) { + try { + StringBuilder sb = new StringBuilder(); + sb.append('{'); + PropertyIterator pit = node.getProperties(); + int count = 0; + while (pit.hasNext()) { + Property p = pit.nextProperty(); + int type = p.getType(); + if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) { + Node ref = p.getNode(); + if (count != 0) + sb.append(','); + // TODO limit depth? + sb.append(toJson(ref)); + count++; + } else if (!p.isMultiple()) { + if (count != 0) + sb.append(','); + sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"'); + count++; + } + } + sb.append('}'); + return sb.toString(); + } catch (RepositoryException e) { + throw new CmsException("Cannot convert " + node + " to JSON", e); + } + } + + public void fromJson(Node node, String json) { + // TODO + } + + public Theme getTheme() { + return theme; + } + + public void setTheme(Theme theme) { + this.theme = theme; + } + + public String getWebPath() { + return webPath; + } + + public void setWebPath(String context) { + this.webPath = context; + } + + public String getRepo() { + return repo; + } + + public void setRepo(String repo) { + this.repo = repo; + } + + public Map getUi() { + return ui; + } + + public void setUi(Map ui) { + this.ui = ui; + } + + // Branding + public String getThemeId() { + return themeId; + } + + public void setThemeId(String themeId) { + this.themeId = themeId; + } + + public String getAdditionalHeaders() { + return additionalHeaders; + } + + public void setAdditionalHeaders(String additionalHeaders) { + this.additionalHeaders = additionalHeaders; + } + + public String getBodyHtml() { + return bodyHtml; + } + + public void setBodyHtml(String bodyHtml) { + this.bodyHtml = bodyHtml; + } + + public String getPageTitle() { + return pageTitle; + } + + public void setPageTitle(String pageTitle) { + this.pageTitle = pageTitle; + } + + public String getPageOverflow() { + return pageOverflow; + } + + public void setPageOverflow(String pageOverflow) { + this.pageOverflow = pageOverflow; + } + + public String getFavicon() { + return favicon; + } + + public void setFavicon(String favicon) { + this.favicon = favicon; + } + + public CmsUiProvider getHeader() { + return header; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public Integer getHeaderHeight() { + return headerHeight; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public CmsUiProvider getLead() { + return lead; + } + + public void setLead(CmsUiProvider lead) { + this.lead = lead; + } + + public CmsUiProvider getEnd() { + return end; + } + + public void setEnd(CmsUiProvider end) { + this.end = end; + } + + public CmsUiProvider getFooter() { + return footer; + } + + public void setFooter(CmsUiProvider footer) { + this.footer = footer; + } + + static class BundleHttpContext implements HttpContext { + private BundleContext bundleContext; + + public BundleHttpContext(BundleContext bundleContext) { + super(); + this.bundleContext = bundleContext; + } + + @Override + public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO Auto-generated method stub + return true; + } + + @Override + public URL getResource(String name) { + + return bundleContext.getBundle().getEntry(name); + } + + @Override + public String getMimeType(String name) { + return null; + } + + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java new file mode 100644 index 000000000..d7c1a63ce --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java @@ -0,0 +1,121 @@ +package org.argeo.cms.ui.script; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import javax.jcr.Repository; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.wiring.BundleWiring; + +public class CmsScriptRwtApplication implements ApplicationConfiguration { + public final static String APP = "APP"; + public final static String BC = "BC"; + + private final Log log = LogFactory.getLog(CmsScriptRwtApplication.class); + + BundleContext bundleContext; + Repository repository; + + ScriptEngine engine; + + public void init(BundleContext bundleContext) { + this.bundleContext = bundleContext; + ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); + ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); + try { +// Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager +// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); +// engine = scriptEngineManager.getEngineByName("JavaScript"); +// if (engine == null) {// Nashorn +// Thread.currentThread().setContextClassLoader(originalCcl); +// scriptEngineManager = new ScriptEngineManager(); +// Thread.currentThread().setContextClassLoader(bundleCl); +// engine = scriptEngineManager.getEngineByName("JavaScript"); +// } + engine = loadScriptEngine(originalCcl, bundleCl); + + // Load script + URL appUrl = bundleContext.getBundle().getEntry("cms/app.js"); + // System.out.println("Loading " + appUrl); + // System.out.println("Loading " + appUrl.getHost()); + // System.out.println("Loading " + appUrl.getPath()); + + CmsScriptApp app = new CmsScriptApp(engine); + engine.put(APP, app); + engine.put(BC, bundleContext); + try (Reader reader = new InputStreamReader(appUrl.openStream())) { + engine.eval(reader); + } catch (IOException | ScriptException e) { + throw new CmsException("Cannot execute " + appUrl, e); + } + + if (log.isDebugEnabled()) + log.debug("CMS script app initialized from " + appUrl); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + Thread.currentThread().setContextClassLoader(originalCcl); + } + } + + public void destroy(BundleContext bundleContext) { + engine = null; + } + + @Override + public void configure(Application application) { + load(application); + } + + void load(Application application) { + CmsScriptApp app = getApp(); + app.apply(bundleContext, repository, application); + if (log.isDebugEnabled()) + log.debug("CMS script app loaded to " + app.getWebPath()); + } + + CmsScriptApp getApp() { + if (engine == null) + throw new IllegalStateException("CMS script app is not initialized"); + return (CmsScriptApp) engine.get(APP); + } + + void update() { + + try { + bundleContext.getBundle().update(); + } catch (BundleException e) { + e.printStackTrace(); + } + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) { + Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager + ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); + ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); + if (engine == null) {// Nashorn + Thread.currentThread().setContextClassLoader(originalCcl); + scriptEngineManager = new ScriptEngineManager(); + Thread.currentThread().setContextClassLoader(bundleCl); + engine = scriptEngineManager.getEngineByName("JavaScript"); + } + return engine; + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptAppActivator.java new file mode 100644 index 000000000..7813156ec --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptAppActivator.java @@ -0,0 +1,46 @@ +package org.argeo.cms.ui.script; + +import javax.jcr.Repository; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class ScriptAppActivator implements BundleActivator { + private final static Log log = LogFactory.getLog(ScriptAppActivator.class); + + @Override + public void start(BundleContext context) throws Exception { + try { + CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication(); + appConfig.init(context); + CmsScriptApp app = appConfig.getApp(); + ServiceTracker repoSt = new ServiceTracker(context, + FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) { + + @Override + public Repository addingService(ServiceReference reference) { + Repository repository = super.addingService(reference); + appConfig.setRepository(repository); + CmsScriptApp app = appConfig.getApp(); + app.register(context, appConfig); + return repository; + } + + }; + repoSt.open(); + } catch (Exception e) { + log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e); + throw e; + } + } + + @Override + public void stop(BundleContext context) throws Exception { + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptUi.java new file mode 100644 index 000000000..bf68fc299 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptUi.java @@ -0,0 +1,116 @@ +package org.argeo.cms.ui.script; + +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.osgi.framework.BundleContext; + +class ScriptUi implements CmsUiProvider { + private final static Log log = LogFactory.getLog(ScriptUi.class); + + private boolean development = true; + private ScriptEngine scriptEngine; + + private URL appUrl; + // private BundleContext bundleContext; + // private String path; + + // private Bindings bindings; + // private String script; + + public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) { + this.scriptEngine = scriptEngine; +//// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); +// ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); +// ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); +// try { +//// Thread.currentThread().setContextClassLoader(bundleCl); +//// scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); +//// scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext); +// scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } finally { +// Thread.currentThread().setContextClassLoader(originalCcl); +// } + this.appUrl = bundleContext.getBundle().getEntry(path); + load(); + } + + private void load() { +// try (Reader reader = new InputStreamReader(appUrl.openStream())) { +// scriptEngine.eval(reader); +// } catch (IOException | ScriptException e) { +// log.warn("Cannot execute " + appUrl, e); +// } + + try { + scriptEngine.eval("load('" + appUrl + "')"); + } catch (ScriptException e) { + log.warn("Cannot execute " + appUrl, e); + } + + } + + // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws + // ScriptException { + // super(); + // this.scriptEngine = scriptEngine; + // this.script = script; + // bindings = scriptEngine.createBindings(); + // scriptEngine.eval(script, bindings); + // } + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + long begin = System.currentTimeMillis(); + // if (bindings == null) { + // bindings = scriptEngine.createBindings(); + // try { + // scriptEngine.eval(script, bindings); + // } catch (ScriptException e) { + // log.warn("Cannot evaluate script", e); + // } + // } + // Bindings bindings = scriptEngine.createBindings(); + // bindings.put("parent", parent); + // bindings.put("context", context); + // URL appUrl = bundleContext.getBundle().getEntry(path); + // try (Reader reader = new InputStreamReader(appUrl.openStream())) { + // scriptEngine.eval(reader,bindings); + // } catch (IOException | ScriptException e) { + // log.warn("Cannot execute " + appUrl, e); + // } + + if (development) + load(); + + Invocable invocable = (Invocable) scriptEngine; + try { + invocable.invokeFunction("createUi", parent, context); + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ScriptException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + long duration = System.currentTimeMillis() - begin; + if (log.isTraceEnabled()) + log.trace(appUrl + " UI in " + duration + " ms"); + return null; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Theme.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Theme.java new file mode 100644 index 000000000..c053b4700 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/Theme.java @@ -0,0 +1,18 @@ +package org.argeo.cms.ui.script; + +import org.argeo.cms.ui.util.CmsTheme; +import org.osgi.framework.BundleContext; + +/** @deprecated Use CmsTheme instead. */ +@Deprecated +public class Theme extends CmsTheme { + + public Theme(BundleContext bundleContext, String symbolicName) { + super(bundleContext, symbolicName); + } + + public Theme(BundleContext bundleContext) { + super(bundleContext); + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/script/cms.js b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/cms.js new file mode 100644 index 000000000..be9618dcb --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/script/cms.js @@ -0,0 +1,90 @@ +// CMS +var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage'); + +var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp'); +var AppUi = Java.type('org.argeo.cms.ui.script.AppUi'); +var Theme = Java.type('org.argeo.cms.ui.script.Theme'); +var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi'); +var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils'); +var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader'); +var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink'); +var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink'); +var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink'); + +// SWT +var SWT = Java.type('org.eclipse.swt.SWT'); +var Composite = Java.type('org.eclipse.swt.widgets.Composite'); +var Label = Java.type('org.eclipse.swt.widgets.Label'); +var Button = Java.type('org.eclipse.swt.widgets.Button'); +var Text = Java.type('org.eclipse.swt.widgets.Text'); +var Browser = Java.type('org.eclipse.swt.browser.Browser'); + +var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout'); +var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout'); +var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout'); +var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout'); +var GridData = Java.type('org.eclipse.swt.layout.GridData'); + +function loadNode(node) { + var json = CmsScriptApp.toJson(node) + var fromJson = JSON.parse(json) + return fromJson +} + +function newArea(parent, style, layout) { + var control = new Composite(parent, SWT.NONE) + control.setLayout(layout) + CmsUtils.style(control, style) + return control +} + +function newLabel(parent, style, text) { + var control = new Label(parent, SWT.WRAP) + control.setText(text) + CmsUtils.style(control, style) + CmsUtils.markup(control) + return control +} + +function newButton(parent, style, text) { + var control = new Button(parent, SWT.FLAT) + control.setText(text) + CmsUtils.style(control, style) + CmsUtils.markup(control) + return control +} + +function newFormLabel(parent, style, text) { + return newLabel(parent, style, '' + text + '') +} + +function newText(parent, style, msg) { + var control = new Text(parent, SWT.NONE) + control.setMessage(msg) + CmsUtils.style(control, style) + return control +} + +function newScrolledPage(parent) { + var scrolled = new ScrolledPage(parent, SWT.NONE) + scrolled.setLayoutData(CmsUtils.fillAll()) + scrolled.setLayout(CmsUtils.noSpaceGridLayout()) + var page = new Composite(scrolled, SWT.NONE) + page.setLayout(CmsUtils.noSpaceGridLayout()) + page.setBackgroundMode(SWT.INHERIT_NONE) + return page +} + +function gridData(control) { + var gridData = new GridData() + control.setLayoutData(gridData) + return gridData +} + +function gridData(control, hAlign, vAlign) { + var gridData = new GridData(hAlign, vAlign, false, false) + control.setLayoutData(gridData) + return gridData +} + +// print(__FILE__, __LINE__, __DIR__) diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/PickUpUserDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/PickUpUserDialog.java index 36a1da28e..a521b973f 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/PickUpUserDialog.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/PickUpUserDialog.java @@ -18,13 +18,13 @@ package org.argeo.cms.ui.useradmin; import java.util.ArrayList; import java.util.List; +import org.argeo.api.NodeConstants; import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; -import org.argeo.node.NodeConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.TrayDialog; import org.eclipse.jface.viewers.DoubleClickEvent; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/UserLP.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/UserLP.java index 2d494ef90..05c6f3778 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/UserLP.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/UserLP.java @@ -1,7 +1,7 @@ package org.argeo.cms.ui.useradmin; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.UserAdminUtils; -import org.argeo.node.NodeConstants; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.swt.SWT; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/BundleResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/BundleResourceLoader.java new file mode 100644 index 000000000..13df119b1 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/BundleResourceLoader.java @@ -0,0 +1,34 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.argeo.cms.CmsException; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; + +/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */ +public class BundleResourceLoader implements ResourceLoader { + private final Bundle bundle; + + public BundleResourceLoader(Bundle bundle) { + this.bundle = bundle; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + URL res = bundle.getEntry(resourceName); + if (res == null) { + res = bundle.getResource(resourceName); + if (res == null) + throw new CmsException("Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName()); + } + return res.openStream(); + } + + public Bundle getBundle() { + return bundle; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java new file mode 100644 index 000000000..b3c4a8a57 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java @@ -0,0 +1,274 @@ +package org.argeo.cms.ui.util; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeUtils; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; + +/** A link to an internal or external location. */ +public class CmsLink implements CmsUiProvider { + private final static Log log = LogFactory.getLog(CmsLink.class); + private BundleContext bundleContext; + + private String label; + private String custom; + private String target; + private String image; + private MouseListener mouseListener; + + private int horizontalAlignment = SWT.CENTER; + private int verticalAlignment = SWT.CENTER; + + private String loggedInLabel = null; + private String loggedInTarget = null; + + // internal + // private Boolean isUrl = false; + private Integer imageWidth, imageHeight; + + public CmsLink() { + super(); + } + + public CmsLink(String label, String target) { + this(label, target, null); + } + + public CmsLink(String label, String target, String custom) { + super(); + this.label = label; + this.target = target; + this.custom = custom; + init(); + } + + public void init() { + if (image != null) { + ImageData image = loadImage(); + if (imageHeight == null && imageWidth == null) { + imageWidth = image.width; + imageHeight = image.height; + } else if (imageHeight == null) { + imageHeight = (imageWidth * image.height) / image.width; + } else if (imageWidth == null) { + imageWidth = (imageHeight * image.width) / image.height; + } + } + } + + /** @return {@link Composite} with a single {@link Label} child. */ + @Override + public Control createUi(final Composite parent, Node context) { +// if (image != null && (imageWidth == null || imageHeight == null)) { +// throw new CmsException("Image is not properly configured." +// + " Make sure bundleContext property is set and init() method has been called."); +// } + + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(CmsUiUtils.noSpaceGridLayout()); + + Label link = new Label(comp, SWT.NONE); + link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); + GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); + if (image != null) { + if (imageHeight != null) + layoutData.heightHint = imageHeight; + if (label == null) + if (imageWidth != null) + layoutData.widthHint = imageWidth; + } + + link.setLayoutData(layoutData); + if (custom != null) { + comp.setData(RWT.CUSTOM_VARIANT, custom); + link.setData(RWT.CUSTOM_VARIANT, custom); + } else { + comp.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK); + link.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK); + } + + // label + StringBuilder labelText = new StringBuilder(); + if (loggedInTarget != null && isLoggedIn()) { + labelText.append(""); + } else if (target != null) { + labelText.append(""); + } + if (image != null) { + registerImageIfNeeded(); + String imageLocation = RWT.getResourceManager().getLocation(image); + labelText.append(""); + + } + + if (loggedInLabel != null && isLoggedIn()) { + labelText.append(' ').append(loggedInLabel); + } else if (label != null) { + labelText.append(' ').append(label); + } + + if ((loggedInTarget != null && isLoggedIn()) || target != null) + labelText.append(""); + + link.setText(labelText.toString()); + + if (mouseListener != null) + link.addMouseListener(mouseListener); + + return comp; + } + + private void registerImageIfNeeded() { + ResourceManager resourceManager = RWT.getResourceManager(); + if (!resourceManager.isRegistered(image)) { + URL res = getImageUrl(); + InputStream inputStream = null; + try { + IOUtils.closeQuietly(inputStream); + inputStream = res.openStream(); + resourceManager.register(image, inputStream); + if (log.isTraceEnabled()) + log.trace("Registered image " + image); + } catch (Exception e) { + throw new CmsException("Cannot load image " + image, e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + } + + private ImageData loadImage() { + URL url = getImageUrl(); + ImageData result = null; + InputStream inputStream = null; + try { + inputStream = url.openStream(); + result = new ImageData(inputStream); + if (log.isTraceEnabled()) + log.trace("Loaded image " + image); + } catch (Exception e) { + throw new CmsException("Cannot load image " + image, e); + } finally { + IOUtils.closeQuietly(inputStream); + } + return result; + } + + private URL getImageUrl() { + URL url; + try { + // pure URL + url = new URL(image); + } catch (MalformedURLException e1) { + url = bundleContext.getBundle().getResource(image); + } + + if (url == null) + throw new CmsException("No image " + image + " available."); + + return url; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setLabel(String label) { + this.label = label; + } + + public void setCustom(String custom) { + this.custom = custom; + } + + public void setTarget(String target) { + this.target = target; + // try { + // new URL(target); + // isUrl = true; + // } catch (MalformedURLException e1) { + // isUrl = false; + // } + } + + public void setImage(String image) { + this.image = image; + } + + public void setLoggedInLabel(String loggedInLabel) { + this.loggedInLabel = loggedInLabel; + } + + public void setLoggedInTarget(String loggedInTarget) { + this.loggedInTarget = loggedInTarget; + } + + public void setMouseListener(MouseListener mouseListener) { + this.mouseListener = mouseListener; + } + + public void setvAlign(String vAlign) { + if ("bottom".equals(vAlign)) { + verticalAlignment = SWT.BOTTOM; + } else if ("top".equals(vAlign)) { + verticalAlignment = SWT.TOP; + } else if ("center".equals(vAlign)) { + verticalAlignment = SWT.CENTER; + } else { + throw new CmsException("Unsupported vertical allignment " + vAlign + " (must be: top, bottom or center)"); + } + } + + protected boolean isLoggedIn() { + return !CurrentUser.isAnonymous(); + } + + public void setImageWidth(Integer imageWidth) { + this.imageWidth = imageWidth; + } + + public void setImageHeight(Integer imageHeight) { + this.imageHeight = imageHeight; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java new file mode 100644 index 000000000..68f1e0f89 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java @@ -0,0 +1,48 @@ +package org.argeo.cms.ui.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Composite; + +/** The main pane of a CMS display, with QA and support areas. */ +public class CmsPane { + + private Composite mainArea; + private Composite qaArea; + private Composite supportArea; + + public CmsPane(Composite parent, int style) { + parent.setLayout(CmsUiUtils.noSpaceGridLayout()); + +// qaArea = new Composite(parent, SWT.NONE); +// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout qaLayout = new RowLayout(); +// qaLayout.spacing = 0; +// qaArea.setLayout(qaLayout); + + mainArea = new Composite(parent, SWT.NONE); + mainArea.setLayout(new GridLayout()); + mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + +// supportArea = new Composite(parent, SWT.NONE); +// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout supportLayout = new RowLayout(); +// supportLayout.spacing = 0; +// supportArea.setLayout(supportLayout); + } + + public Composite getMainArea() { + return mainArea; + } + + public Composite getQaArea() { + return qaArea; + } + + public Composite getSupportArea() { + return supportArea; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsTheme.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsTheme.java new file mode 100644 index 000000000..8b1f874f7 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsTheme.java @@ -0,0 +1,198 @@ +package org.argeo.cms.ui.util; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +/** + * Simplifies the theming of an app (only RAP is supported at this stage).
+ * + * Additional fonts listed in /fonts.txt.
+ * Additional (standard CSS) header in /header.css.
+ * RAP specific CSS files in /rap/*.css.
+ * All images added as additional resources based on extensions + * / ** /*.{png,gif,jpeg,...}.
+ */ +public class CmsTheme { + private final static Log log = LogFactory.getLog(CmsTheme.class); + + private String themeId; + private Map css = new HashMap<>(); + private Map resources = new HashMap<>(); + + private String headerCss; + private List fonts = new ArrayList<>(); + + private String basePath; + private String cssPath; + + public CmsTheme(BundleContext bundleContext) { + this(bundleContext, null); + } + + public CmsTheme(BundleContext bundleContext, String symbolicName) { + Bundle themeBundle; + if (symbolicName == null) { + themeBundle = bundleContext.getBundle(); +// basePath = "/theme/"; +// cssPath = basePath; + } else { + themeBundle = ThemeUtils.findThemeBundle(bundleContext, symbolicName); + } + basePath = "/"; + cssPath = "/rap/"; + this.themeId = RWT.DEFAULT_THEME_ID; + addStyleSheets(themeBundle, new BundleResourceLoader(themeBundle)); + BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); + addResources(themeBRL, "*.png"); + addResources(themeBRL, "*.gif"); + addResources(themeBRL, "*.jpg"); + addResources(themeBRL, "*.jpeg"); + addResources(themeBRL, "*.svg"); + addResources(themeBRL, "*.ico"); + + // fonts + URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt"); + if (fontsUrl != null) { + loadFontsUrl(fontsUrl); + } + + // common CSS header (plain CSS) + URL headerCssUrl = themeBundle.getEntry(basePath + "header.css"); + if (headerCssUrl != null) { + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) { + headerCss = buffer.lines().collect(Collectors.joining("\n")); + } catch (IOException e) { + throw new CmsException("Cannot read " + headerCssUrl, e); + } + } + + } + + public void apply(Application application) { + resources: for (String name : resources.keySet()) { + if (name.startsWith("target/")) + continue resources; // skip maven output + application.addResource(name, resources.get(name)); + if (log.isTraceEnabled()) + log.trace("Added resource " + name); + } + for (String name : css.keySet()) { + application.addStyleSheet(themeId, name, css.get(name)); + if (log.isDebugEnabled()) + log.debug("Added RAP CSS " + name); + } + } + + public String getAdditionalHeaders() { + StringBuilder sb = new StringBuilder(); + if (headerCss != null) { + sb.append("\n"); + } + for (String link : fonts) { + sb.append("\n"); + } + if (sb.length() == 0) + return null; + else + return sb.toString(); + } + + void addStyleSheets(Bundle themeBundle, ResourceLoader ssRL) { + Enumeration themeResources = themeBundle.findEntries(cssPath, "*.css", true); + if (themeResources == null) + return; + while (themeResources.hasMoreElements()) { + String resource = themeResources.nextElement().getPath(); + // remove first '/' so that RWT registers it + resource = resource.substring(1); + if (!resource.endsWith("/")) { + if (css.containsKey(resource)) + log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName()); + css.put(resource, ssRL); + } + + } + + } + + void loadFontsUrl(URL url) { + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { + String line = null; + while ((line = in.readLine()) != null) { + line = line.trim(); + if (!line.equals("") && !line.startsWith("#")) { + fonts.add(line); + } + } + } catch (IOException e) { + throw new CmsException("Cannot load URL " + url, e); + } + } + + void addResources(BundleResourceLoader themeBRL, String pattern) { + Bundle themeBundle = themeBRL.getBundle(); + Enumeration themeResources = themeBundle.findEntries(basePath, pattern, true); + if (themeResources == null) + return; + while (themeResources.hasMoreElements()) { + String resource = themeResources.nextElement().getPath(); + // remove first '/' so that RWT registers it + resource = resource.substring(1); + if (!resource.endsWith("/")) { + if (resources.containsKey(resource)) + log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName()); + resources.put(resource, themeBRL); + } + + } + + } + + public String getThemeId() { + return themeId; + } + + public void setThemeId(String themeId) { + this.themeId = themeId; + } + + public String getBasePath() { + return basePath; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public String getCssPath() { + return cssPath; + } + + public void setCssPath(String cssPath) { + this.cssPath = cssPath; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java new file mode 100644 index 000000000..316e7859f --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java @@ -0,0 +1,248 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.servlet.http.HttpServletRequest; + +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsConstants; +import org.argeo.cms.ui.CmsView; +import org.argeo.eclipse.ui.specific.UiContext; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; + +/** Static utilities for the CMS framework. */ +public class CmsUiUtils implements CmsConstants { + // private final static Log log = LogFactory.getLog(CmsUiUtils.class); + + /** + * The CMS view related to this display, or null if none is available from this + * call. + */ + public static CmsView getCmsView() { + return UiContext.getData(CmsView.KEY); + } + + public static StringBuilder getServerBaseUrl(HttpServletRequest request) { + try { + URL url = new URL(request.getRequestURL().toString()); + StringBuilder buf = new StringBuilder(); + buf.append(url.getProtocol()).append("://").append(url.getHost()); + if (url.getPort() != -1) + buf.append(':').append(url.getPort()); + return buf; + } catch (MalformedURLException e) { + throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e); + } + } + + // + public static String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException { + try { + StringBuilder buf = getServerBaseUrl(request); + buf.append(getDataPath(node)); + return new URL(buf.toString()).toString(); + } catch (MalformedURLException e) { + throw new CmsException("Cannot build data URL for " + node, e); + } + } + + /** A path in the node repository */ + public static String getDataPath(Node node) throws RepositoryException { + return getDataPath(NodeConstants.NODE, node); + } + + public static String getDataPath(String cn, Node node) throws RepositoryException { + return NodeUtils.getDataPath(cn, node); + } + + /** @deprecated Use rowData16px() instead. GridData should not be reused. */ + @Deprecated + public static RowData ROW_DATA_16px = new RowData(16, 16); + + public static GridLayout noSpaceGridLayout() { + return noSpaceGridLayout(new GridLayout()); + } + + public static GridLayout noSpaceGridLayout(int columns) { + return noSpaceGridLayout(new GridLayout(columns, false)); + } + + public static GridLayout noSpaceGridLayout(GridLayout layout) { + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + layout.marginWidth = 0; + layout.marginHeight = 0; + return layout; + } + + // + // GRID DATA + // + public static GridData fillWidth() { + return grabWidth(SWT.FILL, SWT.FILL); + } + + public static GridData fillAll() { + return new GridData(SWT.FILL, SWT.FILL, true, true); + } + + public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { + return new GridData(horizontalAlignment, horizontalAlignment, true, false); + } + + public static RowData rowData16px() { + return new RowData(16, 16); + } + + /** Style widget */ + public static T style(T widget, String style) { + widget.setData(CmsConstants.STYLE, style); + return widget; + } + + /** Enable markups on widget */ + public static T markup(T widget) { + widget.setData(CmsConstants.MARKUP, true); + return widget; + } + + /** + * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}. + * + * @see #markup(Widget) + */ + public static T text(T widget, String txt) { + markup(widget); + if (widget instanceof Label) + ((Label) widget).setText(txt); + else if (widget instanceof Button) + ((Button) widget).setText(txt); + else if (widget instanceof Text) + ((Text) widget).setText(txt); + else + throw new IllegalArgumentException("Unsupported widget type " + widget.getClass()); + return widget; + } + + public static void setItemHeight(Table table, int height) { + table.setData(CmsConstants.ITEM_HEIGHT, height); + } + + /** Dispose all children of a Composite */ + public static void clear(Composite composite) { + for (Control child : composite.getChildren()) + child.dispose(); + } + + // + // JCR + // + public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { + if (has(parent, child)) + return child(parent, child); + return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); + } + + public static Node child(Node parent, Enum en) throws RepositoryException { + return parent.getNode(en.name()); + } + + public static Boolean has(Node parent, Enum en) throws RepositoryException { + return parent.hasNode(en.name()); + } + + public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { + return getOrAdd(parent, en, null); + } + + public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { + if (has(parent, en)) + return child(parent, en); + else if (primaryType == null) + return parent.addNode(en.name()); + else + return parent.addNode(en.name(), primaryType); + } + + // IMAGES + public static String img(String src, String width, String height) { + return imgBuilder(src, width, height).append("/>").toString(); + } + + public static String img(String src, Point size) { + return img(src, Integer.toString(size.x), Integer.toString(size.y)); + } + + public static StringBuilder imgBuilder(String src, String width, String height) { + return new StringBuilder(64).append("").toString(); + } + + /** @return null if not available */ + @Override + public StringBuilder getImageTagBuilder(Node node, Point size) throws RepositoryException { + return getImageTagBuilder(node, Integer.toString(size.x), Integer.toString(size.y)); + } + + /** @return null if not available */ + private StringBuilder getImageTagBuilder(Node node, String width, String height) throws RepositoryException { + String url = getImageUrl(node); + if (url == null) + return null; + return CmsUiUtils.imgBuilder(url, width, height); + } + + /** @return null if not available */ + @Override + public String getImageUrl(Node node) throws RepositoryException { + return CmsUiUtils.getDataPath(node); + // String name = getResourceName(node); + // ResourceManager resourceManager = RWT.getResourceManager(); + // if (!resourceManager.isRegistered(name)) { + // InputStream inputStream = null; + // Binary binary = getImageBinary(node); + // if (binary == null) + // return null; + // try { + // inputStream = binary.getStream(); + // resourceManager.register(name, inputStream); + // } finally { + // IOUtils.closeQuietly(inputStream); + // JcrUtils.closeQuietly(binary); + // } + // if (log.isTraceEnabled()) + // log.trace("Registered image " + name); + // } + // return resourceManager.getLocation(name); + } + + protected String getResourceName(Node node) throws RepositoryException { + String workspace = node.getSession().getWorkspace().getName(); + if (node.hasNode(JCR_CONTENT)) + return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); + else + return workspace + '_' + node.getIdentifier(); + } + + public Binary getImageBinary(Node node) throws RepositoryException { + if (node.isNodeType(NT_FILE)) { + return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); + } else { + return null; + } + } + + public Image getSwtImage(Node node) throws RepositoryException { + InputStream inputStream = null; + Binary binary = getImageBinary(node); + if (binary == null) + return null; + try { + inputStream = binary.getStream(); + return new Image(Display.getCurrent(), inputStream); + } finally { + IOUtils.closeQuietly(inputStream); + JcrUtils.closeQuietly(binary); + } + } + + @Override + public String uploadImage(Node parentNode, String fileName, InputStream in) throws RepositoryException { + InputStream inputStream = null; + try { + String previousResourceName = null; + if (parentNode.hasNode(fileName)) { + Node node = parentNode.getNode(fileName); + previousResourceName = getResourceName(node); + if (node.hasNode(JCR_CONTENT)) { + node.getNode(JCR_CONTENT).remove(); + node.addNode(JCR_CONTENT, NT_RESOURCE); + } + } + + byte[] arr = IOUtils.toByteArray(in); + Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); + inputStream = new ByteArrayInputStream(arr); + ImageData id = new ImageData(inputStream); + processNewImageFile(fileNode, id); + + String mime = Files.probeContentType(Paths.get(fileName)); + fileNode.setProperty(Property.JCR_MIMETYPE, mime); + fileNode.getSession().save(); + + // reset resource manager + ResourceManager resourceManager = RWT.getResourceManager(); + if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { + resourceManager.unregister(previousResourceName); + if (log.isDebugEnabled()) + log.debug("Unregistered image " + previousResourceName); + } + return getImageUrl(fileNode); + } catch (IOException e) { + throw new CmsException("Cannot upload image " + fileName + " in " + parentNode, e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + /** Does nothign by default. */ + protected void processNewImageFile(Node fileNode, ImageData id) throws RepositoryException, IOException { + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/LoginEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/LoginEntryPoint.java new file mode 100644 index 000000000..50d844fcb --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/LoginEntryPoint.java @@ -0,0 +1,181 @@ +package org.argeo.cms.ui.util; + +import java.util.Locale; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.CmsView; +import org.argeo.cms.ui.UxContext; +import org.argeo.cms.ui.widgets.auth.CmsLogin; +import org.argeo.cms.ui.widgets.auth.CmsLoginShell; +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +public class LoginEntryPoint implements EntryPoint, CmsView { + protected final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; + protected final static String HEADER_AUTHORIZATION = "Authorization"; + private final static Log log = LogFactory.getLog(LoginEntryPoint.class); + private LoginContext loginContext; + private UxContext uxContext = null; + + @Override + public int createUI() { + final Display display = createDisplay(); + UiContext.setData(CmsView.KEY, this); + CmsLoginShell loginShell = createCmsLoginShell(); + try { + // try pre-auth + loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, loginShell); + loginContext.login(); + } catch (LoginException e) { + loginShell.createUi(); + loginShell.open(); + + // HttpServletRequest request = RWT.getRequest(); + // String authorization = request.getHeader(HEADER_AUTHORIZATION); + // if (authorization == null || + // !authorization.startsWith("Negotiate")) { + // HttpServletResponse response = RWT.getResponse(); + // response.setStatus(401); + // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate"); + // response.setDateHeader("Date", System.currentTimeMillis()); + // response.setDateHeader("Expires", System.currentTimeMillis() + + // (24 * 60 * 60 * 1000)); + // response.setHeader("Accept-Ranges", "bytes"); + // response.setHeader("Connection", "Keep-Alive"); + // response.setHeader("Keep-Alive", "timeout=5, max=97"); + // // response.setContentType("text/html; charset=UTF-8"); + // } + + while (!loginShell.getShell().isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + } + + if (CurrentUser.getUsername(getSubject()) == null) + return -1; + uxContext = new SimpleUxContext(); + return postLogin(); + } + + protected Display createDisplay() { + return new Display(); + } + + protected int postLogin() { + return 0; + } + + protected HttpServletRequest getRequest() { + return RWT.getRequest(); + } + + protected CmsLoginShell createCmsLoginShell() { + return new CmsLoginShell(this) { + + @Override + public void createContents(Composite parent) { + LoginEntryPoint.this.createLoginPage(parent, this); + } + + @Override + protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, + SelectionListener loginSelectionListener) { + LoginEntryPoint.this.extendsCredentialsBlock(credentialsBlock, selectedLocale, loginSelectionListener); + } + + }; + } + + /** + * To be overridden. CmsLogin#createCredentialsBlock() should be called at + * some point in order to create the credentials composite. In order to use + * the default layout, call CmsLogin#defaultCreateContents() but not + * CmsLogin#createContent(), since it would lead to a stack overflow. + */ + protected void createLoginPage(Composite parent, CmsLogin login) { + login.defaultCreateContents(parent); + } + + protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, + SelectionListener loginSelectionListener) { + + } + + @Override + public void navigateTo(String state) { + // TODO Auto-generated method stub + + } + + @Override + public void authChange(LoginContext loginContext) { + if (loginContext == null) + throw new CmsException("Login context cannot be null"); + // logout previous login context + if (this.loginContext != null) + try { + this.loginContext.logout(); + } catch (LoginException e1) { + log.warn("Could not log out: " + e1); + } + this.loginContext = loginContext; + } + + @Override + public void logout() { + if (loginContext == null) + throw new CmsException("Login context should not bet null"); + try { + CurrentUser.logoutCmsSession(loginContext.getSubject()); + loginContext.logout(); + } catch (LoginException e) { + throw new CmsException("Cannot log out", e); + } + } + + @Override + public void exception(Throwable e) { + // TODO Auto-generated method stub + + } + + // @Override + // public LoginContext getLoginContext() { + // return loginContext; + // } + + protected Subject getSubject() { + return loginContext.getSubject(); + } + + @Override + public boolean isAnonymous() { + return CurrentUser.isAnonymous(getSubject()); + } + + @Override + public CmsImageManager getImageManager() { + // TODO Auto-generated method stub + return null; + } + + @Override + public UxContext getUxContext() { + return uxContext; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java new file mode 100644 index 000000000..d58143659 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java @@ -0,0 +1,22 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.ui.CmsStyles; + +/** + * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on + * a {@link CmsLink} when simple menus are used. + */ +public class MenuLink extends CmsLink { + public MenuLink() { + setCustom(CmsStyles.CMS_MENU_LINK); + } + + public MenuLink(String label, String target, String custom) { + super(label, target, custom); + } + + public MenuLink(String label, String target) { + super(label, target, CmsStyles.CMS_MENU_LINK); + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java new file mode 100644 index 000000000..0f5b079c8 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java @@ -0,0 +1,375 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.security.Privilege; +import javax.jcr.version.VersionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsConstants; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.LifeCycleUiProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.application.Application; +import org.eclipse.rap.rwt.application.Application.OperationMode; +import org.eclipse.rap.rwt.application.ApplicationConfiguration; +import org.eclipse.rap.rwt.application.EntryPoint; +import org.eclipse.rap.rwt.application.EntryPointFactory; +import org.eclipse.rap.rwt.application.ExceptionHandler; +import org.eclipse.rap.rwt.client.WebClient; +import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** A basic generic app based on {@link SimpleErgonomics}. */ +public class SimpleApp implements CmsConstants, ApplicationConfiguration { + private final static Log log = LogFactory.getLog(SimpleApp.class); + + private String contextName = null; + + private Map> branding = new HashMap>(); + private Map> styleSheets = new HashMap>(); + + private List resources = new ArrayList(); + + private BundleContext bundleContext; + + private Repository repository; + private String workspace = null; + private String jcrBasePath = "/"; + private List roPrincipals = Arrays.asList(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER); + private List rwPrincipals = Arrays.asList(NodeConstants.ROLE_USER); + + private CmsUiProvider header; + private Map pages = new LinkedHashMap(); + + private Integer headerHeight = 40; + + private ServiceRegistration appReg; + + public void configure(Application application) { + try { + BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); + + application.setOperationMode(OperationMode.SWT_COMPATIBILITY); + // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); + + application.setExceptionHandler(new CmsExceptionHandler()); + + // loading animated gif + application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE)); + // empty image + application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); + + for (String resource : resources) { + application.addResource(resource, bundleRL); + if (log.isTraceEnabled()) + log.trace("Resource " + resource); + } + + Map defaultBranding = null; + if (branding.containsKey("*")) + defaultBranding = branding.get("*"); + String defaultTheme = defaultBranding.get(WebClient.THEME_ID); + + // entry points + for (String page : pages.keySet()) { + Map properties = defaultBranding != null ? new HashMap(defaultBranding) + : new HashMap(); + if (branding.containsKey(page)) { + properties.putAll(branding.get(page)); + } + // favicon + if (properties.containsKey(WebClient.FAVICON)) { + String themeId = defaultBranding.get(WebClient.THEME_ID); + Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId); + String faviconRelPath = properties.get(WebClient.FAVICON); + application.addResource(faviconRelPath, + new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle())); + if (log.isTraceEnabled()) + log.trace("Favicon " + faviconRelPath); + + } + + // page title + if (!properties.containsKey(WebClient.PAGE_TITLE)) { + if (page.length() > 0) + properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1)); + } + + // default body HTML + if (!properties.containsKey(WebClient.BODY_HTML)) + properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); + + // + // ADD ENTRY POINT + // + application.addEntryPoint("/" + page, + new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties); + log.info("Page /" + page); + } + + // stylesheets and themes + Set themeBundles = new HashSet<>(); + for (String themeId : styleSheets.keySet()) { + Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId); + StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader( + themeBundle != null ? themeBundle : bundleContext.getBundle()); + if (themeBundle != null) + themeBundles.add(themeBundle); + List cssLst = styleSheets.get(themeId); + if (log.isDebugEnabled()) + log.debug("Theme " + themeId); + for (String css : cssLst) { + application.addStyleSheet(themeId, css, styleSheetRL); + if (log.isDebugEnabled()) + log.debug(" CSS " + css); + } + + } + for (Bundle themeBundle : themeBundles) { + BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); + ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.png"); + ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.gif"); + ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.jpg"); + } + } catch (RuntimeException e) { + // Easier access to initialisation errors + log.error("Unexpected exception when configuring RWT application.", e); + throw e; + } + } + + public void init() throws RepositoryException { + Session session = null; + try { + session = NodeUtils.openDataAdminSession(repository, workspace); + // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); + VersionManager vm = session.getWorkspace().getVersionManager(); + JcrUtils.mkdirs(session, jcrBasePath); + session.save(); + if (!vm.isCheckedOut(jcrBasePath)) + vm.checkout(jcrBasePath); + for (String principal : rwPrincipals) + JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE); + for (String principal : roPrincipals) + JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ); + + for (String pageName : pages.keySet()) { + try { + initPage(session, pages.get(pageName)); + session.save(); + } catch (Exception e) { + throw new CmsException("Cannot initialize page " + pageName, e); + } + } + + } finally { + JcrUtils.logoutQuietly(session); + } + + // publish to OSGi + register(); + } + + protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException { + if (page instanceof LifeCycleUiProvider) + ((LifeCycleUiProvider) page).init(adminSession); + } + + public void destroy() { + for (String pageName : pages.keySet()) { + try { + CmsUiProvider page = pages.get(pageName); + if (page instanceof LifeCycleUiProvider) + ((LifeCycleUiProvider) page).destroy(); + } catch (Exception e) { + log.error("Cannot destroy page " + pageName, e); + } + } + } + + protected void register() { + Hashtable props = new Hashtable(); + if (contextName != null) + props.put("contextName", contextName); + appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props); + if (log.isDebugEnabled()) + log.debug("Registered " + (contextName == null ? "/" : contextName)); + } + + protected void unregister() { + appReg.unregister(); + if (log.isDebugEnabled()) + log.debug("Unregistered " + (contextName == null ? "/" : contextName)); + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public void setPages(Map pages) { + this.pages = pages; + } + + public void setJcrBasePath(String basePath) { + this.jcrBasePath = basePath; + } + + public void setRoPrincipals(List roPrincipals) { + this.roPrincipals = roPrincipals; + } + + public void setRwPrincipals(List rwPrincipals) { + this.rwPrincipals = rwPrincipals; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public void setBranding(Map> branding) { + this.branding = branding; + } + + public void setStyleSheets(Map> styleSheets) { + this.styleSheets = styleSheets; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public void setContextName(String contextName) { + this.contextName = contextName; + } + + class CmsExceptionHandler implements ExceptionHandler { + + @Override + public void handleException(Throwable throwable) { + // TODO be smarter + CmsUiUtils.getCmsView().exception(throwable); + } + + } + + private class CmsEntryPointFactory implements EntryPointFactory { + private final CmsUiProvider page; + private final Repository repository; + private final String workspace; + private final Map properties; + + public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace, + Map properties) { + this.page = page; + this.repository = repository; + this.workspace = workspace; + this.properties = properties; + } + + @Override + public EntryPoint create() { + SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) { + private static final long serialVersionUID = -637940404865527290L; + + @Override + protected void createAdminArea(Composite parent) { + Composite adminArea = new Composite(parent, SWT.NONE); + adminArea.setLayout(new FillLayout()); + Button refresh = new Button(adminArea, SWT.PUSH); + refresh.setText("Reload App"); + refresh.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -7671999525536351366L; + + @Override + public void widgetSelected(SelectionEvent e) { + long timeBeforeReload = 1000; + RWT.getClient().getService(JavaScriptExecutor.class).execute( + "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");"); + reloadApp(); + } + }); + } + }; + // entryPoint.setState(""); + entryPoint.setHeader(header); + entryPoint.setHeaderHeight(headerHeight); + // CmsSession.current.set(entryPoint); + return entryPoint; + } + + private void reloadApp() { + new Thread("Refresh app") { + @Override + public void run() { + unregister(); + register(); + } + }.start(); + } + } + + private static ResourceLoader createResourceLoader(final String resourceName) { + return new ResourceLoader() { + public InputStream getResourceAsStream(String resourceName) throws IOException { + return getClass().getClassLoader().getResourceAsStream(resourceName); + } + }; + } + + // private static ResourceLoader createUrlResourceLoader(final URL url) { + // return new ResourceLoader() { + // public InputStream getResourceAsStream(String resourceName) + // throws IOException { + // return url.openStream(); + // } + // }; + // } + + /* + * TEXTS + */ + private static String DEFAULT_LOADING_BODY = "" + + "" + ""; +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java new file mode 100644 index 000000000..04ce5a290 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java @@ -0,0 +1,96 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +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; + +/** A header in three parts */ +public class SimpleCmsHeader implements CmsUiProvider { + private List lead = new ArrayList(); + private List center = new ArrayList(); + private List end = new ArrayList(); + + private Boolean subPartsSameWidth = false; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite header = new Composite(parent, SWT.NONE); + header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); + header.setBackgroundMode(SWT.INHERIT_DEFAULT); + header.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(3, false))); + + configurePart(context, header, lead); + configurePart(context, header, center); + configurePart(context, header, end); + return header; + } + + protected void configurePart(Node context, Composite parent, List partProviders) + throws RepositoryException { + final int style; + final String custom; + if (lead == partProviders) { + style = SWT.LEAD; + custom = CmsStyles.CMS_HEADER_LEAD; + } else if (center == partProviders) { + style = SWT.CENTER; + custom = CmsStyles.CMS_HEADER_CENTER; + } else if (end == partProviders) { + style = SWT.END; + custom = CmsStyles.CMS_HEADER_END; + } else { + throw new CmsException("Unsupported part providers " + partProviders); + } + + Composite part = new Composite(parent, SWT.NONE); + part.setData(RWT.CUSTOM_VARIANT, custom); + GridData gridData = new GridData(style, SWT.FILL, true, true); + part.setLayoutData(gridData); + part.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); + for (CmsUiProvider uiProvider : partProviders) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + } + + public void setLead(List lead) { + this.lead = lead; + } + + public void setCenter(List center) { + this.center = center; + } + + public void setEnd(List end) { + this.end = end; + } + + public void setSubPartsSameWidth(Boolean subPartsSameWidth) { + this.subPartsSameWidth = subPartsSameWidth; + } + + public List getLead() { + return lead; + } + + public List getCenter() { + return center; + } + + public List getEnd() { + return end; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java new file mode 100644 index 000000000..8e0e7c179 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java @@ -0,0 +1,118 @@ +package org.argeo.cms.ui.util; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +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; + +public class SimpleDynamicPages implements CmsUiProvider { + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + if (context == null) + throw new CmsException("Context cannot be null"); + parent.setLayout(new GridLayout(2, false)); + + // parent + if (!context.getPath().equals("/")) { + new CmsLink("..", context.getParent().getPath()).createUi(parent, + context); + new Label(parent, SWT.NONE).setText(context.getParent() + .getPrimaryNodeType().getName()); + } + + // context + Label contextL = new Label(parent, SWT.NONE); + contextL.setData(RWT.MARKUP_ENABLED, true); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() + .getName()); + + // children + // Label childrenL = new Label(parent, SWT.NONE); + // childrenL.setData(RWT.MARKUP_ENABLED, true); + // childrenL.setText("Children:"); + // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, + // false, 2, 1)); + + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), child.getPath()).createUi(parent, + context); + + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() + .getName()); + } + + // properties + // Label propsL = new Label(parent, SWT.NONE); + // propsL.setData(RWT.MARKUP_ENABLED, true); + // propsL.setText("Properties:"); + // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, + // 2, 1)); + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils + .getPropertyDefinitionAsString(property)); + + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private String getPropAsString(Property property) + throws RepositoryException { + String result = ""; + DateFormat timeFormatter = new SimpleDateFormat(""); + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) + throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleErgonomics.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleErgonomics.java new file mode 100644 index 000000000..92133f572 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleErgonomics.java @@ -0,0 +1,229 @@ +package org.argeo.cms.ui.util; + +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.AbstractCmsEntryPoint; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.cms.ui.UxContext; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Simple header/body ergonomics. */ +public class SimpleErgonomics extends AbstractCmsEntryPoint { + private static final long serialVersionUID = 8743413921359548523L; + + private final static Log log = LogFactory.getLog(SimpleErgonomics.class); + + private boolean uiInitialized = false; + private Composite headerArea; + private Composite leftArea; + private Composite rightArea; + private Composite footerArea; + private Composite bodyArea; + private final CmsUiProvider uiProvider; + + private CmsUiProvider header; + private Integer headerHeight = 0; + private Integer footerHeight = 0; + private CmsUiProvider lead; + private CmsUiProvider end; + private CmsUiProvider footer; + + private CmsImageManager imageManager = new DefaultImageManager(); + private UxContext uxContext = null; + + public SimpleErgonomics(Repository repository, String workspace, String defaultPath, CmsUiProvider uiProvider, + Map factoryProperties) { + super(repository, workspace, defaultPath, factoryProperties); + this.uiProvider = uiProvider; + } + + @Override + protected void initUi(Composite parent) { + parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + parent.setLayout(CmsUiUtils.noSpaceGridLayout(new GridLayout(3, false))); + + uxContext = new SimpleUxContext(); + if (!getUxContext().isMasterData()) + createAdminArea(parent); + headerArea = new Composite(parent, SWT.NONE); + headerArea.setLayout(new FillLayout()); + GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); + headerData.heightHint = headerHeight; + headerArea.setLayoutData(headerData); + + // TODO: bi-directional + leftArea = new Composite(parent, SWT.NONE); + leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); + leftArea.setLayout(CmsUiUtils.noSpaceGridLayout()); + + bodyArea = new Composite(parent, SWT.NONE); + bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); + bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + bodyArea.setLayout(CmsUiUtils.noSpaceGridLayout()); + + // TODO: bi-directional + rightArea = new Composite(parent, SWT.NONE); + rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); + rightArea.setLayout(CmsUiUtils.noSpaceGridLayout()); + + footerArea = new Composite(parent, SWT.NONE); + // footerArea.setLayout(new FillLayout()); + GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); + footerData.heightHint = footerHeight; + footerArea.setLayoutData(footerData); + + uiInitialized = true; + refresh(); + } + + @Override + protected void refresh() { + if (!uiInitialized) + return; + if (getState() == null) + setState(""); + refreshSides(); + refreshBody(); + if (log.isTraceEnabled()) + log.trace("UI refreshed " + getNode()); + } + + protected void createAdminArea(Composite parent) { + } + + @Deprecated + protected void refreshHeader() { + if (header == null) + return; + + for (Control child : headerArea.getChildren()) + child.dispose(); + try { + header.createUi(headerArea, getNode()); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh header", e); + } + headerArea.layout(true, true); + } + + protected void refreshSides() { + refresh(headerArea, header, CmsStyles.CMS_HEADER); + refresh(leftArea, lead, CmsStyles.CMS_LEAD); + refresh(rightArea, end, CmsStyles.CMS_END); + refresh(footerArea, footer, CmsStyles.CMS_FOOTER); + } + + private void refresh(Composite area, CmsUiProvider uiProvider, String style) { + if (uiProvider == null) + return; + + for (Control child : area.getChildren()) + child.dispose(); + CmsUiUtils.style(area, style); + try { + uiProvider.createUi(area, getNode()); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh header", e); + } + area.layout(true, true); + } + + protected void refreshBody() { + // Exception + Throwable exception = getException(); + if (exception != null) { + SystemNotifications systemNotifications = new SystemNotifications(bodyArea); + systemNotifications.notifyException(exception); + resetException(); + return; + // TODO report + } + + // clear + for (Control child : bodyArea.getChildren()) + child.dispose(); + bodyArea.setLayout(CmsUiUtils.noSpaceGridLayout()); + + try { + Node node = getNode(); +// if (node == null) +// log.error("Context cannot be null"); +// else + uiProvider.createUi(bodyArea, node); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh body", e); + } + + bodyArea.layout(true, true); + } + + @Override + public UxContext getUxContext() { + return uxContext; + } + + @Override + public CmsImageManager getImageManager() { + return imageManager; + } + + public void setHeader(CmsUiProvider header) { + this.header = header; + } + + public void setHeaderHeight(Integer headerHeight) { + this.headerHeight = headerHeight; + } + + public void setImageManager(CmsImageManager imageManager) { + this.imageManager = imageManager; + } + + public CmsUiProvider getLead() { + return lead; + } + + public void setLead(CmsUiProvider lead) { + this.lead = lead; + } + + public CmsUiProvider getEnd() { + return end; + } + + public void setEnd(CmsUiProvider end) { + this.end = end; + } + + public CmsUiProvider getFooter() { + return footer; + } + + public void setFooter(CmsUiProvider footer) { + this.footer = footer; + } + + public CmsUiProvider getHeader() { + return header; + } + + public void setFooterHeight(Integer footerHeight) { + this.footerHeight = footerHeight; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java new file mode 100644 index 000000000..ac09b2a02 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java @@ -0,0 +1,5 @@ +package org.argeo.cms.ui.util; + +public class SimpleImageManager extends DefaultImageManager { + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java new file mode 100644 index 000000000..d394f23ac --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java @@ -0,0 +1,32 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class SimpleStaticPage implements CmsUiProvider { + private String text; + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Label textC = new Label(parent, SWT.WRAP); + textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); + textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); + textC.setText(text); + + return textC; + } + + public void setText(String text) { + this.text = text; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleUxContext.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleUxContext.java new file mode 100644 index 000000000..f85a1ab53 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleUxContext.java @@ -0,0 +1,50 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.ui.UxContext; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +public class SimpleUxContext implements UxContext { + private Point size; + private Point small = new Point(400, 400); + + public SimpleUxContext() { + this(Display.getCurrent().getBounds()); + } + + public SimpleUxContext(Rectangle rect) { + this.size = new Point(rect.width, rect.height); + } + + public SimpleUxContext(Point size) { + this.size = size; + } + + @Override + public boolean isPortrait() { + return size.x >= size.y; + } + + @Override + public boolean isLandscape() { + return size.x < size.y; + } + + @Override + public boolean isSquare() { + return size.x == size.y; + } + + @Override + public boolean isSmall() { + return size.x <= small.x || size.y <= small.y; + } + + @Override + public boolean isMasterData() { + // TODO make it configurable + return true; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java new file mode 100644 index 000000000..ad1523c1a --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java @@ -0,0 +1,71 @@ +package org.argeo.cms.ui.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.CmsException; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; + +/** {@link ResourceLoader} caching stylesheets. */ +public class StyleSheetResourceLoader implements ResourceLoader { + private Bundle themeBundle; + private Map stylesheets = new LinkedHashMap(); + + public StyleSheetResourceLoader(Bundle themeBundle) { + this.themeBundle = themeBundle; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + if (!stylesheets.containsKey(resourceName)) { + // TODO deal with other bundles + // Bundle bundle = bundleContext.getBundle(); + // String location = + // bundle.getLocation().substring("initial@reference:".length()); + // if (location.startsWith("file:")) { + // Path path = null; + // try { + // path = Paths.get(new URI(location)); + // } catch (URISyntaxException e) { + // e.printStackTrace(); + // } + // if (path != null) { + // Path resourcePath = path.resolve(resourceName); + // if (Files.exists(resourcePath)) + // return Files.newInputStream(resourcePath); + // } + // } + + URL res = themeBundle.getEntry(resourceName); + if (res == null) + throw new CmsException( + "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(res.openStream(), out); + stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); + } + return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); + // return res.openStream(); + } + + private class StyleSheet { + private byte[] data; + + public StyleSheet(byte[] data) { + super(); + this.data = data; + } + + public byte[] getData() { + return data; + } + + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java new file mode 100644 index 000000000..f53e55262 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java @@ -0,0 +1,128 @@ +package org.argeo.cms.ui.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsStyles; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +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.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Shell displaying system notifications such as exceptions */ +public class SystemNotifications extends Shell implements CmsStyles, + MouseListener { + private static final long serialVersionUID = -8129377525216022683L; + + private Control source; + + public SystemNotifications(Control source) { + super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); + + this.source = source; + + // TODO UI + // setLocation(source.toDisplay(source.getSize().x - getSize().x, + // source.getSize().y)); + setLayout(new GridLayout()); + addMouseListener(this); + + addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + close(); + dispose(); + } + }); + + } + + public void notifyException(Throwable exception) { + Composite pane = this; + + Label lbl = new Label(pane, SWT.NONE); + lbl.setText(exception.getLocalizedMessage() + + (exception instanceof CmsException ? "" : "(" + + exception.getClass().getName() + ")") + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (exception.getCause() != null) + appendCause(pane, exception.getCause()); + + StringBuilder mailToUrl = new StringBuilder("mailto:?"); + try { + mailToUrl.append("subject=").append( + URLEncoder.encode( + "Exception " + + new SimpleDateFormat("yyyy-MM-dd hh:mm") + .format(new Date()), "UTF-8") + .replace("+", "%20")); + + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + IOUtils.closeQuietly(sw); + + // see + // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character + String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( + "+", "%20"); + mailToUrl.append("&body=").append(encoded); + } catch (UnsupportedEncodingException e) { + mailToUrl.append("&body=").append("Could not encode: ") + .append(e.getMessage()); + } + Label mailTo = new Label(pane, SWT.NONE); + CmsUiUtils.markup(mailTo); + mailTo.setText("Send details"); + mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + + pack(); + layout(); + + setLocation(source.toDisplay(source.getSize().x - getSize().x, + source.getSize().y - getSize().y)); + open(); + } + + private void appendCause(Composite parent, Throwable e) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" + + e.getClass().getName() + ")" + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (e.getCause() != null) + appendCause(parent, e.getCause()); + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + close(); + dispose(); + } + + @Override + public void mouseUp(MouseEvent e) { + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/ThemeUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/ThemeUtils.java new file mode 100644 index 000000000..90b684040 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/ThemeUtils.java @@ -0,0 +1,50 @@ +package org.argeo.cms.ui.util; + +import java.net.URL; +import java.util.Enumeration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.rap.rwt.application.Application; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +public class ThemeUtils { + final static Log log = LogFactory.getLog(ThemeUtils.class); + + public static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { + if (themeId == null) + return null; + // TODO optimize + // TODO deal with multiple versions + Bundle themeBundle = null; + if (themeId != null) { + for (Bundle bundle : bundleContext.getBundles()) + if (themeId.equals(bundle.getSymbolicName())) { + themeBundle = bundle; + break; + } + } + return themeBundle; + } + + public static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL, + String pattern) { + Enumeration themeResources = themeBundle.findEntries("/", pattern, true); + if (themeResources == null) + return; + while (themeResources.hasMoreElements()) { + String resource = themeResources.nextElement().getPath(); + // remove first '/' so that RWT registers it + resource = resource.substring(1); + if (!resource.endsWith("/")) { + application.addResource(resource, themeBRL); + if (log.isTraceEnabled()) + log.trace("Registered " + resource + " from theme " + themeBundle); + } + + } + + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java new file mode 100644 index 000000000..07b60696b --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java @@ -0,0 +1,55 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.widgets.auth.CmsLoginShell; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** The site-related user menu */ +public class UserMenu extends CmsLoginShell { + private final Control source; + private final Node context; + + public UserMenu(Control source, Node context) { + super(CmsUiUtils.getCmsView()); + this.context = context; + createUi(); + if (source == null) + throw new CmsException("Source control cannot be null."); + this.source = source; + open(); + } + + @Override + protected Shell createShell() { + return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + } + + @Override + public void open() { + Shell shell = getShell(); + shell.pack(); + shell.layout(); + shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); + shell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + closeShell(); + } + }); + super.open(); + } + + protected Node getContext() { + return context; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java new file mode 100644 index 000000000..cc470e414 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java @@ -0,0 +1,84 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.widgets.auth.CmsLoginShell; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** Open the user menu when clicked */ +public class UserMenuLink extends MenuLink { + + public UserMenuLink() { + setCustom(CmsStyles.CMS_USER_MENU_LINK); + } + + @Override + public Control createUi(Composite parent, Node context) { + if (CurrentUser.isAnonymous()) + setLabel(CmsMsg.login.lead()); + else { + setLabel(CurrentUser.getDisplayName()); + } + Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; + link.addMouseListener(new UserMenuLinkController(context)); + return link.getParent(); + } + + protected CmsLoginShell createUserMenu(Control source, Node context) { + return new UserMenu(source.getParent(), context); + } + + private class UserMenuLinkController implements MouseListener, DisposeListener { + private static final long serialVersionUID = 3634864186295639792L; + + private CmsLoginShell userMenu = null; + private long lastDisposeTS = 0l; + + private final Node context; + + public UserMenuLinkController(Node context) { + this.context = context; + } + + // + // MOUSE LISTENER + // + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + if (userMenu == null) { + long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; + // avoid to reopen the menu, if one has clicked gain + if (durationSinceLastDispose > 200) { + userMenu = createUserMenu(source, context); + userMenu.getShell().addDisposeListener(this); + } + } + } + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void widgetDisposed(DisposeEvent event) { + userMenu = null; + lastDisposeTS = System.currentTimeMillis(); + } + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java new file mode 100644 index 000000000..e7dfb4b88 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java @@ -0,0 +1,43 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +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; + +public class VerticalMenu implements CmsUiProvider { + private List items = new ArrayList(); + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite part = new Composite(parent, SWT.NONE); + part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); +// part.setData(RWT.CUSTOM_VARIANT, custom); + part.setLayout(CmsUiUtils.noSpaceGridLayout()); + for (CmsUiProvider uiProvider : items) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); + } + return part; + } + + public void add(CmsUiProvider uiProvider) { + items.add(uiProvider); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java new file mode 100644 index 000000000..1445fed33 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java @@ -0,0 +1,324 @@ +package org.argeo.cms.ui.viewers; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsEditable; +import org.argeo.cms.ui.widgets.ScrolledPage; +import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +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.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.SAXParseException; + +/** Base class for viewers related to a page */ +public abstract class AbstractPageViewer extends ContentViewer implements Observer { + private static final long serialVersionUID = 5438688173410341485L; + + private final static Log log = LogFactory.getLog(AbstractPageViewer.class); + + private final boolean readOnly; + /** The basis for the layouts, typically a ScrolledPage. */ + private final Composite page; + private final CmsEditable cmsEditable; + + private MouseListener mouseListener; + private FocusListener focusListener; + + private EditablePart edited; + private ISelection selection = StructuredSelection.EMPTY; + + private AccessControlContext accessControlContext; + + protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { + // read only at UI level + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + + this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + + if (cmsEditable.canEdit()) { + mouseListener = createMouseListener(); + focusListener = createFocusListener(); + } + page = findPage(parent); + accessControlContext = AccessController.getContext(); + } + + /** + * Can be called to simplify the called to isModelInitialized() and initModel() + */ + protected void initModelIfNeeded(Node node) { + try { + if (!isModelInitialized(node)) + if (getCmsEditable().canEdit()) { + initModel(node); + node.getSession().save(); + } + } catch (Exception e) { + throw new CmsException("Cannot initialize model", e); + } + } + + /** Called if user can edit and model is not initialized */ + protected Boolean isModelInitialized(Node node) throws RepositoryException { + return true; + } + + /** Called if user can edit and model is not initialized */ + protected void initModel(Node node) throws RepositoryException { + } + + /** Create (retrieve) the MouseListener to use. */ + protected MouseListener createMouseListener() { + return new MouseAdapter() { + private static final long serialVersionUID = 1L; + }; + } + + /** Create (retrieve) the FocusListener to use. */ + protected FocusListener createFocusListener() { + return new FocusListener() { + private static final long serialVersionUID = 1L; + + @Override + public void focusLost(FocusEvent event) { + } + + @Override + public void focusGained(FocusEvent event) { + } + }; + } + + protected Composite findPage(Composite composite) { + if (composite instanceof ScrolledPage) { + return (ScrolledPage) composite; + } else { + if (composite.getParent() == null) + return composite; + return findPage(composite.getParent()); + } + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) + editingStateChanged(cmsEditable); + } + + /** To be overridden in order to provide the actual refresh */ + protected void refresh(Control control) throws RepositoryException { + } + + /** To be overridden.Save the edited part. */ + protected void save(EditablePart part) throws RepositoryException { + } + + /** Prepare the edited part */ + protected void prepare(EditablePart part, Object caretPosition) { + } + + /** Notified when the editing state changed. Does nothing, to be overridden */ + protected void editingStateChanged(CmsEditable cmsEditable) { + } + + @Override + public void refresh() { + // TODO check actual context in order to notice a discrepancy + Subject viewerSubject = getViewerSubject(); + Subject.doAs(viewerSubject, (PrivilegedAction) () -> { + try { + if (cmsEditable.canEdit() && !readOnly) + mouseListener = createMouseListener(); + else + mouseListener = null; + refresh(getControl()); + layout(getControl()); + } catch (RepositoryException e) { + throw new CmsException("Cannot refresh", e); + } + return null; + }); + } + + @Override + public void setSelection(ISelection selection, boolean reveal) { + this.selection = selection; + } + + protected void updateContent(EditablePart part) throws RepositoryException { + } + + // LOW LEVEL EDITION + protected void edit(EditablePart part, Object caretPosition) { + try { + if (edited == part) + return; + + if (edited != null && edited != part) { + EditablePart previouslyEdited = edited; + try { + stopEditing(true); + } catch (Exception e) { + notifyEditionException(e); + edit(previouslyEdited, caretPosition); + return; + } + } + + part.startEditing(); + updateContent(part); + prepare(part, caretPosition); + edited = part; + layout(part.getControl()); + } catch (RepositoryException e) { + throw new CmsException("Cannot edit " + part, e); + } + } + + private void stopEditing(Boolean save) throws RepositoryException { + if (edited instanceof Widget && ((Widget) edited).isDisposed()) { + edited = null; + return; + } + + assert edited != null; + if (edited == null) { + if (log.isTraceEnabled()) + log.warn("Told to stop editing while not editing anything"); + return; + } + + if (save) + save(edited); + + edited.stopEditing(); + updateContent(edited); + layout(((EditablePart) edited).getControl()); + edited = null; + } + + // METHODS AVAILABLE TO EXTENDING CLASSES + protected void saveEdit() { + try { + if (edited != null) + stopEditing(true); + } catch (RepositoryException e) { + throw new CmsException("Cannot stop editing", e); + } + } + + protected void cancelEdit() { + try { + if (edited != null) + stopEditing(false); + } catch (RepositoryException e) { + throw new CmsException("Cannot cancel editing", e); + } + } + + /** Layout this controls from the related base page. */ + public void layout(Control... controls) { + page.layout(controls); + } + + /** + * Find the first {@link EditablePart} in the parents hierarchy of this control + */ + protected EditablePart findDataParent(Control parent) { + if (parent instanceof EditablePart) { + return (EditablePart) parent; + } + if (parent.getParent() != null) + return findDataParent(parent.getParent()); + else + throw new CmsException("No data parent found"); + } + + // UTILITIES + /** Check whether the edited part is in a proper state */ + protected void checkEdited() { + if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) + throw new CmsException("Edited should not be null or disposed at this stage"); + } + + /** Persist all changes. */ + protected void persistChanges(Session session) throws RepositoryException { + session.save(); + session.refresh(false); + // TODO notify that changes have been persisted + } + + /** Convenience method using a Node in order to save the underlying session. */ + protected void persistChanges(Node anyNode) throws RepositoryException { + persistChanges(anyNode.getSession()); + } + + /** Notify edition exception */ + protected void notifyEditionException(Throwable e) { + Throwable eToLog = e; + if (e instanceof IllegalArgumentException) + if (e.getCause() instanceof SAXParseException) + eToLog = e.getCause(); + log.error(eToLog.getMessage(), eToLog); +// if (log.isTraceEnabled()) +// log.trace("Full stack of " + eToLog.getMessage(), e); + // TODO Light error notification popup + } + + protected Subject getViewerSubject() { + Subject res = null; + if (accessControlContext != null) { + res = Subject.getSubject(accessControlContext); + } + if (res == null) + throw new CmsException("No subject associated with this viewer"); + return res; + } + + // GETTERS / SETTERS + public boolean isReadOnly() { + return readOnly; + } + + protected EditablePart getEdited() { + return edited; + } + + public MouseListener getMouseListener() { + return mouseListener; + } + + public FocusListener getFocusListener() { + return focusListener; + } + + public CmsEditable getCmsEditable() { + return cmsEditable; + } + + @Override + public ISelection getSelection() { + return selection; + } +} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/EditablePart.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/EditablePart.java new file mode 100644 index 000000000..158beaf1e --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/EditablePart.java @@ -0,0 +1,11 @@ +package org.argeo.cms.ui.viewers; + +import org.eclipse.swt.widgets.Control; + +public interface EditablePart { + public void startEditing(); + + public void stopEditing(); + + public Control getControl(); +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java new file mode 100644 index 000000000..4ca45d191 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java @@ -0,0 +1,9 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +/** An editable part related to a JCR Item */ +public interface ItemPart { + public Item getItem() throws RepositoryException; +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java new file mode 100644 index 000000000..dfd143829 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java @@ -0,0 +1,101 @@ +package org.argeo.cms.ui.viewers; + +import java.util.Observable; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.VersionManager; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsEditable; +import org.argeo.cms.ui.CmsEditionEvent; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** Provides the CmsEditable semantic based on JCR versioning. */ +public class JcrVersionCmsEditable extends Observable implements CmsEditable { + private final String nodePath;// cache + private final VersionManager versionManager; + private final Boolean canEdit; + + public JcrVersionCmsEditable(Node node) throws RepositoryException { + this.nodePath = node.getPath(); + if (node.getSession().hasPermission(node.getPath(), + Session.ACTION_SET_PROPERTY)) { + // was Session.ACTION_ADD_NODE + canEdit = true; + if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { + node.addMixin(NodeType.MIX_VERSIONABLE); + node.getSession().save(); + } + versionManager = node.getSession().getWorkspace() + .getVersionManager(); + } else { + canEdit = false; + versionManager = null; + } + + // bind keys + if (canEdit) { + Display display = Display.getCurrent(); + display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", + "CTRL+E" }); + display.addFilter(SWT.KeyDown, new Listener() { + private static final long serialVersionUID = -4378653870463187318L; + + public void handleEvent(Event e) { + boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; + if (ctrlPressed && e.keyCode == '\r') + stopEditing(); + else if (ctrlPressed && e.keyCode == 'E') + stopEditing(); + } + }); + } + } + + @Override + public Boolean canEdit() { + return canEdit; + } + + public Boolean isEditing() { + try { + if (!canEdit()) + return false; + return versionManager.isCheckedOut(nodePath); + } catch (RepositoryException e) { + throw new CmsException("Cannot check whether " + nodePath + + " is editing", e); + } + } + + @Override + public void startEditing() { + try { + versionManager.checkout(nodePath); + setChanged(); + } catch (RepositoryException e1) { + throw new CmsException("Cannot publish " + nodePath); + } + notifyObservers(new CmsEditionEvent(nodePath, + CmsEditionEvent.START_EDITING)); + } + + @Override + public void stopEditing() { + try { + versionManager.checkin(nodePath); + setChanged(); + } catch (RepositoryException e1) { + throw new CmsException("Cannot publish " + nodePath, e1); + } + notifyObservers(new CmsEditionEvent(nodePath, + CmsEditionEvent.STOP_EDITING)); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java new file mode 100644 index 000000000..b51d4fcba --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Node; + +/** An editable part related to a node */ +public interface NodePart extends ItemPart { + public Node getNode(); +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java new file mode 100644 index 000000000..793079e20 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Property; + +/** An editable part related to a JCR Property */ +public interface PropertyPart extends ItemPart { + public Property getProperty(); +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java new file mode 100644 index 000000000..83ea56076 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java @@ -0,0 +1,149 @@ +package org.argeo.cms.ui.viewers; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.widgets.JcrComposite; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +public class Section extends JcrComposite { + private static final long serialVersionUID = -5933796173755739207L; + + private final Section parentSection; + private Composite sectionHeader; + private final Integer relativeDepth; + + public Section(Composite parent, int style, Node node) throws RepositoryException { + this(parent, findSection(parent), style, node); + } + + public Section(Section section, int style, Node node) throws RepositoryException { + this(section, section, style, node); + } + + protected Section(Composite parent, Section parentSection, int style, Node node) throws RepositoryException { + super(parent, style, node); + this.parentSection = parentSection; + if (parentSection != null) { + relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); + } else { + relativeDepth = 0; + } + setLayout(CmsUiUtils.noSpaceGridLayout()); + } + + public Map getSubSections() throws RepositoryException { + LinkedHashMap result = new LinkedHashMap(); + for (Control child : getChildren()) { + if (child instanceof Composite) { + collectDirectSubSections((Composite) child, result); + } + } + return Collections.unmodifiableMap(result); + } + + private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) + throws RepositoryException { + if (composite == sectionHeader || composite instanceof EditablePart) + return; + if (composite instanceof Section) { + Section section = (Section) composite; + subSections.put(section.getNodeId(), section); + return; + } + + for (Control child : composite.getChildren()) + if (child instanceof Composite) + collectDirectSubSections((Composite) child, subSections); + } + + public void createHeader() { + if (sectionHeader != null) + throw new CmsException("Section header was already created"); + + sectionHeader = new Composite(this, SWT.NONE); + sectionHeader.setLayoutData(CmsUiUtils.fillWidth()); + sectionHeader.setLayout(CmsUiUtils.noSpaceGridLayout()); + // sectionHeader.moveAbove(null); + // layout(); + } + + public Composite getHeader() { + if (sectionHeader != null && sectionHeader.isDisposed()) + sectionHeader = null; + return sectionHeader; + } + + // SECTION PARTS + public SectionPart getSectionPart(String partId) { + for (Control child : getChildren()) { + if (child instanceof SectionPart) { + SectionPart paragraph = (SectionPart) child; + if (paragraph.getPartId().equals(partId)) + return paragraph; + } + } + return null; + } + + public SectionPart nextSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) + if (i + 1 < children.length) { + Composite next = (Composite) children[i + 1]; + return (SectionPart) next; + } else { + // next section + } + } + return null; + } + + public SectionPart previousSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) + if (i != 0) { + Composite previous = (Composite) children[i - 1]; + return (SectionPart) previous; + } else { + // previous section + } + } + return null; + } + + @Override + public String toString() { + if (parentSection == null) + return "Main section " + getNode(); + return "Section " + getNode(); + } + + public Section getParentSection() { + return parentSection; + } + + public Integer getRelativeDepth() { + return relativeDepth; + } + + /** Recursively finds the related section in the parents (can be itself) */ + public static Section findSection(Control control) { + if (control == null) + return null; + if (control instanceof Section) + return (Section) control; + else + return findSection(control.getParent()); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java new file mode 100644 index 000000000..f0b367f5a --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java @@ -0,0 +1,9 @@ +package org.argeo.cms.ui.viewers; + + +/** An editable part dynamically related to a Section */ +public interface SectionPart extends EditablePart, NodePart { + public String getPartId(); + + public Section getSection(); +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java new file mode 100644 index 000000000..0a327cd51 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java @@ -0,0 +1,112 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** A stylable and editable image. */ +public abstract class EditableImage extends StyledControl { + private static final long serialVersionUID = -5689145523114022890L; + private final static Log log = LogFactory.getLog(EditableImage.class); + + private Point preferredImageSize; + private Boolean loaded = false; + + public EditableImage(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + public EditableImage(Composite parent, int swtStyle, + Point preferredImageSize) { + super(parent, swtStyle); + this.preferredImageSize = preferredImageSize; + } + + public EditableImage(Composite parent, int style, Node node, + boolean cacheImmediately, Point preferredImageSize) + throws RepositoryException { + super(parent, style, node, cacheImmediately); + this.preferredImageSize = preferredImageSize; + } + + @Override + protected void setContainerLayoutData(Composite composite) { + // composite.setLayoutData(fillWidth()); + } + + @Override + protected void setControlLayoutData(Control control) { + // control.setLayoutData(fillWidth()); + } + + /** To be overriden. */ + protected String createImgTag() throws RepositoryException { + return CmsUiUtils.noImg(preferredImageSize != null ? preferredImageSize + : getSize()); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle()); + // lbl.setLayoutData(CmsUiUtils.fillWidth()); + CmsUiUtils.markup(lbl); + CmsUiUtils.style(lbl, style); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + load(lbl); + return lbl; + } + + /** To be overriden. */ + protected synchronized Boolean load(Control control) { + String imgTag; + try { + imgTag = createImgTag(); + } catch (Exception e) { + // throw new CmsException("Cannot retrieve image", e); + log.error("Cannot retrieve image", e); + imgTag = CmsUiUtils.noImg(preferredImageSize); + loaded = false; + } + + if (imgTag == null) { + loaded = false; + imgTag = CmsUiUtils.noImg(preferredImageSize); + } else + loaded = true; + if (control != null) { + ((Label) control).setText(imgTag); + control.setSize(preferredImageSize != null ? preferredImageSize + : getSize()); + } else { + loaded = false; + } + getParent().layout(); + return loaded; + } + + public void setPreferredSize(Point size) { + this.preferredImageSize = size; + if (!loaded) { + load((Label) getControl()); + } + } + + protected Text createText(Composite box, String style) { + Text text = new Text(box, getStyle()); + CmsUiUtils.style(text, style); + return text; + } + + public Point getPreferredImageSize() { + return preferredImageSize; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java new file mode 100644 index 000000000..21f48138c --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java @@ -0,0 +1,76 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.util.CmsUiUtils; +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.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable text part displaying styled text. */ +public class EditableText extends StyledControl { + private static final long serialVersionUID = -6372283442330912755L; + + public EditableText(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + public EditableText(Composite parent, int style, Item item) + throws RepositoryException { + this(parent, style, item, false); + } + + public EditableText(Composite parent, int style, Item item, + boolean cacheImmediately) throws RepositoryException { + super(parent, style, item, cacheImmediately); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) + return createText(box, style); + else + return createLabel(box, style); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsUiUtils.fillWidth()); + CmsUiUtils.style(lbl, style); + CmsUiUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createText(Composite box, String style) { + final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); + GridData textLayoutData = CmsUiUtils.fillWidth(); + // textLayoutData.heightHint = preferredHeight; + text.setLayoutData(textLayoutData); + CmsUiUtils.style(text, style); + text.setFocus(); + return text; + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) + ((Label) child).setText(text); + else if (child instanceof Text) + ((Text) child).setText(text); + } + + public Text getAsText() { + return (Text) getControl(); + } + + public Label getAsLabel() { + return (Label) getControl(); + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java new file mode 100644 index 000000000..055fcf56f --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java @@ -0,0 +1,150 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.CmsImageManager; +import org.argeo.cms.ui.internal.JcrFileUploadReceiver; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.viewers.NodePart; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +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.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** An image within the Argeo Text framework */ +public class Img extends EditableImage implements SectionPart, NodePart { + private static final long serialVersionUID = 6233572783968188476L; + + private final Section section; + + private final CmsImageManager imageManager; + private FileUploadHandler currentUploadHandler = null; + private FileUploadListener fileUploadListener; + + public Img(Composite parent, int swtStyle, Node imgNode, + Point preferredImageSize) throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, + preferredImageSize); + setStyle(TextStyles.TEXT_IMAGE); + } + + public Img(Composite parent, int swtStyle, Node imgNode) + throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, null); + setStyle(TextStyles.TEXT_IMAGE); + } + + Img(Section section, Composite parent, int swtStyle, Node imgNode, + Point preferredImageSize) throws RepositoryException { + super(parent, swtStyle, imgNode, false, preferredImageSize); + this.section = section; + imageManager = CmsUiUtils.getCmsView().getImageManager(); + CmsUiUtils.style(this, TextStyles.TEXT_IMG); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + try { + return createImageChooser(box, style); + } catch (RepositoryException e) { + throw new CmsException("Cannot create image chooser", e); + } + } else { + return createLabel(box, style); + } + } + + @Override + public synchronized void stopEditing() { + super.stopEditing(); + fileUploadListener = null; + } + + @Override + protected synchronized Boolean load(Control lbl) { + try { + Node imgNode = getNode(); + boolean loaded = imageManager.load(imgNode, lbl, + getPreferredImageSize()); + // getParent().layout(); + return loaded; + } catch (RepositoryException e) { + throw new CmsException("Cannot load " + getNodeId() + + " from image manager", e); + } + } + + protected Control createImageChooser(Composite box, String style) + throws RepositoryException { + // FileDialog fileDialog = new FileDialog(getShell()); + // fileDialog.open(); + // String fileName = fileDialog.getFileName(); + CmsImageManager imageManager = CmsUiUtils.getCmsView().getImageManager(); + Node node = getNode(); + JcrFileUploadReceiver receiver = new JcrFileUploadReceiver( + node.getParent(), node.getName() + '[' + node.getIndex() + ']', + imageManager); + if (currentUploadHandler != null) + currentUploadHandler.dispose(); + currentUploadHandler = prepareUpload(receiver); + final ServerPushSession pushSession = new ServerPushSession(); + final FileUpload fileUpload = new FileUpload(box, SWT.NONE); + CmsUiUtils.style(fileUpload, style); + fileUpload.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -9158471843941668562L; + + @Override + public void widgetSelected(SelectionEvent e) { + pushSession.start(); + fileUpload.submit(currentUploadHandler.getUploadUrl()); + } + }); + return fileUpload; + } + + protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { + final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); + if (fileUploadListener != null) + uploadHandler.addUploadListener(fileUploadListener); + return uploadHandler; + } + + @Override + public Section getSection() { + return section; + } + + public void setFileUploadListener(FileUploadListener fileUploadListener) { + this.fileUploadListener = fileUploadListener; + if (currentUploadHandler != null) + currentUploadHandler.addUploadListener(fileUploadListener); + } + + @Override + public Node getItem() throws RepositoryException { + return getNode(); + } + + @Override + public String getPartId() { + return getNodeId(); + } + + @Override + public String toString() { + return "Img #" + getPartId(); + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java new file mode 100644 index 000000000..d47aba792 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java @@ -0,0 +1,175 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.cms.CmsException; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; + +/** A composite which can (optionally) manage a JCR Item. */ +public class JcrComposite extends Composite { + private static final long serialVersionUID = -1447009015451153367L; + + private final Session session; + + private String nodeId; + private String property = null; + private Node cache; + + /** Regular composite constructor. No layout is set. */ + public JcrComposite(Composite parent, int style) { + super(parent, style); + session = null; + nodeId = null; + } + + public JcrComposite(Composite parent, int style, Item item) + throws RepositoryException { + this(parent, style, item, false); + } + + public JcrComposite(Composite parent, int style, Item item, + boolean cacheImmediately) throws RepositoryException { + super(parent, style); + this.session = item.getSession(); + if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { + // (useless?) optimization: we only save a pointer to the session, + // not even a reference to the item + this.nodeId = null; + } else { + Node node; + Property property = null; + if (item instanceof Node) { + node = (Node) item; + } else {// Property + property = (Property) item; + if (property.isMultiple())// TODO manage property index + throw new CmsException( + "Multiple properties not supported yet."); + this.property = property.getName(); + node = property.getParent(); + } + this.nodeId = node.getIdentifier(); + if (cacheImmediately) + this.cache = node; + } + setLayout(CmsUiUtils.noSpaceGridLayout()); + } + + public synchronized Node getNode() { + try { + if (!itemIsNode()) + throw new CmsException("Item is not a Node"); + return getNodeInternal(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get node " + nodeId, e); + } + } + + private synchronized Node getNodeInternal() throws RepositoryException { + if (cache != null) + return cache; + else if (session != null) + if (nodeId != null) + return session.getNodeByIdentifier(nodeId); + else + return null; + else + return null; + } + + public synchronized Property getProperty() { + try { + if (itemIsNode()) + throw new CmsException("Item is not a Property"); + Node node = getNodeInternal(); + if (!node.hasProperty(property)) + throw new CmsException("Property " + property + + " is not set on " + node); + return node.getProperty(property); + } catch (RepositoryException e) { + throw new CmsException("Cannot get property " + property + + " from node " + nodeId, e); + } + } + + public synchronized Boolean itemIsNode() { + return property == null; + } + + /** Set/update the cache or change the node */ + public synchronized void setNode(Node node) throws RepositoryException { + if (!itemIsNode()) + throw new CmsException("Cannot set a Node on a Property"); + + if (node == null) {// clear cache + this.cache = null; + return; + } + + if (session == null || session != node.getSession())// check session + throw new CmsException("Uncompatible session"); + + if (nodeId == null || !nodeId.equals(node.getIdentifier())) { + nodeId = node.getIdentifier(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } + + /** Set/update the cache or change the property */ + public synchronized void setProperty(Property prop) + throws RepositoryException { + if (itemIsNode()) + throw new CmsException("Cannot set a Property on a Node"); + + if (prop == null) {// clear cache + this.cache = null; + return; + } + + if (session == null || session != prop.getSession())// check session + throw new CmsException("Uncompatible session"); + + Node node = prop.getNode(); + if (nodeId == null || !nodeId.equals(node.getIdentifier()) + || !property.equals(prop.getName())) { + nodeId = node.getIdentifier(); + property = prop.getName(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } + + public synchronized String getNodeId() { + return nodeId; + } + + /** Change the node, does nothing if same. */ + public synchronized void setNodeId(String nodeId) + throws RepositoryException { + if (this.nodeId != null && this.nodeId.equals(nodeId)) + return; + this.nodeId = nodeId; + if (cache != null) + cache = session.getNodeByIdentifier(this.nodeId); + itemUpdated(); + } + + protected synchronized void itemUpdated() { + layout(); + } + + public Session getSession() { + return session; + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/ScrolledPage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/ScrolledPage.java new file mode 100644 index 000000000..5dd00adf2 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/ScrolledPage.java @@ -0,0 +1,60 @@ +package org.argeo.cms.ui.widgets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; + +/** + * A composite that can be scrolled vertically. It wraps a + * {@link ScrolledComposite} (and is being wrapped by it), simplifying its + * configuration. + */ +public class ScrolledPage extends Composite { + private static final long serialVersionUID = 1593536965663574437L; + + private ScrolledComposite scrolledComposite; + + public ScrolledPage(Composite parent, int style) { + super(new ScrolledComposite(parent, SWT.V_SCROLL), style); + scrolledComposite = (ScrolledComposite) getParent(); + scrolledComposite.setContent(this); + + scrolledComposite.setExpandVertical(true); + scrolledComposite.setExpandHorizontal(true); + scrolledComposite.addControlListener(new ScrollControlListener()); + } + + @Override + public void layout(boolean changed, boolean all) { + updateScroll(); + super.layout(changed, all); + } + + protected void updateScroll() { + Rectangle r = scrolledComposite.getClientArea(); + Point preferredSize = computeSize(r.width, SWT.DEFAULT); + scrolledComposite.setMinHeight(preferredSize.y); + } + + // public ScrolledComposite getScrolledComposite() { + // return this.scrolledComposite; + // } + + /** Set it on the wrapping scrolled composite */ + @Override + public void setLayoutData(Object layoutData) { + scrolledComposite.setLayoutData(layoutData); + } + + private class ScrollControlListener extends + org.eclipse.swt.events.ControlAdapter { + private static final long serialVersionUID = -3586986238567483316L; + + public void controlResized(ControlEvent e) { + updateScroll(); + } + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java new file mode 100644 index 000000000..b085fdf9c --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java @@ -0,0 +1,134 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.CmsConstants; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Editable text part displaying styled text. */ +public abstract class StyledControl extends JcrComposite implements CmsConstants { + private static final long serialVersionUID = -6372283442330912755L; + private Control control; + + private Composite container; + private Composite box; + + protected MouseListener mouseListener; + protected FocusListener focusListener; + + private Boolean editing = Boolean.FALSE; + + public StyledControl(Composite parent, int swtStyle) { + super(parent, swtStyle); + setLayout(CmsUiUtils.noSpaceGridLayout()); + } + + public StyledControl(Composite parent, int style, Item item) throws RepositoryException { + super(parent, style, item); + } + + public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { + super(parent, style, item, cacheImmediately); + } + + protected abstract Control createControl(Composite box, String style); + + protected Composite createBox(Composite parent) { + Composite box = new Composite(parent, SWT.INHERIT_DEFAULT); + setContainerLayoutData(box); + box.setLayout(CmsUiUtils.noSpaceGridLayout()); + // new Label(box, SWT.NONE).setText("BOX"); + return box; + } + + public Control getControl() { + return control; + } + + protected synchronized Boolean isEditing() { + return editing; + } + + public synchronized void startEditing() { + assert !isEditing(); + editing = true; + // int height = control.getSize().y; + String style = (String) control.getData(STYLE); + clear(false); + control = createControl(box, style); + setControlLayoutData(control); + + // add the focus listener to the newly created edition control + if (focusListener != null) + control.addFocusListener(focusListener); + } + + public synchronized void stopEditing() { + assert isEditing(); + editing = false; + String style = (String) control.getData(STYLE); + clear(false); + control = createControl(box, style); + setControlLayoutData(control); + } + + public void setStyle(String style) { + Object currentStyle = null; + if (control != null) + currentStyle = control.getData(STYLE); + if (currentStyle != null && currentStyle.equals(style)) + return; + + // Integer preferredHeight = control != null ? control.getSize().y : + // null; + clear(true); + control = createControl(box, style); + setControlLayoutData(control); + + control.getParent().setData(STYLE, style + "_box"); + control.getParent().getParent().setData(STYLE, style + "_container"); + } + + /** To be overridden */ + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsUiUtils.fillWidth()); + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsUiUtils.fillWidth()); + } + + protected void clear(boolean deep) { + if (deep) { + for (Control control : getChildren()) + control.dispose(); + container = createBox(this); + box = createBox(container); + } else { + control.dispose(); + } + } + + public void setMouseListener(MouseListener mouseListener) { + if (this.mouseListener != null && control != null) + control.removeMouseListener(this.mouseListener); + this.mouseListener = mouseListener; + if (control != null && this.mouseListener != null) + control.addMouseListener(mouseListener); + } + + public void setFocusListener(FocusListener focusListener) { + if (this.focusListener != null && control != null) + control.removeFocusListener(this.focusListener); + this.focusListener = focusListener; + if (control != null && this.focusListener != null) + control.addFocusListener(focusListener); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java new file mode 100644 index 000000000..e461ed0df --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.widgets; + +/** Styles references in the CSS. */ +public interface TextStyles { + /** The whole page area */ + public final static String TEXT_AREA = "text_area"; + /** Area providing controls for editing text */ + public final static String TEXT_EDITOR_HEADER = "text_editor_header"; + /** The styled composite for editing the text */ + public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; + /** A section */ + public final static String TEXT_SECTION = "text_section"; + /** A paragraph */ + public final static String TEXT_PARAGRAPH = "text_paragraph"; + /** An image */ + public final static String TEXT_IMG = "text_img"; + /** The dialog to edit styled paragraph */ + public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; + + /* + * DEFAULT TEXT STYLES + */ + /** Default style for text body */ + public final static String TEXT_DEFAULT = "text_default"; + /** Fixed-width, typically code */ + public final static String TEXT_PRE = "text_pre"; + /** Quote */ + public final static String TEXT_QUOTE = "text_quote"; + /** Title */ + public final static String TEXT_TITLE = "text_title"; + /** Header (to be dynamically completed with the depth, e.g. text_h1) */ + public final static String TEXT_H = "text_h"; + + /** Default style for images */ + public final static String TEXT_IMAGE = "text_image"; + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/AbstractLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/AbstractLoginDialog.java new file mode 100644 index 000000000..1d34b9df3 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/AbstractLoginDialog.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.cms.ui.widgets.auth; + +import java.io.IOException; +import java.util.Arrays; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.operation.ModalContext; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.osgi.framework.FrameworkUtil; + +/** Base for login dialogs */ +public abstract class AbstractLoginDialog extends TrayDialog implements CallbackHandler { + private static final long serialVersionUID = -8046708963512717709L; + + private final static Log log = LogFactory.getLog(AbstractLoginDialog.class); + + private Thread modalContextThread = null; + boolean processCallbacks = false; + boolean isCancelled = false; + Callback[] callbackArray; + + protected final Callback[] getCallbacks() { + return this.callbackArray; + } + + public abstract void internalHandle(); + + public boolean isCancelled() { + return isCancelled; + } + + protected AbstractLoginDialog(Shell parentShell) { + super(parentShell); + } + + /* + * (non-Javadoc) + * + * @see + * javax.security.auth.callback.CallbackHandler#handle(javax.security.auth + * .callback.Callback[]) + */ + public void handle(final Callback[] callbacks) throws IOException { + // clean previous usage + if (processCallbacks) { + // this handler was already used + processCallbacks = false; + } + + if (modalContextThread != null) { + try { + modalContextThread.join(1000); + } catch (InterruptedException e) { + // silent + } + modalContextThread = null; + } + + // initialize + this.callbackArray = callbacks; + final Display display = Display.getDefault(); + display.syncExec(new Runnable() { + + public void run() { + isCancelled = false; + setBlockOnOpen(false); + open(); + + final Button okButton = getButton(IDialogConstants.OK_ID); + okButton.setText("Login"); + okButton.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = -200281625679096775L; + + public void widgetSelected(final SelectionEvent event) { + processCallbacks = true; + } + + public void widgetDefaultSelected(final SelectionEvent event) { + // nothing to do + } + }); + final Button cancel = getButton(IDialogConstants.CANCEL_ID); + cancel.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = -3826030278084915815L; + + public void widgetSelected(final SelectionEvent event) { + isCancelled = true; + processCallbacks = true; + } + + public void widgetDefaultSelected(final SelectionEvent event) { + // nothing to do + } + }); + } + }); + try { + ModalContext.setAllowReadAndDispatch(true); // Works for now. + ModalContext.run(new IRunnableWithProgress() { + + public void run(final IProgressMonitor monitor) { + modalContextThread = Thread.currentThread(); + // Wait here until OK or cancel is pressed, then let it rip. + // The event + // listener + // is responsible for closing the dialog (in the + // loginSucceeded + // event). + while (!processCallbacks && (modalContextThread != null) + && (modalContextThread == Thread.currentThread()) + && FrameworkUtil.getBundle(AbstractLoginDialog.class).getBundleContext() != null) { + // Note: SecurityUiPlugin.getDefault() != null is false + // when the OSGi runtime is shut down + try { + Thread.sleep(100); + // if (display.isDisposed()) { + // log.warn("Display is disposed, killing login + // dialog thread"); + // throw new ThreadDeath(); + // } + } catch (final Exception e) { + // do nothing + } + } + processCallbacks = false; + // Call the adapter to handle the callbacks + if (!isCancelled()) + internalHandle(); + else + // clear callbacks are when cancelling + for (Callback callback : callbacks) + if (callback instanceof PasswordCallback) { + char[] arr = ((PasswordCallback) callback).getPassword(); + if (arr != null) { + Arrays.fill(arr, '*'); + ((PasswordCallback) callback).setPassword(null); + } + } else if (callback instanceof NameCallback) + ((NameCallback) callback).setName(null); + } + }, true, new NullProgressMonitor(), Display.getDefault()); + } catch (ThreadDeath e) { + isCancelled = true; + log.debug("Thread " + Thread.currentThread().getId() + " died"); + throw e; + } catch (Exception e) { + isCancelled = true; + IOException ioe = new IOException("Unexpected issue in login dialog, see root cause for more details"); + ioe.initCause(e); + throw ioe; + } finally { + // so that the modal thread dies + processCallbacks = true; + // try { + // // wait for the modal context thread to gracefully exit + // modalContextThread.join(); + // } catch (InterruptedException ie) { + // // silent + // } + modalContextThread = null; + } + } + + protected void configureShell(Shell shell) { + super.configureShell(shell); + shell.setText("Authentication"); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLogin.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLogin.java new file mode 100644 index 000000000..4533f6498 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLogin.java @@ -0,0 +1,335 @@ +package org.argeo.cms.ui.widgets.auth; + +import static org.argeo.cms.CmsMsg.password; +import static org.argeo.cms.CmsMsg.username; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.LanguageCallback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeState; +import org.argeo.cms.CmsMsg; +import org.argeo.cms.LocaleUtils; +import org.argeo.cms.auth.HttpRequestCallback; +import org.argeo.cms.ui.CmsStyles; +import org.argeo.cms.ui.CmsView; +import org.argeo.cms.ui.internal.Activator; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.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.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.Shell; +import org.eclipse.swt.widgets.Text; + +public class CmsLogin implements CmsStyles, CallbackHandler { + private final static Log log = LogFactory.getLog(CmsLogin.class); + + private Composite parent; + private Text usernameT, passwordT; + private Composite credentialsBlock; + private final SelectionListener loginSelectionListener; + + private final Locale defaultLocale; + private LocaleChoice localeChoice = null; + + private final CmsView cmsView; + + // optional subject to be set explicitly + private Subject subject = null; + + public CmsLogin(CmsView cmsView) { + this.cmsView = cmsView; + NodeState nodeState = Activator.getNodeState(); + if (nodeState != null) { + defaultLocale = nodeState.getDefaultLocale(); + List locales = nodeState.getLocales(); + if (locales != null) + localeChoice = new LocaleChoice(locales, defaultLocale); + } else { + defaultLocale = Locale.getDefault(); + } + loginSelectionListener = new SelectionListener() { + private static final long serialVersionUID = -8832133363830973578L; + + @Override + public void widgetSelected(SelectionEvent e) { + login(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }; + } + + protected boolean isAnonymous() { + return cmsView.isAnonymous(); + } + + public final void createUi(Composite parent) { + this.parent = parent; + createContents(parent); + } + + protected void createContents(Composite parent) { + defaultCreateContents(parent); + } + + public final void defaultCreateContents(Composite parent) { + parent.setLayout(CmsUiUtils.noSpaceGridLayout()); + Composite credentialsBlock = createCredentialsBlock(parent); + if (parent instanceof Shell) { + credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + } + } + + public final Composite createCredentialsBlock(Composite parent) { + if (isAnonymous()) { + return anonymousUi(parent); + } else { + return userUi(parent); + } + } + + protected Composite getCredentialsBlock() { + return credentialsBlock; + } + + protected Composite userUi(Composite parent) { + Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); + credentialsBlock = new Composite(parent, SWT.NONE); + credentialsBlock.setLayout(new GridLayout()); + // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); + + specificUserUi(credentialsBlock); + + Label l = new Label(credentialsBlock, SWT.NONE); + CmsUiUtils.style(l, CMS_USER_MENU_ITEM); + l.setText(CmsMsg.logout.lead(locale)); + GridData lData = CmsUiUtils.fillWidth(); + lData.widthHint = 120; + l.setLayoutData(lData); + + l.addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6444395812777413116L; + + public void mouseDown(MouseEvent e) { + logout(); + } + }); + return credentialsBlock; + } + + /** To be overridden */ + protected void specificUserUi(Composite parent) { + + } + + protected Composite anonymousUi(Composite parent) { + Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); + // We need a composite for the traversal + credentialsBlock = new Composite(parent, SWT.NONE); + credentialsBlock.setLayout(new GridLayout()); + // credentialsBlock.setLayoutData(CmsUiUtils.fillAll()); + CmsUiUtils.style(credentialsBlock, CMS_LOGIN_DIALOG); + + Integer textWidth = 120; + if (parent instanceof Shell) + CmsUiUtils.style(parent, CMS_USER_MENU); + // new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); + usernameT = new Text(credentialsBlock, SWT.BORDER); + usernameT.setMessage(username.lead(locale)); + CmsUiUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME); + GridData gd = CmsUiUtils.fillWidth(); + gd.widthHint = textWidth; + usernameT.setLayoutData(gd); + + // new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); + passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); + passwordT.setMessage(password.lead(locale)); + CmsUiUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD); + gd = CmsUiUtils.fillWidth(); + gd.widthHint = textWidth; + passwordT.setLayoutData(gd); + + TraverseListener tl = new TraverseListener() { + private static final long serialVersionUID = -1158892811534971856L; + + public void keyTraversed(TraverseEvent e) { + if (e.detail == SWT.TRAVERSE_RETURN) + login(); + } + }; + credentialsBlock.addTraverseListener(tl); + usernameT.addTraverseListener(tl); + passwordT.addTraverseListener(tl); + parent.setTabList(new Control[] { credentialsBlock }); + credentialsBlock.setTabList(new Control[] { usernameT, passwordT }); + + // Button + Button loginButton = new Button(credentialsBlock, SWT.PUSH); + loginButton.setText(CmsMsg.login.lead(locale)); + loginButton.setLayoutData(CmsUiUtils.fillWidth()); + loginButton.addSelectionListener(loginSelectionListener); + + extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener); + if (localeChoice != null) + createLocalesBlock(credentialsBlock); + return credentialsBlock; + } + + /** + * To be overridden in order to provide custom login button and other links. + */ + protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, + SelectionListener loginSelectionListener) { + + } + + protected void updateLocale(Locale selectedLocale) { + // save already entered values + String usernameStr = usernameT.getText(); + char[] pwd = passwordT.getTextChars(); + + for (Control child : parent.getChildren()) + child.dispose(); + createContents(parent); + if (parent.getParent() != null) + parent.getParent().layout(); + else + parent.layout(); + usernameT.setText(usernameStr); + passwordT.setTextChars(pwd); + } + + protected Composite createLocalesBlock(final Composite parent) { + Composite c = new Composite(parent, SWT.NONE); + CmsUiUtils.style(c, CMS_USER_MENU_ITEM); + c.setLayout(CmsUiUtils.noSpaceGridLayout()); + c.setLayoutData(CmsUiUtils.fillAll()); + + SelectionListener selectionListener = new SelectionAdapter() { + private static final long serialVersionUID = 4891637813567806762L; + + public void widgetSelected(SelectionEvent event) { + Button button = (Button) event.widget; + if (button.getSelection()) { + localeChoice.setSelectedIndex((Integer) event.widget.getData()); + updateLocale(localeChoice.getSelectedLocale()); + } + }; + }; + + List locales = localeChoice.getLocales(); + for (Integer i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + Button button = new Button(c, SWT.RADIO); + CmsUiUtils.style(button, CMS_USER_MENU_ITEM); + button.setData(i); + button.setText(LocaleUtils.lead(locale.getDisplayName(locale), locale) + " (" + locale + ")"); + // button.addListener(SWT.Selection, listener); + button.addSelectionListener(selectionListener); + if (i == localeChoice.getSelectedIndex()) + button.setSelection(true); + } + return c; + } + + protected boolean login() { + // TODO use CmsVie in order to retrieve subject? + // Subject subject = cmsView.getLoginContext().getSubject(); + // LoginContext loginContext = cmsView.getLoginContext(); + try { + // + // LOGIN + // + // loginContext.logout(); + LoginContext loginContext; + if (subject == null) + loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, this); + else + loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject, this); + loginContext.login(); + cmsView.authChange(loginContext); + return true; + } catch (LoginException e) { + if (log.isTraceEnabled()) + log.warn("Login failed: " + e.getMessage(), e); + else + log.warn("Login failed: " + e.getMessage()); + + try { + Thread.sleep(3000); + } catch (InterruptedException e2) { + // silent + } + // ErrorFeedback.show("Login failed", e); + return false; + } + // catch (LoginException e) { + // log.error("Cannot login", e); + // return false; + // } + } + + protected void logout() { + cmsView.logout(); + cmsView.navigateTo("~"); + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback && usernameT != null) + ((NameCallback) callback).setName(usernameT.getText()); + else if (callback instanceof PasswordCallback && passwordT != null) + ((PasswordCallback) callback).setPassword(passwordT.getTextChars()); + else if (callback instanceof HttpRequestCallback) { + ((HttpRequestCallback) callback).setRequest(UiContext.getHttpRequest()); + ((HttpRequestCallback) callback).setResponse(UiContext.getHttpResponse()); + } else if (callback instanceof LanguageCallback) { + Locale toUse = null; + if (localeChoice != null) + toUse = localeChoice.getSelectedLocale(); + else if (defaultLocale != null) + toUse = defaultLocale; + + if (toUse != null) { + ((LanguageCallback) callback).setLocale(toUse); + UiContext.setLocale(toUse); + } + + } + } + } + + public void setSubject(Subject subject) { + this.subject = subject; + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLoginShell.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLoginShell.java new file mode 100644 index 000000000..5460ffd0e --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLoginShell.java @@ -0,0 +1,72 @@ +package org.argeo.cms.ui.widgets.auth; + +import org.argeo.cms.ui.CmsView; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** The site-related user menu */ +public class CmsLoginShell extends CmsLogin { + private final Shell shell; + + public CmsLoginShell(CmsView cmsView) { + super(cmsView); + shell = createShell(); + CmsUiUtils.style(shell, CMS_USER_MENU); +// createUi(shell); + } + + /** To be overridden. */ + protected Shell createShell() { + Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM); + shell.setMaximized(true); + return shell; + } + + /** To be overridden. */ + public void open() { + shell.open(); + } + + @Override + protected boolean login() { + boolean success = false; + try { + success = super.login(); + return success; + } finally { + if (success) + closeShell(); + else { + for (Control child : shell.getChildren()) + child.dispose(); + createUi(shell); + shell.layout(); + // TODO error message + } + } + } + + @Override + protected void logout() { + closeShell(); + super.logout(); + } + + protected void closeShell() { + if (!shell.isDisposed()) { + shell.close(); + shell.dispose(); + } + } + + public Shell getShell() { + return shell; + } + + public void createUi(){ + createUi(shell); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CompositeCallbackHandler.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CompositeCallbackHandler.java new file mode 100644 index 000000000..974b6765b --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CompositeCallbackHandler.java @@ -0,0 +1,273 @@ +package org.argeo.cms.ui.widgets.auth; + +import java.io.IOException; +import java.util.Arrays; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.TextOutputCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * A composite that can populate itself based on {@link Callback}s. It can be + * used directly as a {@link CallbackHandler} or be used by one by calling the + * {@link #createCallbackHandlers(Callback[])}. Supported standard + * {@link Callback}s are:
+ *
    + *
  • {@link PasswordCallback}
  • + *
  • {@link NameCallback}
  • + *
  • {@link TextOutputCallback}
  • + *
+ * Supported Argeo {@link Callback}s are:
+ *
    + *
  • {@link LocaleChoice}
  • + *
+ */ +public class CompositeCallbackHandler extends Composite implements CallbackHandler { + private static final long serialVersionUID = -928223893722723777L; + + private boolean wasUsedAlready = false; + private boolean isSubmitted = false; + private boolean isCanceled = false; + + public CompositeCallbackHandler(Composite parent, int style) { + super(parent, style); + } + + @Override + public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + // reset + if (wasUsedAlready && !isSubmitted() && !isCanceled()) { + cancel(); + for (Control control : getChildren()) + control.dispose(); + isSubmitted = false; + isCanceled = false; + } + + for (Callback callback : callbacks) + checkCallbackSupported(callback); + // create controls synchronously in the UI thread + getDisplay().syncExec(new Runnable() { + + @Override + public void run() { + createCallbackHandlers(callbacks); + } + }); + + if (!wasUsedAlready) + wasUsedAlready = true; + + // while (!isSubmitted() && !isCanceled()) { + // try { + // wait(1000l); + // } catch (InterruptedException e) { + // // silent + // } + // } + + // cleanCallbacksAfterCancel(callbacks); + } + + public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException { + if (callback instanceof TextOutputCallback || callback instanceof NameCallback + || callback instanceof PasswordCallback || callback instanceof LocaleChoice) { + return; + } else { + throw new UnsupportedCallbackException(callback); + } + } + + /** + * Set writable callbacks to null if the handle is canceled (check is done + * by the method) + */ + public void cleanCallbacksAfterCancel(Callback[] callbacks) { + if (isCanceled()) { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + ((NameCallback) callback).setName(null); + } else if (callback instanceof PasswordCallback) { + PasswordCallback pCallback = (PasswordCallback) callback; + char[] arr = pCallback.getPassword(); + if (arr != null) { + Arrays.fill(arr, '*'); + pCallback.setPassword(null); + } + } + } + } + } + + public void createCallbackHandlers(Callback[] callbacks) { + Composite composite = this; + for (int i = 0; i < callbacks.length; i++) { + Callback callback = callbacks[i]; + if (callback instanceof TextOutputCallback) { + createLabelTextoutputHandler(composite, (TextOutputCallback) callback); + } else if (callback instanceof NameCallback) { + createNameHandler(composite, (NameCallback) callback); + } else if (callback instanceof PasswordCallback) { + createPasswordHandler(composite, (PasswordCallback) callback); + } else if (callback instanceof LocaleChoice) { + createLocaleHandler(composite, (LocaleChoice) callback); + } + } + } + + protected Text createNameHandler(Composite composite, final NameCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getPrompt()); + final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER); + if (callback.getDefaultName() != null) { + // set default value, if provided + text.setText(callback.getDefaultName()); + callback.setName(callback.getDefaultName()); + } + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + text.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 7300032545287292973L; + + public void modifyText(ModifyEvent event) { + callback.setName(text.getText()); + } + }); + text.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 1820530045857665111L; + + @Override + public void widgetSelected(SelectionEvent e) { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + submit(); + } + }); + + text.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8698107785092095713L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + } + }); + return text; + } + + protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getPrompt()); + final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); + passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + passwordText.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = -7099363995047686732L; + + public void modifyText(ModifyEvent event) { + callback.setPassword(passwordText.getTextChars()); + } + }); + passwordText.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 1820530045857665111L; + + @Override + public void widgetSelected(SelectionEvent e) { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + submit(); + } + }); + return passwordText; + } + + protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) { + String[] labels = callback.getSupportedLocalesLabels(); + if (labels.length == 0) + return null; + Label label = new Label(composite, SWT.NONE); + label.setText("Language"); + + final Combo combo = new Combo(composite, SWT.READ_ONLY); + combo.setItems(labels); + combo.select(callback.getDefaultIndex()); + combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + combo.addSelectionListener(new SelectionListener() { + private static final long serialVersionUID = 38678989091946277L; + + @Override + public void widgetSelected(SelectionEvent e) { + callback.setSelectedIndex(combo.getSelectionIndex()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + return combo; + } + + protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) { + Label label = new Label(composite, SWT.NONE); + label.setText(callback.getMessage()); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + data.horizontalSpan = 2; + label.setLayoutData(data); + return label; + // TODO: find a way to pass this information + // int messageType = callback.getMessageType(); + // int dialogMessageType = IMessageProvider.NONE; + // switch (messageType) { + // case TextOutputCallback.INFORMATION: + // dialogMessageType = IMessageProvider.INFORMATION; + // break; + // case TextOutputCallback.WARNING: + // dialogMessageType = IMessageProvider.WARNING; + // break; + // case TextOutputCallback.ERROR: + // dialogMessageType = IMessageProvider.ERROR; + // break; + // } + // setMessage(callback.getMessage(), dialogMessageType); + } + + synchronized boolean isSubmitted() { + return isSubmitted; + } + + synchronized boolean isCanceled() { + return isCanceled; + } + + protected synchronized void submit() { + isSubmitted = true; + notifyAll(); + } + + protected synchronized void cancel() { + isCanceled = true; + notifyAll(); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DefaultLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DefaultLoginDialog.java new file mode 100644 index 000000000..10edbf318 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DefaultLoginDialog.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.cms.ui.widgets.auth; + +import javax.security.auth.callback.CallbackHandler; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** Default authentication dialog, to be used as {@link CallbackHandler}. */ +public class DefaultLoginDialog extends AbstractLoginDialog { + private static final long serialVersionUID = -8551827590693035734L; + + public DefaultLoginDialog() { + this(Display.getCurrent().getActiveShell()); + } + + public DefaultLoginDialog(Shell parentShell) { + super(parentShell); + } + + protected Point getInitialSize() { + return new Point(350, 180); + } + + @Override + protected Control createContents(Composite parent) { + Control control = super.createContents(parent); + parent.pack(); + + // Move the dialog to the center of the top level shell. + Rectangle shellBounds; + if (Display.getCurrent().getActiveShell() != null) // RCP + shellBounds = Display.getCurrent().getActiveShell().getBounds(); + else + shellBounds = Display.getCurrent().getBounds();// RAP + Point dialogSize = parent.getSize(); + int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; + int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; + parent.setLocation(x, y); + return control; + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + CompositeCallbackHandler composite = new CompositeCallbackHandler( + dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + composite.createCallbackHandlers(getCallbacks()); + return composite; + } + + public void internalHandle() { + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DynamicCallbackHandler.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DynamicCallbackHandler.java new file mode 100644 index 000000000..e470bda0b --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DynamicCallbackHandler.java @@ -0,0 +1,34 @@ +package org.argeo.cms.ui.widgets.auth; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.argeo.eclipse.ui.dialogs.LightweightDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class DynamicCallbackHandler implements CallbackHandler { + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + Shell activeShell = Display.getCurrent().getActiveShell(); + LightweightDialog dialog = new LightweightDialog(activeShell) { + + @Override + protected Control createDialogArea(Composite parent) { + CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE); + cch.createCallbackHandlers(callbacks); + return cch; + } + }; + dialog.setBlockOnOpen(true); + dialog.open(); + } + +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/LocaleChoice.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/LocaleChoice.java new file mode 100644 index 000000000..0c8031b9e --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/LocaleChoice.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.cms.ui.widgets.auth; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import javax.security.auth.callback.LanguageCallback; + +import org.argeo.cms.CmsException; +import org.argeo.cms.LocaleUtils; + +/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */ +public class LocaleChoice { + private final List locales; + + private Integer selectedIndex = null; + private final Integer defaultIndex; + + public LocaleChoice(List locales, Locale defaultLocale) { + Integer defaultIndex = null; + this.locales = Collections.unmodifiableList(locales); + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).equals(defaultLocale)) + defaultIndex = i; + + // based on language only + if (defaultIndex == null) + for (int i = 0; i < locales.size(); i++) + if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage())) + defaultIndex = i; + + if (defaultIndex == null) + throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales); + this.defaultIndex = defaultIndex; + + this.selectedIndex = defaultIndex; + } + + /** + * Convenience constructor based on a comma separated list of iso codes (en, + * en_US, fr_CA, etc.). Default selection is default locale. + */ + public LocaleChoice(String locales, Locale defaultLocale) { + this(LocaleUtils.asLocaleList(locales), defaultLocale); + } + + public String[] getSupportedLocalesLabels() { + String[] labels = new String[locales.size()]; + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (locale.getCountry().equals("")) + labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]"; + else + labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") [" + + locale.getLanguage() + "_" + locale.getCountry() + "]"; + + } + return labels; + } + + public Locale getSelectedLocale() { + if (selectedIndex == null) + return null; + return locales.get(selectedIndex); + } + + public void setSelectedIndex(Integer selectedIndex) { + this.selectedIndex = selectedIndex; + } + + public Integer getSelectedIndex() { + return selectedIndex; + } + + public Integer getDefaultIndex() { + return defaultIndex; + } + + public List getLocales() { + return locales; + } + + public Locale getDefaultLocale() { + return locales.get(getDefaultIndex()); + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java deleted file mode 100644 index 7342e1052..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -import org.argeo.cms.CmsException; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; - -/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */ -public class BundleResourceLoader implements ResourceLoader { - private final Bundle bundle; - - public BundleResourceLoader(Bundle bundle) { - this.bundle = bundle; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - URL res = bundle.getEntry(resourceName); - if (res == null) { - res = bundle.getResource(resourceName); - if (res == null) - throw new CmsException("Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName()); - } - return res.openStream(); - } - - public Bundle getBundle() { - return bundle; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java deleted file mode 100644 index b18770f18..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java +++ /dev/null @@ -1,274 +0,0 @@ -package org.argeo.cms.util; - -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.node.NodeUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; - -/** A link to an internal or external location. */ -public class CmsLink implements CmsUiProvider { - private final static Log log = LogFactory.getLog(CmsLink.class); - private BundleContext bundleContext; - - private String label; - private String custom; - private String target; - private String image; - private MouseListener mouseListener; - - private int horizontalAlignment = SWT.CENTER; - private int verticalAlignment = SWT.CENTER; - - private String loggedInLabel = null; - private String loggedInTarget = null; - - // internal - // private Boolean isUrl = false; - private Integer imageWidth, imageHeight; - - public CmsLink() { - super(); - } - - public CmsLink(String label, String target) { - this(label, target, null); - } - - public CmsLink(String label, String target, String custom) { - super(); - this.label = label; - this.target = target; - this.custom = custom; - init(); - } - - public void init() { - if (image != null) { - ImageData image = loadImage(); - if (imageHeight == null && imageWidth == null) { - imageWidth = image.width; - imageHeight = image.height; - } else if (imageHeight == null) { - imageHeight = (imageWidth * image.height) / image.width; - } else if (imageWidth == null) { - imageWidth = (imageHeight * image.width) / image.height; - } - } - } - - /** @return {@link Composite} with a single {@link Label} child. */ - @Override - public Control createUi(final Composite parent, Node context) { -// if (image != null && (imageWidth == null || imageHeight == null)) { -// throw new CmsException("Image is not properly configured." -// + " Make sure bundleContext property is set and init() method has been called."); -// } - - Composite comp = new Composite(parent, SWT.NONE); - comp.setLayout(CmsUtils.noSpaceGridLayout()); - - Label link = new Label(comp, SWT.NONE); - link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); - if (image != null) { - if (imageHeight != null) - layoutData.heightHint = imageHeight; - if (label == null) - if (imageWidth != null) - layoutData.widthHint = imageWidth; - } - - link.setLayoutData(layoutData); - if (custom != null) { - comp.setData(RWT.CUSTOM_VARIANT, custom); - link.setData(RWT.CUSTOM_VARIANT, custom); - } else { - comp.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK); - link.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_LINK); - } - - // label - StringBuilder labelText = new StringBuilder(); - if (loggedInTarget != null && isLoggedIn()) { - labelText.append(""); - } else if (target != null) { - labelText.append(""); - } - if (image != null) { - registerImageIfNeeded(); - String imageLocation = RWT.getResourceManager().getLocation(image); - labelText.append(""); - - } - - if (loggedInLabel != null && isLoggedIn()) { - labelText.append(' ').append(loggedInLabel); - } else if (label != null) { - labelText.append(' ').append(label); - } - - if ((loggedInTarget != null && isLoggedIn()) || target != null) - labelText.append(""); - - link.setText(labelText.toString()); - - if (mouseListener != null) - link.addMouseListener(mouseListener); - - return comp; - } - - private void registerImageIfNeeded() { - ResourceManager resourceManager = RWT.getResourceManager(); - if (!resourceManager.isRegistered(image)) { - URL res = getImageUrl(); - InputStream inputStream = null; - try { - IOUtils.closeQuietly(inputStream); - inputStream = res.openStream(); - resourceManager.register(image, inputStream); - if (log.isTraceEnabled()) - log.trace("Registered image " + image); - } catch (Exception e) { - throw new CmsException("Cannot load image " + image, e); - } finally { - IOUtils.closeQuietly(inputStream); - } - } - } - - private ImageData loadImage() { - URL url = getImageUrl(); - ImageData result = null; - InputStream inputStream = null; - try { - inputStream = url.openStream(); - result = new ImageData(inputStream); - if (log.isTraceEnabled()) - log.trace("Loaded image " + image); - } catch (Exception e) { - throw new CmsException("Cannot load image " + image, e); - } finally { - IOUtils.closeQuietly(inputStream); - } - return result; - } - - private URL getImageUrl() { - URL url; - try { - // pure URL - url = new URL(image); - } catch (MalformedURLException e1) { - url = bundleContext.getBundle().getResource(image); - } - - if (url == null) - throw new CmsException("No image " + image + " available."); - - return url; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setLabel(String label) { - this.label = label; - } - - public void setCustom(String custom) { - this.custom = custom; - } - - public void setTarget(String target) { - this.target = target; - // try { - // new URL(target); - // isUrl = true; - // } catch (MalformedURLException e1) { - // isUrl = false; - // } - } - - public void setImage(String image) { - this.image = image; - } - - public void setLoggedInLabel(String loggedInLabel) { - this.loggedInLabel = loggedInLabel; - } - - public void setLoggedInTarget(String loggedInTarget) { - this.loggedInTarget = loggedInTarget; - } - - public void setMouseListener(MouseListener mouseListener) { - this.mouseListener = mouseListener; - } - - public void setvAlign(String vAlign) { - if ("bottom".equals(vAlign)) { - verticalAlignment = SWT.BOTTOM; - } else if ("top".equals(vAlign)) { - verticalAlignment = SWT.TOP; - } else if ("center".equals(vAlign)) { - verticalAlignment = SWT.CENTER; - } else { - throw new CmsException("Unsupported vertical allignment " + vAlign + " (must be: top, bottom or center)"); - } - } - - protected boolean isLoggedIn() { - return !CurrentUser.isAnonymous(); - } - - public void setImageWidth(Integer imageWidth) { - this.imageWidth = imageWidth; - } - - public void setImageHeight(Integer imageHeight) { - this.imageHeight = imageHeight; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsPane.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsPane.java deleted file mode 100644 index a8d085cfe..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsPane.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.cms.util; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Composite; - -/** The main pane of a CMS display, with QA and support areas. */ -public class CmsPane { - - private Composite mainArea; - private Composite qaArea; - private Composite supportArea; - - public CmsPane(Composite parent, int style) { - parent.setLayout(CmsUtils.noSpaceGridLayout()); - -// qaArea = new Composite(parent, SWT.NONE); -// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout qaLayout = new RowLayout(); -// qaLayout.spacing = 0; -// qaArea.setLayout(qaLayout); - - mainArea = new Composite(parent, SWT.NONE); - mainArea.setLayout(new GridLayout()); - mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - -// supportArea = new Composite(parent, SWT.NONE); -// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout supportLayout = new RowLayout(); -// supportLayout.spacing = 0; -// supportArea.setLayout(supportLayout); - } - - public Composite getMainArea() { - return mainArea; - } - - public Composite getQaArea() { - return qaArea; - } - - public Composite getSupportArea() { - return supportArea; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsTheme.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsTheme.java deleted file mode 100644 index 8bb7098cf..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsTheme.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.argeo.cms.util; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; - -/** - * Simplifies the theming of an app (only RAP is supported at this stage).
- * - * Additional fonts listed in /fonts.txt.
- * Additional (standard CSS) header in /header.css.
- * RAP specific CSS files in /rap/*.css.
- * All images added as additional resources based on extensions - * / ** /*.{png,gif,jpeg,...}.
- */ -public class CmsTheme { - private final static Log log = LogFactory.getLog(CmsTheme.class); - - private String themeId; - private Map css = new HashMap<>(); - private Map resources = new HashMap<>(); - - private String headerCss; - private List fonts = new ArrayList<>(); - - private String basePath; - private String cssPath; - - public CmsTheme(BundleContext bundleContext) { - this(bundleContext, null); - } - - public CmsTheme(BundleContext bundleContext, String symbolicName) { - Bundle themeBundle; - if (symbolicName == null) { - themeBundle = bundleContext.getBundle(); -// basePath = "/theme/"; -// cssPath = basePath; - } else { - themeBundle = ThemeUtils.findThemeBundle(bundleContext, symbolicName); - } - basePath = "/"; - cssPath = "/rap/"; - this.themeId = RWT.DEFAULT_THEME_ID; - addStyleSheets(themeBundle, new BundleResourceLoader(themeBundle)); - BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); - addResources(themeBRL, "*.png"); - addResources(themeBRL, "*.gif"); - addResources(themeBRL, "*.jpg"); - addResources(themeBRL, "*.jpeg"); - addResources(themeBRL, "*.svg"); - addResources(themeBRL, "*.ico"); - - // fonts - URL fontsUrl = themeBundle.getEntry(basePath + "fonts.txt"); - if (fontsUrl != null) { - loadFontsUrl(fontsUrl); - } - - // common CSS header (plain CSS) - URL headerCssUrl = themeBundle.getEntry(basePath + "header.css"); - if (headerCssUrl != null) { - try (BufferedReader buffer = new BufferedReader(new InputStreamReader(headerCssUrl.openStream(), UTF_8))) { - headerCss = buffer.lines().collect(Collectors.joining("\n")); - } catch (IOException e) { - throw new CmsException("Cannot read " + headerCssUrl, e); - } - } - - } - - public void apply(Application application) { - resources: for (String name : resources.keySet()) { - if (name.startsWith("target/")) - continue resources; // skip maven output - application.addResource(name, resources.get(name)); - if (log.isTraceEnabled()) - log.trace("Added resource " + name); - } - for (String name : css.keySet()) { - application.addStyleSheet(themeId, name, css.get(name)); - if (log.isDebugEnabled()) - log.debug("Added RAP CSS " + name); - } - } - - public String getAdditionalHeaders() { - StringBuilder sb = new StringBuilder(); - if (headerCss != null) { - sb.append("\n"); - } - for (String link : fonts) { - sb.append("\n"); - } - if (sb.length() == 0) - return null; - else - return sb.toString(); - } - - void addStyleSheets(Bundle themeBundle, ResourceLoader ssRL) { - Enumeration themeResources = themeBundle.findEntries(cssPath, "*.css", true); - if (themeResources == null) - return; - while (themeResources.hasMoreElements()) { - String resource = themeResources.nextElement().getPath(); - // remove first '/' so that RWT registers it - resource = resource.substring(1); - if (!resource.endsWith("/")) { - if (css.containsKey(resource)) - log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName()); - css.put(resource, ssRL); - } - - } - - } - - void loadFontsUrl(URL url) { - try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { - String line = null; - while ((line = in.readLine()) != null) { - line = line.trim(); - if (!line.equals("") && !line.startsWith("#")) { - fonts.add(line); - } - } - } catch (IOException e) { - throw new CmsException("Cannot load URL " + url, e); - } - } - - void addResources(BundleResourceLoader themeBRL, String pattern) { - Bundle themeBundle = themeBRL.getBundle(); - Enumeration themeResources = themeBundle.findEntries(basePath, pattern, true); - if (themeResources == null) - return; - while (themeResources.hasMoreElements()) { - String resource = themeResources.nextElement().getPath(); - // remove first '/' so that RWT registers it - resource = resource.substring(1); - if (!resource.endsWith("/")) { - if (resources.containsKey(resource)) - log.warn("Overriding " + resource + " from " + themeBundle.getSymbolicName()); - resources.put(resource, themeBRL); - } - - } - - } - - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getBasePath() { - return basePath; - } - - public void setBasePath(String basePath) { - this.basePath = basePath; - } - - public String getCssPath() { - return cssPath; - } - - public void setCssPath(String cssPath) { - this.cssPath = cssPath; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java deleted file mode 100644 index f4004e493..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java +++ /dev/null @@ -1,248 +0,0 @@ -package org.argeo.cms.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.ui.CmsView; -import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Widget; - -/** Static utilities for the CMS framework. */ -public class CmsUtils implements CmsConstants { - // private final static Log log = LogFactory.getLog(CmsUtils.class); - - /** - * The CMS view related to this display, or null if none is available from this - * call. - */ - public static CmsView getCmsView() { - return UiContext.getData(CmsView.KEY); - } - - public static StringBuilder getServerBaseUrl(HttpServletRequest request) { - try { - URL url = new URL(request.getRequestURL().toString()); - StringBuilder buf = new StringBuilder(); - buf.append(url.getProtocol()).append("://").append(url.getHost()); - if (url.getPort() != -1) - buf.append(':').append(url.getPort()); - return buf; - } catch (MalformedURLException e) { - throw new CmsException("Cannot extract server base URL from " + request.getRequestURL(), e); - } - } - - // - public static String getDataUrl(Node node, HttpServletRequest request) throws RepositoryException { - try { - StringBuilder buf = getServerBaseUrl(request); - buf.append(getDataPath(node)); - return new URL(buf.toString()).toString(); - } catch (MalformedURLException e) { - throw new CmsException("Cannot build data URL for " + node, e); - } - } - - /** A path in the node repository */ - public static String getDataPath(Node node) throws RepositoryException { - return getDataPath(NodeConstants.NODE, node); - } - - public static String getDataPath(String cn, Node node) throws RepositoryException { - return NodeUtils.getDataPath(cn, node); - } - - /** @deprecated Use rowData16px() instead. GridData should not be reused. */ - @Deprecated - public static RowData ROW_DATA_16px = new RowData(16, 16); - - public static GridLayout noSpaceGridLayout() { - return noSpaceGridLayout(new GridLayout()); - } - - public static GridLayout noSpaceGridLayout(int columns) { - return noSpaceGridLayout(new GridLayout(columns, false)); - } - - public static GridLayout noSpaceGridLayout(GridLayout layout) { - layout.horizontalSpacing = 0; - layout.verticalSpacing = 0; - layout.marginWidth = 0; - layout.marginHeight = 0; - return layout; - } - - // - // GRID DATA - // - public static GridData fillWidth() { - return grabWidth(SWT.FILL, SWT.FILL); - } - - public static GridData fillAll() { - return new GridData(SWT.FILL, SWT.FILL, true, true); - } - - public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) { - return new GridData(horizontalAlignment, horizontalAlignment, true, false); - } - - public static RowData rowData16px() { - return new RowData(16, 16); - } - - /** Style widget */ - public static T style(T widget, String style) { - widget.setData(CmsConstants.STYLE, style); - return widget; - } - - /** Enable markups on widget */ - public static T markup(T widget) { - widget.setData(CmsConstants.MARKUP, true); - return widget; - } - - /** - * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}. - * - * @see #markup(Widget) - */ - public static T text(T widget, String txt) { - markup(widget); - if (widget instanceof Label) - ((Label) widget).setText(txt); - else if (widget instanceof Button) - ((Button) widget).setText(txt); - else if (widget instanceof Text) - ((Text) widget).setText(txt); - else - throw new IllegalArgumentException("Unsupported widget type " + widget.getClass()); - return widget; - } - - public static void setItemHeight(Table table, int height) { - table.setData(CmsConstants.ITEM_HEIGHT, height); - } - - /** Dispose all children of a Composite */ - public static void clear(Composite composite) { - for (Control child : composite.getChildren()) - child.dispose(); - } - - // - // JCR - // - public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { - if (has(parent, child)) - return child(parent, child); - return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); - } - - public static Node child(Node parent, Enum en) throws RepositoryException { - return parent.getNode(en.name()); - } - - public static Boolean has(Node parent, Enum en) throws RepositoryException { - return parent.hasNode(en.name()); - } - - public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { - return getOrAdd(parent, en, null); - } - - public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { - if (has(parent, en)) - return child(parent, en); - else if (primaryType == null) - return parent.addNode(en.name()); - else - return parent.addNode(en.name(), primaryType); - } - - // IMAGES - public static String img(String src, String width, String height) { - return imgBuilder(src, width, height).append("/>").toString(); - } - - public static String img(String src, Point size) { - return img(src, Integer.toString(size.x), Integer.toString(size.y)); - } - - public static StringBuilder imgBuilder(String src, String width, String height) { - return new StringBuilder(64).append("").toString(); - } - - /** @return null if not available */ - @Override - public StringBuilder getImageTagBuilder(Node node, Point size) throws RepositoryException { - return getImageTagBuilder(node, Integer.toString(size.x), Integer.toString(size.y)); - } - - /** @return null if not available */ - private StringBuilder getImageTagBuilder(Node node, String width, String height) throws RepositoryException { - String url = getImageUrl(node); - if (url == null) - return null; - return CmsUtils.imgBuilder(url, width, height); - } - - /** @return null if not available */ - @Override - public String getImageUrl(Node node) throws RepositoryException { - return CmsUtils.getDataPath(node); - // String name = getResourceName(node); - // ResourceManager resourceManager = RWT.getResourceManager(); - // if (!resourceManager.isRegistered(name)) { - // InputStream inputStream = null; - // Binary binary = getImageBinary(node); - // if (binary == null) - // return null; - // try { - // inputStream = binary.getStream(); - // resourceManager.register(name, inputStream); - // } finally { - // IOUtils.closeQuietly(inputStream); - // JcrUtils.closeQuietly(binary); - // } - // if (log.isTraceEnabled()) - // log.trace("Registered image " + name); - // } - // return resourceManager.getLocation(name); - } - - protected String getResourceName(Node node) throws RepositoryException { - String workspace = node.getSession().getWorkspace().getName(); - if (node.hasNode(JCR_CONTENT)) - return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); - else - return workspace + '_' + node.getIdentifier(); - } - - public Binary getImageBinary(Node node) throws RepositoryException { - if (node.isNodeType(NT_FILE)) { - return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); - } else { - return null; - } - } - - public Image getSwtImage(Node node) throws RepositoryException { - InputStream inputStream = null; - Binary binary = getImageBinary(node); - if (binary == null) - return null; - try { - inputStream = binary.getStream(); - return new Image(Display.getCurrent(), inputStream); - } finally { - IOUtils.closeQuietly(inputStream); - JcrUtils.closeQuietly(binary); - } - } - - @Override - public String uploadImage(Node parentNode, String fileName, InputStream in) throws RepositoryException { - InputStream inputStream = null; - try { - String previousResourceName = null; - if (parentNode.hasNode(fileName)) { - Node node = parentNode.getNode(fileName); - previousResourceName = getResourceName(node); - if (node.hasNode(JCR_CONTENT)) { - node.getNode(JCR_CONTENT).remove(); - node.addNode(JCR_CONTENT, NT_RESOURCE); - } - } - - byte[] arr = IOUtils.toByteArray(in); - Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); - inputStream = new ByteArrayInputStream(arr); - ImageData id = new ImageData(inputStream); - processNewImageFile(fileNode, id); - - String mime = Files.probeContentType(Paths.get(fileName)); - fileNode.setProperty(Property.JCR_MIMETYPE, mime); - fileNode.getSession().save(); - - // reset resource manager - ResourceManager resourceManager = RWT.getResourceManager(); - if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { - resourceManager.unregister(previousResourceName); - if (log.isDebugEnabled()) - log.debug("Unregistered image " + previousResourceName); - } - return getImageUrl(fileNode); - } catch (IOException e) { - throw new CmsException("Cannot upload image " + fileName + " in " + parentNode, e); - } finally { - IOUtils.closeQuietly(inputStream); - } - } - - /** Does nothign by default. */ - protected void processNewImageFile(Node fileNode, ImageData id) throws RepositoryException, IOException { - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/LoginEntryPoint.java b/org.argeo.cms.ui/src/org/argeo/cms/util/LoginEntryPoint.java deleted file mode 100644 index b7bf9103a..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/LoginEntryPoint.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.argeo.cms.util; - -import java.util.Locale; - -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.ui.UxContext; -import org.argeo.cms.widgets.auth.CmsLogin; -import org.argeo.cms.widgets.auth.CmsLoginShell; -import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.node.NodeConstants; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; - -public class LoginEntryPoint implements EntryPoint, CmsView { - protected final static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate"; - protected final static String HEADER_AUTHORIZATION = "Authorization"; - private final static Log log = LogFactory.getLog(LoginEntryPoint.class); - private LoginContext loginContext; - private UxContext uxContext = null; - - @Override - public int createUI() { - final Display display = createDisplay(); - UiContext.setData(CmsView.KEY, this); - CmsLoginShell loginShell = createCmsLoginShell(); - try { - // try pre-auth - loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, loginShell); - loginContext.login(); - } catch (LoginException e) { - loginShell.createUi(); - loginShell.open(); - - // HttpServletRequest request = RWT.getRequest(); - // String authorization = request.getHeader(HEADER_AUTHORIZATION); - // if (authorization == null || - // !authorization.startsWith("Negotiate")) { - // HttpServletResponse response = RWT.getResponse(); - // response.setStatus(401); - // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate"); - // response.setDateHeader("Date", System.currentTimeMillis()); - // response.setDateHeader("Expires", System.currentTimeMillis() + - // (24 * 60 * 60 * 1000)); - // response.setHeader("Accept-Ranges", "bytes"); - // response.setHeader("Connection", "Keep-Alive"); - // response.setHeader("Keep-Alive", "timeout=5, max=97"); - // // response.setContentType("text/html; charset=UTF-8"); - // } - - while (!loginShell.getShell().isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - } - - if (CurrentUser.getUsername(getSubject()) == null) - return -1; - uxContext = new SimpleUxContext(); - return postLogin(); - } - - protected Display createDisplay() { - return new Display(); - } - - protected int postLogin() { - return 0; - } - - protected HttpServletRequest getRequest() { - return RWT.getRequest(); - } - - protected CmsLoginShell createCmsLoginShell() { - return new CmsLoginShell(this) { - - @Override - public void createContents(Composite parent) { - LoginEntryPoint.this.createLoginPage(parent, this); - } - - @Override - protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, - SelectionListener loginSelectionListener) { - LoginEntryPoint.this.extendsCredentialsBlock(credentialsBlock, selectedLocale, loginSelectionListener); - } - - }; - } - - /** - * To be overridden. CmsLogin#createCredentialsBlock() should be called at - * some point in order to create the credentials composite. In order to use - * the default layout, call CmsLogin#defaultCreateContents() but not - * CmsLogin#createContent(), since it would lead to a stack overflow. - */ - protected void createLoginPage(Composite parent, CmsLogin login) { - login.defaultCreateContents(parent); - } - - protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, - SelectionListener loginSelectionListener) { - - } - - @Override - public void navigateTo(String state) { - // TODO Auto-generated method stub - - } - - @Override - public void authChange(LoginContext loginContext) { - if (loginContext == null) - throw new CmsException("Login context cannot be null"); - // logout previous login context - if (this.loginContext != null) - try { - this.loginContext.logout(); - } catch (LoginException e1) { - log.warn("Could not log out: " + e1); - } - this.loginContext = loginContext; - } - - @Override - public void logout() { - if (loginContext == null) - throw new CmsException("Login context should not bet null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - } catch (LoginException e) { - throw new CmsException("Cannot log out", e); - } - } - - @Override - public void exception(Throwable e) { - // TODO Auto-generated method stub - - } - - // @Override - // public LoginContext getLoginContext() { - // return loginContext; - // } - - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public CmsImageManager getImageManager() { - // TODO Auto-generated method stub - return null; - } - - @Override - public UxContext getUxContext() { - return uxContext; - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java deleted file mode 100644 index 79fd7cb72..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.argeo.cms.util; - -import org.argeo.cms.ui.CmsStyles; - -/** - * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on - * a {@link CmsLink} when simple menus are used. - */ -public class MenuLink extends CmsLink { - public MenuLink() { - setCustom(CmsStyles.CMS_MENU_LINK); - } - - public MenuLink(String label, String target, String custom) { - super(label, target, custom); - } - - public MenuLink(String label, String target) { - super(label, target, CmsStyles.CMS_MENU_LINK); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java deleted file mode 100644 index 327438b56..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java +++ /dev/null @@ -1,375 +0,0 @@ -package org.argeo.cms.util; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.security.Privilege; -import javax.jcr.version.VersionManager; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.LifeCycleUiProvider; -import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -/** A basic generic app based on {@link SimpleErgonomics}. */ -public class SimpleApp implements CmsConstants, ApplicationConfiguration { - private final static Log log = LogFactory.getLog(SimpleApp.class); - - private String contextName = null; - - private Map> branding = new HashMap>(); - private Map> styleSheets = new HashMap>(); - - private List resources = new ArrayList(); - - private BundleContext bundleContext; - - private Repository repository; - private String workspace = null; - private String jcrBasePath = "/"; - private List roPrincipals = Arrays.asList(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER); - private List rwPrincipals = Arrays.asList(NodeConstants.ROLE_USER); - - private CmsUiProvider header; - private Map pages = new LinkedHashMap(); - - private Integer headerHeight = 40; - - private ServiceRegistration appReg; - - public void configure(Application application) { - try { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE)); - // empty image - application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - Map defaultBranding = null; - if (branding.containsKey("*")) - defaultBranding = branding.get("*"); - String defaultTheme = defaultBranding.get(WebClient.THEME_ID); - - // entry points - for (String page : pages.keySet()) { - Map properties = defaultBranding != null ? new HashMap(defaultBranding) - : new HashMap(); - if (branding.containsKey(page)) { - properties.putAll(branding.get(page)); - } - // favicon - if (properties.containsKey(WebClient.FAVICON)) { - String themeId = defaultBranding.get(WebClient.THEME_ID); - Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId); - String faviconRelPath = properties.get(WebClient.FAVICON); - application.addResource(faviconRelPath, - new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle())); - if (log.isTraceEnabled()) - log.trace("Favicon " + faviconRelPath); - - } - - // page title - if (!properties.containsKey(WebClient.PAGE_TITLE)) { - if (page.length() > 0) - properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1)); - } - - // default body HTML - if (!properties.containsKey(WebClient.BODY_HTML)) - properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); - - // - // ADD ENTRY POINT - // - application.addEntryPoint("/" + page, - new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties); - log.info("Page /" + page); - } - - // stylesheets and themes - Set themeBundles = new HashSet<>(); - for (String themeId : styleSheets.keySet()) { - Bundle themeBundle = ThemeUtils.findThemeBundle(bundleContext, themeId); - StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader( - themeBundle != null ? themeBundle : bundleContext.getBundle()); - if (themeBundle != null) - themeBundles.add(themeBundle); - List cssLst = styleSheets.get(themeId); - if (log.isDebugEnabled()) - log.debug("Theme " + themeId); - for (String css : cssLst) { - application.addStyleSheet(themeId, css, styleSheetRL); - if (log.isDebugEnabled()) - log.debug(" CSS " + css); - } - - } - for (Bundle themeBundle : themeBundles) { - BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); - ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.png"); - ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.gif"); - ThemeUtils.addThemeResources(application, themeBundle, themeBRL, "*.jpg"); - } - } catch (RuntimeException e) { - // Easier access to initialisation errors - log.error("Unexpected exception when configuring RWT application.", e); - throw e; - } - } - - public void init() throws RepositoryException { - Session session = null; - try { - session = NodeUtils.openDataAdminSession(repository, workspace); - // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); - VersionManager vm = session.getWorkspace().getVersionManager(); - JcrUtils.mkdirs(session, jcrBasePath); - session.save(); - if (!vm.isCheckedOut(jcrBasePath)) - vm.checkout(jcrBasePath); - for (String principal : rwPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE); - for (String principal : roPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ); - - for (String pageName : pages.keySet()) { - try { - initPage(session, pages.get(pageName)); - session.save(); - } catch (Exception e) { - throw new CmsException("Cannot initialize page " + pageName, e); - } - } - - } finally { - JcrUtils.logoutQuietly(session); - } - - // publish to OSGi - register(); - } - - protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException { - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).init(adminSession); - } - - public void destroy() { - for (String pageName : pages.keySet()) { - try { - CmsUiProvider page = pages.get(pageName); - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).destroy(); - } catch (Exception e) { - log.error("Cannot destroy page " + pageName, e); - } - } - } - - protected void register() { - Hashtable props = new Hashtable(); - if (contextName != null) - props.put("contextName", contextName); - appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props); - if (log.isDebugEnabled()) - log.debug("Registered " + (contextName == null ? "/" : contextName)); - } - - protected void unregister() { - appReg.unregister(); - if (log.isDebugEnabled()) - log.debug("Unregistered " + (contextName == null ? "/" : contextName)); - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public void setWorkspace(String workspace) { - this.workspace = workspace; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setPages(Map pages) { - this.pages = pages; - } - - public void setJcrBasePath(String basePath) { - this.jcrBasePath = basePath; - } - - public void setRoPrincipals(List roPrincipals) { - this.roPrincipals = roPrincipals; - } - - public void setRwPrincipals(List rwPrincipals) { - this.rwPrincipals = rwPrincipals; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setBranding(Map> branding) { - this.branding = branding; - } - - public void setStyleSheets(Map> styleSheets) { - this.styleSheets = styleSheets; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setResources(List resources) { - this.resources = resources; - } - - public void setContextName(String contextName) { - this.contextName = contextName; - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUtils.getCmsView().exception(throwable); - } - - } - - private class CmsEntryPointFactory implements EntryPointFactory { - private final CmsUiProvider page; - private final Repository repository; - private final String workspace; - private final Map properties; - - public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace, - Map properties) { - this.page = page; - this.repository = repository; - this.workspace = workspace; - this.properties = properties; - } - - @Override - public EntryPoint create() { - SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) { - private static final long serialVersionUID = -637940404865527290L; - - @Override - protected void createAdminArea(Composite parent) { - Composite adminArea = new Composite(parent, SWT.NONE); - adminArea.setLayout(new FillLayout()); - Button refresh = new Button(adminArea, SWT.PUSH); - refresh.setText("Reload App"); - refresh.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7671999525536351366L; - - @Override - public void widgetSelected(SelectionEvent e) { - long timeBeforeReload = 1000; - RWT.getClient().getService(JavaScriptExecutor.class).execute( - "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");"); - reloadApp(); - } - }); - } - }; - // entryPoint.setState(""); - entryPoint.setHeader(header); - entryPoint.setHeaderHeight(headerHeight); - // CmsSession.current.set(entryPoint); - return entryPoint; - } - - private void reloadApp() { - new Thread("Refresh app") { - @Override - public void run() { - unregister(); - register(); - } - }.start(); - } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - // private static ResourceLoader createUrlResourceLoader(final URL url) { - // return new ResourceLoader() { - // public InputStream getResourceAsStream(String resourceName) - // throws IOException { - // return url.openStream(); - // } - // }; - // } - - /* - * TEXTS - */ - private static String DEFAULT_LOADING_BODY = "" - + "" + ""; -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java deleted file mode 100644 index ae1299dc8..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.argeo.cms.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -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; - -/** A header in three parts */ -public class SimpleCmsHeader implements CmsUiProvider { - private List lead = new ArrayList(); - private List center = new ArrayList(); - private List end = new ArrayList(); - - private Boolean subPartsSameWidth = false; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite header = new Composite(parent, SWT.NONE); - header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); - header.setBackgroundMode(SWT.INHERIT_DEFAULT); - header.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(3, false))); - - configurePart(context, header, lead); - configurePart(context, header, center); - configurePart(context, header, end); - return header; - } - - protected void configurePart(Node context, Composite parent, List partProviders) - throws RepositoryException { - final int style; - final String custom; - if (lead == partProviders) { - style = SWT.LEAD; - custom = CmsStyles.CMS_HEADER_LEAD; - } else if (center == partProviders) { - style = SWT.CENTER; - custom = CmsStyles.CMS_HEADER_CENTER; - } else if (end == partProviders) { - style = SWT.END; - custom = CmsStyles.CMS_HEADER_END; - } else { - throw new CmsException("Unsupported part providers " + partProviders); - } - - Composite part = new Composite(parent, SWT.NONE); - part.setData(RWT.CUSTOM_VARIANT, custom); - GridData gridData = new GridData(style, SWT.FILL, true, true); - part.setLayoutData(gridData); - part.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); - for (CmsUiProvider uiProvider : partProviders) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - } - } - - public void setLead(List lead) { - this.lead = lead; - } - - public void setCenter(List center) { - this.center = center; - } - - public void setEnd(List end) { - this.end = end; - } - - public void setSubPartsSameWidth(Boolean subPartsSameWidth) { - this.subPartsSameWidth = subPartsSameWidth; - } - - public List getLead() { - return lead; - } - - public List getCenter() { - return center; - } - - public List getEnd() { - return end; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java deleted file mode 100644 index dd95e7f35..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.argeo.cms.util; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -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; - -public class SimpleDynamicPages implements CmsUiProvider { - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - if (context == null) - throw new CmsException("Context cannot be null"); - parent.setLayout(new GridLayout(2, false)); - - // parent - if (!context.getPath().equals("/")) { - new CmsLink("..", context.getParent().getPath()).createUi(parent, - context); - new Label(parent, SWT.NONE).setText(context.getParent() - .getPrimaryNodeType().getName()); - } - - // context - Label contextL = new Label(parent, SWT.NONE); - contextL.setData(RWT.MARKUP_ENABLED, true); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() - .getName()); - - // children - // Label childrenL = new Label(parent, SWT.NONE); - // childrenL.setData(RWT.MARKUP_ENABLED, true); - // childrenL.setText("Children:"); - // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, - // false, 2, 1)); - - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), child.getPath()).createUi(parent, - context); - - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() - .getName()); - } - - // properties - // Label propsL = new Label(parent, SWT.NONE); - // propsL.setData(RWT.MARKUP_ENABLED, true); - // propsL.setText("Properties:"); - // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, - // 2, 1)); - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils - .getPropertyDefinitionAsString(property)); - - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private String getPropAsString(Property property) - throws RepositoryException { - String result = ""; - DateFormat timeFormatter = new SimpleDateFormat(""); - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) - throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java deleted file mode 100644 index 3f06d88e8..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.argeo.cms.util; - -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.AbstractCmsEntryPoint; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.UxContext; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Simple header/body ergonomics. */ -public class SimpleErgonomics extends AbstractCmsEntryPoint { - private static final long serialVersionUID = 8743413921359548523L; - - private final static Log log = LogFactory.getLog(SimpleErgonomics.class); - - private boolean uiInitialized = false; - private Composite headerArea; - private Composite leftArea; - private Composite rightArea; - private Composite footerArea; - private Composite bodyArea; - private final CmsUiProvider uiProvider; - - private CmsUiProvider header; - private Integer headerHeight = 0; - private Integer footerHeight = 0; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - private CmsImageManager imageManager = new DefaultImageManager(); - private UxContext uxContext = null; - - public SimpleErgonomics(Repository repository, String workspace, String defaultPath, CmsUiProvider uiProvider, - Map factoryProperties) { - super(repository, workspace, defaultPath, factoryProperties); - this.uiProvider = uiProvider; - } - - @Override - protected void initUi(Composite parent) { - parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - parent.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(3, false))); - - uxContext = new SimpleUxContext(); - if (!getUxContext().isMasterData()) - createAdminArea(parent); - headerArea = new Composite(parent, SWT.NONE); - headerArea.setLayout(new FillLayout()); - GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - headerData.heightHint = headerHeight; - headerArea.setLayoutData(headerData); - - // TODO: bi-directional - leftArea = new Composite(parent, SWT.NONE); - leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - leftArea.setLayout(CmsUtils.noSpaceGridLayout()); - - bodyArea = new Composite(parent, SWT.NONE); - bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); - bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - bodyArea.setLayout(CmsUtils.noSpaceGridLayout()); - - // TODO: bi-directional - rightArea = new Composite(parent, SWT.NONE); - rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); - rightArea.setLayout(CmsUtils.noSpaceGridLayout()); - - footerArea = new Composite(parent, SWT.NONE); - // footerArea.setLayout(new FillLayout()); - GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - footerData.heightHint = footerHeight; - footerArea.setLayoutData(footerData); - - uiInitialized = true; - refresh(); - } - - @Override - protected void refresh() { - if (!uiInitialized) - return; - if (getState() == null) - setState(""); - refreshSides(); - refreshBody(); - if (log.isTraceEnabled()) - log.trace("UI refreshed " + getNode()); - } - - protected void createAdminArea(Composite parent) { - } - - @Deprecated - protected void refreshHeader() { - if (header == null) - return; - - for (Control child : headerArea.getChildren()) - child.dispose(); - try { - header.createUi(headerArea, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - headerArea.layout(true, true); - } - - protected void refreshSides() { - refresh(headerArea, header, CmsStyles.CMS_HEADER); - refresh(leftArea, lead, CmsStyles.CMS_LEAD); - refresh(rightArea, end, CmsStyles.CMS_END); - refresh(footerArea, footer, CmsStyles.CMS_FOOTER); - } - - private void refresh(Composite area, CmsUiProvider uiProvider, String style) { - if (uiProvider == null) - return; - - for (Control child : area.getChildren()) - child.dispose(); - CmsUtils.style(area, style); - try { - uiProvider.createUi(area, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - area.layout(true, true); - } - - protected void refreshBody() { - // Exception - Throwable exception = getException(); - if (exception != null) { - SystemNotifications systemNotifications = new SystemNotifications(bodyArea); - systemNotifications.notifyException(exception); - resetException(); - return; - // TODO report - } - - // clear - for (Control child : bodyArea.getChildren()) - child.dispose(); - bodyArea.setLayout(CmsUtils.noSpaceGridLayout()); - - try { - Node node = getNode(); -// if (node == null) -// log.error("Context cannot be null"); -// else - uiProvider.createUi(bodyArea, node); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh body", e); - } - - bodyArea.layout(true, true); - } - - @Override - public UxContext getUxContext() { - return uxContext; - } - - @Override - public CmsImageManager getImageManager() { - return imageManager; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setImageManager(CmsImageManager imageManager) { - this.imageManager = imageManager; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setFooterHeight(Integer footerHeight) { - this.footerHeight = footerHeight; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleImageManager.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleImageManager.java deleted file mode 100644 index 4e90766a6..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleImageManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.cms.util; - -public class SimpleImageManager extends DefaultImageManager { - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java deleted file mode 100644 index 8ac3e963a..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.util; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class SimpleStaticPage implements CmsUiProvider { - private String text; - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Label textC = new Label(parent, SWT.WRAP); - textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); - textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - textC.setText(text); - - return textC; - } - - public void setText(String text) { - this.text = text; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java deleted file mode 100644 index bde67e404..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.cms.util; - -import org.argeo.cms.ui.UxContext; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Display; - -public class SimpleUxContext implements UxContext { - private Point size; - private Point small = new Point(400, 400); - - public SimpleUxContext() { - this(Display.getCurrent().getBounds()); - } - - public SimpleUxContext(Rectangle rect) { - this.size = new Point(rect.width, rect.height); - } - - public SimpleUxContext(Point size) { - this.size = size; - } - - @Override - public boolean isPortrait() { - return size.x >= size.y; - } - - @Override - public boolean isLandscape() { - return size.x < size.y; - } - - @Override - public boolean isSquare() { - return size.x == size.y; - } - - @Override - public boolean isSmall() { - return size.x <= small.x || size.y <= small.y; - } - - @Override - public boolean isMasterData() { - // TODO make it configurable - return true; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java b/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java deleted file mode 100644 index face42b0f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.argeo.cms.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.CmsException; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; - -/** {@link ResourceLoader} caching stylesheets. */ -public class StyleSheetResourceLoader implements ResourceLoader { - private Bundle themeBundle; - private Map stylesheets = new LinkedHashMap(); - - public StyleSheetResourceLoader(Bundle themeBundle) { - this.themeBundle = themeBundle; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - if (!stylesheets.containsKey(resourceName)) { - // TODO deal with other bundles - // Bundle bundle = bundleContext.getBundle(); - // String location = - // bundle.getLocation().substring("initial@reference:".length()); - // if (location.startsWith("file:")) { - // Path path = null; - // try { - // path = Paths.get(new URI(location)); - // } catch (URISyntaxException e) { - // e.printStackTrace(); - // } - // if (path != null) { - // Path resourcePath = path.resolve(resourceName); - // if (Files.exists(resourcePath)) - // return Files.newInputStream(resourcePath); - // } - // } - - URL res = themeBundle.getEntry(resourceName); - if (res == null) - throw new CmsException( - "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(res.openStream(), out); - stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); - } - return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); - // return res.openStream(); - } - - private class StyleSheet { - private byte[] data; - - public StyleSheet(byte[] data) { - super(); - this.data = data; - } - - public byte[] getData() { - return data; - } - - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java deleted file mode 100644 index 5fa79a331..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.argeo.cms.util; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsStyles; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -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.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Shell displaying system notifications such as exceptions */ -public class SystemNotifications extends Shell implements CmsStyles, - MouseListener { - private static final long serialVersionUID = -8129377525216022683L; - - private Control source; - - public SystemNotifications(Control source) { - super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); - - this.source = source; - - // TODO UI - // setLocation(source.toDisplay(source.getSize().x - getSize().x, - // source.getSize().y)); - setLayout(new GridLayout()); - addMouseListener(this); - - addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - close(); - dispose(); - } - }); - - } - - public void notifyException(Throwable exception) { - Composite pane = this; - - Label lbl = new Label(pane, SWT.NONE); - lbl.setText(exception.getLocalizedMessage() - + (exception instanceof CmsException ? "" : "(" - + exception.getClass().getName() + ")") + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (exception.getCause() != null) - appendCause(pane, exception.getCause()); - - StringBuilder mailToUrl = new StringBuilder("mailto:?"); - try { - mailToUrl.append("subject=").append( - URLEncoder.encode( - "Exception " - + new SimpleDateFormat("yyyy-MM-dd hh:mm") - .format(new Date()), "UTF-8") - .replace("+", "%20")); - - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - IOUtils.closeQuietly(sw); - - // see - // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character - String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( - "+", "%20"); - mailToUrl.append("&body=").append(encoded); - } catch (UnsupportedEncodingException e) { - mailToUrl.append("&body=").append("Could not encode: ") - .append(e.getMessage()); - } - Label mailTo = new Label(pane, SWT.NONE); - CmsUtils.markup(mailTo); - mailTo.setText("Send details"); - mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - - pack(); - layout(); - - setLocation(source.toDisplay(source.getSize().x - getSize().x, - source.getSize().y - getSize().y)); - open(); - } - - private void appendCause(Composite parent, Throwable e) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" - + e.getClass().getName() + ")" + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (e.getCause() != null) - appendCause(parent, e.getCause()); - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseDown(MouseEvent e) { - close(); - dispose(); - } - - @Override - public void mouseUp(MouseEvent e) { - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/ThemeUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/util/ThemeUtils.java deleted file mode 100644 index fdc1cb78a..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/ThemeUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.argeo.cms.util; - -import java.net.URL; -import java.util.Enumeration; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.eclipse.rap.rwt.application.Application; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; - -public class ThemeUtils { - final static Log log = LogFactory.getLog(ThemeUtils.class); - - public static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { - if (themeId == null) - return null; - // TODO optimize - // TODO deal with multiple versions - Bundle themeBundle = null; - if (themeId != null) { - for (Bundle bundle : bundleContext.getBundles()) - if (themeId.equals(bundle.getSymbolicName())) { - themeBundle = bundle; - break; - } - } - return themeBundle; - } - - public static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL, - String pattern) { - Enumeration themeResources = themeBundle.findEntries("/", pattern, true); - if (themeResources == null) - return; - while (themeResources.hasMoreElements()) { - String resource = themeResources.nextElement().getPath(); - // remove first '/' so that RWT registers it - resource = resource.substring(1); - if (!resource.endsWith("/")) { - application.addResource(resource, themeBRL); - if (log.isTraceEnabled()) - log.trace("Registered " + resource + " from theme " + themeBundle); - } - - } - - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java deleted file mode 100644 index 58b470dc8..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.argeo.cms.util; - -import javax.jcr.Node; - -import org.argeo.cms.CmsException; -import org.argeo.cms.widgets.auth.CmsLoginShell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** The site-related user menu */ -public class UserMenu extends CmsLoginShell { - private final Control source; - private final Node context; - - public UserMenu(Control source, Node context) { - super(CmsUtils.getCmsView()); - this.context = context; - createUi(); - if (source == null) - throw new CmsException("Source control cannot be null."); - this.source = source; - open(); - } - - @Override - protected Shell createShell() { - return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - } - - @Override - public void open() { - Shell shell = getShell(); - shell.pack(); - shell.layout(); - shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); - shell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - closeShell(); - } - }); - super.open(); - } - - protected Node getContext() { - return context; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java deleted file mode 100644 index 839567f4f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.cms.util; - -import javax.jcr.Node; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.widgets.auth.CmsLoginShell; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -/** Open the user menu when clicked */ -public class UserMenuLink extends MenuLink { - - public UserMenuLink() { - setCustom(CmsStyles.CMS_USER_MENU_LINK); - } - - @Override - public Control createUi(Composite parent, Node context) { - if (CurrentUser.isAnonymous()) - setLabel(CmsMsg.login.lead()); - else { - setLabel(CurrentUser.getDisplayName()); - } - Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; - link.addMouseListener(new UserMenuLinkController(context)); - return link.getParent(); - } - - protected CmsLoginShell createUserMenu(Control source, Node context) { - return new UserMenu(source.getParent(), context); - } - - private class UserMenuLinkController implements MouseListener, DisposeListener { - private static final long serialVersionUID = 3634864186295639792L; - - private CmsLoginShell userMenu = null; - private long lastDisposeTS = 0l; - - private final Node context; - - public UserMenuLinkController(Node context) { - this.context = context; - } - - // - // MOUSE LISTENER - // - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - if (userMenu == null) { - long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; - // avoid to reopen the menu, if one has clicked gain - if (durationSinceLastDispose > 200) { - userMenu = createUserMenu(source, context); - userMenu.getShell().addDisposeListener(this); - } - } - } - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseUp(MouseEvent e) { - } - - @Override - public void widgetDisposed(DisposeEvent event) { - userMenu = null; - lastDisposeTS = System.currentTimeMillis(); - } - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/VerticalMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/VerticalMenu.java deleted file mode 100644 index d0ea610f1..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/VerticalMenu.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.argeo.cms.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -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; - -public class VerticalMenu implements CmsUiProvider { - private List items = new ArrayList(); - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite part = new Composite(parent, SWT.NONE); - part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); -// part.setData(RWT.CUSTOM_VARIANT, custom); - part.setLayout(CmsUtils.noSpaceGridLayout()); - for (CmsUiProvider uiProvider : items) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - } - return part; - } - - public void add(CmsUiProvider uiProvider) { - items.add(uiProvider); - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java deleted file mode 100644 index eaaf4bc3f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java +++ /dev/null @@ -1,324 +0,0 @@ -package org.argeo.cms.viewers; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.security.auth.Subject; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsEditable; -import org.argeo.cms.widgets.ScrolledPage; -import org.eclipse.jface.viewers.ContentViewer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.StructuredSelection; -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.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.SAXParseException; - -/** Base class for viewers related to a page */ -public abstract class AbstractPageViewer extends ContentViewer implements Observer { - private static final long serialVersionUID = 5438688173410341485L; - - private final static Log log = LogFactory.getLog(AbstractPageViewer.class); - - private final boolean readOnly; - /** The basis for the layouts, typically a ScrolledPage. */ - private final Composite page; - private final CmsEditable cmsEditable; - - private MouseListener mouseListener; - private FocusListener focusListener; - - private EditablePart edited; - private ISelection selection = StructuredSelection.EMPTY; - - private AccessControlContext accessControlContext; - - protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { - // read only at UI level - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - - this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - - if (cmsEditable.canEdit()) { - mouseListener = createMouseListener(); - focusListener = createFocusListener(); - } - page = findPage(parent); - accessControlContext = AccessController.getContext(); - } - - /** - * Can be called to simplify the called to isModelInitialized() and initModel() - */ - protected void initModelIfNeeded(Node node) { - try { - if (!isModelInitialized(node)) - if (getCmsEditable().canEdit()) { - initModel(node); - node.getSession().save(); - } - } catch (Exception e) { - throw new CmsException("Cannot initialize model", e); - } - } - - /** Called if user can edit and model is not initialized */ - protected Boolean isModelInitialized(Node node) throws RepositoryException { - return true; - } - - /** Called if user can edit and model is not initialized */ - protected void initModel(Node node) throws RepositoryException { - } - - /** Create (retrieve) the MouseListener to use. */ - protected MouseListener createMouseListener() { - return new MouseAdapter() { - private static final long serialVersionUID = 1L; - }; - } - - /** Create (retrieve) the FocusListener to use. */ - protected FocusListener createFocusListener() { - return new FocusListener() { - private static final long serialVersionUID = 1L; - - @Override - public void focusLost(FocusEvent event) { - } - - @Override - public void focusGained(FocusEvent event) { - } - }; - } - - protected Composite findPage(Composite composite) { - if (composite instanceof ScrolledPage) { - return (ScrolledPage) composite; - } else { - if (composite.getParent() == null) - return composite; - return findPage(composite.getParent()); - } - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) - editingStateChanged(cmsEditable); - } - - /** To be overridden in order to provide the actual refresh */ - protected void refresh(Control control) throws RepositoryException { - } - - /** To be overridden.Save the edited part. */ - protected void save(EditablePart part) throws RepositoryException { - } - - /** Prepare the edited part */ - protected void prepare(EditablePart part, Object caretPosition) { - } - - /** Notified when the editing state changed. Does nothing, to be overridden */ - protected void editingStateChanged(CmsEditable cmsEditable) { - } - - @Override - public void refresh() { - // TODO check actual context in order to notice a discrepancy - Subject viewerSubject = getViewerSubject(); - Subject.doAs(viewerSubject, (PrivilegedAction) () -> { - try { - if (cmsEditable.canEdit() && !readOnly) - mouseListener = createMouseListener(); - else - mouseListener = null; - refresh(getControl()); - layout(getControl()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh", e); - } - return null; - }); - } - - @Override - public void setSelection(ISelection selection, boolean reveal) { - this.selection = selection; - } - - protected void updateContent(EditablePart part) throws RepositoryException { - } - - // LOW LEVEL EDITION - protected void edit(EditablePart part, Object caretPosition) { - try { - if (edited == part) - return; - - if (edited != null && edited != part) { - EditablePart previouslyEdited = edited; - try { - stopEditing(true); - } catch (Exception e) { - notifyEditionException(e); - edit(previouslyEdited, caretPosition); - return; - } - } - - part.startEditing(); - updateContent(part); - prepare(part, caretPosition); - edited = part; - layout(part.getControl()); - } catch (RepositoryException e) { - throw new CmsException("Cannot edit " + part, e); - } - } - - private void stopEditing(Boolean save) throws RepositoryException { - if (edited instanceof Widget && ((Widget) edited).isDisposed()) { - edited = null; - return; - } - - assert edited != null; - if (edited == null) { - if (log.isTraceEnabled()) - log.warn("Told to stop editing while not editing anything"); - return; - } - - if (save) - save(edited); - - edited.stopEditing(); - updateContent(edited); - layout(((EditablePart) edited).getControl()); - edited = null; - } - - // METHODS AVAILABLE TO EXTENDING CLASSES - protected void saveEdit() { - try { - if (edited != null) - stopEditing(true); - } catch (RepositoryException e) { - throw new CmsException("Cannot stop editing", e); - } - } - - protected void cancelEdit() { - try { - if (edited != null) - stopEditing(false); - } catch (RepositoryException e) { - throw new CmsException("Cannot cancel editing", e); - } - } - - /** Layout this controls from the related base page. */ - public void layout(Control... controls) { - page.layout(controls); - } - - /** - * Find the first {@link EditablePart} in the parents hierarchy of this control - */ - protected EditablePart findDataParent(Control parent) { - if (parent instanceof EditablePart) { - return (EditablePart) parent; - } - if (parent.getParent() != null) - return findDataParent(parent.getParent()); - else - throw new CmsException("No data parent found"); - } - - // UTILITIES - /** Check whether the edited part is in a proper state */ - protected void checkEdited() { - if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) - throw new CmsException("Edited should not be null or disposed at this stage"); - } - - /** Persist all changes. */ - protected void persistChanges(Session session) throws RepositoryException { - session.save(); - session.refresh(false); - // TODO notify that changes have been persisted - } - - /** Convenience method using a Node in order to save the underlying session. */ - protected void persistChanges(Node anyNode) throws RepositoryException { - persistChanges(anyNode.getSession()); - } - - /** Notify edition exception */ - protected void notifyEditionException(Throwable e) { - Throwable eToLog = e; - if (e instanceof IllegalArgumentException) - if (e.getCause() instanceof SAXParseException) - eToLog = e.getCause(); - log.error(eToLog.getMessage(), eToLog); -// if (log.isTraceEnabled()) -// log.trace("Full stack of " + eToLog.getMessage(), e); - // TODO Light error notification popup - } - - protected Subject getViewerSubject() { - Subject res = null; - if (accessControlContext != null) { - res = Subject.getSubject(accessControlContext); - } - if (res == null) - throw new CmsException("No subject associated with this viewer"); - return res; - } - - // GETTERS / SETTERS - public boolean isReadOnly() { - return readOnly; - } - - protected EditablePart getEdited() { - return edited; - } - - public MouseListener getMouseListener() { - return mouseListener; - } - - public FocusListener getFocusListener() { - return focusListener; - } - - public CmsEditable getCmsEditable() { - return cmsEditable; - } - - @Override - public ISelection getSelection() { - return selection; - } -} \ No newline at end of file diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java deleted file mode 100644 index 99f8acf9a..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.cms.viewers; - -import org.eclipse.swt.widgets.Control; - -public interface EditablePart { - public void startEditing(); - - public void stopEditing(); - - public Control getControl(); -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java deleted file mode 100644 index 52e5a88eb..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.cms.viewers; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -/** An editable part related to a JCR Item */ -public interface ItemPart { - public Item getItem() throws RepositoryException; -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java deleted file mode 100644 index bc3166307..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.argeo.cms.viewers; - -import java.util.Observable; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.VersionManager; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsEditable; -import org.argeo.cms.ui.CmsEditionEvent; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; - -/** Provides the CmsEditable semantic based on JCR versioning. */ -public class JcrVersionCmsEditable extends Observable implements CmsEditable { - private final String nodePath;// cache - private final VersionManager versionManager; - private final Boolean canEdit; - - public JcrVersionCmsEditable(Node node) throws RepositoryException { - this.nodePath = node.getPath(); - if (node.getSession().hasPermission(node.getPath(), - Session.ACTION_SET_PROPERTY)) { - // was Session.ACTION_ADD_NODE - canEdit = true; - if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { - node.addMixin(NodeType.MIX_VERSIONABLE); - node.getSession().save(); - } - versionManager = node.getSession().getWorkspace() - .getVersionManager(); - } else { - canEdit = false; - versionManager = null; - } - - // bind keys - if (canEdit) { - Display display = Display.getCurrent(); - display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", - "CTRL+E" }); - display.addFilter(SWT.KeyDown, new Listener() { - private static final long serialVersionUID = -4378653870463187318L; - - public void handleEvent(Event e) { - boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; - if (ctrlPressed && e.keyCode == '\r') - stopEditing(); - else if (ctrlPressed && e.keyCode == 'E') - stopEditing(); - } - }); - } - } - - @Override - public Boolean canEdit() { - return canEdit; - } - - public Boolean isEditing() { - try { - if (!canEdit()) - return false; - return versionManager.isCheckedOut(nodePath); - } catch (RepositoryException e) { - throw new CmsException("Cannot check whether " + nodePath - + " is editing", e); - } - } - - @Override - public void startEditing() { - try { - versionManager.checkout(nodePath); - setChanged(); - } catch (RepositoryException e1) { - throw new CmsException("Cannot publish " + nodePath); - } - notifyObservers(new CmsEditionEvent(nodePath, - CmsEditionEvent.START_EDITING)); - } - - @Override - public void stopEditing() { - try { - versionManager.checkin(nodePath); - setChanged(); - } catch (RepositoryException e1) { - throw new CmsException("Cannot publish " + nodePath, e1); - } - notifyObservers(new CmsEditionEvent(nodePath, - CmsEditionEvent.STOP_EDITING)); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java deleted file mode 100644 index db9a60a87..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.viewers; - -import javax.jcr.Node; - -/** An editable part related to a node */ -public interface NodePart extends ItemPart { - public Node getNode(); -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java deleted file mode 100644 index 50fdd0601..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.viewers; - -import javax.jcr.Property; - -/** An editable part related to a JCR Property */ -public interface PropertyPart extends ItemPart { - public Property getProperty(); -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java deleted file mode 100644 index e59b57de6..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.argeo.cms.viewers; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.widgets.JcrComposite; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -public class Section extends JcrComposite { - private static final long serialVersionUID = -5933796173755739207L; - - private final Section parentSection; - private Composite sectionHeader; - private final Integer relativeDepth; - - public Section(Composite parent, int style, Node node) throws RepositoryException { - this(parent, findSection(parent), style, node); - } - - public Section(Section section, int style, Node node) throws RepositoryException { - this(section, section, style, node); - } - - protected Section(Composite parent, Section parentSection, int style, Node node) throws RepositoryException { - super(parent, style, node); - this.parentSection = parentSection; - if (parentSection != null) { - relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); - } else { - relativeDepth = 0; - } - setLayout(CmsUtils.noSpaceGridLayout()); - } - - public Map getSubSections() throws RepositoryException { - LinkedHashMap result = new LinkedHashMap(); - for (Control child : getChildren()) { - if (child instanceof Composite) { - collectDirectSubSections((Composite) child, result); - } - } - return Collections.unmodifiableMap(result); - } - - private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) - throws RepositoryException { - if (composite == sectionHeader || composite instanceof EditablePart) - return; - if (composite instanceof Section) { - Section section = (Section) composite; - subSections.put(section.getNodeId(), section); - return; - } - - for (Control child : composite.getChildren()) - if (child instanceof Composite) - collectDirectSubSections((Composite) child, subSections); - } - - public void createHeader() { - if (sectionHeader != null) - throw new CmsException("Section header was already created"); - - sectionHeader = new Composite(this, SWT.NONE); - sectionHeader.setLayoutData(CmsUtils.fillWidth()); - sectionHeader.setLayout(CmsUtils.noSpaceGridLayout()); - // sectionHeader.moveAbove(null); - // layout(); - } - - public Composite getHeader() { - if (sectionHeader != null && sectionHeader.isDisposed()) - sectionHeader = null; - return sectionHeader; - } - - // SECTION PARTS - public SectionPart getSectionPart(String partId) { - for (Control child : getChildren()) { - if (child instanceof SectionPart) { - SectionPart paragraph = (SectionPart) child; - if (paragraph.getPartId().equals(partId)) - return paragraph; - } - } - return null; - } - - public SectionPart nextSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) - if (i + 1 < children.length) { - Composite next = (Composite) children[i + 1]; - return (SectionPart) next; - } else { - // next section - } - } - return null; - } - - public SectionPart previousSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) - if (i != 0) { - Composite previous = (Composite) children[i - 1]; - return (SectionPart) previous; - } else { - // previous section - } - } - return null; - } - - @Override - public String toString() { - if (parentSection == null) - return "Main section " + getNode(); - return "Section " + getNode(); - } - - public Section getParentSection() { - return parentSection; - } - - public Integer getRelativeDepth() { - return relativeDepth; - } - - /** Recursively finds the related section in the parents (can be itself) */ - public static Section findSection(Control control) { - if (control == null) - return null; - if (control instanceof Section) - return (Section) control; - else - return findSection(control.getParent()); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java b/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java deleted file mode 100644 index 6cd45c5ba..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.cms.viewers; - - -/** An editable part dynamically related to a Section */ -public interface SectionPart extends EditablePart, NodePart { - public String getPartId(); - - public Section getSection(); -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java deleted file mode 100644 index 2e70eb89f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.argeo.cms.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** A stylable and editable image. */ -public abstract class EditableImage extends StyledControl { - private static final long serialVersionUID = -5689145523114022890L; - private final static Log log = LogFactory.getLog(EditableImage.class); - - private Point preferredImageSize; - private Boolean loaded = false; - - public EditableImage(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - public EditableImage(Composite parent, int swtStyle, - Point preferredImageSize) { - super(parent, swtStyle); - this.preferredImageSize = preferredImageSize; - } - - public EditableImage(Composite parent, int style, Node node, - boolean cacheImmediately, Point preferredImageSize) - throws RepositoryException { - super(parent, style, node, cacheImmediately); - this.preferredImageSize = preferredImageSize; - } - - @Override - protected void setContainerLayoutData(Composite composite) { - // composite.setLayoutData(fillWidth()); - } - - @Override - protected void setControlLayoutData(Control control) { - // control.setLayoutData(fillWidth()); - } - - /** To be overriden. */ - protected String createImgTag() throws RepositoryException { - return CmsUtils.noImg(preferredImageSize != null ? preferredImageSize - : getSize()); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle()); - // lbl.setLayoutData(CmsUtils.fillWidth()); - CmsUtils.markup(lbl); - CmsUtils.style(lbl, style); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - load(lbl); - return lbl; - } - - /** To be overriden. */ - protected synchronized Boolean load(Control control) { - String imgTag; - try { - imgTag = createImgTag(); - } catch (Exception e) { - // throw new CmsException("Cannot retrieve image", e); - log.error("Cannot retrieve image", e); - imgTag = CmsUtils.noImg(preferredImageSize); - loaded = false; - } - - if (imgTag == null) { - loaded = false; - imgTag = CmsUtils.noImg(preferredImageSize); - } else - loaded = true; - if (control != null) { - ((Label) control).setText(imgTag); - control.setSize(preferredImageSize != null ? preferredImageSize - : getSize()); - } else { - loaded = false; - } - getParent().layout(); - return loaded; - } - - public void setPreferredSize(Point size) { - this.preferredImageSize = size; - if (!loaded) { - load((Label) getControl()); - } - } - - protected Text createText(Composite box, String style) { - Text text = new Text(box, getStyle()); - CmsUtils.style(text, style); - return text; - } - - public Point getPreferredImageSize() { - return preferredImageSize; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java deleted file mode 100644 index e7c56ea72..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.argeo.cms.widgets; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.cms.util.CmsUtils; -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.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable text part displaying styled text. */ -public class EditableText extends StyledControl { - private static final long serialVersionUID = -6372283442330912755L; - - public EditableText(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - public EditableText(Composite parent, int style, Item item) - throws RepositoryException { - this(parent, style, item, false); - } - - public EditableText(Composite parent, int style, Item item, - boolean cacheImmediately) throws RepositoryException { - super(parent, style, item, cacheImmediately); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) - return createText(box, style); - else - return createLabel(box, style); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsUtils.fillWidth()); - CmsUtils.style(lbl, style); - CmsUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createText(Composite box, String style) { - final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); - GridData textLayoutData = CmsUtils.fillWidth(); - // textLayoutData.heightHint = preferredHeight; - text.setLayoutData(textLayoutData); - CmsUtils.style(text, style); - text.setFocus(); - return text; - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) - ((Label) child).setText(text); - else if (child instanceof Text) - ((Text) child).setText(text); - } - - public Text getAsText() { - return (Text) getControl(); - } - - public Label getAsLabel() { - return (Label) getControl(); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/Img.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/Img.java deleted file mode 100644 index 59c4823b2..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/Img.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.argeo.cms.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.CmsException; -import org.argeo.cms.ui.CmsImageManager; -import org.argeo.cms.ui.internal.JcrFileUploadReceiver; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.viewers.NodePart; -import org.argeo.cms.viewers.Section; -import org.argeo.cms.viewers.SectionPart; -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.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** An image within the Argeo Text framework */ -public class Img extends EditableImage implements SectionPart, NodePart { - private static final long serialVersionUID = 6233572783968188476L; - - private final Section section; - - private final CmsImageManager imageManager; - private FileUploadHandler currentUploadHandler = null; - private FileUploadListener fileUploadListener; - - public Img(Composite parent, int swtStyle, Node imgNode, - Point preferredImageSize) throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, - preferredImageSize); - setStyle(TextStyles.TEXT_IMAGE); - } - - public Img(Composite parent, int swtStyle, Node imgNode) - throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, null); - setStyle(TextStyles.TEXT_IMAGE); - } - - Img(Section section, Composite parent, int swtStyle, Node imgNode, - Point preferredImageSize) throws RepositoryException { - super(parent, swtStyle, imgNode, false, preferredImageSize); - this.section = section; - imageManager = CmsUtils.getCmsView().getImageManager(); - CmsUtils.style(this, TextStyles.TEXT_IMG); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - try { - return createImageChooser(box, style); - } catch (RepositoryException e) { - throw new CmsException("Cannot create image chooser", e); - } - } else { - return createLabel(box, style); - } - } - - @Override - public synchronized void stopEditing() { - super.stopEditing(); - fileUploadListener = null; - } - - @Override - protected synchronized Boolean load(Control lbl) { - try { - Node imgNode = getNode(); - boolean loaded = imageManager.load(imgNode, lbl, - getPreferredImageSize()); - // getParent().layout(); - return loaded; - } catch (RepositoryException e) { - throw new CmsException("Cannot load " + getNodeId() - + " from image manager", e); - } - } - - protected Control createImageChooser(Composite box, String style) - throws RepositoryException { - // FileDialog fileDialog = new FileDialog(getShell()); - // fileDialog.open(); - // String fileName = fileDialog.getFileName(); - CmsImageManager imageManager = CmsUtils.getCmsView().getImageManager(); - Node node = getNode(); - JcrFileUploadReceiver receiver = new JcrFileUploadReceiver( - node.getParent(), node.getName() + '[' + node.getIndex() + ']', - imageManager); - if (currentUploadHandler != null) - currentUploadHandler.dispose(); - currentUploadHandler = prepareUpload(receiver); - final ServerPushSession pushSession = new ServerPushSession(); - final FileUpload fileUpload = new FileUpload(box, SWT.NONE); - CmsUtils.style(fileUpload, style); - fileUpload.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -9158471843941668562L; - - @Override - public void widgetSelected(SelectionEvent e) { - pushSession.start(); - fileUpload.submit(currentUploadHandler.getUploadUrl()); - } - }); - return fileUpload; - } - - protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { - final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); - if (fileUploadListener != null) - uploadHandler.addUploadListener(fileUploadListener); - return uploadHandler; - } - - @Override - public Section getSection() { - return section; - } - - public void setFileUploadListener(FileUploadListener fileUploadListener) { - this.fileUploadListener = fileUploadListener; - if (currentUploadHandler != null) - currentUploadHandler.addUploadListener(fileUploadListener); - } - - @Override - public Node getItem() throws RepositoryException { - return getNode(); - } - - @Override - public String getPartId() { - return getNodeId(); - } - - @Override - public String toString() { - return "Img #" + getPartId(); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java deleted file mode 100644 index 358b45315..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.cms.widgets; - -import javax.jcr.Item; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.cms.CmsException; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; - -/** A composite which can (optionally) manage a JCR Item. */ -public class JcrComposite extends Composite { - private static final long serialVersionUID = -1447009015451153367L; - - private final Session session; - - private String nodeId; - private String property = null; - private Node cache; - - /** Regular composite constructor. No layout is set. */ - public JcrComposite(Composite parent, int style) { - super(parent, style); - session = null; - nodeId = null; - } - - public JcrComposite(Composite parent, int style, Item item) - throws RepositoryException { - this(parent, style, item, false); - } - - public JcrComposite(Composite parent, int style, Item item, - boolean cacheImmediately) throws RepositoryException { - super(parent, style); - this.session = item.getSession(); - if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { - // (useless?) optimization: we only save a pointer to the session, - // not even a reference to the item - this.nodeId = null; - } else { - Node node; - Property property = null; - if (item instanceof Node) { - node = (Node) item; - } else {// Property - property = (Property) item; - if (property.isMultiple())// TODO manage property index - throw new CmsException( - "Multiple properties not supported yet."); - this.property = property.getName(); - node = property.getParent(); - } - this.nodeId = node.getIdentifier(); - if (cacheImmediately) - this.cache = node; - } - setLayout(CmsUtils.noSpaceGridLayout()); - } - - public synchronized Node getNode() { - try { - if (!itemIsNode()) - throw new CmsException("Item is not a Node"); - return getNodeInternal(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get node " + nodeId, e); - } - } - - private synchronized Node getNodeInternal() throws RepositoryException { - if (cache != null) - return cache; - else if (session != null) - if (nodeId != null) - return session.getNodeByIdentifier(nodeId); - else - return null; - else - return null; - } - - public synchronized Property getProperty() { - try { - if (itemIsNode()) - throw new CmsException("Item is not a Property"); - Node node = getNodeInternal(); - if (!node.hasProperty(property)) - throw new CmsException("Property " + property - + " is not set on " + node); - return node.getProperty(property); - } catch (RepositoryException e) { - throw new CmsException("Cannot get property " + property - + " from node " + nodeId, e); - } - } - - public synchronized Boolean itemIsNode() { - return property == null; - } - - /** Set/update the cache or change the node */ - public synchronized void setNode(Node node) throws RepositoryException { - if (!itemIsNode()) - throw new CmsException("Cannot set a Node on a Property"); - - if (node == null) {// clear cache - this.cache = null; - return; - } - - if (session == null || session != node.getSession())// check session - throw new CmsException("Uncompatible session"); - - if (nodeId == null || !nodeId.equals(node.getIdentifier())) { - nodeId = node.getIdentifier(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } - - /** Set/update the cache or change the property */ - public synchronized void setProperty(Property prop) - throws RepositoryException { - if (itemIsNode()) - throw new CmsException("Cannot set a Property on a Node"); - - if (prop == null) {// clear cache - this.cache = null; - return; - } - - if (session == null || session != prop.getSession())// check session - throw new CmsException("Uncompatible session"); - - Node node = prop.getNode(); - if (nodeId == null || !nodeId.equals(node.getIdentifier()) - || !property.equals(prop.getName())) { - nodeId = node.getIdentifier(); - property = prop.getName(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } - - public synchronized String getNodeId() { - return nodeId; - } - - /** Change the node, does nothing if same. */ - public synchronized void setNodeId(String nodeId) - throws RepositoryException { - if (this.nodeId != null && this.nodeId.equals(nodeId)) - return; - this.nodeId = nodeId; - if (cache != null) - cache = session.getNodeByIdentifier(this.nodeId); - itemUpdated(); - } - - protected synchronized void itemUpdated() { - layout(); - } - - public Session getSession() { - return session; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java deleted file mode 100644 index c36ed2052..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.argeo.cms.widgets; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Composite; - -/** - * A composite that can be scrolled vertically. It wraps a - * {@link ScrolledComposite} (and is being wrapped by it), simplifying its - * configuration. - */ -public class ScrolledPage extends Composite { - private static final long serialVersionUID = 1593536965663574437L; - - private ScrolledComposite scrolledComposite; - - public ScrolledPage(Composite parent, int style) { - super(new ScrolledComposite(parent, SWT.V_SCROLL), style); - scrolledComposite = (ScrolledComposite) getParent(); - scrolledComposite.setContent(this); - - scrolledComposite.setExpandVertical(true); - scrolledComposite.setExpandHorizontal(true); - scrolledComposite.addControlListener(new ScrollControlListener()); - } - - @Override - public void layout(boolean changed, boolean all) { - updateScroll(); - super.layout(changed, all); - } - - protected void updateScroll() { - Rectangle r = scrolledComposite.getClientArea(); - Point preferredSize = computeSize(r.width, SWT.DEFAULT); - scrolledComposite.setMinHeight(preferredSize.y); - } - - // public ScrolledComposite getScrolledComposite() { - // return this.scrolledComposite; - // } - - /** Set it on the wrapping scrolled composite */ - @Override - public void setLayoutData(Object layoutData) { - scrolledComposite.setLayoutData(layoutData); - } - - private class ScrollControlListener extends - org.eclipse.swt.events.ControlAdapter { - private static final long serialVersionUID = -3586986238567483316L; - - public void controlResized(ControlEvent e) { - updateScroll(); - } - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java deleted file mode 100644 index 4d999d501..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.argeo.cms.widgets; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.CmsConstants; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Editable text part displaying styled text. */ -public abstract class StyledControl extends JcrComposite implements CmsConstants { - private static final long serialVersionUID = -6372283442330912755L; - private Control control; - - private Composite container; - private Composite box; - - protected MouseListener mouseListener; - protected FocusListener focusListener; - - private Boolean editing = Boolean.FALSE; - - public StyledControl(Composite parent, int swtStyle) { - super(parent, swtStyle); - setLayout(CmsUtils.noSpaceGridLayout()); - } - - public StyledControl(Composite parent, int style, Item item) throws RepositoryException { - super(parent, style, item); - } - - public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { - super(parent, style, item, cacheImmediately); - } - - protected abstract Control createControl(Composite box, String style); - - protected Composite createBox(Composite parent) { - Composite box = new Composite(parent, SWT.INHERIT_DEFAULT); - setContainerLayoutData(box); - box.setLayout(CmsUtils.noSpaceGridLayout()); - // new Label(box, SWT.NONE).setText("BOX"); - return box; - } - - public Control getControl() { - return control; - } - - protected synchronized Boolean isEditing() { - return editing; - } - - public synchronized void startEditing() { - assert !isEditing(); - editing = true; - // int height = control.getSize().y; - String style = (String) control.getData(STYLE); - clear(false); - control = createControl(box, style); - setControlLayoutData(control); - - // add the focus listener to the newly created edition control - if (focusListener != null) - control.addFocusListener(focusListener); - } - - public synchronized void stopEditing() { - assert isEditing(); - editing = false; - String style = (String) control.getData(STYLE); - clear(false); - control = createControl(box, style); - setControlLayoutData(control); - } - - public void setStyle(String style) { - Object currentStyle = null; - if (control != null) - currentStyle = control.getData(STYLE); - if (currentStyle != null && currentStyle.equals(style)) - return; - - // Integer preferredHeight = control != null ? control.getSize().y : - // null; - clear(true); - control = createControl(box, style); - setControlLayoutData(control); - - control.getParent().setData(STYLE, style + "_box"); - control.getParent().getParent().setData(STYLE, style + "_container"); - } - - /** To be overridden */ - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsUtils.fillWidth()); - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsUtils.fillWidth()); - } - - protected void clear(boolean deep) { - if (deep) { - for (Control control : getChildren()) - control.dispose(); - container = createBox(this); - box = createBox(container); - } else { - control.dispose(); - } - } - - public void setMouseListener(MouseListener mouseListener) { - if (this.mouseListener != null && control != null) - control.removeMouseListener(this.mouseListener); - this.mouseListener = mouseListener; - if (control != null && this.mouseListener != null) - control.addMouseListener(mouseListener); - } - - public void setFocusListener(FocusListener focusListener) { - if (this.focusListener != null && control != null) - control.removeFocusListener(this.focusListener); - this.focusListener = focusListener; - if (control != null && this.focusListener != null) - control.addFocusListener(focusListener); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/TextStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/TextStyles.java deleted file mode 100644 index be5bc5fea..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/TextStyles.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.widgets; - -/** Styles references in the CSS. */ -public interface TextStyles { - /** The whole page area */ - public final static String TEXT_AREA = "text_area"; - /** Area providing controls for editing text */ - public final static String TEXT_EDITOR_HEADER = "text_editor_header"; - /** The styled composite for editing the text */ - public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; - /** A section */ - public final static String TEXT_SECTION = "text_section"; - /** A paragraph */ - public final static String TEXT_PARAGRAPH = "text_paragraph"; - /** An image */ - public final static String TEXT_IMG = "text_img"; - /** The dialog to edit styled paragraph */ - public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; - - /* - * DEFAULT TEXT STYLES - */ - /** Default style for text body */ - public final static String TEXT_DEFAULT = "text_default"; - /** Fixed-width, typically code */ - public final static String TEXT_PRE = "text_pre"; - /** Quote */ - public final static String TEXT_QUOTE = "text_quote"; - /** Title */ - public final static String TEXT_TITLE = "text_title"; - /** Header (to be dynamically completed with the depth, e.g. text_h1) */ - public final static String TEXT_H = "text_h"; - - /** Default style for images */ - public final static String TEXT_IMAGE = "text_image"; - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java deleted file mode 100644 index b86fcb0b0..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.cms.widgets.auth; - -import java.io.IOException; -import java.util.Arrays; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.dialogs.TrayDialog; -import org.eclipse.jface.operation.IRunnableWithProgress; -import org.eclipse.jface.operation.ModalContext; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.osgi.framework.FrameworkUtil; - -/** Base for login dialogs */ -public abstract class AbstractLoginDialog extends TrayDialog implements CallbackHandler { - private static final long serialVersionUID = -8046708963512717709L; - - private final static Log log = LogFactory.getLog(AbstractLoginDialog.class); - - private Thread modalContextThread = null; - boolean processCallbacks = false; - boolean isCancelled = false; - Callback[] callbackArray; - - protected final Callback[] getCallbacks() { - return this.callbackArray; - } - - public abstract void internalHandle(); - - public boolean isCancelled() { - return isCancelled; - } - - protected AbstractLoginDialog(Shell parentShell) { - super(parentShell); - } - - /* - * (non-Javadoc) - * - * @see - * javax.security.auth.callback.CallbackHandler#handle(javax.security.auth - * .callback.Callback[]) - */ - public void handle(final Callback[] callbacks) throws IOException { - // clean previous usage - if (processCallbacks) { - // this handler was already used - processCallbacks = false; - } - - if (modalContextThread != null) { - try { - modalContextThread.join(1000); - } catch (InterruptedException e) { - // silent - } - modalContextThread = null; - } - - // initialize - this.callbackArray = callbacks; - final Display display = Display.getDefault(); - display.syncExec(new Runnable() { - - public void run() { - isCancelled = false; - setBlockOnOpen(false); - open(); - - final Button okButton = getButton(IDialogConstants.OK_ID); - okButton.setText("Login"); - okButton.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = -200281625679096775L; - - public void widgetSelected(final SelectionEvent event) { - processCallbacks = true; - } - - public void widgetDefaultSelected(final SelectionEvent event) { - // nothing to do - } - }); - final Button cancel = getButton(IDialogConstants.CANCEL_ID); - cancel.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = -3826030278084915815L; - - public void widgetSelected(final SelectionEvent event) { - isCancelled = true; - processCallbacks = true; - } - - public void widgetDefaultSelected(final SelectionEvent event) { - // nothing to do - } - }); - } - }); - try { - ModalContext.setAllowReadAndDispatch(true); // Works for now. - ModalContext.run(new IRunnableWithProgress() { - - public void run(final IProgressMonitor monitor) { - modalContextThread = Thread.currentThread(); - // Wait here until OK or cancel is pressed, then let it rip. - // The event - // listener - // is responsible for closing the dialog (in the - // loginSucceeded - // event). - while (!processCallbacks && (modalContextThread != null) - && (modalContextThread == Thread.currentThread()) - && FrameworkUtil.getBundle(AbstractLoginDialog.class).getBundleContext() != null) { - // Note: SecurityUiPlugin.getDefault() != null is false - // when the OSGi runtime is shut down - try { - Thread.sleep(100); - // if (display.isDisposed()) { - // log.warn("Display is disposed, killing login - // dialog thread"); - // throw new ThreadDeath(); - // } - } catch (final Exception e) { - // do nothing - } - } - processCallbacks = false; - // Call the adapter to handle the callbacks - if (!isCancelled()) - internalHandle(); - else - // clear callbacks are when cancelling - for (Callback callback : callbacks) - if (callback instanceof PasswordCallback) { - char[] arr = ((PasswordCallback) callback).getPassword(); - if (arr != null) { - Arrays.fill(arr, '*'); - ((PasswordCallback) callback).setPassword(null); - } - } else if (callback instanceof NameCallback) - ((NameCallback) callback).setName(null); - } - }, true, new NullProgressMonitor(), Display.getDefault()); - } catch (ThreadDeath e) { - isCancelled = true; - log.debug("Thread " + Thread.currentThread().getId() + " died"); - throw e; - } catch (Exception e) { - isCancelled = true; - IOException ioe = new IOException("Unexpected issue in login dialog, see root cause for more details"); - ioe.initCause(e); - throw ioe; - } finally { - // so that the modal thread dies - processCallbacks = true; - // try { - // // wait for the modal context thread to gracefully exit - // modalContextThread.join(); - // } catch (InterruptedException ie) { - // // silent - // } - modalContextThread = null; - } - } - - protected void configureShell(Shell shell) { - super.configureShell(shell); - shell.setText("Authentication"); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java deleted file mode 100644 index cc0e766d4..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java +++ /dev/null @@ -1,335 +0,0 @@ -package org.argeo.cms.widgets.auth; - -import static org.argeo.cms.CmsMsg.password; -import static org.argeo.cms.CmsMsg.username; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.LanguageCallback; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsMsg; -import org.argeo.cms.LocaleUtils; -import org.argeo.cms.auth.HttpRequestCallback; -import org.argeo.cms.ui.CmsStyles; -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.ui.internal.Activator; -import org.argeo.cms.util.CmsUtils; -import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeState; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.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.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.Shell; -import org.eclipse.swt.widgets.Text; - -public class CmsLogin implements CmsStyles, CallbackHandler { - private final static Log log = LogFactory.getLog(CmsLogin.class); - - private Composite parent; - private Text usernameT, passwordT; - private Composite credentialsBlock; - private final SelectionListener loginSelectionListener; - - private final Locale defaultLocale; - private LocaleChoice localeChoice = null; - - private final CmsView cmsView; - - // optional subject to be set explicitly - private Subject subject = null; - - public CmsLogin(CmsView cmsView) { - this.cmsView = cmsView; - NodeState nodeState = Activator.getNodeState(); - if (nodeState != null) { - defaultLocale = nodeState.getDefaultLocale(); - List locales = nodeState.getLocales(); - if (locales != null) - localeChoice = new LocaleChoice(locales, defaultLocale); - } else { - defaultLocale = Locale.getDefault(); - } - loginSelectionListener = new SelectionListener() { - private static final long serialVersionUID = -8832133363830973578L; - - @Override - public void widgetSelected(SelectionEvent e) { - login(); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - }; - } - - protected boolean isAnonymous() { - return cmsView.isAnonymous(); - } - - public final void createUi(Composite parent) { - this.parent = parent; - createContents(parent); - } - - protected void createContents(Composite parent) { - defaultCreateContents(parent); - } - - public final void defaultCreateContents(Composite parent) { - parent.setLayout(CmsUtils.noSpaceGridLayout()); - Composite credentialsBlock = createCredentialsBlock(parent); - if (parent instanceof Shell) { - credentialsBlock.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - } - } - - public final Composite createCredentialsBlock(Composite parent) { - if (isAnonymous()) { - return anonymousUi(parent); - } else { - return userUi(parent); - } - } - - protected Composite getCredentialsBlock() { - return credentialsBlock; - } - - protected Composite userUi(Composite parent) { - Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); - credentialsBlock = new Composite(parent, SWT.NONE); - credentialsBlock.setLayout(new GridLayout()); - // credentialsBlock.setLayoutData(CmsUtils.fillAll()); - - specificUserUi(credentialsBlock); - - Label l = new Label(credentialsBlock, SWT.NONE); - CmsUtils.style(l, CMS_USER_MENU_ITEM); - l.setText(CmsMsg.logout.lead(locale)); - GridData lData = CmsUtils.fillWidth(); - lData.widthHint = 120; - l.setLayoutData(lData); - - l.addMouseListener(new MouseAdapter() { - private static final long serialVersionUID = 6444395812777413116L; - - public void mouseDown(MouseEvent e) { - logout(); - } - }); - return credentialsBlock; - } - - /** To be overridden */ - protected void specificUserUi(Composite parent) { - - } - - protected Composite anonymousUi(Composite parent) { - Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale(); - // We need a composite for the traversal - credentialsBlock = new Composite(parent, SWT.NONE); - credentialsBlock.setLayout(new GridLayout()); - // credentialsBlock.setLayoutData(CmsUtils.fillAll()); - CmsUtils.style(credentialsBlock, CMS_LOGIN_DIALOG); - - Integer textWidth = 120; - if (parent instanceof Shell) - CmsUtils.style(parent, CMS_USER_MENU); - // new Label(this, SWT.NONE).setText(CmsMsg.username.lead()); - usernameT = new Text(credentialsBlock, SWT.BORDER); - usernameT.setMessage(username.lead(locale)); - CmsUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME); - GridData gd = CmsUtils.fillWidth(); - gd.widthHint = textWidth; - usernameT.setLayoutData(gd); - - // new Label(this, SWT.NONE).setText(CmsMsg.password.lead()); - passwordT = new Text(credentialsBlock, SWT.BORDER | SWT.PASSWORD); - passwordT.setMessage(password.lead(locale)); - CmsUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD); - gd = CmsUtils.fillWidth(); - gd.widthHint = textWidth; - passwordT.setLayoutData(gd); - - TraverseListener tl = new TraverseListener() { - private static final long serialVersionUID = -1158892811534971856L; - - public void keyTraversed(TraverseEvent e) { - if (e.detail == SWT.TRAVERSE_RETURN) - login(); - } - }; - credentialsBlock.addTraverseListener(tl); - usernameT.addTraverseListener(tl); - passwordT.addTraverseListener(tl); - parent.setTabList(new Control[] { credentialsBlock }); - credentialsBlock.setTabList(new Control[] { usernameT, passwordT }); - - // Button - Button loginButton = new Button(credentialsBlock, SWT.PUSH); - loginButton.setText(CmsMsg.login.lead(locale)); - loginButton.setLayoutData(CmsUtils.fillWidth()); - loginButton.addSelectionListener(loginSelectionListener); - - extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener); - if (localeChoice != null) - createLocalesBlock(credentialsBlock); - return credentialsBlock; - } - - /** - * To be overridden in order to provide custom login button and other links. - */ - protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale, - SelectionListener loginSelectionListener) { - - } - - protected void updateLocale(Locale selectedLocale) { - // save already entered values - String usernameStr = usernameT.getText(); - char[] pwd = passwordT.getTextChars(); - - for (Control child : parent.getChildren()) - child.dispose(); - createContents(parent); - if (parent.getParent() != null) - parent.getParent().layout(); - else - parent.layout(); - usernameT.setText(usernameStr); - passwordT.setTextChars(pwd); - } - - protected Composite createLocalesBlock(final Composite parent) { - Composite c = new Composite(parent, SWT.NONE); - CmsUtils.style(c, CMS_USER_MENU_ITEM); - c.setLayout(CmsUtils.noSpaceGridLayout()); - c.setLayoutData(CmsUtils.fillAll()); - - SelectionListener selectionListener = new SelectionAdapter() { - private static final long serialVersionUID = 4891637813567806762L; - - public void widgetSelected(SelectionEvent event) { - Button button = (Button) event.widget; - if (button.getSelection()) { - localeChoice.setSelectedIndex((Integer) event.widget.getData()); - updateLocale(localeChoice.getSelectedLocale()); - } - }; - }; - - List locales = localeChoice.getLocales(); - for (Integer i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - Button button = new Button(c, SWT.RADIO); - CmsUtils.style(button, CMS_USER_MENU_ITEM); - button.setData(i); - button.setText(LocaleUtils.lead(locale.getDisplayName(locale), locale) + " (" + locale + ")"); - // button.addListener(SWT.Selection, listener); - button.addSelectionListener(selectionListener); - if (i == localeChoice.getSelectedIndex()) - button.setSelection(true); - } - return c; - } - - protected boolean login() { - // TODO use CmsVie in order to retrieve subject? - // Subject subject = cmsView.getLoginContext().getSubject(); - // LoginContext loginContext = cmsView.getLoginContext(); - try { - // - // LOGIN - // - // loginContext.logout(); - LoginContext loginContext; - if (subject == null) - loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, this); - else - loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_USER, subject, this); - loginContext.login(); - cmsView.authChange(loginContext); - return true; - } catch (LoginException e) { - if (log.isTraceEnabled()) - log.warn("Login failed: " + e.getMessage(), e); - else - log.warn("Login failed: " + e.getMessage()); - - try { - Thread.sleep(3000); - } catch (InterruptedException e2) { - // silent - } - // ErrorFeedback.show("Login failed", e); - return false; - } - // catch (LoginException e) { - // log.error("Cannot login", e); - // return false; - // } - } - - protected void logout() { - cmsView.logout(); - cmsView.navigateTo("~"); - } - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback && usernameT != null) - ((NameCallback) callback).setName(usernameT.getText()); - else if (callback instanceof PasswordCallback && passwordT != null) - ((PasswordCallback) callback).setPassword(passwordT.getTextChars()); - else if (callback instanceof HttpRequestCallback) { - ((HttpRequestCallback) callback).setRequest(UiContext.getHttpRequest()); - ((HttpRequestCallback) callback).setResponse(UiContext.getHttpResponse()); - } else if (callback instanceof LanguageCallback) { - Locale toUse = null; - if (localeChoice != null) - toUse = localeChoice.getSelectedLocale(); - else if (defaultLocale != null) - toUse = defaultLocale; - - if (toUse != null) { - ((LanguageCallback) callback).setLocale(toUse); - UiContext.setLocale(toUse); - } - - } - } - } - - public void setSubject(Subject subject) { - this.subject = subject; - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java deleted file mode 100644 index dea632de8..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.argeo.cms.widgets.auth; - -import org.argeo.cms.ui.CmsView; -import org.argeo.cms.util.CmsUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** The site-related user menu */ -public class CmsLoginShell extends CmsLogin { - private final Shell shell; - - public CmsLoginShell(CmsView cmsView) { - super(cmsView); - shell = createShell(); - CmsUtils.style(shell, CMS_USER_MENU); -// createUi(shell); - } - - /** To be overridden. */ - protected Shell createShell() { - Shell shell = new Shell(Display.getCurrent(), SWT.NO_TRIM); - shell.setMaximized(true); - return shell; - } - - /** To be overridden. */ - public void open() { - shell.open(); - } - - @Override - protected boolean login() { - boolean success = false; - try { - success = super.login(); - return success; - } finally { - if (success) - closeShell(); - else { - for (Control child : shell.getChildren()) - child.dispose(); - createUi(shell); - shell.layout(); - // TODO error message - } - } - } - - @Override - protected void logout() { - closeShell(); - super.logout(); - } - - protected void closeShell() { - if (!shell.isDisposed()) { - shell.close(); - shell.dispose(); - } - } - - public Shell getShell() { - return shell; - } - - public void createUi(){ - createUi(shell); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java deleted file mode 100644 index 1f72e23c1..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java +++ /dev/null @@ -1,273 +0,0 @@ -package org.argeo.cms.widgets.auth; - -import java.io.IOException; -import java.util.Arrays; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.TextOutputCallback; -import javax.security.auth.callback.UnsupportedCallbackException; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** - * A composite that can populate itself based on {@link Callback}s. It can be - * used directly as a {@link CallbackHandler} or be used by one by calling the - * {@link #createCallbackHandlers(Callback[])}. Supported standard - * {@link Callback}s are:
- *
    - *
  • {@link PasswordCallback}
  • - *
  • {@link NameCallback}
  • - *
  • {@link TextOutputCallback}
  • - *
- * Supported Argeo {@link Callback}s are:
- *
    - *
  • {@link LocaleChoice}
  • - *
- */ -public class CompositeCallbackHandler extends Composite implements CallbackHandler { - private static final long serialVersionUID = -928223893722723777L; - - private boolean wasUsedAlready = false; - private boolean isSubmitted = false; - private boolean isCanceled = false; - - public CompositeCallbackHandler(Composite parent, int style) { - super(parent, style); - } - - @Override - public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { - // reset - if (wasUsedAlready && !isSubmitted() && !isCanceled()) { - cancel(); - for (Control control : getChildren()) - control.dispose(); - isSubmitted = false; - isCanceled = false; - } - - for (Callback callback : callbacks) - checkCallbackSupported(callback); - // create controls synchronously in the UI thread - getDisplay().syncExec(new Runnable() { - - @Override - public void run() { - createCallbackHandlers(callbacks); - } - }); - - if (!wasUsedAlready) - wasUsedAlready = true; - - // while (!isSubmitted() && !isCanceled()) { - // try { - // wait(1000l); - // } catch (InterruptedException e) { - // // silent - // } - // } - - // cleanCallbacksAfterCancel(callbacks); - } - - public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException { - if (callback instanceof TextOutputCallback || callback instanceof NameCallback - || callback instanceof PasswordCallback || callback instanceof LocaleChoice) { - return; - } else { - throw new UnsupportedCallbackException(callback); - } - } - - /** - * Set writable callbacks to null if the handle is canceled (check is done - * by the method) - */ - public void cleanCallbacksAfterCancel(Callback[] callbacks) { - if (isCanceled()) { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback) { - ((NameCallback) callback).setName(null); - } else if (callback instanceof PasswordCallback) { - PasswordCallback pCallback = (PasswordCallback) callback; - char[] arr = pCallback.getPassword(); - if (arr != null) { - Arrays.fill(arr, '*'); - pCallback.setPassword(null); - } - } - } - } - } - - public void createCallbackHandlers(Callback[] callbacks) { - Composite composite = this; - for (int i = 0; i < callbacks.length; i++) { - Callback callback = callbacks[i]; - if (callback instanceof TextOutputCallback) { - createLabelTextoutputHandler(composite, (TextOutputCallback) callback); - } else if (callback instanceof NameCallback) { - createNameHandler(composite, (NameCallback) callback); - } else if (callback instanceof PasswordCallback) { - createPasswordHandler(composite, (PasswordCallback) callback); - } else if (callback instanceof LocaleChoice) { - createLocaleHandler(composite, (LocaleChoice) callback); - } - } - } - - protected Text createNameHandler(Composite composite, final NameCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getPrompt()); - final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER); - if (callback.getDefaultName() != null) { - // set default value, if provided - text.setText(callback.getDefaultName()); - callback.setName(callback.getDefaultName()); - } - text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - text.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 7300032545287292973L; - - public void modifyText(ModifyEvent event) { - callback.setName(text.getText()); - } - }); - text.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 1820530045857665111L; - - @Override - public void widgetSelected(SelectionEvent e) { - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - submit(); - } - }); - - text.addKeyListener(new KeyListener() { - private static final long serialVersionUID = -8698107785092095713L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - } - }); - return text; - } - - protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getPrompt()); - final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER); - passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - passwordText.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = -7099363995047686732L; - - public void modifyText(ModifyEvent event) { - callback.setPassword(passwordText.getTextChars()); - } - }); - passwordText.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 1820530045857665111L; - - @Override - public void widgetSelected(SelectionEvent e) { - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - submit(); - } - }); - return passwordText; - } - - protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) { - String[] labels = callback.getSupportedLocalesLabels(); - if (labels.length == 0) - return null; - Label label = new Label(composite, SWT.NONE); - label.setText("Language"); - - final Combo combo = new Combo(composite, SWT.READ_ONLY); - combo.setItems(labels); - combo.select(callback.getDefaultIndex()); - combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - combo.addSelectionListener(new SelectionListener() { - private static final long serialVersionUID = 38678989091946277L; - - @Override - public void widgetSelected(SelectionEvent e) { - callback.setSelectedIndex(combo.getSelectionIndex()); - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - return combo; - } - - protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) { - Label label = new Label(composite, SWT.NONE); - label.setText(callback.getMessage()); - GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); - data.horizontalSpan = 2; - label.setLayoutData(data); - return label; - // TODO: find a way to pass this information - // int messageType = callback.getMessageType(); - // int dialogMessageType = IMessageProvider.NONE; - // switch (messageType) { - // case TextOutputCallback.INFORMATION: - // dialogMessageType = IMessageProvider.INFORMATION; - // break; - // case TextOutputCallback.WARNING: - // dialogMessageType = IMessageProvider.WARNING; - // break; - // case TextOutputCallback.ERROR: - // dialogMessageType = IMessageProvider.ERROR; - // break; - // } - // setMessage(callback.getMessage(), dialogMessageType); - } - - synchronized boolean isSubmitted() { - return isSubmitted; - } - - synchronized boolean isCanceled() { - return isCanceled; - } - - protected synchronized void submit() { - isSubmitted = true; - notifyAll(); - } - - protected synchronized void cancel() { - isCanceled = true; - notifyAll(); - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java deleted file mode 100644 index b8de34b7c..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.cms.widgets.auth; - -import javax.security.auth.callback.CallbackHandler; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** Default authentication dialog, to be used as {@link CallbackHandler}. */ -public class DefaultLoginDialog extends AbstractLoginDialog { - private static final long serialVersionUID = -8551827590693035734L; - - public DefaultLoginDialog() { - this(Display.getCurrent().getActiveShell()); - } - - public DefaultLoginDialog(Shell parentShell) { - super(parentShell); - } - - protected Point getInitialSize() { - return new Point(350, 180); - } - - @Override - protected Control createContents(Composite parent) { - Control control = super.createContents(parent); - parent.pack(); - - // Move the dialog to the center of the top level shell. - Rectangle shellBounds; - if (Display.getCurrent().getActiveShell() != null) // RCP - shellBounds = Display.getCurrent().getActiveShell().getBounds(); - else - shellBounds = Display.getCurrent().getBounds();// RAP - Point dialogSize = parent.getSize(); - int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2; - int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2; - parent.setLocation(x, y); - return control; - } - - protected Control createDialogArea(Composite parent) { - Composite dialogarea = (Composite) super.createDialogArea(parent); - CompositeCallbackHandler composite = new CompositeCallbackHandler( - dialogarea, SWT.NONE); - composite.setLayout(new GridLayout(2, false)); - composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - composite.createCallbackHandlers(getCallbacks()); - return composite; - } - - public void internalHandle() { - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DynamicCallbackHandler.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DynamicCallbackHandler.java deleted file mode 100644 index b2063555f..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DynamicCallbackHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.argeo.cms.widgets.auth; - -import java.io.IOException; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.UnsupportedCallbackException; - -import org.argeo.eclipse.ui.dialogs.LightweightDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -public class DynamicCallbackHandler implements CallbackHandler { - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - Shell activeShell = Display.getCurrent().getActiveShell(); - LightweightDialog dialog = new LightweightDialog(activeShell) { - - @Override - protected Control createDialogArea(Composite parent) { - CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE); - cch.createCallbackHandlers(callbacks); - return cch; - } - }; - dialog.setBlockOnOpen(true); - dialog.open(); - } - -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java deleted file mode 100644 index b3ae47df5..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.cms.widgets.auth; - -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -import javax.security.auth.callback.LanguageCallback; - -import org.argeo.cms.CmsException; -import org.argeo.cms.LocaleUtils; - -/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */ -public class LocaleChoice { - private final List locales; - - private Integer selectedIndex = null; - private final Integer defaultIndex; - - public LocaleChoice(List locales, Locale defaultLocale) { - Integer defaultIndex = null; - this.locales = Collections.unmodifiableList(locales); - for (int i = 0; i < locales.size(); i++) - if (locales.get(i).equals(defaultLocale)) - defaultIndex = i; - - // based on language only - if (defaultIndex == null) - for (int i = 0; i < locales.size(); i++) - if (locales.get(i).getLanguage().equals(defaultLocale.getLanguage())) - defaultIndex = i; - - if (defaultIndex == null) - throw new CmsException("Default locale " + defaultLocale + " is not in available locales " + locales); - this.defaultIndex = defaultIndex; - - this.selectedIndex = defaultIndex; - } - - /** - * Convenience constructor based on a comma separated list of iso codes (en, - * en_US, fr_CA, etc.). Default selection is default locale. - */ - public LocaleChoice(String locales, Locale defaultLocale) { - this(LocaleUtils.asLocaleList(locales), defaultLocale); - } - - public String[] getSupportedLocalesLabels() { - String[] labels = new String[locales.size()]; - for (int i = 0; i < locales.size(); i++) { - Locale locale = locales.get(i); - if (locale.getCountry().equals("")) - labels[i] = locale.getDisplayLanguage(locale) + " [" + locale.getLanguage() + "]"; - else - labels[i] = locale.getDisplayLanguage(locale) + " (" + locale.getDisplayCountry(locale) + ") [" - + locale.getLanguage() + "_" + locale.getCountry() + "]"; - - } - return labels; - } - - public Locale getSelectedLocale() { - if (selectedIndex == null) - return null; - return locales.get(selectedIndex); - } - - public void setSelectedIndex(Integer selectedIndex) { - this.selectedIndex = selectedIndex; - } - - public Integer getSelectedIndex() { - return selectedIndex; - } - - public Integer getDefaultIndex() { - return defaultIndex; - } - - public List getLocales() { - return locales; - } - - public Locale getDefaultLocale() { - return locales.get(getDefaultIndex()); - } -} diff --git a/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java b/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java index 44ef29d6a..893067f61 100644 --- a/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java +++ b/org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java @@ -25,19 +25,19 @@ import javax.jcr.PropertyType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.argeo.api.tabular.TabularColumn; +import org.argeo.api.tabular.TabularRow; +import org.argeo.api.tabular.TabularRowIterator; +import org.argeo.api.tabular.TabularWriter; import org.argeo.cms.ArgeoTypes; import org.argeo.jackrabbit.unit.AbstractJackrabbitTestCase; -import org.argeo.node.tabular.TabularColumn; -import org.argeo.node.tabular.TabularRow; -import org.argeo.node.tabular.TabularRowIterator; -import org.argeo.node.tabular.TabularWriter; public class JcrTabularTest extends AbstractJackrabbitTestCase { private final static Log log = LogFactory.getLog(JcrTabularTest.class); public void testWriteReadCsv() throws Exception { // session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE); - InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/node/ldap.cnd")); + InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/api/ldap.cnd")); CndImporter.registerNodeTypes(reader, session()); reader.close(); reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/cms/argeo.cnd")); diff --git a/org.argeo.cms/pom.xml b/org.argeo.cms/pom.xml index 69c833e9c..78cfb65bd 100644 --- a/org.argeo.cms/pom.xml +++ b/org.argeo.cms/pom.xml @@ -13,7 +13,7 @@ org.argeo.commons - org.argeo.node.api + org.argeo.api 2.1.88-SNAPSHOT diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java index ab40e720c..4d1fceb58 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java @@ -13,6 +13,10 @@ import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.AnonymousPrincipal; +import org.argeo.api.security.DataAdminPrincipal; +import org.argeo.api.security.NodeSecurityUtils; //import org.apache.jackrabbit.core.security.AnonymousPrincipal; //import org.apache.jackrabbit.core.security.SecurityConstants; //import org.apache.jackrabbit.core.security.principal.AdminPrincipal; @@ -21,10 +25,6 @@ import org.argeo.cms.internal.auth.CmsSessionImpl; import org.argeo.cms.internal.auth.ImpliedByPrincipal; import org.argeo.cms.internal.http.WebCmsSessionImpl; import org.argeo.cms.internal.kernel.Activator; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.AnonymousPrincipal; -import org.argeo.node.security.DataAdminPrincipal; -import org.argeo.node.security.NodeSecurityUtils; import org.argeo.osgi.useradmin.AuthenticatingUser; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; diff --git a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java index 43bddaf8d..e1425015c 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java @@ -28,11 +28,11 @@ import java.util.UUID; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.auth.CmsSessionImpl; import org.argeo.cms.internal.auth.ImpliedByPrincipal; import org.argeo.cms.internal.kernel.Activator; -import org.argeo.node.NodeConstants; import org.osgi.service.useradmin.Authorization; /** diff --git a/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java index 09fece03a..4a4151edc 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java @@ -30,7 +30,7 @@ import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; -import org.argeo.node.security.PBEKeySpecCallback; +import org.argeo.api.security.PBEKeySpecCallback; import org.argeo.util.PasswordEncryption; /** Adds a secret key to the private credentials */ diff --git a/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java index 4d2cc3339..e3da327e6 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java @@ -16,10 +16,10 @@ import javax.security.auth.x500.X500Principal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.DataAdminPrincipal; import org.argeo.cms.internal.auth.ImpliedByPrincipal; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.DataAdminPrincipal; import org.argeo.osgi.useradmin.IpaUtils; public class SingleUserLoginModule implements LoginModule { diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java index 692d214bc..f7d426e26 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java @@ -27,11 +27,11 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.CryptoKeyring; import org.argeo.cms.CmsException; import org.argeo.cms.internal.kernel.Activator; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.CryptoKeyring; import org.argeo.osgi.useradmin.AuthenticatingUser; import org.argeo.osgi.useradmin.IpaUtils; import org.argeo.osgi.useradmin.OsUserUtils; diff --git a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java index 326b0f4da..3dbc7ad52 100644 --- a/org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java @@ -6,9 +6,9 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.naming.LdapAttrs; -import org.argeo.node.NodeConstants; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; diff --git a/org.argeo.cms/src/org/argeo/cms/i18n/DefaultsResourceBundle.java b/org.argeo.cms/src/org/argeo/cms/i18n/DefaultsResourceBundle.java deleted file mode 100644 index 78d717a20..000000000 --- a/org.argeo.cms/src/org/argeo/cms/i18n/DefaultsResourceBundle.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.argeo.cms.i18n; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Enumeration; -import java.util.ResourceBundle; -import java.util.Vector; - -import org.argeo.cms.CmsException; - -/** Expose the default values as a {@link ResourceBundle} */ -@Deprecated -public class DefaultsResourceBundle extends ResourceBundle { - - @Override - protected Object handleGetObject(String key) { - Object obj; - try { - Field field = getClass().getField(key); - obj = field.getType().getMethod("getDefault") - .invoke(field.get(null)); - } catch (Exception e) { - throw new CmsException("Cannot get default for " + key, e); - } - return obj; - } - - @Override - public Enumeration getKeys() { - Vector res = new Vector(); - final Field[] fieldArray = getClass().getDeclaredFields(); - - for (Field field : fieldArray) { - if (Modifier.isStatic(field.getModifiers()) - && field.getType().isAssignableFrom(LocaleUtils.class)) { - res.add(field.getName()); - } - } - return res.elements(); - } - -} diff --git a/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java b/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java deleted file mode 100644 index 71a31413e..000000000 --- a/org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.argeo.cms.i18n; - -import java.util.List; -import java.util.Locale; - -import org.argeo.cms.auth.CurrentUser; - -/** - * Utilities simplifying the development of localization enums. - * - * @deprecated Use {@link org.argeo.cms.LocaleUtils} - */ -@Deprecated -public class LocaleUtils { - public static Object local(Enum en) { - return org.argeo.cms.LocaleUtils.local(en); - } - - public static Object local(Enum en, Locale locale) { - return org.argeo.cms.LocaleUtils.local(en, locale); - } - - public static Object local(Enum en, Locale locale, String resource) { - return org.argeo.cms.LocaleUtils.local(en, locale, resource); - } - - public static Object local(Enum en, Locale locale, String resource, ClassLoader classLoader) { - return org.argeo.cms.LocaleUtils.local(en, locale, resource, classLoader); - } - - public static String lead(String raw, Locale locale) { - return org.argeo.cms.LocaleUtils.lead(raw, locale); - } - - public static String lead(Localized localized) { - return org.argeo.cms.LocaleUtils.lead(localized); - } - - public static String lead(Localized localized, Locale locale) { - return org.argeo.cms.LocaleUtils.lead(localized, locale); - } - - static Locale getCurrentLocale() { - return CurrentUser.locale(); - // return UiContext.getLocale(); - // FIXME look into Subject or settings - // return Locale.getDefault(); - } - - /** Returns null if argument is null. */ - public static List asLocaleList(Object locales) { - return org.argeo.cms.LocaleUtils.asLocaleList(locales); - } -} diff --git a/org.argeo.cms/src/org/argeo/cms/i18n/Localized.java b/org.argeo.cms/src/org/argeo/cms/i18n/Localized.java deleted file mode 100644 index 7d32193bf..000000000 --- a/org.argeo.cms/src/org/argeo/cms/i18n/Localized.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.i18n; - -/** - * Localized object. - * - * @deprecated Use {@link org.argeo.cms.Localized} instead. - */ -@Deprecated -public interface Localized extends org.argeo.cms.Localized { -} diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java index cb3a3484a..5bc13526c 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java @@ -16,10 +16,10 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.CmsSessionId; import org.argeo.cms.auth.HttpRequestCallback; import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.node.NodeConstants; import org.osgi.service.useradmin.Authorization; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java index 50967391c..bc37b5f4f 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java @@ -13,11 +13,11 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.CmsSessionId; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.HttpRequestCallback; import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.node.NodeConstants; /** Externally authenticate an http session. */ public class CmsLogoutServlet extends HttpServlet { diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsPrivateServletContext.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsPrivateServletContext.java index c968d779d..4c7c8997a 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsPrivateServletContext.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsPrivateServletContext.java @@ -1,6 +1,6 @@ package org.argeo.cms.integration; -import static org.argeo.node.NodeConstants.LOGIN_CONTEXT_USER; +import static org.argeo.api.NodeConstants.LOGIN_CONTEXT_USER; import java.io.IOException; import java.security.AccessControlContext; diff --git a/org.argeo.cms/src/org/argeo/cms/integration/CmsTokenServlet.java b/org.argeo.cms/src/org/argeo/cms/integration/CmsTokenServlet.java index b2e5a7ed3..11a6944b2 100644 --- a/org.argeo.cms/src/org/argeo/cms/integration/CmsTokenServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/integration/CmsTokenServlet.java @@ -15,11 +15,11 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsUserManager; import org.argeo.cms.auth.HttpRequestCallback; import org.argeo.cms.auth.HttpRequestCallbackHandler; import org.argeo.naming.NamingUtils; -import org.argeo.node.NodeConstants; import org.osgi.service.useradmin.Authorization; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java index ce38cf0ee..f8d5863e6 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java @@ -26,11 +26,11 @@ import javax.security.auth.login.LoginException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.NodeSecurityUtils; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CmsSession; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.NodeSecurityUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java index 869dbb23c..d9b8f42d1 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java @@ -25,6 +25,7 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsUserManager; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.auth.UserAdminUtils; @@ -32,7 +33,6 @@ import org.argeo.jcr.JcrUtils; import org.argeo.naming.LdapAttrs; import org.argeo.naming.NamingUtils; import org.argeo.naming.SharedSecret; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.TokenUtils; import org.argeo.osgi.useradmin.UserAdminConf; import org.osgi.framework.InvalidSyntaxException; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java b/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java index 93f63530e..df469524d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java @@ -10,8 +10,8 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.node.NodeConstants; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.service.http.HttpContext; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java b/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java index f9e17a107..f81f26f7a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java @@ -25,10 +25,10 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/jcr/RepositoryBuilder.java b/org.argeo.cms/src/org/argeo/cms/internal/jcr/RepositoryBuilder.java index b3ab11a33..872d8ae53 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/jcr/RepositoryBuilder.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/jcr/RepositoryBuilder.java @@ -21,10 +21,10 @@ import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.cache.CacheManager; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.jackrabbit.core.config.RepositoryConfigurationParser; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.kernel.CmsPaths; import org.argeo.jcr.ArgeoJcrException; -import org.argeo.node.NodeConstants; import org.xml.sax.InputSource; /** Can interpret properties in order to create an actual JCR repository. */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java index 5728d4fa5..d2d7d25b7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java @@ -15,13 +15,13 @@ import javax.security.auth.login.Configuration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.ArgeoLogger; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeDeployment; +import org.argeo.api.NodeInstance; +import org.argeo.api.NodeState; import org.argeo.cms.CmsException; import org.argeo.ident.IdentClient; -import org.argeo.node.ArgeoLogger; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeDeployment; -import org.argeo.node.NodeInstance; -import org.argeo.node.NodeState; import org.argeo.util.LangUtils; import org.ietf.jgss.GSSCredential; import org.osgi.framework.BundleActivator; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index dc6459087..b81a81b3b 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -1,6 +1,6 @@ package org.argeo.cms.internal.kernel; -import static org.argeo.node.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE; +import static org.argeo.api.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE; import java.io.File; import java.io.InputStreamReader; @@ -26,15 +26,15 @@ import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.commons.cnd.CndImporter; import org.apache.jackrabbit.core.RepositoryContext; import org.apache.jackrabbit.core.RepositoryImpl; +import org.argeo.api.DataModelNamespace; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeDeployment; +import org.argeo.api.NodeState; +import org.argeo.api.security.CryptoKeyring; +import org.argeo.api.security.Keyring; import org.argeo.cms.ArgeoNames; import org.argeo.cms.CmsException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.DataModelNamespace; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeDeployment; -import org.argeo.node.NodeState; -import org.argeo.node.security.CryptoKeyring; -import org.argeo.node.security.Keyring; import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.util.LangUtils; import org.eclipse.equinox.http.jetty.JettyConfigurator; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java index 3304bb41d..dea21a2e7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java @@ -16,14 +16,14 @@ import javax.jcr.RepositoryFactory; import javax.jcr.Session; import javax.jcr.nodetype.NodeType; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.jackrabbit.fs.AbstractJackrabbitFsProvider; import org.argeo.jcr.fs.JcrFileSystem; import org.argeo.jcr.fs.JcrFileSystemProvider; import org.argeo.jcr.fs.JcrFsException; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsInstance.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsInstance.java index c972f725c..1af4095ce 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsInstance.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsInstance.java @@ -5,9 +5,9 @@ import javax.naming.ldap.LdapName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeInstance; import org.argeo.cms.CmsException; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeInstance; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java index 6c97fa141..ca469f0f5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java @@ -15,10 +15,10 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeState; import org.argeo.cms.CmsException; import org.argeo.cms.LocaleUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeState; import org.argeo.transaction.simple.SimpleTransactionManager; import org.argeo.util.LangUtils; import org.osgi.framework.BundleContext; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java index 19117a892..acf0dbf7c 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java @@ -1,6 +1,6 @@ package org.argeo.cms.internal.kernel; -import static org.argeo.node.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE; +import static org.argeo.api.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE; import java.util.ArrayList; import java.util.Collections; @@ -10,8 +10,8 @@ import java.util.TreeMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.DataModelNamespace; import org.argeo.cms.CmsException; -import org.argeo.node.DataModelNamespace; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java index d52fe663b..9d2925948 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java @@ -20,13 +20,13 @@ import javax.websocket.server.ServerEndpointConfig; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.http.InternalHttpConstants; import org.argeo.cms.websocket.CmsWebSocketConfigurator; import org.argeo.naming.AttributesDictionary; import org.argeo.naming.LdifParser; import org.argeo.naming.LdifWriter; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.UserAdminConf; import org.eclipse.equinox.http.jetty.JettyConfigurator; import org.osgi.framework.BundleContext; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/EgoRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/EgoRepository.java index 9c5e5f5f7..375175bb1 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/EgoRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/EgoRepository.java @@ -17,11 +17,11 @@ import javax.naming.ldap.LdapName; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; import org.argeo.cms.CmsException; import org.argeo.jcr.JcrRepositoryWrapper; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; /** * Make sure each user has a home directory available. diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java index 6eaebeb64..390131aa7 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java @@ -21,10 +21,10 @@ import javax.security.auth.x500.X500Principal; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.http.InternalHttpConstants; import org.argeo.cms.internal.jcr.RepoConf; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.UserAdminConf; /** diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitLocalRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitLocalRepository.java index c59a599f1..35575de1d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitLocalRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitLocalRepository.java @@ -9,7 +9,7 @@ import javax.jcr.Session; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.core.RepositoryImpl; -import org.argeo.node.NodeConstants; +import org.argeo.api.NodeConstants; class JackrabbitLocalRepository extends LocalRepository { private final static Log log = LogFactory.getLog(JackrabbitLocalRepository.class); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index 13ff4b8b2..8fa7779f9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -1,6 +1,6 @@ package org.argeo.cms.internal.kernel; -import org.argeo.node.NodeConstants; +import org.argeo.api.NodeConstants; public interface KernelConstants { String[] DEFAULT_CNDS = { "/org/argeo/jcr/argeo.cnd", "/org/argeo/cms/cms.cnd" }; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java index 86289f0ff..bf8e8d38e 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java @@ -24,9 +24,9 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.apache.commons.logging.Log; +import org.argeo.api.DataModelNamespace; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; -import org.argeo.node.DataModelNamespace; -import org.argeo.node.NodeConstants; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java index 4356d18ff..fd085e214 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java @@ -2,8 +2,8 @@ package org.argeo.cms.internal.kernel; import javax.jcr.Repository; +import org.argeo.api.NodeConstants; import org.argeo.jcr.JcrRepositoryWrapper; -import org.argeo.node.NodeConstants; class LocalRepository extends JcrRepositoryWrapper { private final String cn; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java index 3174285db..ea764c3fb 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java @@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.server.SessionProvider; import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet; import org.apache.jackrabbit.webdav.simple.SimpleWebdavServlet; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.http.CmsSessionProvider; import org.argeo.cms.internal.http.DataHttpContext; @@ -22,7 +23,6 @@ import org.argeo.cms.internal.http.HttpUtils; import org.argeo.cms.internal.http.LinkServlet; import org.argeo.cms.internal.http.PrivateHttpContext; import org.argeo.cms.internal.http.RobotServlet; -import org.argeo.node.NodeConstants; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java index 947d4d89e..1d296c5b9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java @@ -46,11 +46,11 @@ import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.spi.LoggingEvent; +import org.argeo.api.ArgeoLogListener; +import org.argeo.api.ArgeoLogger; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; -import org.argeo.node.ArgeoLogListener; -import org.argeo.node.ArgeoLogger; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.UserAdminConf; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepositoryFactory.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepositoryFactory.java index ae6623826..47e8b5cd5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepositoryFactory.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepositoryFactory.java @@ -28,9 +28,9 @@ import javax.jcr.RepositoryFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.internal.jcr.RepoConf; import org.argeo.jcr.ArgeoJcrException; -import org.argeo.node.NodeConstants; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java index ba4ad8360..cb80f7c7f 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -34,11 +34,11 @@ import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.httpclient.params.HttpParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; import org.argeo.naming.DnsBrowser; -import org.argeo.node.NodeConstants; import org.argeo.osgi.useradmin.AbstractUserDirectory; import org.argeo.osgi.useradmin.AggregatingUserAdmin; import org.argeo.osgi.useradmin.LdapUserAdmin; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryServiceFactory.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryServiceFactory.java index be99bf20e..a06b12bbd 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryServiceFactory.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryServiceFactory.java @@ -11,10 +11,10 @@ import javax.jcr.RepositoryFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.core.RepositoryContext; +import org.argeo.api.NodeConstants; import org.argeo.cms.CmsException; import org.argeo.cms.internal.jcr.RepoConf; import org.argeo.cms.internal.jcr.RepositoryBuilder; -import org.argeo.node.NodeConstants; import org.argeo.util.LangUtils; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java index 9e6e3b96b..127cb9a8a 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java @@ -8,7 +8,7 @@ import java.util.PropertyPermission; import javax.security.auth.AuthPermission; -import org.argeo.node.NodeUtils; +import org.argeo.api.NodeUtils; import org.osgi.framework.AdminPermission; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg index 1d43afd9a..b9f05a4ab 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg @@ -11,7 +11,7 @@ ANONYMOUS { }; DATA_ADMIN { - org.argeo.node.DataAdminLoginModule requisite; + org.argeo.api.DataAdminLoginModule requisite; }; NODE { @@ -19,7 +19,7 @@ NODE { keyTab="${osgi.instance.area}node/krb5.keytab" useKeyTab=true storeKey=true; - org.argeo.node.DataAdminLoginModule requisite; + org.argeo.api.DataAdminLoginModule requisite; }; KEYRING { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg index e54277a3c..9a59613e9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg @@ -10,11 +10,11 @@ ANONYMOUS { }; DATA_ADMIN { - org.argeo.node.DataAdminLoginModule requisite; + org.argeo.api.DataAdminLoginModule requisite; }; NODE { - org.argeo.node.DataAdminLoginModule requisite; + org.argeo.api.DataAdminLoginModule requisite; }; KEYRING { diff --git a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java index 779406a1d..0e356edf7 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java @@ -41,11 +41,11 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.apache.commons.io.IOUtils; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.CryptoKeyring; +import org.argeo.api.security.Keyring; +import org.argeo.api.security.PBEKeySpecCallback; import org.argeo.cms.CmsException; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.CryptoKeyring; -import org.argeo.node.security.Keyring; -import org.argeo.node.security.PBEKeySpecCallback; /** username / password based keyring. TODO internationalize */ public abstract class AbstractKeyring implements Keyring, CryptoKeyring { diff --git a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java index c75d38fc8..45b7c4beb 100644 --- a/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java +++ b/org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java @@ -41,14 +41,14 @@ import javax.jcr.query.Query; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; +import org.argeo.api.NodeUtils; +import org.argeo.api.security.PBEKeySpecCallback; import org.argeo.cms.ArgeoNames; import org.argeo.cms.ArgeoTypes; import org.argeo.cms.CmsException; import org.argeo.jcr.ArgeoJcrException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeConstants; -import org.argeo.node.NodeUtils; -import org.argeo.node.security.PBEKeySpecCallback; /** JCR based implementation of a keyring */ public class JcrKeyring extends AbstractKeyring implements ArgeoNames { diff --git a/org.argeo.cms/src/org/argeo/cms/tabular/CsvTabularWriter.java b/org.argeo.cms/src/org/argeo/cms/tabular/CsvTabularWriter.java index d22f44e73..f4b53f875 100644 --- a/org.argeo.cms/src/org/argeo/cms/tabular/CsvTabularWriter.java +++ b/org.argeo.cms/src/org/argeo/cms/tabular/CsvTabularWriter.java @@ -17,7 +17,7 @@ package org.argeo.cms.tabular; import java.io.OutputStream; -import org.argeo.node.tabular.TabularWriter; +import org.argeo.api.tabular.TabularWriter; import org.argeo.util.CsvWriter; /** Write tabular content in a stream as CSV. Wraps a {@link CsvWriter}. */ diff --git a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java index 04dce92c8..c13d62244 100644 --- a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java +++ b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java @@ -28,12 +28,12 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import org.apache.commons.io.IOUtils; +import org.argeo.api.tabular.ArrayTabularRow; +import org.argeo.api.tabular.TabularColumn; +import org.argeo.api.tabular.TabularRow; +import org.argeo.api.tabular.TabularRowIterator; import org.argeo.cms.ArgeoTypes; import org.argeo.jcr.ArgeoJcrException; -import org.argeo.node.tabular.ArrayTabularRow; -import org.argeo.node.tabular.TabularColumn; -import org.argeo.node.tabular.TabularRow; -import org.argeo.node.tabular.TabularRowIterator; import org.argeo.util.CsvParser; /** Iterates over the rows of a {@link ArgeoTypes#ARGEO_TABLE} node. */ diff --git a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java index a6e1e286c..e581e2b35 100644 --- a/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java +++ b/org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java @@ -27,11 +27,11 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import org.apache.commons.io.IOUtils; +import org.argeo.api.tabular.TabularColumn; +import org.argeo.api.tabular.TabularWriter; import org.argeo.cms.ArgeoTypes; import org.argeo.jcr.ArgeoJcrException; import org.argeo.jcr.JcrUtils; -import org.argeo.node.tabular.TabularColumn; -import org.argeo.node.tabular.TabularWriter; import org.argeo.util.CsvWriter; /** Write / reference tabular content in a JCR repository. */ diff --git a/org.argeo.cms/src/org/argeo/cms/websocket/CmsWebSocketConfigurator.java b/org.argeo.cms/src/org/argeo/cms/websocket/CmsWebSocketConfigurator.java index f72527af1..652f25298 100644 --- a/org.argeo.cms/src/org/argeo/cms/websocket/CmsWebSocketConfigurator.java +++ b/org.argeo.cms/src/org/argeo/cms/websocket/CmsWebSocketConfigurator.java @@ -15,8 +15,8 @@ import javax.websocket.server.ServerEndpointConfig.Configurator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeConstants; import org.argeo.cms.auth.HttpRequestCallbackHandler; -import org.argeo.node.NodeConstants; import org.osgi.service.http.context.ServletContextHelper; /** Customises the initialisation of a new web socket. */ diff --git a/org.argeo.eclipse.ui.rap/bnd.bnd b/org.argeo.eclipse.ui.rap/bnd.bnd index 0b6fdb5ef..fb50c88ef 100644 --- a/org.argeo.eclipse.ui.rap/bnd.bnd +++ b/org.argeo.eclipse.ui.rap/bnd.bnd @@ -1,5 +1,5 @@ Import-Package: org.eclipse.swt,\ org.eclipse.jface.dialogs,\ -org.argeo.eclipse.ui.utils,\ +org.argeo.eclipse.ui.util,\ org.eclipse.swt.events,\ * diff --git a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFile.java b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFile.java index efc07337d..3a12c522b 100644 --- a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFile.java +++ b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFile.java @@ -18,7 +18,7 @@ package org.argeo.eclipse.ui.specific; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.utils.SingleSourcingConstants; +import org.argeo.eclipse.ui.util.SingleSourcingConstants; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.rap.rwt.RWT; diff --git a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFileService.java b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFileService.java index a181a2993..4a75cc1f6 100644 --- a/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFileService.java +++ b/org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFileService.java @@ -15,8 +15,8 @@ */ package org.argeo.eclipse.ui.specific; -import static org.argeo.eclipse.ui.utils.SingleSourcingConstants.FILE_SCHEME; -import static org.argeo.eclipse.ui.utils.SingleSourcingConstants.SCHEME_HOST_SEPARATOR; +import static org.argeo.eclipse.ui.util.SingleSourcingConstants.FILE_SCHEME; +import static org.argeo.eclipse.ui.util.SingleSourcingConstants.SCHEME_HOST_SEPARATOR; import java.io.IOException; import java.nio.file.Files; @@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.utils.SingleSourcingConstants; +import org.argeo.eclipse.ui.util.SingleSourcingConstants; import org.eclipse.rap.rwt.service.ServiceHandler; /** diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java new file mode 100644 index 000000000..88006ff93 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Note that it might not be very + * reliable as long as we have not fixed login and multi repository issues that + * will be addressed in the next version. + * + * NOTE: id used here is the real id of the JCR Node, not the JCR Path + * + * Relies on common approach for JCR file handling implementation. + * + */ +@SuppressWarnings("deprecation") +public class JcrFileProvider implements FileProvider { + + // private Object[] rootNodes; + private Node refNode; + + /** + * Must be set in order for the provider to be able to get current session + * and thus have the ability to get the file node corresponding to a given + * file ID + * + * @param refNode + */ + public void setReferenceNode(Node refNode) { + // FIXME : this introduces some concurrency ISSUES. + this.refNode = refNode; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); + } + } + + /** + * Throws an exception if the node is not found in the current repository (a + * bit like a FileNotFoundException) + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = refNode.getSession().getNodeByIdentifier(fileId); + + // rootNodes: for (int j = 0; j < rootNodes.length; j++) { + // // in case we have a classic JCR Node + // if (rootNodes[j] instanceof Node) { + // Node curNode = (Node) rootNodes[j]; + // if (result != null) + // break rootNodes; + // } // Case of a repository Node + // else if (rootNodes[j] instanceof RepositoryNode) { + // Object[] nodes = ((RepositoryNode) rootNodes[j]) + // .getChildren(); + // for (int i = 0; i < nodes.length; i++) { + // Node node = (Node) nodes[i]; + // result = node.getSession().getNodeByIdentifier(fileId); + // if (result != null) + // break rootNodes; + // } + // } + // } + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + Node child = null; + + boolean isValid = true; + if (!result.isNodeType(NodeType.NT_FILE)) + // useless: mandatory child node + // || !result.hasNode(Property.JCR_CONTENT)) + isValid = false; + else { + child = result.getNode(Property.JCR_CONTENT); + if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) + isValid = false; + } + + if (!isValid) + throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE + + "' file node must have a child node named jcr:content " + + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); + return child; + + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); + } + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java new file mode 100644 index 000000000..3fb1308e4 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.eclipse.ui.jcr.util; + +import java.util.Comparator; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** Compares two JCR items (node or properties) based on their names. */ +public class JcrItemsComparator implements Comparator { + public int compare(Item o1, Item o2) { + try { + // TODO: put folder before files + return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); + } + } + +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java new file mode 100644 index 000000000..5111f7a56 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.eclipse.ui.jcr.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.IElementComparer; + +/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ +public class NodeViewerComparer implements IElementComparer { + + // force comparison on Node IDs only. + public boolean equals(Object elementA, Object elementB) { + if (!(elementA instanceof Node) || !(elementB instanceof Node)) { + return elementA == null ? elementB == null : elementA + .equals(elementB); + } else { + + boolean result = false; + try { + String idA = ((Node) elementA).getIdentifier(); + String idB = ((Node) elementB).getIdentifier(); + result = idA == null ? idB == null : idA.equals(idB); + } catch (RepositoryException re) { + throw new EclipseUiException("cannot compare nodes", re); + } + + return result; + } + } + + public int hashCode(Object element) { + // TODO enhanced this method. + return element.getClass().toString().hashCode(); + } +} \ No newline at end of file diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java new file mode 100644 index 000000000..16f4d9a03 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Unlike the + * JcrFileProvider , it relies on a single session and manages + * nodes with path only. + * + * Note that considered id is the JCR path + * + * Relies on common approach for JCR file handling implementation. + */ +@SuppressWarnings("deprecation") +public class SingleSessionFileProvider implements FileProvider { + + private Session session; + + public SingleSessionFileProvider(Session session) { + this.session = session; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + + fileId, re); + } + } + + /** + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = null; + result = session.getNode(fileId); + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + // Ensure that the node have the correct type. + if (!result.isNodeType(NodeType.NT_FILE)) + throw new EclipseUiException( + "Cannot open file children Node that are not of " + + NodeType.NT_RESOURCE + " type."); + + Node child = result.getNodes().nextNode(); + if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) + throw new EclipseUiException( + "ERROR: IN the current implemented model, " + + NodeType.NT_FILE + + " file node must have one and only one child of the nt:ressource, where actual data is stored"); + return child; + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + + fileId, re); + } + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java deleted file mode 100644 index 472101f34..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.eclipse.ui.jcr.utils; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Note that it might not be very - * reliable as long as we have not fixed login and multi repository issues that - * will be addressed in the next version. - * - * NOTE: id used here is the real id of the JCR Node, not the JCR Path - * - * Relies on common approach for JCR file handling implementation. - * - */ -@SuppressWarnings("deprecation") -public class JcrFileProvider implements FileProvider { - - // private Object[] rootNodes; - private Node refNode; - - /** - * Must be set in order for the provider to be able to get current session - * and thus have the ability to get the file node corresponding to a given - * file ID - * - * @param refNode - */ - public void setReferenceNode(Node refNode) { - // FIXME : this introduces some concurrency ISSUES. - this.refNode = refNode; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); - } - } - - /** - * Throws an exception if the node is not found in the current repository (a - * bit like a FileNotFoundException) - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = refNode.getSession().getNodeByIdentifier(fileId); - - // rootNodes: for (int j = 0; j < rootNodes.length; j++) { - // // in case we have a classic JCR Node - // if (rootNodes[j] instanceof Node) { - // Node curNode = (Node) rootNodes[j]; - // if (result != null) - // break rootNodes; - // } // Case of a repository Node - // else if (rootNodes[j] instanceof RepositoryNode) { - // Object[] nodes = ((RepositoryNode) rootNodes[j]) - // .getChildren(); - // for (int i = 0; i < nodes.length; i++) { - // Node node = (Node) nodes[i]; - // result = node.getSession().getNodeByIdentifier(fileId); - // if (result != null) - // break rootNodes; - // } - // } - // } - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - Node child = null; - - boolean isValid = true; - if (!result.isNodeType(NodeType.NT_FILE)) - // useless: mandatory child node - // || !result.hasNode(Property.JCR_CONTENT)) - isValid = false; - else { - child = result.getNode(Property.JCR_CONTENT); - if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) - isValid = false; - } - - if (!isValid) - throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE - + "' file node must have a child node named jcr:content " - + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); - return child; - - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); - } - } -} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java deleted file mode 100644 index 015670ba5..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.eclipse.ui.jcr.utils; - -import java.util.Comparator; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** Compares two JCR items (node or properties) based on their names. */ -public class JcrItemsComparator implements Comparator { - public int compare(Item o1, Item o2) { - try { - // TODO: put folder before files - return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); - } - } - -} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java deleted file mode 100644 index db8ca0866..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.eclipse.ui.jcr.utils; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.IElementComparer; - -/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ -public class NodeViewerComparer implements IElementComparer { - - // force comparison on Node IDs only. - public boolean equals(Object elementA, Object elementB) { - if (!(elementA instanceof Node) || !(elementB instanceof Node)) { - return elementA == null ? elementB == null : elementA - .equals(elementB); - } else { - - boolean result = false; - try { - String idA = ((Node) elementA).getIdentifier(); - String idB = ((Node) elementB).getIdentifier(); - result = idA == null ? idB == null : idA.equals(idB); - } catch (RepositoryException re) { - throw new EclipseUiException("cannot compare nodes", re); - } - - return result; - } - } - - public int hashCode(Object element) { - // TODO enhanced this method. - return element.getClass().toString().hashCode(); - } -} \ No newline at end of file diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java deleted file mode 100644 index 05754d024..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.eclipse.ui.jcr.utils; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Unlike the - * JcrFileProvider , it relies on a single session and manages - * nodes with path only. - * - * Note that considered id is the JCR path - * - * Relies on common approach for JCR file handling implementation. - */ -@SuppressWarnings("deprecation") -public class SingleSessionFileProvider implements FileProvider { - - private Session session; - - public SingleSessionFileProvider(Session session) { - this.session = session; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " - + fileId, re); - } - } - - /** - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = null; - result = session.getNode(fileId); - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - // Ensure that the node have the correct type. - if (!result.isNodeType(NodeType.NT_FILE)) - throw new EclipseUiException( - "Cannot open file children Node that are not of " - + NodeType.NT_RESOURCE + " type."); - - Node child = result.getNodes().nextNode(); - if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) - throw new EclipseUiException( - "ERROR: IN the current implemented model, " - + NodeType.NT_FILE - + " file node must have one and only one child of the nt:ressource, where actual data is stored"); - return child; - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " - + fileId, re); - } - } -} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java index d6dc82cef..57139056c 100644 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java @@ -6,7 +6,7 @@ import java.util.List; import org.argeo.eclipse.ui.ColumnDefinition; import org.argeo.eclipse.ui.EclipseUiException; import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.utils.ViewerUtils; +import org.argeo.eclipse.ui.util.ViewerUtils; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnLabelProvider; diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/SingleSourcingConstants.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/SingleSourcingConstants.java new file mode 100644 index 000000000..b99f37a72 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/SingleSourcingConstants.java @@ -0,0 +1,17 @@ +package org.argeo.eclipse.ui.util; + +/** + * Centralise constants that are used in both RAP and RCP specific code to avoid + * duplicated declaration + */ +public interface SingleSourcingConstants { + + // Single sourced open file command + String OPEN_FILE_CMD_ID = "org.argeo.cms.ui.workbench.openFile"; + String PARAM_FILE_NAME = "param.fileName"; + String PARAM_FILE_URI = "param.fileURI"; + + String SCHEME_HOST_SEPARATOR = "://"; + String FILE_SCHEME = "file"; + String JCR_SCHEME = "jcr"; +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/ViewerUtils.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/ViewerUtils.java new file mode 100644 index 000000000..dbebb790c --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/ViewerUtils.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007-2012 Argeo GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.argeo.eclipse.ui.util; + +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers. + */ +public class ViewerUtils { + + /** + * Creates a basic column for the given table. For the time being, we do not + * support movable columns. + */ + public static TableColumn createColumn(Table parent, String name, int style, int width) { + TableColumn result = new TableColumn(parent, style); + result.setText(name); + result.setWidth(width); + result.setResizable(true); + return result; + } + + /** + * Creates a TableViewerColumn for the given viewer. For the time being, we do + * not support movable columns. + */ + public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) { + TableViewerColumn tvc = new TableViewerColumn(parent, style); + TableColumn column = tvc.getColumn(); + column.setText(name); + column.setWidth(width); + column.setResizable(true); + return tvc; + } + + // public static TableViewerColumn createTableViewerColumn(TableViewer parent, + // Localized name, int style, int width) { + // return createTableViewerColumn(parent, name.lead(), style, width); + // } + + /** + * Creates a TreeViewerColumn for the given viewer. For the time being, we do + * not support movable columns. + */ + public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) { + TreeViewerColumn tvc = new TreeViewerColumn(parent, style); + TreeColumn column = tvc.getColumn(); + column.setText(name); + column.setWidth(width); + column.setResizable(true); + return tvc; + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/SingleSourcingConstants.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/SingleSourcingConstants.java deleted file mode 100644 index a45eeda4f..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/SingleSourcingConstants.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.eclipse.ui.utils; - -/** - * Centralise constants that are used in both RAP and RCP specific code to avoid - * duplicated declaration - */ -public interface SingleSourcingConstants { - - // Single sourced open file command - String OPEN_FILE_CMD_ID = "org.argeo.cms.ui.workbench.openFile"; - String PARAM_FILE_NAME = "param.fileName"; - String PARAM_FILE_URI = "param.fileURI"; - - String SCHEME_HOST_SEPARATOR = "://"; - String FILE_SCHEME = "file"; - String JCR_SCHEME = "jcr"; -} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/ViewerUtils.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/ViewerUtils.java deleted file mode 100644 index 3c029d350..000000000 --- a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/ViewerUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.eclipse.ui.utils; - -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.TreeViewerColumn; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TreeColumn; - -/** - * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers. - */ -public class ViewerUtils { - - /** - * Creates a basic column for the given table. For the time being, we do not - * support movable columns. - */ - public static TableColumn createColumn(Table parent, String name, int style, int width) { - TableColumn result = new TableColumn(parent, style); - result.setText(name); - result.setWidth(width); - result.setResizable(true); - return result; - } - - /** - * Creates a TableViewerColumn for the given viewer. For the time being, we do - * not support movable columns. - */ - public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) { - TableViewerColumn tvc = new TableViewerColumn(parent, style); - TableColumn column = tvc.getColumn(); - column.setText(name); - column.setWidth(width); - column.setResizable(true); - return tvc; - } - - // public static TableViewerColumn createTableViewerColumn(TableViewer parent, - // Localized name, int style, int width) { - // return createTableViewerColumn(parent, name.lead(), style, width); - // } - - /** - * Creates a TreeViewerColumn for the given viewer. For the time being, we do - * not support movable columns. - */ - public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) { - TreeViewerColumn tvc = new TreeViewerColumn(parent, style); - TreeColumn column = tvc.getColumn(); - column.setText(name); - column.setWidth(width); - column.setResizable(true); - return tvc; - } -} diff --git a/org.argeo.ext.equinox.jetty/bnd.bnd b/org.argeo.ext.equinox.jetty/bnd.bnd index 3422e55e7..c114be661 100644 --- a/org.argeo.ext.equinox.jetty/bnd.bnd +++ b/org.argeo.ext.equinox.jetty/bnd.bnd @@ -5,5 +5,5 @@ org.eclipse.jetty.websocket.api,\ org.eclipse.jetty.websocket.common,\ org.osgi.service.http,\ org.argeo.cms.auth,\ -org.argeo.node,\ +org.argeo.api,\ * \ No newline at end of file diff --git a/org.argeo.ext.jackrabbit/bnd.bnd b/org.argeo.ext.jackrabbit/bnd.bnd index 3dbc51f9c..7893b81d9 100644 --- a/org.argeo.ext.jackrabbit/bnd.bnd +++ b/org.argeo.ext.jackrabbit/bnd.bnd @@ -1,3 +1,3 @@ Fragment-Host: org.apache.jackrabbit.core -Import-Package: org.argeo.node,\ +Import-Package: org.argeo.api,\ * diff --git a/org.argeo.ext.jackrabbit/pom.xml b/org.argeo.ext.jackrabbit/pom.xml index 65d5134bc..d8a529de8 100644 --- a/org.argeo.ext.jackrabbit/pom.xml +++ b/org.argeo.ext.jackrabbit/pom.xml @@ -12,7 +12,7 @@ org.argeo.commons - org.argeo.node.api + org.argeo.api 2.1.88-SNAPSHOT diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java index 36401596f..3976f3ba9 100644 --- a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java @@ -37,10 +37,10 @@ import org.apache.jackrabbit.core.security.SystemPrincipal; import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager; import org.apache.jackrabbit.core.security.principal.AdminPrincipal; import org.apache.jackrabbit.core.security.principal.PrincipalProvider; +import org.argeo.api.NodeConstants; +import org.argeo.api.security.AnonymousPrincipal; +import org.argeo.api.security.DataAdminPrincipal; import org.argeo.cms.auth.CmsSession; -import org.argeo.node.NodeConstants; -import org.argeo.node.security.AnonymousPrincipal; -import org.argeo.node.security.DataAdminPrincipal; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; diff --git a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java index f7de8d003..4b7734623 100644 --- a/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java +++ b/org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java @@ -12,7 +12,7 @@ import javax.security.auth.x500.X500Principal; import org.apache.jackrabbit.core.security.AnonymousPrincipal; import org.apache.jackrabbit.core.security.SecurityConstants; import org.apache.jackrabbit.core.security.principal.AdminPrincipal; -import org.argeo.node.security.DataAdminPrincipal; +import org.argeo.api.security.DataAdminPrincipal; public class SystemJackrabbitLoginModule implements LoginModule { private Subject subject; @@ -30,7 +30,7 @@ public class SystemJackrabbitLoginModule implements LoginModule { @Override public boolean commit() throws LoginException { - Set anonPrincipal = subject.getPrincipals(org.argeo.node.security.AnonymousPrincipal.class); + Set anonPrincipal = subject.getPrincipals(org.argeo.api.security.AnonymousPrincipal.class); if (!anonPrincipal.isEmpty()) { subject.getPrincipals().add(new AnonymousPrincipal()); return true; diff --git a/org.argeo.maintenance/pom.xml b/org.argeo.maintenance/pom.xml index a51b73c2b..b65148668 100644 --- a/org.argeo.maintenance/pom.xml +++ b/org.argeo.maintenance/pom.xml @@ -23,7 +23,7 @@ org.argeo.commons - org.argeo.node.api + org.argeo.api 2.1.88-SNAPSHOT diff --git a/org.argeo.maintenance/src/org/argeo/maintenance/AbstractMaintenanceService.java b/org.argeo.maintenance/src/org/argeo/maintenance/AbstractMaintenanceService.java index 6755a647a..c756bd8e0 100644 --- a/org.argeo.maintenance/src/org/argeo/maintenance/AbstractMaintenanceService.java +++ b/org.argeo.maintenance/src/org/argeo/maintenance/AbstractMaintenanceService.java @@ -13,10 +13,10 @@ import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeUtils; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrUtils; import org.argeo.naming.Distinguished; -import org.argeo.node.NodeUtils; import org.osgi.service.useradmin.Group; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.UserAdmin; diff --git a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java index 6d1016a06..a292dcf23 100644 --- a/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java +++ b/org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java @@ -31,8 +31,8 @@ import javax.jcr.Session; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.api.NodeUtils; import org.argeo.jcr.JcrUtils; -import org.argeo.node.NodeUtils; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.xml.sax.SAXException; diff --git a/org.argeo.node.api/.classpath b/org.argeo.node.api/.classpath deleted file mode 100644 index eca7bdba8..000000000 --- a/org.argeo.node.api/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.node.api/.gitignore b/org.argeo.node.api/.gitignore deleted file mode 100644 index 09e3bc9b2..000000000 --- a/org.argeo.node.api/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin/ -/target/ diff --git a/org.argeo.node.api/.project b/org.argeo.node.api/.project deleted file mode 100644 index 9573f0c01..000000000 --- a/org.argeo.node.api/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.node.api - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.node.api/META-INF/.gitignore b/org.argeo.node.api/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.node.api/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.node.api/bnd.bnd b/org.argeo.node.api/bnd.bnd deleted file mode 100644 index f0bd0ceaa..000000000 --- a/org.argeo.node.api/bnd.bnd +++ /dev/null @@ -1 +0,0 @@ -Provide-Capability: cms.datamodel;name=ldap;cnd=/org/argeo/node/ldap.cnd;abstract=true diff --git a/org.argeo.node.api/build.properties b/org.argeo.node.api/build.properties deleted file mode 100644 index 34d2e4d2d..000000000 --- a/org.argeo.node.api/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/org.argeo.node.api/pom.xml b/org.argeo.node.api/pom.xml deleted file mode 100644 index c148f2a13..000000000 --- a/org.argeo.node.api/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - argeo-commons - 2.1.88-SNAPSHOT - .. - - org.argeo.node.api - Argeo Node API - jar - - - \ No newline at end of file diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java deleted file mode 100644 index 303bbef7e..000000000 --- a/org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node; - -/** Framework agnostic interface for log notifications */ -public interface ArgeoLogListener { - /** - * Appends a log - * - * @param username - * authentified user, null for anonymous - * @param level - * INFO, DEBUG, WARN, etc. (logging framework specific) - * @param category - * hierarchy (logging framework specific) - * @param thread - * name of the thread which logged this message - * @param msg - * any object as long as its toString() method returns the - * message - * @param exception - * exception in log4j ThrowableStrRep format - */ - public void appendLog(String username, Long timestamp, String level, - String category, String thread, Object msg, String[] exception); -} diff --git a/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java b/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java deleted file mode 100644 index 213286d44..000000000 --- a/org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node; - -/** - * Logging framework agnostic identifying a logging service, to which one can - * register - */ -public interface ArgeoLogger { - /** - * Register for events by threads with the same authentication (or all - * threads if admin) - */ - public void register(ArgeoLogListener listener, - Integer numberOfPreviousEvents); - - /** - * For admin use only: register for all users - * - * @param listener - * the log listener - * @param numberOfPreviousEvents - * the number of previous events to notify - * @param everything - * if true even anonymous is logged - */ - public void registerForAll(ArgeoLogListener listener, - Integer numberOfPreviousEvents, boolean everything); - - public void unregister(ArgeoLogListener listener); - - public void unregisterForAll(ArgeoLogListener listener); -} diff --git a/org.argeo.node.api/src/org/argeo/node/DataAdminLoginModule.java b/org.argeo.node.api/src/org/argeo/node/DataAdminLoginModule.java deleted file mode 100644 index 307474821..000000000 --- a/org.argeo.node.api/src/org/argeo/node/DataAdminLoginModule.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.argeo.node; - -import java.util.Map; - -import javax.security.auth.AuthPermission; -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; - -import org.argeo.node.security.DataAdminPrincipal; - -/** - * Log-in a system process as data admin. Protection is via - * {@link AuthPermission} on this login module, so if it can be accessed it will - * always succeed. - */ -public class DataAdminLoginModule implements LoginModule { - private Subject subject; - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, - Map options) { - this.subject = subject; - } - - @Override - public boolean login() throws LoginException { - return true; - } - - @Override - public boolean commit() throws LoginException { - subject.getPrincipals().add(new DataAdminPrincipal()); - return true; - } - - @Override - public boolean abort() throws LoginException { - return true; - } - - @Override - public boolean logout() throws LoginException { - subject.getPrincipals().removeAll(subject.getPrincipals(DataAdminPrincipal.class)); - return true; - } -} diff --git a/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java b/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java deleted file mode 100644 index 58e4a645a..000000000 --- a/org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.argeo.node; - -import org.osgi.resource.Namespace; - -/** CMS Data Model capability namespace. */ -public class DataModelNamespace extends Namespace { - - public static final String CMS_DATA_MODEL_NAMESPACE = "cms.datamodel"; - public static final String NAME = "name"; - public static final String CND = "cnd"; - /** If 'true', indicates that no repository should be published */ - public static final String ABSTRACT = "abstract"; - - private DataModelNamespace() { - // empty - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/MvcProvider.java b/org.argeo.node.api/src/org/argeo/node/MvcProvider.java deleted file mode 100644 index 4c52b2070..000000000 --- a/org.argeo.node.api/src/org/argeo/node/MvcProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.argeo.node; - -import java.util.function.BiFunction; - -/** - * Stateless UI part creator. Takes a parent view (V) and a model context (M) in - * order to create a view part (W) which can then be further configured. Such - * object can be used as services and reference other part of the model which - * are relevant for all created UI part. - */ -@FunctionalInterface -public interface MvcProvider extends BiFunction { - /** - * Whether this parent view is supported. - * - * @return true by default. - */ - default boolean isViewSupported(V parent) { - return true; - } - - /** - * Whether this context is supported. - * - * @return true by default. - */ - default boolean isModelSupported(M context) { - return true; - } - - default W createUiPart(V parent, M context) { - if (!isViewSupported(parent)) - throw new IllegalArgumentException("Parent view " + parent + "is not supported."); - if (!isModelSupported(context)) - throw new IllegalArgumentException("Model context " + context + "is not supported."); - return apply(parent, context); - } -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java b/org.argeo.node.api/src/org/argeo/node/NodeConstants.java deleted file mode 100644 index c639c7584..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeConstants.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.node; - -public interface NodeConstants { - /* - * DN ATTRIBUTES (RFC 4514) - */ - String CN = "cn"; - String L = "l"; - String ST = "st"; - String O = "o"; - String OU = "ou"; - String C = "c"; - String STREET = "street"; - String DC = "dc"; - String UID = "uid"; - - /* - * STANDARD ATTRIBUTES - */ - String LABELED_URI = "labeledUri"; - - /* - * COMMON NAMES - */ - String NODE = "node"; - String EGO = "ego"; - String HOME = "home"; - String SRV = "srv"; - String GUESTS = "guests"; - String PUBLIC = "public"; - - /* - * BASE DNs - */ - String DEPLOY_BASEDN = "ou=deploy,ou=node"; - - /* - * STANDARD VALUES - */ - String DEFAULT = "default"; - - /* - * RESERVED ROLES - */ - String ROLES_BASEDN = "ou=roles,ou=node"; - String TOKENS_BASEDN = "ou=tokens,ou=node"; - String ROLE_ADMIN = "cn=admin," + ROLES_BASEDN; - String ROLE_USER_ADMIN = "cn=userAdmin," + ROLES_BASEDN; - String ROLE_DATA_ADMIN = "cn=dataAdmin," + ROLES_BASEDN; - // Special system groups that cannot be edited: - // user U anonymous = everyone - String ROLE_USER = "cn=user," + ROLES_BASEDN; - String ROLE_ANONYMOUS = "cn=anonymous," + ROLES_BASEDN; - // Account lifecycle - String ROLE_REGISTERING = "cn=registering," + ROLES_BASEDN; - - /* - * LOGIN CONTEXTS - */ - String LOGIN_CONTEXT_NODE = "NODE"; - String LOGIN_CONTEXT_USER = "USER"; - String LOGIN_CONTEXT_ANONYMOUS = "ANONYMOUS"; - String LOGIN_CONTEXT_DATA_ADMIN = "DATA_ADMIN"; - String LOGIN_CONTEXT_SINGLE_USER = "SINGLE_USER"; - String LOGIN_CONTEXT_KEYRING = "KEYRING"; - - /* - * PATHS - */ - String PATH_DATA = "/data"; - String PATH_JCR = "/jcr"; - String PATH_FILES = "/files"; - // String PATH_JCR_PUB = "/pub"; - - /* - * FILE SYSTEMS - */ - String SCHEME_NODE = NODE; - - /* - * KERBEROS - */ - String NODE_SERVICE = NODE; - - /* - * INIT FRAMEWORK PROPERTIES - */ - String NODE_INIT = "argeo.node.init"; - String I18N_DEFAULT_LOCALE = "argeo.i18n.defaultLocale"; - String I18N_LOCALES = "argeo.i18n.locales"; - // Node Security - String ROLES_URI = "argeo.node.roles.uri"; - String TOKENS_URI = "argeo.node.tokens.uri"; - /** URI to an LDIF file or LDAP server used as initialization or backend */ - String USERADMIN_URIS = "argeo.node.useradmin.uris"; - // Transaction manager - String TRANSACTION_MANAGER = "argeo.node.transaction.manager"; - String TRANSACTION_MANAGER_SIMPLE = "simple"; - String TRANSACTION_MANAGER_BITRONIX = "bitronix"; - // Node - /** Properties configuring the node repository */ - String NODE_REPO_PROP_PREFIX = "argeo.node.repo."; - /** Additional standalone repositories, related to data models. */ - String NODE_REPOS_PROP_PREFIX = "argeo.node.repos."; - // HTTP - String HTTP_PORT = "org.osgi.service.http.port"; - String HTTP_PORT_SECURE = "org.osgi.service.http.port.secure"; - /** - * The HTTP header used to convey the DN of a client verified by a reverse - * proxy. Typically SSL_CLIENT_S_DN for Apache. - */ - String HTTP_PROXY_SSL_DN = "argeo.http.proxy.ssl.dn"; - - /* - * PIDs - */ - String NODE_STATE_PID = "org.argeo.node.state"; - String NODE_DEPLOYMENT_PID = "org.argeo.node.deployment"; - String NODE_INSTANCE_PID = "org.argeo.node.instance"; - - String NODE_KEYRING_PID = "org.argeo.node.keyring"; - String NODE_FS_PROVIDER_PID = "org.argeo.node.fsProvider"; - - /* - * FACTORY PIDs - */ - String NODE_REPOS_FACTORY_PID = "org.argeo.node.repos"; - String NODE_USER_ADMIN_PID = "org.argeo.node.userAdmin"; -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java b/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java deleted file mode 100644 index 8e5558d60..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeDeployment.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.node; - -public interface NodeDeployment { - Long getAvailableSince(); -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeInstance.java b/org.argeo.node.api/src/org/argeo/node/NodeInstance.java deleted file mode 100644 index aa1b5cee3..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeInstance.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.argeo.node; - -import javax.naming.ldap.LdapName; - -/** The structured data */ -public interface NodeInstance { - /** - * To be used as an identifier of a workgroup, typically as a value for the - * 'businessCategory' attribute in LDAP. - */ - public final static String WORKGROUP = "workgroup"; - - /** Mark this group as a workgroup */ - void createWorkgroup(LdapName groupDn); -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeNames.java b/org.argeo.node.api/src/org/argeo/node/NodeNames.java deleted file mode 100644 index 7ded1115d..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeNames.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node; - -/** JCR types in the http://www.argeo.org/node namespace */ -@Deprecated -public interface NodeNames { - String LDAP_UID = "ldap:"+NodeConstants.UID; - String LDAP_CN = "ldap:"+NodeConstants.CN; -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeOID.java b/org.argeo.node.api/src/org/argeo/node/NodeOID.java deleted file mode 100644 index 387d511d2..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeOID.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.node; - -interface NodeOID { - String BASE = "1.3.6.1.4.1" + ".48308" + ".1"; - - // ATTRIBUTE TYPES - String ATTRIBUTE_TYPES = BASE + ".4"; - - // OBJECT CLASSES - String OBJECT_CLASSES = BASE + ".6"; -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeState.java b/org.argeo.node.api/src/org/argeo/node/NodeState.java deleted file mode 100644 index d7148c68f..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeState.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.argeo.node; - -import java.util.List; -import java.util.Locale; - -public interface NodeState { - Locale getDefaultLocale(); - - List getLocales(); - - String getHostname(); - - boolean isClean(); - - Long getAvailableSince(); - -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeTypes.java b/org.argeo.node.api/src/org/argeo/node/NodeTypes.java deleted file mode 100644 index 891f54659..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeTypes.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node; - -/** JCR types in the http://www.argeo.org/node namespace */ -@Deprecated -public interface NodeTypes { - String NODE_USER_HOME = "node:userHome"; - String NODE_GROUP_HOME = "node:groupHome"; -} diff --git a/org.argeo.node.api/src/org/argeo/node/NodeUtils.java b/org.argeo.node.api/src/org/argeo/node/NodeUtils.java deleted file mode 100644 index a735e181f..000000000 --- a/org.argeo.node.api/src/org/argeo/node/NodeUtils.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node; - -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.NoSuchWorkspaceException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.query.Query; -import javax.jcr.query.QueryResult; -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; -import javax.security.auth.AuthPermission; -import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -/** Utilities related to Argeo model in JCR */ -public class NodeUtils { - /** - * Wraps the call to the repository factory based on parameter - * {@link NodeConstants#CN} in order to simplify it and protect against future - * API changes. - */ - public static Repository getRepositoryByAlias(RepositoryFactory repositoryFactory, String alias) { - try { - Map parameters = new HashMap(); - parameters.put(NodeConstants.CN, alias); - return repositoryFactory.getRepository(parameters); - } catch (RepositoryException e) { - throw new RuntimeException("Unexpected exception when trying to retrieve repository with alias " + alias, - e); - } - } - - /** - * Wraps the call to the repository factory based on parameter - * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against - * future API changes. - */ - public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri) { - return getRepositoryByUri(repositoryFactory, uri, null); - } - - /** - * Wraps the call to the repository factory based on parameter - * {@link NodeConstants#LABELED_URI} in order to simplify it and protect against - * future API changes. - */ - public static Repository getRepositoryByUri(RepositoryFactory repositoryFactory, String uri, String alias) { - try { - Map parameters = new HashMap(); - parameters.put(NodeConstants.LABELED_URI, uri); - if (alias != null) - parameters.put(NodeConstants.CN, alias); - return repositoryFactory.getRepository(parameters); - } catch (RepositoryException e) { - throw new RuntimeException("Unexpected exception when trying to retrieve repository with uri " + uri, e); - } - } - - /** - * Returns the home node of the user or null if none was found. - * - * @param session the session to use in order to perform the search, this can - * be a session with a different user ID than the one searched, - * typically when a system or admin session is used. - * @param username the username of the user - */ - public static Node getUserHome(Session session, String username) { -// try { -// QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory(); -// Selector sel = qomf.selector(NodeTypes.NODE_USER_HOME, "sel"); -// DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_UID); -// StaticOperand sop = qomf.literal(session.getValueFactory().createValue(username)); -// Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop); -// Query query = qomf.createQuery(sel, constraint, null, null); -// return querySingleNode(query); -// } catch (RepositoryException e) { -// throw new RuntimeException("Cannot find home for user " + username, e); -// } - - try { - checkUserWorkspace(session, username); - String homePath = getHomePath(username); - if (session.itemExists(homePath)) - return session.getNode(homePath); - // legacy - homePath = "/home/" + username; - if (session.itemExists(homePath)) - return session.getNode(homePath); - return null; - } catch (RepositoryException e) { - throw new RuntimeException("Cannot find home for user " + username, e); - } - } - - private static String getHomePath(String username) { - LdapName dn; - try { - dn = new LdapName(username); - } catch (InvalidNameException e) { - throw new IllegalArgumentException("Invalid name " + username, e); - } - String userId = dn.getRdn(dn.size() - 1).getValue().toString(); - return '/' + userId; - } - - private static void checkUserWorkspace(Session session, String username) { - String workspaceName = session.getWorkspace().getName(); - if (!NodeConstants.HOME.equals(workspaceName)) - throw new IllegalArgumentException(workspaceName + " is not the home workspace for user " + username); - } - - /** - * Returns the home node of the user or null if none was found. - * - * @param session the session to use in order to perform the search, this can - * be a session with a different user ID than the one searched, - * typically when a system or admin session is used. - * @param groupname the name of the group - */ - public static Node getGroupHome(Session session, String groupname) { -// try { -// QueryObjectModelFactory qomf = session.getWorkspace().getQueryManager().getQOMFactory(); -// Selector sel = qomf.selector(NodeTypes.NODE_GROUP_HOME, "sel"); -// DynamicOperand dop = qomf.propertyValue(sel.getSelectorName(), NodeNames.LDAP_CN); -// StaticOperand sop = qomf.literal(session.getValueFactory().createValue(cn)); -// Constraint constraint = qomf.comparison(dop, QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, sop); -// Query query = qomf.createQuery(sel, constraint, null, null); -// return querySingleNode(query); -// } catch (RepositoryException e) { -// throw new RuntimeException("Cannot find home for group " + cn, e); -// } - - try { - checkGroupWorkspace(session, groupname); - String homePath = getGroupPath(groupname); - if (session.itemExists(homePath)) - return session.getNode(homePath); - // legacy - homePath = "/groups/" + groupname; - if (session.itemExists(homePath)) - return session.getNode(homePath); - return null; - } catch (RepositoryException e) { - throw new RuntimeException("Cannot find home for group " + groupname, e); - } - - } - - private static String getGroupPath(String groupname) { - String cn; - try { - LdapName dn = new LdapName(groupname); - cn = dn.getRdn(dn.size() - 1).getValue().toString(); - } catch (InvalidNameException e) { - cn = groupname; - } - return '/' + cn; - } - - private static void checkGroupWorkspace(Session session, String groupname) { - String workspaceName = session.getWorkspace().getName(); - if (!NodeConstants.SRV.equals(workspaceName)) - throw new IllegalArgumentException(workspaceName + " is not the group workspace for group " + groupname); - } - - /** - * Queries one single node. - * - * @return one single node or null if none was found - * @throws ArgeoJcrException if more than one node was found - */ - private static Node querySingleNode(Query query) { - NodeIterator nodeIterator; - try { - QueryResult queryResult = query.execute(); - nodeIterator = queryResult.getNodes(); - } catch (RepositoryException e) { - throw new RuntimeException("Cannot execute query " + query, e); - } - Node node; - if (nodeIterator.hasNext()) - node = nodeIterator.nextNode(); - else - return null; - - if (nodeIterator.hasNext()) - throw new RuntimeException("Query returned more than one node."); - return node; - } - - /** Returns the home node of the session user or null if none was found. */ - public static Node getUserHome(Session session) { - String userID = session.getUserID(); - return getUserHome(session, userID); - } - - /** - * Translate the path to this node into a path containing the name of the - * repository and the name of the workspace. - */ - public static String getDataPath(String cn, Node node) throws RepositoryException { - assert node != null; - StringBuilder buf = new StringBuilder(NodeConstants.PATH_DATA); - return buf.append('/').append(cn).append('/').append(node.getSession().getWorkspace().getName()) - .append(node.getPath()).toString(); - } - - /** - * Open a JCR session with full read/write rights on the data, as - * {@link NodeConstants#ROLE_USER_ADMIN}, using the - * {@link NodeConstants#LOGIN_CONTEXT_DATA_ADMIN} login context. For security - * hardened deployement, use {@link AuthPermission} on this login context. - */ - public static Session openDataAdminSession(Repository repository, String workspaceName) { - ClassLoader currentCl = Thread.currentThread().getContextClassLoader(); - LoginContext loginContext; - try { - loginContext = new LoginContext(NodeConstants.LOGIN_CONTEXT_DATA_ADMIN); - loginContext.login(); - } catch (LoginException e1) { - throw new RuntimeException("Could not login as data admin", e1); - } finally { - Thread.currentThread().setContextClassLoader(currentCl); - } - return Subject.doAs(loginContext.getSubject(), new PrivilegedAction() { - - @Override - public Session run() { - try { - return repository.login(workspaceName); - } catch (NoSuchWorkspaceException e) { - throw new IllegalArgumentException("No workspace " + workspaceName + " available", e); - } catch (RepositoryException e) { - throw new RuntimeException("Cannot open data admin session", e); - } - } - - }); - } - - /** Singleton. */ - private NodeUtils() { - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/ldap.cnd b/org.argeo.node.api/src/org/argeo/node/ldap.cnd deleted file mode 100644 index a2306c60e..000000000 --- a/org.argeo.node.api/src/org/argeo/node/ldap.cnd +++ /dev/null @@ -1 +0,0 @@ - diff --git a/org.argeo.node.api/src/org/argeo/node/node.cnd b/org.argeo.node.api/src/org/argeo/node/node.cnd deleted file mode 100644 index d8a26b64e..000000000 --- a/org.argeo.node.api/src/org/argeo/node/node.cnd +++ /dev/null @@ -1,9 +0,0 @@ - - -[node:userHome] -mixin -- ldap:uid (STRING) m - -[node:groupHome] -mixin -- ldap:cn (STRING) m diff --git a/org.argeo.node.api/src/org/argeo/node/package-info.java b/org.argeo.node.api/src/org/argeo/node/package-info.java deleted file mode 100644 index fda3baec5..000000000 --- a/org.argeo.node.api/src/org/argeo/node/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Abstractions or constants related to an Argeo Node, an active repository of - * linked data. - */ -package org.argeo.node; \ No newline at end of file diff --git a/org.argeo.node.api/src/org/argeo/node/security/AnonymousPrincipal.java b/org.argeo.node.api/src/org/argeo/node/security/AnonymousPrincipal.java deleted file mode 100644 index 141f9d1f4..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/AnonymousPrincipal.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.node.security; - -import java.security.Principal; - -import javax.naming.ldap.LdapName; - -import org.argeo.node.NodeConstants; - -/** Marker for anonymous users. */ -public final class AnonymousPrincipal implements Principal { - private final String name = NodeConstants.ROLE_ANONYMOUS; - - @Override - public String getName() { - return name; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - - @Override - public String toString() { - return name.toString(); - } - - public LdapName getLdapName(){ - return NodeSecurityUtils.ROLE_ANONYMOUS_NAME; - } -} diff --git a/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java b/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java deleted file mode 100644 index dd3402277..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.security; - -/** - * Marker interface for an advanced keyring based on cryptography. - */ -public interface CryptoKeyring extends Keyring { - public void changePassword(char[] oldPassword, char[] newPassword); - - public void unlock(char[] password); -} diff --git a/org.argeo.node.api/src/org/argeo/node/security/DataAdminPrincipal.java b/org.argeo.node.api/src/org/argeo/node/security/DataAdminPrincipal.java deleted file mode 100644 index 53d2cedc6..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/DataAdminPrincipal.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.argeo.node.security; - -import java.security.Principal; - -import org.argeo.node.NodeConstants; - -/** Allows to modify any data. */ -public final class DataAdminPrincipal implements Principal { - private final String name = NodeConstants.ROLE_DATA_ADMIN; - - @Override - public String getName() { - return name; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof DataAdminPrincipal; - } - - @Override - public String toString() { - return name.toString(); - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/security/Keyring.java b/org.argeo.node.api/src/org/argeo/node/security/Keyring.java deleted file mode 100644 index fe054c3cc..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/Keyring.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.security; - -import java.io.InputStream; - -/** - * Access to private (typically encrypted) data. The keyring is responsible for - * retrieving the necessary credentials. Experimental. This API may - * change. - */ -public interface Keyring { - /** - * Returns the confidential information as chars. Must ask for it if it is - * not stored. - */ - public char[] getAsChars(String path); - - /** - * Returns the confidential information as a stream. Must ask for it if it - * is not stored. - */ - public InputStream getAsStream(String path); - - public void set(String path, char[] arr); - - public void set(String path, InputStream in); -} diff --git a/org.argeo.node.api/src/org/argeo/node/security/NodeSecurityUtils.java b/org.argeo.node.api/src/org/argeo/node/security/NodeSecurityUtils.java deleted file mode 100644 index 7c784b0dc..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/NodeSecurityUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.argeo.node.security; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.naming.InvalidNameException; -import javax.naming.ldap.LdapName; - -import org.argeo.node.NodeConstants; - -public class NodeSecurityUtils { - public final static LdapName ROLE_ADMIN_NAME, ROLE_DATA_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME, - ROLE_USER_ADMIN_NAME; - public final static List RESERVED_ROLES; - static { - try { - ROLE_ADMIN_NAME = new LdapName(NodeConstants.ROLE_ADMIN); - ROLE_DATA_ADMIN_NAME = new LdapName(NodeConstants.ROLE_DATA_ADMIN); - ROLE_USER_NAME = new LdapName(NodeConstants.ROLE_USER); - ROLE_USER_ADMIN_NAME = new LdapName(NodeConstants.ROLE_USER_ADMIN); - ROLE_ANONYMOUS_NAME = new LdapName(NodeConstants.ROLE_ANONYMOUS); - RESERVED_ROLES = Collections.unmodifiableList(Arrays.asList( - new LdapName[] { ROLE_ADMIN_NAME, ROLE_ANONYMOUS_NAME, ROLE_USER_NAME, ROLE_USER_ADMIN_NAME })); - } catch (InvalidNameException e) { - throw new Error("Cannot initialize login module class", e); - } - } - - public static void checkUserName(LdapName name) throws IllegalArgumentException { - if (RESERVED_ROLES.contains(name)) - throw new IllegalArgumentException(name + " is a reserved name"); - } - - public static void checkImpliedPrincipalName(LdapName roleName) throws IllegalArgumentException { -// if (ROLE_USER_NAME.equals(roleName) || ROLE_ANONYMOUS_NAME.equals(roleName)) -// throw new IllegalArgumentException(roleName + " cannot be listed as role"); - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/security/PBEKeySpecCallback.java b/org.argeo.node.api/src/org/argeo/node/security/PBEKeySpecCallback.java deleted file mode 100644 index f03ba9ddf..000000000 --- a/org.argeo.node.api/src/org/argeo/node/security/PBEKeySpecCallback.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.security; - -import javax.crypto.spec.PBEKeySpec; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.PasswordCallback; - -/** - * All information required to set up a {@link PBEKeySpec} bar the password - * itself (use a {@link PasswordCallback}) - */ -public class PBEKeySpecCallback implements Callback { - private String secretKeyFactory; - private byte[] salt; - private Integer iterationCount; - /** Can be null for some algorithms */ - private Integer keyLength; - /** Can be null, will trigger secret key encryption if not */ - private String secretKeyEncryption; - - private String encryptedPasswordHashCipher; - private byte[] encryptedPasswordHash; - - public void set(String secretKeyFactory, byte[] salt, - Integer iterationCount, Integer keyLength, - String secretKeyEncryption) { - this.secretKeyFactory = secretKeyFactory; - this.salt = salt; - this.iterationCount = iterationCount; - this.keyLength = keyLength; - this.secretKeyEncryption = secretKeyEncryption; -// this.encryptedPasswordHashCipher = encryptedPasswordHashCipher; -// this.encryptedPasswordHash = encryptedPasswordHash; - } - - public String getSecretKeyFactory() { - return secretKeyFactory; - } - - public byte[] getSalt() { - return salt; - } - - public Integer getIterationCount() { - return iterationCount; - } - - public Integer getKeyLength() { - return keyLength; - } - - public String getSecretKeyEncryption() { - return secretKeyEncryption; - } - - public String getEncryptedPasswordHashCipher() { - return encryptedPasswordHashCipher; - } - - public byte[] getEncryptedPasswordHash() { - return encryptedPasswordHash; - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/ArrayTabularRow.java b/org.argeo.node.api/src/org/argeo/node/tabular/ArrayTabularRow.java deleted file mode 100644 index 97bf025dc..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/ArrayTabularRow.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - -import java.util.List; - -/** Minimal tabular row wrapping an {@link Object} array */ -public class ArrayTabularRow implements TabularRow { - private final Object[] arr; - - public ArrayTabularRow(List objs) { - this.arr = objs.toArray(); - } - - public Object get(Integer col) { - return arr[col]; - } - - public int size() { - return arr.length; - } - - public Object[] toArray() { - return arr; - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/TabularColumn.java b/org.argeo.node.api/src/org/argeo/node/tabular/TabularColumn.java deleted file mode 100644 index 5cd11d198..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/TabularColumn.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - -/** The column in a tabular content */ -public class TabularColumn { - private String name; - /** - * JCR types, see - * http://www.day.com/maven/javax.jcr/javadocs/jcr-2.0/index.html - * ?javax/jcr/PropertyType.html - */ - private Integer type; - - /** column with default type */ - public TabularColumn(String name) { - super(); - this.name = name; - } - - public TabularColumn(String name, Integer type) { - super(); - this.name = name; - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getType() { - return type; - } - - public void setType(Integer type) { - this.type = type; - } - -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/TabularContent.java b/org.argeo.node.api/src/org/argeo/node/tabular/TabularContent.java deleted file mode 100644 index a5aa9f678..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/TabularContent.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - -import java.util.List; - -/** - * Content organized as a table, possibly with headers. Only JCR types are - * supported even though there is not direct dependency on JCR. - */ -public interface TabularContent { - /** The headers of this table or null is none available. */ - public List getColumns(); - - public TabularRowIterator read(); -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/TabularRow.java b/org.argeo.node.api/src/org/argeo/node/tabular/TabularRow.java deleted file mode 100644 index f652df947..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/TabularRow.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - -/** A row of tabular data */ -public interface TabularRow { - /** The value at this column index */ - public Object get(Integer col); - - /** The raw objects (direct references) */ - public Object[] toArray(); - - /** Number of columns */ - public int size(); -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/TabularRowIterator.java b/org.argeo.node.api/src/org/argeo/node/tabular/TabularRowIterator.java deleted file mode 100644 index 98a04a65c..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/TabularRowIterator.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - -import java.util.Iterator; - -/** Navigation of rows */ -public interface TabularRowIterator extends Iterator { - /** - * Current row number, has to be incremented by each call to next() ; starts at 0, will - * therefore be 1 for the first row returned. - */ - public Long getCurrentRowNumber(); -} diff --git a/org.argeo.node.api/src/org/argeo/node/tabular/TabularWriter.java b/org.argeo.node.api/src/org/argeo/node/tabular/TabularWriter.java deleted file mode 100644 index b7febee65..000000000 --- a/org.argeo.node.api/src/org/argeo/node/tabular/TabularWriter.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.argeo.node.tabular; - - -/** Write to a tabular content */ -public interface TabularWriter { - /** Append a new row of data */ - public void appendRow(Object[] row); - - /** Finish persisting data and release resources */ - public void close(); -} diff --git a/org.argeo.util/src/org/argeo/util/Tester.java b/org.argeo.util/src/org/argeo/util/Tester.java new file mode 100644 index 000000000..31a2be4ec --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/Tester.java @@ -0,0 +1,126 @@ +package org.argeo.util; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** A generic tester based on Java assertions and functional programming. */ +public class Tester { + private Map results = Collections.synchronizedSortedMap(new TreeMap<>()); + + private ClassLoader classLoader; + + /** Use {@link Thread#getContextClassLoader()} by default. */ + public Tester() { + this(Thread.currentThread().getContextClassLoader()); + } + + public Tester(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public void execute(String className) { + Class clss; + try { + clss = classLoader.loadClass(className); + boolean assertionsEnabled = clss.desiredAssertionStatus(); + if (!assertionsEnabled) + throw new IllegalStateException("Test runner " + getClass().getName() + + " requires Java assertions to be enabled. Call the JVM with the -ea argument."); + } catch (Exception e1) { + throw new IllegalArgumentException("Cannot initalise test for " + className, e1); + + } + List methods = findMethods(clss); + if (methods.size() == 0) + throw new IllegalArgumentException("No test method found in " + clss); + // TODO make order more predictable? + for (Method method : methods) { + String uid = method.getDeclaringClass().getName() + "#" + method.getName(); + TesterStatus testStatus = new TesterStatus(uid); + Object obj = null; + try { + beforeTest(uid, method); + obj = clss.getDeclaredConstructor().newInstance(); + method.invoke(obj); + testStatus.setPassed(); + afterTestPassed(uid, method, obj); + } catch (Exception e) { + testStatus.setFailed(e); + afterTestFailed(uid, method, obj, e); + } finally { + results.put(uid, testStatus); + } + } + } + + protected void beforeTest(String uid, Method method) { + // System.out.println(uid + ": STARTING"); + } + + protected void afterTestPassed(String uid, Method method, Object obj) { + System.out.println(uid + ": PASSED"); + } + + protected void afterTestFailed(String uid, Method method, Object obj, Throwable e) { + System.out.println(uid + ": FAILED"); + e.printStackTrace(); + } + + protected List findMethods(Class clss) { + List methods = new ArrayList(); +// Method call = getMethod(clss, "call"); +// if (call != null) +// methods.add(call); +// + for (Method method : clss.getMethods()) { + if (method.getName().startsWith("test")) { + methods.add(method); + } + } + return methods; + } + + protected Method getMethod(Class clss, String name, Class... parameterTypes) { + try { + return clss.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + return null; + } catch (SecurityException e) { + throw new IllegalStateException(e); + } + } + + public static void main(String[] args) { + // deal with arguments + String className; + if (args.length < 1) { + System.err.println(usage()); + System.exit(1); + throw new IllegalArgumentException(); + } else { + className = args[0]; + } + + Tester test = new Tester(); + try { + test.execute(className); + } catch (Throwable e) { + e.printStackTrace(); + } + + Map r = test.results; + for (String uid : r.keySet()) { + TesterStatus testStatus = r.get(uid); + System.out.println(testStatus); + } + } + + public static String usage() { + return "java " + Tester.class.getName() + " [test class name]"; + + } +} diff --git a/org.argeo.util/src/org/argeo/util/TesterStatus.java b/org.argeo.util/src/org/argeo/util/TesterStatus.java new file mode 100644 index 000000000..d1d14ed06 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/TesterStatus.java @@ -0,0 +1,98 @@ +package org.argeo.util; + +import java.io.Serializable; + +/** The status of a test. */ +public class TesterStatus implements Serializable { + private static final long serialVersionUID = 6272975746885487000L; + + private Boolean passed = null; + private final String uid; + private Throwable throwable = null; + + public TesterStatus(String uid) { + this.uid = uid; + } + + /** For cloning. */ + public TesterStatus(String uid, Boolean passed, Throwable throwable) { + this(uid); + this.passed = passed; + this.throwable = throwable; + } + + public synchronized Boolean isRunning() { + return passed == null; + } + + public synchronized Boolean isPassed() { + assert passed != null; + return passed; + } + + public synchronized Boolean isFailed() { + assert passed != null; + return !passed; + } + + public synchronized void setPassed() { + setStatus(true); + } + + public synchronized void setFailed() { + setStatus(false); + } + + public synchronized void setFailed(Throwable throwable) { + setStatus(false); + setThrowable(throwable); + } + + protected void setStatus(Boolean passed) { + if (this.passed != null) + throw new IllegalStateException("Passed status of test " + uid + " is already set (to " + passed + ")"); + this.passed = passed; + } + + protected void setThrowable(Throwable throwable) { + if (this.throwable != null) + throw new IllegalStateException("Throwable of test " + uid + " is already set (to " + passed + ")"); + this.throwable = throwable; + } + + public String getUid() { + return uid; + } + + public Throwable getThrowable() { + return throwable; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + // TODO Auto-generated method stub + return super.clone(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof TesterStatus) { + TesterStatus other = (TesterStatus) o; + // we don't check consistency for performance purposes + // this equals() is supposed to be used in collections or for transfer + return other.uid.equals(uid); + } + return false; + } + + @Override + public int hashCode() { + return uid.hashCode(); + } + + @Override + public String toString() { + return uid + "\t" + (passed ? "passed" : "failed"); + } + +} diff --git a/org.argeo.util/src/org/argeo/util/test/Tester.java b/org.argeo.util/src/org/argeo/util/test/Tester.java deleted file mode 100644 index 36e27de72..000000000 --- a/org.argeo.util/src/org/argeo/util/test/Tester.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.argeo.util.test; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** A generic tester based on Java assertions and functional programming. */ -public class Tester { - private Map results = Collections.synchronizedSortedMap(new TreeMap<>()); - - private ClassLoader classLoader; - - /** Use {@link Thread#getContextClassLoader()} by default. */ - public Tester() { - this(Thread.currentThread().getContextClassLoader()); - } - - public Tester(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public void execute(String className) { - Class clss; - try { - clss = classLoader.loadClass(className); - boolean assertionsEnabled = clss.desiredAssertionStatus(); - if (!assertionsEnabled) - throw new IllegalStateException("Test runner " + getClass().getName() - + " requires Java assertions to be enabled. Call the JVM with the -ea argument."); - } catch (Exception e1) { - throw new IllegalArgumentException("Cannot initalise test for " + className, e1); - - } - List methods = findMethods(clss); - if (methods.size() == 0) - throw new IllegalArgumentException("No test method found in " + clss); - // TODO make order more predictable? - for (Method method : methods) { - String uid = method.getDeclaringClass().getName() + "#" + method.getName(); - TesterStatus testStatus = new TesterStatus(uid); - Object obj = null; - try { - beforeTest(uid, method); - obj = clss.getDeclaredConstructor().newInstance(); - method.invoke(obj); - testStatus.setPassed(); - afterTestPassed(uid, method, obj); - } catch (Exception e) { - testStatus.setFailed(e); - afterTestFailed(uid, method, obj, e); - } finally { - results.put(uid, testStatus); - } - } - } - - protected void beforeTest(String uid, Method method) { - // System.out.println(uid + ": STARTING"); - } - - protected void afterTestPassed(String uid, Method method, Object obj) { - System.out.println(uid + ": PASSED"); - } - - protected void afterTestFailed(String uid, Method method, Object obj, Throwable e) { - System.out.println(uid + ": FAILED"); - e.printStackTrace(); - } - - protected List findMethods(Class clss) { - List methods = new ArrayList(); -// Method call = getMethod(clss, "call"); -// if (call != null) -// methods.add(call); -// - for (Method method : clss.getMethods()) { - if (method.getName().startsWith("test")) { - methods.add(method); - } - } - return methods; - } - - protected Method getMethod(Class clss, String name, Class... parameterTypes) { - try { - return clss.getMethod(name, parameterTypes); - } catch (NoSuchMethodException e) { - return null; - } catch (SecurityException e) { - throw new IllegalStateException(e); - } - } - - public static void main(String[] args) { - // deal with arguments - String className; - if (args.length < 1) { - System.err.println(usage()); - System.exit(1); - throw new IllegalArgumentException(); - } else { - className = args[0]; - } - - Tester test = new Tester(); - try { - test.execute(className); - } catch (Throwable e) { - e.printStackTrace(); - } - - Map r = test.results; - for (String uid : r.keySet()) { - TesterStatus testStatus = r.get(uid); - System.out.println(testStatus); - } - } - - public static String usage() { - return "java " + Tester.class.getName() + " [test class name]"; - - } -} diff --git a/org.argeo.util/src/org/argeo/util/test/TesterStatus.java b/org.argeo.util/src/org/argeo/util/test/TesterStatus.java deleted file mode 100644 index d533e9457..000000000 --- a/org.argeo.util/src/org/argeo/util/test/TesterStatus.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.util.test; - -import java.io.Serializable; - -/** The status of a test. */ -public class TesterStatus implements Serializable { - private static final long serialVersionUID = 6272975746885487000L; - - private Boolean passed = null; - private final String uid; - private Throwable throwable = null; - - public TesterStatus(String uid) { - this.uid = uid; - } - - /** For cloning. */ - public TesterStatus(String uid, Boolean passed, Throwable throwable) { - this(uid); - this.passed = passed; - this.throwable = throwable; - } - - public synchronized Boolean isRunning() { - return passed == null; - } - - public synchronized Boolean isPassed() { - assert passed != null; - return passed; - } - - public synchronized Boolean isFailed() { - assert passed != null; - return !passed; - } - - public synchronized void setPassed() { - setStatus(true); - } - - public synchronized void setFailed() { - setStatus(false); - } - - public synchronized void setFailed(Throwable throwable) { - setStatus(false); - setThrowable(throwable); - } - - protected void setStatus(Boolean passed) { - if (this.passed != null) - throw new IllegalStateException("Passed status of test " + uid + " is already set (to " + passed + ")"); - this.passed = passed; - } - - protected void setThrowable(Throwable throwable) { - if (this.throwable != null) - throw new IllegalStateException("Throwable of test " + uid + " is already set (to " + passed + ")"); - this.throwable = throwable; - } - - public String getUid() { - return uid; - } - - public Throwable getThrowable() { - return throwable; - } - - @Override - protected Object clone() throws CloneNotSupportedException { - // TODO Auto-generated method stub - return super.clone(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof TesterStatus) { - TesterStatus other = (TesterStatus) o; - // we don't check consistency for performance purposes - // this equals() is supposed to be used in collections or for transfer - return other.uid.equals(uid); - } - return false; - } - - @Override - public int hashCode() { - return uid.hashCode(); - } - - @Override - public String toString() { - return uid + "\t" + (passed ? "passed" : "failed"); - } - -} diff --git a/pom.xml b/pom.xml index 713a3916b..ed1b7d167 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ org.argeo.eclipse.ui org.argeo.eclipse.ui.rap - org.argeo.node.api + org.argeo.api org.argeo.maintenance org.argeo.cms org.argeo.cms.ui.theme @@ -195,7 +195,7 @@ limitations under the License. false -Xdoclint:none - *.internal.*,org.eclipse.* + *.internal.*,org.eclipse.*,org.argeo.cms.ui.eclipse.forms.* UTF-8 true