Rename packages in order to make future stable documentation clearer.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 12 Mar 2020 08:48:40 +0000 (09:48 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 12 Mar 2020 08:48:40 +0000 (09:48 +0100)
334 files changed:
dep/org.argeo.dep.cms.node/pom.xml
org.argeo.api/.classpath [new file with mode: 0644]
org.argeo.api/.gitignore [new file with mode: 0644]
org.argeo.api/.project [new file with mode: 0644]
org.argeo.api/META-INF/.gitignore [new file with mode: 0644]
org.argeo.api/bnd.bnd [new file with mode: 0644]
org.argeo.api/build.properties [new file with mode: 0644]
org.argeo.api/pom.xml [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/ArgeoLogListener.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/ArgeoLogger.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/DataAdminLoginModule.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/DataModelNamespace.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/MvcProvider.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeConstants.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeDeployment.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeInstance.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeNames.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeOID.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeState.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeTypes.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/NodeUtils.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/ldap.cnd [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/node.cnd [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/package-info.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/AnonymousPrincipal.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/CryptoKeyring.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/DataAdminPrincipal.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/Keyring.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/NodeSecurityUtils.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/security/PBEKeySpecCallback.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/ArrayTabularRow.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/TabularColumn.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/TabularContent.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/TabularRow.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/TabularRowIterator.java [new file with mode: 0644]
org.argeo.api/src/org/argeo/api/tabular/TabularWriter.java [new file with mode: 0644]
org.argeo.cms.e4.rap/OSGI-INF/cms-demo-rap.xml [deleted file]
org.argeo.cms.e4.rap/bnd.bnd
org.argeo.cms.e4.rap/cms/app.js [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4DemoApp.java [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-removeButtons.js [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbar.js [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/CkEditor-toolbarGroups.json [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/HtmlEditor.java [deleted file]
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/parts/test.json [deleted file]
org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml
org.argeo.cms.e4/OSGI-INF/homeRepository.xml
org.argeo.cms.e4/bnd.bnd
org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/contexts/OsgiFilterContextFunction.java [deleted file]
org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java
org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java
org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java
org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java
org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java
org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/forms/MarkupValidatorCopy.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/AppUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/Branding.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptApp.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/CmsScriptRwtApplication.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/ScriptAppActivator.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/ScriptUi.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/Theme.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/script/cms.js [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/ui/AbstractCmsEntryPoint.java
org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java
org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java
org.argeo.cms.ui/src/org/argeo/cms/ui/img/Dummy.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java
org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java
org.argeo.cms.ui/src/org/argeo/cms/ui/script/AppUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/Branding.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptApp.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptAppActivator.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/ScriptUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/Theme.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/script/cms.js [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/PickUpUserDialog.java
org.argeo.cms.ui/src/org/argeo/cms/ui/useradmin/UserLP.java
org.argeo.cms.ui/src/org/argeo/cms/ui/util/BundleResourceLoader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsTheme.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/LoginEntryPoint.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleApp.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleErgonomics.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleUxContext.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/ThemeUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/EditablePart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/ScrolledPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/AbstractLoginDialog.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLogin.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CmsLoginShell.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/CompositeCallbackHandler.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DefaultLoginDialog.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/DynamicCallbackHandler.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/auth/LocaleChoice.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsPane.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsTheme.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/DefaultImageManager.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/LoginEntryPoint.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleImageManager.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/ThemeUtils.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/util/VerticalMenu.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/Img.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/TextStyles.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DynamicCallbackHandler.java [deleted file]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java [deleted file]
org.argeo.cms/ext/test/org/argeo/cms/tabular/JcrTabularTest.java
org.argeo.cms/pom.xml
org.argeo.cms/src/org/argeo/cms/auth/CmsAuthUtils.java
org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
org.argeo.cms/src/org/argeo/cms/auth/KeyringLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/SingleUserLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/UserAdminLoginModule.java
org.argeo.cms/src/org/argeo/cms/auth/UserAdminUtils.java
org.argeo.cms/src/org/argeo/cms/i18n/DefaultsResourceBundle.java [deleted file]
org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java [deleted file]
org.argeo.cms/src/org/argeo/cms/i18n/Localized.java [deleted file]
org.argeo.cms/src/org/argeo/cms/integration/CmsLoginServlet.java
org.argeo.cms/src/org/argeo/cms/integration/CmsLogoutServlet.java
org.argeo.cms/src/org/argeo/cms/integration/CmsPrivateServletContext.java
org.argeo.cms/src/org/argeo/cms/integration/CmsTokenServlet.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsSessionImpl.java
org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java
org.argeo.cms/src/org/argeo/cms/internal/http/DataHttpContext.java
org.argeo.cms/src/org/argeo/cms/internal/http/LinkServlet.java
org.argeo.cms/src/org/argeo/cms/internal/jcr/RepositoryBuilder.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsFsProvider.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsInstance.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/DeployConfig.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/EgoRepository.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/InitUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/JackrabbitLocalRepository.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/LocalRepository.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeRepositoryFactory.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/RepositoryServiceFactory.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/SecurityProfile.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas-ipa.cfg
org.argeo.cms/src/org/argeo/cms/internal/kernel/jaas.cfg
org.argeo.cms/src/org/argeo/cms/security/AbstractKeyring.java
org.argeo.cms/src/org/argeo/cms/security/JcrKeyring.java
org.argeo.cms/src/org/argeo/cms/tabular/CsvTabularWriter.java
org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularRowIterator.java
org.argeo.cms/src/org/argeo/cms/tabular/JcrTabularWriter.java
org.argeo.cms/src/org/argeo/cms/websocket/CmsWebSocketConfigurator.java
org.argeo.eclipse.ui.rap/bnd.bnd
org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFile.java
org.argeo.eclipse.ui.rap/src/org/argeo/eclipse/ui/specific/OpenFileService.java
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrFileProvider.java [deleted file]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/JcrItemsComparator.java [deleted file]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/NodeViewerComparer.java [deleted file]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/jcr/utils/SingleSessionFileProvider.java [deleted file]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/SingleSourcingConstants.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/util/ViewerUtils.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/SingleSourcingConstants.java [deleted file]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/utils/ViewerUtils.java [deleted file]
org.argeo.ext.equinox.jetty/bnd.bnd
org.argeo.ext.jackrabbit/bnd.bnd
org.argeo.ext.jackrabbit/pom.xml
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/ArgeoSecurityManager.java
org.argeo.ext.jackrabbit/src/org/argeo/security/jackrabbit/SystemJackrabbitLoginModule.java
org.argeo.maintenance/pom.xml
org.argeo.maintenance/src/org/argeo/maintenance/AbstractMaintenanceService.java
org.argeo.maintenance/src/org/argeo/maintenance/backup/LogicalBackup.java
org.argeo.node.api/.classpath [deleted file]
org.argeo.node.api/.gitignore [deleted file]
org.argeo.node.api/.project [deleted file]
org.argeo.node.api/META-INF/.gitignore [deleted file]
org.argeo.node.api/bnd.bnd [deleted file]
org.argeo.node.api/build.properties [deleted file]
org.argeo.node.api/pom.xml [deleted file]
org.argeo.node.api/src/org/argeo/node/ArgeoLogListener.java [deleted file]
org.argeo.node.api/src/org/argeo/node/ArgeoLogger.java [deleted file]
org.argeo.node.api/src/org/argeo/node/DataAdminLoginModule.java [deleted file]
org.argeo.node.api/src/org/argeo/node/DataModelNamespace.java [deleted file]
org.argeo.node.api/src/org/argeo/node/MvcProvider.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeConstants.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeDeployment.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeInstance.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeNames.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeOID.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeState.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeTypes.java [deleted file]
org.argeo.node.api/src/org/argeo/node/NodeUtils.java [deleted file]
org.argeo.node.api/src/org/argeo/node/ldap.cnd [deleted file]
org.argeo.node.api/src/org/argeo/node/node.cnd [deleted file]
org.argeo.node.api/src/org/argeo/node/package-info.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/AnonymousPrincipal.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/CryptoKeyring.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/DataAdminPrincipal.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/Keyring.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/NodeSecurityUtils.java [deleted file]
org.argeo.node.api/src/org/argeo/node/security/PBEKeySpecCallback.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/ArrayTabularRow.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/TabularColumn.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/TabularContent.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/TabularRow.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/TabularRowIterator.java [deleted file]
org.argeo.node.api/src/org/argeo/node/tabular/TabularWriter.java [deleted file]
org.argeo.util/src/org/argeo/util/Tester.java [new file with mode: 0644]
org.argeo.util/src/org/argeo/util/TesterStatus.java [new file with mode: 0644]
org.argeo.util/src/org/argeo/util/test/Tester.java [deleted file]
org.argeo.util/src/org/argeo/util/test/TesterStatus.java [deleted file]
pom.xml

index 7d6eb4310599c15773bae137d5746e8197ce618b..b202c69fe657f5194216b2a0d2afe60fdc88f330 100644 (file)
@@ -22,7 +22,7 @@
                <!-- Argeo Commons -->
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.node.api</artifactId>
+                       <artifactId>org.argeo.api</artifactId>
                        <version>2.1.88-SNAPSHOT</version>
                </dependency>
                <dependency>
diff --git a/org.argeo.api/.classpath b/org.argeo.api/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.argeo.api/.gitignore b/org.argeo.api/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/org.argeo.api/.project b/org.argeo.api/.project
new file mode 100644 (file)
index 0000000..a396aad
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.api</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.api/META-INF/.gitignore b/org.argeo.api/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/org.argeo.api/bnd.bnd b/org.argeo.api/bnd.bnd
new file mode 100644 (file)
index 0000000..f0bd0ce
--- /dev/null
@@ -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 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -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 (file)
index 0000000..74987c7
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons</groupId>
+               <artifactId>argeo-commons</artifactId>
+               <version>2.1.88-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.api</artifactId>
+       <name>Argeo Node API</name>
+       <packaging>jar</packaging>
+       <dependencies>
+       </dependencies>
+</project>
\ 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 (file)
index 0000000..925c9b0
--- /dev/null
@@ -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 (file)
index 0000000..eed02ac
--- /dev/null
@@ -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 (file)
index 0000000..295196a
--- /dev/null
@@ -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<String, ?> sharedState,
+                       Map<String, ?> 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 (file)
index 0000000..421b11a
--- /dev/null
@@ -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 (file)
index 0000000..9c5c4a0
--- /dev/null
@@ -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<V, M, W> extends BiFunction<V, M, W> {
+       /**
+        * 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 (file)
index 0000000..d2dfb83
--- /dev/null
@@ -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 (file)
index 0000000..224d244
--- /dev/null
@@ -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 (file)
index 0000000..167ba81
--- /dev/null
@@ -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 (file)
index 0000000..e44f08b
--- /dev/null
@@ -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 (file)
index 0000000..ade1163
--- /dev/null
@@ -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 (file)
index 0000000..30de095
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.api;
+
+import java.util.List;
+import java.util.Locale;
+
+public interface NodeState {
+       Locale getDefaultLocale();
+
+       List<Locale> 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 (file)
index 0000000..b408664
--- /dev/null
@@ -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 (file)
index 0000000..f64cbe6
--- /dev/null
@@ -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<String, String> parameters = new HashMap<String, String>();
+                       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<String, String> parameters = new HashMap<String, String>();
+                       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<Session>() {
+
+                       @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 (file)
index 0000000..a2306c6
--- /dev/null
@@ -0,0 +1 @@
+<ldap = 'http://www.argeo.org/ns/ldap'>
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 (file)
index 0000000..d8a26b6
--- /dev/null
@@ -0,0 +1,9 @@
+<node = 'http://www.argeo.org/ns/node'>
+
+[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 (file)
index 0000000..1ea483a
--- /dev/null
@@ -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 (file)
index 0000000..d07b055
--- /dev/null
@@ -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 (file)
index 0000000..a6f37a4
--- /dev/null
@@ -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 (file)
index 0000000..7581d8d
--- /dev/null
@@ -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 (file)
index 0000000..c9228f0
--- /dev/null
@@ -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. <b>Experimental. This API may
+ * change.</b>
+ */
+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 (file)
index 0000000..2458512
--- /dev/null
@@ -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<LdapName> 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 (file)
index 0000000..ba441e2
--- /dev/null
@@ -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 (file)
index 0000000..c9f7710
--- /dev/null
@@ -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 (file)
index 0000000..b38c346
--- /dev/null
@@ -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 (file)
index 0000000..0ffef21
--- /dev/null
@@ -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 <code>null</code> is none available. */
+       public List<TabularColumn> 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 (file)
index 0000000..5437015
--- /dev/null
@@ -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 (file)
index 0000000..d48b37b
--- /dev/null
@@ -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<TabularRow> {
+       /**
+        * 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 (file)
index 0000000..1a02947
--- /dev/null
@@ -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 (file)
index e23b11b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="optional" name="CMS Demo RAP">
-   <implementation class="org.argeo.cms.e4.rap.CmsE4DemoApp"/>
-   <service>
-      <provide interface="org.eclipse.rap.rwt.application.ApplicationConfiguration"/>
-      <property name="contextName" type="String" value="demoXXX"/>
-   </service>
-</scr:component>
index da09b3f8bc076dc6851d9111c0bbe54cc990fac9..90dc8d42b6ec790d4a1fd5fbe8960c9e1be876c3 100644 (file)
@@ -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 (file)
index cd5e099..0000000
+++ /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 (file)
index 2987e4d..0000000
+++ /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");
-       }
-
-}
index a062bea6bd013d062d0ea598907a76168a4e84f4..f7a1ed667c8aee878928f453d6ffe5da4fd84024 100644 (file)
@@ -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 (file)
index 20e82e3..0000000
+++ /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 (file)
index 3058655..0000000
+++ /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 (file)
index a886c27..0000000
+++ /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 (file)
index 68963f9..0000000
+++ /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("<a href='http://googl.com'>Google</a>");
-               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 (file)
index eed3f0e..0000000
+++ /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
-}
index 8653f09dfe22e496c8aae8e9c50dab440e559ca5..3fd7fdafb16fa4e1356d1de50dc058eb3bc00c1b 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default CallbackHandler">
-   <implementation class="org.argeo.cms.widgets.auth.DynamicCallbackHandler"/>
+   <implementation class="org.argeo.cms.ui.widgets.auth.DynamicCallbackHandler"/>
    <service>
       <provide interface="javax.security.auth.callback.CallbackHandler"/>
    </service>
index c03e62e76a712110d33e09b1477cba29a668e38a..65690f262a411ff41cdd9f76e2fbfde316bcf3c7 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="Home Repository">
-   <implementation class="org.argeo.cms.e4.contexts.OsgiFilterContextFunction"/>
+   <implementation class="org.argeo.cms.e4.OsgiFilterContextFunction"/>
    <property name="service.context.key" type="String" value="(cn=home)"/>
    <service>
       <provide interface="org.eclipse.e4.core.contexts.IContextFunction"/>
index 5e64fedef2f9d8073c4ab17b6b732a8f8c8e69a2..8714dab794e92e1e0a5fd39a2b481005ba601bd7 100644 (file)
@@ -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 (file)
index 0000000..c42a02a
--- /dev/null
@@ -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 (file)
index b0fdcc1..0000000
+++ /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]);
-               }
-       }
-
-}
index 59d7ed401a5e926b536eedbbc86ddfe347a8414a..21ea955b4ace673162cd054587a235db041ca2d4 100644 (file)
@@ -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;
 
index 378409399eae05c5a384e5fe1d73eb2dbfabba0f..51528abd49fe986defea80c02746ee243029a0a4 100644 (file)
@@ -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;
index 3e328daa3ee874045cf50015c8d1307565ec0f8f..89a1bf5d1cb42ace23746e8c63de3956a7846bf7 100644 (file)
@@ -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());
index 89c44ca836f0f41d4cd05b5749ffa5c016898dce..aefa95dbf16a3f7bbea16f1482baa94ac5938c23 100644 (file)
@@ -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 (file)
index 0000000..c831831
--- /dev/null
@@ -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> T getService(Class<? extends T> clazz) {
+               return bc.getService(bc.getServiceReference(clazz));
+       }
+
+       protected <T> Collection<ServiceReference<T>> getServiceReferences(Class<T> 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 (file)
index 0000000..2212b7b
--- /dev/null
@@ -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<String, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<String, Browse.FilterEntitiesVirtualTable>();
+       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("<b>" + context.getName() + "</b>");
+               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 = "<binary>";
+                       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 (file)
index 0000000..7e8a991
--- /dev/null
@@ -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("<span style='font-variant: small-caps;'>Provided Servers</span><br/>");
+
+               ServiceReference<HttpService> 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("<b>http</b> ").append(httpPort).append("<br/>");
+                       if (httpsPort != null)
+                               text.append("<b>https</b> ").append(httpsPort).append("<br/>");
+
+               }
+
+               text.append("<br/>");
+               text.append("<span style='font-variant: small-caps;'>Referenced Servers</span><br/>");
+
+               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 (file)
index 0000000..a750e95
--- /dev/null
@@ -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<ServiceReference<RepositoryContext>> contexts = getServiceReferences(RepositoryContext.class,
+                               "(" + NodeConstants.CN + "=*)");
+               StringBuffer text = new StringBuffer();
+               text.append("<span style='font-variant: small-caps;'>Jackrabbit Repositories</span><br/>");
+               for (ServiceReference<RepositoryContext> 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("<b>" + alias + "</b><br/>");
+                       text.append("rootNodeId: " + rootNodeId + "<br/>");
+                       try {
+                               FileStore fileStore = Files.getFileStore(repoHomePath);
+                               text.append("partition: " + fileStore.toString() + "<br/>");
+                               text.append(
+                                               percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)<br/>");
+                       } 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("<span style=''>" + text.toString() + "</span>");
+       }
+
+       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 = "<span style='color:green;font-weight:bold'>";
+               else if (percent < 95)
+                       span = "<span style='color:orange;font-weight:bold'>";
+               else
+                       span = "<span style='color:red;font-weight:bold'>";
+               return span + percent + "%</span>";
+       }
+
+       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 (file)
index 0000000..a746b3c
--- /dev/null
@@ -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<NodeState> nodeStateRef = bc.getServiceReference(NodeState.class);
+               if (nodeStateRef == null)
+                       throw new IllegalStateException("No CMS state available");
+               NodeState nodeState = bc.getService(nodeStateRef);
+               ServiceReference<NodeDeployment> nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class);
+               Label label = new Label(composite, SWT.WRAP);
+               CmsUiUtils.markup(label);
+               if (nodeDeploymentRef == null) {
+                       label.setText("Not yet deployed on <br>" + nodeState.getHostname() + "</br>, 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("[" + "<b>" + nodeState.getHostname() + "</b>]# " + "Deployment state " + stateUuid
+                                       + ", available since <b>" + calendar.getTime() + "</b>");
+               }
+       }
+
+       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 (file)
index 0000000..05923fb
--- /dev/null
@@ -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<LogEntry> logEntries = (Enumeration<LogEntry>) 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 (file)
index 0000000..df1be51
--- /dev/null
@@ -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 (file)
index 0000000..122f5cf
--- /dev/null
@@ -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 (file)
index 0000000..4edbf56
--- /dev/null
@@ -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<UserAdmin> userAdminRef = bc.getServiceReference(UserAdmin.class);
+               UserAdmin userAdmin = bc.getService(userAdminRef);
+               StringBuffer text = new StringBuffer();
+               text.append("<span style='font-variant: small-caps;'>Domains</span><br/>");
+               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("<span style='font-weight:bold;font-style: italic'>");
+                       else
+                               text.append("<span style='font-weight:bold'>");
+
+                       text.append(rootDn);
+                       text.append("</span><br/>");
+                       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.<br/>");
+                       } 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;
+       }
+}
index 3c9347a71381f4f8f3644b521e35627bce6a4ee7..c957656c640f766a2bf83d7a87b9adeb28c30732 100644 (file)
@@ -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;
        }
 
index a2c920aa8e8e4ba9729e6eba7ee50989dc45122b..12cc5399972e7dfd76a49d188e45aa7e1b8aec8d 100644 (file)
  */
 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;
        // }
 
index e9a4a25f15c18ab7c8817f5e0c97ca8c5cbbff46..6f0828dd44a79e98d61a3a1f7380ea977e43b47a 100644 (file)
@@ -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;
index 5eecaac215c8ab10c618a5ab760cccd0195167cc..951dd2415892dc92539bf188b50e2b4f1fd8fb4b 100644 (file)
@@ -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;
index ed797c58a34ab1f4cb97ba33d21a0093d40f05af..7513102fe8352a664ee00f9163d2c19e1b04231f 100644 (file)
@@ -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;
index a35499984673b46afd9f6dec5f22c97207705ec1..3d67b8c3b599786e50fa02233114bf5c9e9e86d1 100644 (file)
@@ -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;
index 377605f5d126ebbb22a00224c5eb51d5d483e250..a47ca2dc3a66ac69058dc00dd6a7f591272483ef 100644 (file)
@@ -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;
index 859081e945cb06998c21d6650e99d77606f363bf..d9a75b89e4e0d8780a5910c314e9b375809493db 100644 (file)
@@ -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;
index 7be078ac56f73cf155396a7de81661b28798e0a4..e090fe251e7839b9405da7b0892f69eb38e3f606 100644 (file)
@@ -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 (file)
index ece0be3..0000000
+++ /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 (file)
index 859f64b..0000000
+++ /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<String> values;
-
-       // TODO manage within the CSS
-       private int rowSpacing = 5;
-       private int rowMarging = 0;
-       private int oneValueMargingRight = 5;
-       private int btnWidth = 16;
-       private int btnHeight = 16;
-       private int btnHorizontalIndent = 3;
-
-       public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List<String> 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<String> getValues() {
-               return values;
-       }
-
-       public void setValues(List<String> values) {
-               this.values = values;
-       }
-
-       // Row layout items do not need explicit layout data
-       protected void setControlLayoutData(Control control) {
-       }
-
-       /** To be overridden */
-       protected void setContainerLayoutData(Composite composite) {
-               composite.setLayoutData(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 (file)
index 928a285..0000000
+++ /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 (file)
index dd3ff29..0000000
+++ /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 = "&#38;";
-       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("<br/>", "\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 (file)
index 18df3e4..0000000
+++ /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 (file)
index 92ce9da..0000000
+++ /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 (file)
index d2af709..0000000
+++ /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<String> 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<String> valStrings = new ArrayList<String>();
-                       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<String> valueStrings = new ArrayList<String>();
-
-                       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<String> 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", "<br/>");
-                       // Do not make the update if validation fails
-                       try {
-                               MarkupValidatorCopy.getInstance().validate(value);
-                       } catch (Exception e) {
-                               log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node
-                                               + ", String cannot be validated - " + e.getMessage());
-                               return;
-                       }
-                       // TODO check if the newly created property is of the correct type,
-                       // otherwise the property will be silently created with a STRING
-                       // property type.
-                       node.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 (file)
index 6a496a2..0000000
+++ /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 (file)
index be08702..0000000
+++ /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("<a href=\"tel:");
-               builder.append(value).append("\" target=\"_blank\" >").append(label)
-                               .append("</a>");
-               return builder.toString();
-       }
-
-       /**
-        * Creates the read-only HTML snippet to display in a label with styling
-        * enabled in order to provide a click-able mail
-        */
-       public static String getMailLink(String value) {
-               return getMailLink(value, value);
-       }
-
-       /**
-        * Creates the read-only HTML snippet to display in a label with styling
-        * enabled in order to provide a click-able mail
-        * 
-        * @param value
-        * @param label
-        *            a potentially distinct label
-        * @return
-        */
-       public static String getMailLink(String value, String label) {
-               StringBuilder builder = new StringBuilder();
-               value = replaceAmpersand(value);
-               builder.append("<a href=\"mailto:");
-               builder.append(value).append("\" >").append(label).append("</a>");
-               return builder.toString();
-       }
-
-       /**
-        * Creates the read-only HTML snippet to display in a label with styling
-        * enabled in order to provide a click-able link
-        */
-       public static String getUrlLink(String value) {
-               return getUrlLink(value, value);
-       }
-
-       /**
-        * Creates the read-only HTML snippet to display in a label with styling
-        * enabled in order to provide a click-able link
-        */
-       public static String getUrlLink(String value, String label) {
-               StringBuilder builder = new StringBuilder();
-               value = replaceAmpersand(value);
-               label = replaceAmpersand(label);
-               if (!(value.startsWith("http://") || value.startsWith("https://")))
-                       value = "http://" + value;
-               builder.append("<a href=\"");
-               builder.append(value + "\" target=\"_blank\" >" + label + "</a>");
-               return builder.toString();
-       }
-
-       private static String AMPERSAND = "&#38;";
-
-       /**
-        * Cleans a String by replacing any '&#38;' by its HTML encoding '&#38;#38;' to
-        * avoid <code>SAXParseException</code> while rendering HTML with RWT
-        */
-       public static String replaceAmpersand(String value) {
-               value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
-               return value;
-       }
-
-       // Prevents instantiation
-       private FormUtils() {
-       }
-}
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 (file)
index f1a5a8c..0000000
+++ /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<String, String[]> 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("<html>");
-               markup.append(text);
-               markup.append("</html>");
-               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("<!DOCTYPE html [");
-               result.append("<!ENTITY quot \"&#34;\">");
-               result.append("<!ENTITY amp \"&#38;\">");
-               result.append("<!ENTITY apos \"&#39;\">");
-               result.append("<!ENTITY lt \"&#60;\">");
-               result.append("<!ENTITY gt \"&#62;\">");
-               result.append("<!ENTITY nbsp \"&#160;\">");
-               result.append("<!ENTITY ensp \"&#8194;\">");
-               result.append("<!ENTITY emsp \"&#8195;\">");
-               result.append("<!ENTITY ndash \"&#8211;\">");
-               result.append("<!ENTITY mdash \"&#8212;\">");
-               result.append("]>");
-               return result.toString();
-       }
-
-       private static Map<String, String[]> createSupportedElementsMap() {
-               Map<String, String[]> result = new HashMap<String, String[]>();
-               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<String> 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 (file)
index 8c893f2..0000000
+++ /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> T getService(Class<? extends T> clazz) {
-               return bc.getService(bc.getServiceReference(clazz));
-       }
-
-       protected <T> Collection<ServiceReference<T>> getServiceReferences(Class<T> 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 (file)
index 384cd72..0000000
+++ /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<String, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<String, Browse.FilterEntitiesVirtualTable>();
-       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("<b>" + context.getName() + "</b>");
-               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 = "<binary>";
-                       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 (file)
index f4f3079..0000000
+++ /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("<span style='font-variant: small-caps;'>Provided Servers</span><br/>");
-
-               ServiceReference<HttpService> 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("<b>http</b> ").append(httpPort).append("<br/>");
-                       if (httpsPort != null)
-                               text.append("<b>https</b> ").append(httpsPort).append("<br/>");
-
-               }
-
-               text.append("<br/>");
-               text.append("<span style='font-variant: small-caps;'>Referenced Servers</span><br/>");
-
-               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 (file)
index 613e3cb..0000000
+++ /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<ServiceReference<RepositoryContext>> contexts = getServiceReferences(RepositoryContext.class,
-                               "(" + NodeConstants.CN + "=*)");
-               StringBuffer text = new StringBuffer();
-               text.append("<span style='font-variant: small-caps;'>Jackrabbit Repositories</span><br/>");
-               for (ServiceReference<RepositoryContext> 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("<b>" + alias + "</b><br/>");
-                       text.append("rootNodeId: " + rootNodeId + "<br/>");
-                       try {
-                               FileStore fileStore = Files.getFileStore(repoHomePath);
-                               text.append("partition: " + fileStore.toString() + "<br/>");
-                               text.append(
-                                               percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)<br/>");
-                       } 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("<span style=''>" + text.toString() + "</span>");
-       }
-
-       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 = "<span style='color:green;font-weight:bold'>";
-               else if (percent < 95)
-                       span = "<span style='color:orange;font-weight:bold'>";
-               else
-                       span = "<span style='color:red;font-weight:bold'>";
-               return span + percent + "%</span>";
-       }
-
-       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 (file)
index 8dda4c4..0000000
+++ /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<NodeState> nodeStateRef = bc.getServiceReference(NodeState.class);
-               if (nodeStateRef == null)
-                       throw new IllegalStateException("No CMS state available");
-               NodeState nodeState = bc.getService(nodeStateRef);
-               ServiceReference<NodeDeployment> nodeDeploymentRef = bc.getServiceReference(NodeDeployment.class);
-               Label label = new Label(composite, SWT.WRAP);
-               CmsUtils.markup(label);
-               if (nodeDeploymentRef == null) {
-                       label.setText("Not yet deployed on <br>" + nodeState.getHostname() + "</br>, 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("[" + "<b>" + nodeState.getHostname() + "</b>]# " + "Deployment state " + stateUuid
-                                       + ", available since <b>" + calendar.getTime() + "</b>");
-               }
-       }
-
-       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 (file)
index 8fb9643..0000000
+++ /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<LogEntry> logEntries = (Enumeration<LogEntry>) 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 (file)
index fef25d7..0000000
+++ /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 (file)
index 11b0b90..0000000
+++ /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 (file)
index 8a90344..0000000
+++ /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 (file)
index 9fcdaf9..0000000
+++ /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<UserAdmin> userAdminRef = bc.getServiceReference(UserAdmin.class);
-               UserAdmin userAdmin = bc.getService(userAdminRef);
-               StringBuffer text = new StringBuffer();
-               text.append("<span style='font-variant: small-caps;'>Domains</span><br/>");
-               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("<span style='font-weight:bold;font-style: italic'>");
-                       else
-                               text.append("<span style='font-weight:bold'>");
-
-                       text.append(rootDn);
-                       text.append("</span><br/>");
-                       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.<br/>");
-                       } 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 (file)
index a2a5547..0000000
+++ /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<String, String> 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<String, String> 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 (file)
index 39b5b53..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.cms.script;
-
-import java.util.Map;
-
-public interface Branding {
-       public void applyBranding(Map<String, String> 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 (file)
index e639412..0000000
+++ /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<ApplicationConfiguration> 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<String> resources = new ArrayList<>();
-
-       private Map<String, AppUi> 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<URL> 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<String, String> 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<String> 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<String, String> 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<String, AppUi> getUi() {
-               return ui;
-       }
-
-       public void setUi(Map<String, AppUi> 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 (file)
index cc5ea7f..0000000
+++ /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 (file)
index edf25bb..0000000
+++ /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<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
-                                       FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
-
-                               @Override
-                               public Repository addingService(ServiceReference<Repository> 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 (file)
index fd9aa9d..0000000
+++ /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 (file)
index 584f521..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.argeo.cms.script;
-
-import org.argeo.cms.util.CmsTheme;
-import org.osgi.framework.BundleContext;
-
-/** @deprecated Use <code>CmsTheme</code> 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 (file)
index ac2eecf..0000000
+++ /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, '<b>' + text + '</b>')
-}
-
-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__)
index 2cd86ce08edc115300b1e9cda9d0309b4d8626bc..6b61ae3967e9895349091b5745d2bad8e06bb8c6 100644 (file)
@@ -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;
index 6ef4b315ce83d62badc8fb9c310d009d2bc9b98b..8a12c100ec7368ccff6d4acaec3c4b0f78304f55 100644 (file)
@@ -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;
 
index fa85dff462c3dcac791c2f947faa4cf3a9431cee..eb50ef2f31ba1e3384217d8a1e30565b66de3873 100644 (file)
@@ -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)
index de41bbfe15c8b31b9b48537b9f85ea5cf700feec..b3860f780ac112ce89aff4114a6894f6bef80e8d 100644 (file)
@@ -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 (file)
index 0000000..e74de5e
--- /dev/null
@@ -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 (file)
index 0000000..24b1d0c
--- /dev/null
@@ -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<String> values;
+
+       // TODO manage within the CSS
+       private int rowSpacing = 5;
+       private int rowMarging = 0;
+       private int oneValueMargingRight = 5;
+       private int btnWidth = 16;
+       private int btnHeight = 16;
+       private int btnHorizontalIndent = 3;
+
+       public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List<String> 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<String> getValues() {
+               return values;
+       }
+
+       public void setValues(List<String> values) {
+               this.values = values;
+       }
+
+       // Row layout items do not need explicit layout data
+       protected void setControlLayoutData(Control control) {
+       }
+
+       /** To be overridden */
+       protected void setContainerLayoutData(Composite composite) {
+               composite.setLayoutData(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 (file)
index 0000000..4428d14
--- /dev/null
@@ -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 (file)
index 0000000..1ab0338
--- /dev/null
@@ -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 = "&#38;";
+       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("<br/>", "\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 (file)
index 0000000..fe9f7e7
--- /dev/null
@@ -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 (file)
index 0000000..7d46127
--- /dev/null
@@ -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 (file)
index 0000000..3ae2036
--- /dev/null
@@ -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<String> 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<String> valStrings = new ArrayList<String>();
+                       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<String> valueStrings = new ArrayList<String>();
+
+                       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<String> 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", "<br/>");
+                       // Do not make the update if validation fails
+                       try {
+                               MarkupValidatorCopy.getInstance().validate(value);
+                       } catch (Exception e) {
+                               log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node
+                                               + ", String cannot be validated - " + e.getMessage());
+                               return;
+                       }
+                       // TODO check if the newly created property is of the correct type,
+                       // otherwise the property will be silently created with a STRING
+                       // property type.
+                       node.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 (file)
index 0000000..7e1b8b0
--- /dev/null
@@ -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 (file)
index 0000000..a5475d6
--- /dev/null
@@ -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("<a href=\"tel:");
+               builder.append(value).append("\" target=\"_blank\" >").append(label)
+                               .append("</a>");
+               return builder.toString();
+       }
+
+       /**
+        * Creates the read-only HTML snippet to display in a label with styling
+        * enabled in order to provide a click-able mail
+        */
+       public static String getMailLink(String value) {
+               return getMailLink(value, value);
+       }
+
+       /**
+        * Creates the read-only HTML snippet to display in a label with styling
+        * enabled in order to provide a click-able mail
+        * 
+        * @param value
+        * @param label
+        *            a potentially distinct label
+        * @return
+        */
+       public static String getMailLink(String value, String label) {
+               StringBuilder builder = new StringBuilder();
+               value = replaceAmpersand(value);
+               builder.append("<a href=\"mailto:");
+               builder.append(value).append("\" >").append(label).append("</a>");
+               return builder.toString();
+       }
+
+       /**
+        * Creates the read-only HTML snippet to display in a label with styling
+        * enabled in order to provide a click-able link
+        */
+       public static String getUrlLink(String value) {
+               return getUrlLink(value, value);
+       }
+
+       /**
+        * Creates the read-only HTML snippet to display in a label with styling
+        * enabled in order to provide a click-able link
+        */
+       public static String getUrlLink(String value, String label) {
+               StringBuilder builder = new StringBuilder();
+               value = replaceAmpersand(value);
+               label = replaceAmpersand(label);
+               if (!(value.startsWith("http://") || value.startsWith("https://")))
+                       value = "http://" + value;
+               builder.append("<a href=\"");
+               builder.append(value + "\" target=\"_blank\" >" + label + "</a>");
+               return builder.toString();
+       }
+
+       private static String AMPERSAND = "&#38;";
+
+       /**
+        * Cleans a String by replacing any '&#38;' by its HTML encoding '&#38;#38;' to
+        * avoid <code>SAXParseException</code> while rendering HTML with RWT
+        */
+       public static String replaceAmpersand(String value) {
+               value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
+               return value;
+       }
+
+       // Prevents instantiation
+       private FormUtils() {
+       }
+}
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 (file)
index 0000000..3f588d1
--- /dev/null
@@ -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<String, String[]> 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("<html>");
+               markup.append(text);
+               markup.append("</html>");
+               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("<!DOCTYPE html [");
+               result.append("<!ENTITY quot \"&#34;\">");
+               result.append("<!ENTITY amp \"&#38;\">");
+               result.append("<!ENTITY apos \"&#39;\">");
+               result.append("<!ENTITY lt \"&#60;\">");
+               result.append("<!ENTITY gt \"&#62;\">");
+               result.append("<!ENTITY nbsp \"&#160;\">");
+               result.append("<!ENTITY ensp \"&#8194;\">");
+               result.append("<!ENTITY emsp \"&#8195;\">");
+               result.append("<!ENTITY ndash \"&#8211;\">");
+               result.append("<!ENTITY mdash \"&#8212;\">");
+               result.append("]>");
+               return result.toString();
+       }
+
+       private static Map<String, String[]> createSupportedElementsMap() {
+               Map<String, String[]> result = new HashMap<String, String[]>();
+               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<String> 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);
+                               }
+                       }
+               }
+
+       }
+
+}
index 8aab1ec21277a4afee48c2c8d78d9599e6076dc9..4e0067521d4c70d7ff057b1b6f52707c004f2f79 100644 (file)
@@ -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();
 
index ffb5f0a830f2626cd791e244a781d093eacac032..f45629b04278900acb7bdf1b6237bce83c85ee1c 100644 (file)
@@ -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 (file)
index 4c48f71..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.argeo.cms.ui.img;
-
-public class Dummy {
-
-}
index 6417b25fee9571ce860061688f9babdc9209d126..9ffddd8f3a202a6d985e6de1502d582c257fe8fe 100644 (file)
@@ -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;
index 3bb1fdf159b7973e69bb33a118e0dd368a3cacfc..5602d991b0b88a70f9dab4215ff3e618aec0a215 100644 (file)
@@ -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;
        }
 
index 88cd17b81eb1a7a38b36ae426577542c227543be..5e21cc4288d2be69659fe1831208bcc9629d13ee 100644 (file)
@@ -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;
index d5e639f621f5a9fcef2849a12c040e8afc547c59..7a0603eb7616c3fbb8ad841fee6dbfe3886e3910 100644 (file)
@@ -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 */
index b688e625938386bb7a7e0ad92e2042f733fd60e8..8f9ac1c75f24fa2b95d17ddfe17d543398cfd0d8 100644 (file)
@@ -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;
 
index 9cccb52b13791070ab7b093968e8120335a94706..0a662e216026ed82382e0ed28d8036be91c4e25b 100644 (file)
@@ -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;
 
index d67b1330cc93dd959673c37edcc8796e552b1beb..5883be5ba969aae80ac5a071dd85072fc5da2e50 100644 (file)
@@ -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;
 
index ad173cf38b146cb3f021a17487888aeb4751f74f..58bbc3baf3322860446659c33422e7af4659ee41 100644 (file)
@@ -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 {
index c772424b7152c5be5af596e8d5ebbf4979d52d6e..a5af700b10003ffb37ec57b83e3ac6e7456163c0 100644 (file)
@@ -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 (file)
index 0000000..2293786
--- /dev/null
@@ -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<String, String> 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<String, String> 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 (file)
index 0000000..f72338e
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.cms.ui.script;
+
+import java.util.Map;
+
+public interface Branding {
+       public void applyBranding(Map<String, String> 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 (file)
index 0000000..c3e1a72
--- /dev/null
@@ -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<ApplicationConfiguration> 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<String> resources = new ArrayList<>();
+
+       private Map<String, AppUi> 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<URL> 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<String, String> 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<String> 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<String, String> 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<String, AppUi> getUi() {
+               return ui;
+       }
+
+       public void setUi(Map<String, AppUi> 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 (file)
index 0000000..d7c1a63
--- /dev/null
@@ -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 (file)
index 0000000..7813156
--- /dev/null
@@ -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<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
+                                       FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
+
+                               @Override
+                               public Repository addingService(ServiceReference<Repository> 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 (file)
index 0000000..bf68fc2
--- /dev/null
@@ -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 (file)
index 0000000..c053b47
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.cms.ui.script;
+
+import org.argeo.cms.ui.util.CmsTheme;
+import org.osgi.framework.BundleContext;
+
+/** @deprecated Use <code>CmsTheme</code> 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 (file)
index 0000000..be9618d
--- /dev/null
@@ -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, '<b>' + text + '</b>')
+}
+
+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__)
index 36a1da28e470e502a7f885d5fc8219d999e60d63..a521b973f587517510b3aa39e579697080931430 100644 (file)
@@ -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;
index 2d494ef90d3e02026c1c184bc4ddc0568c3ab4b8..05c6f3778294de06ac8ceb2dc3d5cf2277213c19 100644 (file)
@@ -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 (file)
index 0000000..13df119
--- /dev/null
@@ -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 (file)
index 0000000..b3c4a8a
--- /dev/null
@@ -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("<a style='color:inherit;text-decoration:inherit;' href=\"");
+                       if (loggedInTarget.equals("")) {
+                               try {
+                                       Node homeNode = NodeUtils.getUserHome(context.getSession());
+                                       String homePath = homeNode.getPath();
+                                       labelText.append("/#" + homePath);
+                               } catch (RepositoryException e) {
+                                       throw new CmsException("Cannot get home path", e);
+                               }
+                       } else {
+                               labelText.append(loggedInTarget);
+                       }
+                       labelText.append("\">");
+               } else if (target != null) {
+                       labelText.append("<a style='color:inherit;text-decoration:inherit;' href=\"");
+                       labelText.append(target);
+                       labelText.append("\">");
+               }
+               if (image != null) {
+                       registerImageIfNeeded();
+                       String imageLocation = RWT.getResourceManager().getLocation(image);
+                       labelText.append("<img");
+                       if (imageWidth != null)
+                               labelText.append(" width='").append(imageWidth).append('\'');
+                       if (imageHeight != null)
+                               labelText.append(" height='").append(imageHeight).append('\'');
+                       labelText.append(" src=\"").append(imageLocation).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("</a>");
+
+               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 (file)
index 0000000..68f1e0f
--- /dev/null
@@ -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 (file)
index 0000000..8b1f874
--- /dev/null
@@ -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).<br>
+ * 
+ * Additional fonts listed in <code>/fonts.txt</code>.<br>
+ * Additional (standard CSS) header in <code>/header.css</code>.<br>
+ * RAP specific CSS files in <code>/rap/*.css</code>.<br>
+ * All images added as additional resources based on extensions
+ * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
+ */
+public class CmsTheme {
+       private final static Log log = LogFactory.getLog(CmsTheme.class);
+
+       private String themeId;
+       private Map<String, ResourceLoader> css = new HashMap<>();
+       private Map<String, ResourceLoader> resources = new HashMap<>();
+
+       private String headerCss;
+       private List<String> 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("<style type='text/css'>\n");
+                       sb.append(headerCss);
+                       sb.append("\n</style>\n");
+               }
+               for (String link : fonts) {
+                       sb.append("<link rel='stylesheet' href='");
+                       sb.append(link);
+                       sb.append("'/>\n");
+               }
+               if (sb.length() == 0)
+                       return null;
+               else
+                       return sb.toString();
+       }
+
+       void addStyleSheets(Bundle themeBundle, ResourceLoader ssRL) {
+               Enumeration<URL> 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<URL> 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 (file)
index 0000000..316e785
--- /dev/null
@@ -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 extends Widget> T style(T widget, String style) {
+               widget.setData(CmsConstants.STYLE, style);
+               return widget;
+       }
+
+       /** Enable markups on widget */
+       public static <T extends Widget> 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 extends Widget> 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("<img width='").append(width).append("' height='").append(height)
+                               .append("' src='").append(src).append("'");
+       }
+
+       public static String noImg(Point size) {
+               ResourceManager rm = RWT.getResourceManager();
+               return CmsUiUtils.img(rm.getLocation(NO_IMAGE), size);
+       }
+
+       public static String noImg() {
+               return noImg(NO_IMAGE_SIZE);
+       }
+
+       public static Image noImage(Point size) {
+               ResourceManager rm = RWT.getResourceManager();
+               InputStream in = null;
+               try {
+                       in = rm.getRegisteredContent(NO_IMAGE);
+                       ImageData id = new ImageData(in);
+                       ImageData scaled = id.scaledTo(size.x, size.y);
+                       Image image = new Image(Display.getCurrent(), scaled);
+                       return image;
+               } finally {
+                       try {
+                               in.close();
+                       } catch (IOException e) {
+                               // silent
+                       }
+               }
+       }
+
+       /** Lorem ipsum text to be used during development. */
+       public final static String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
+                       + " Etiam eleifend hendrerit sem, ac ultricies massa ornare ac."
+                       + " Cras aliquam sodales risus, vitae varius lacus molestie quis."
+                       + " Vivamus consequat, leo id lacinia volutpat, eros diam efficitur urna, finibus interdum risus turpis at nisi."
+                       + " Curabitur vulputate nulla quis scelerisque fringilla. Integer consectetur turpis id lobortis accumsan."
+                       + " Pellentesque commodo turpis ac diam ultricies dignissim."
+                       + " Curabitur sit amet dolor volutpat lacus aliquam ornare quis sed velit."
+                       + " Integer varius quis est et tristique."
+                       + " Suspendisse pharetra porttitor purus, eget condimentum magna."
+                       + " Duis vitae turpis eros. Sed tincidunt lacinia rutrum."
+                       + " Aliquam velit velit, rutrum ut augue sed, condimentum lacinia augue.";
+
+       /** Singleton. */
+       private CmsUiUtils() {
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java
new file mode 100644 (file)
index 0000000..e2ab5c8
--- /dev/null
@@ -0,0 +1,246 @@
+package org.argeo.cms.ui.util;
+
+import static javax.jcr.Node.JCR_CONTENT;
+import static javax.jcr.Property.JCR_DATA;
+import static javax.jcr.nodetype.NodeType.NT_FILE;
+import static javax.jcr.nodetype.NodeType.NT_RESOURCE;
+import static org.argeo.cms.ui.CmsConstants.NO_IMAGE_SIZE;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+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.ui.CmsImageManager;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.ResourceManager;
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+
+/** Manages only public images so far. */
+public class DefaultImageManager implements CmsImageManager {
+       private final static Log log = LogFactory.getLog(DefaultImageManager.class);
+//     private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
+
+       public Boolean load(Node node, Control control, Point preferredSize) throws RepositoryException {
+               Point imageSize = getImageSize(node);
+               Point size;
+               String imgTag = null;
+               if (preferredSize == null || imageSize.x == 0 || imageSize.y == 0
+                               || (preferredSize.x == 0 && preferredSize.y == 0)) {
+                       if (imageSize.x != 0 && imageSize.y != 0) {
+                               // actual image size if completely known
+                               size = imageSize;
+                       } else {
+                               // no image if not completely known
+                               size = resizeTo(NO_IMAGE_SIZE, preferredSize != null ? preferredSize : imageSize);
+                               imgTag = CmsUiUtils.noImg(size);
+                       }
+
+               } else if (preferredSize.x != 0 && preferredSize.y != 0) {
+                       // given size if completely provided
+                       size = preferredSize;
+               } else {
+                       // at this stage :
+                       // image is completely known
+                       assert imageSize.x != 0 && imageSize.y != 0;
+                       // one and only one of the dimension as been specified
+                       assert preferredSize.x == 0 || preferredSize.y == 0;
+                       size = resizeTo(imageSize, preferredSize);
+               }
+
+               boolean loaded = false;
+               if (control == null)
+                       return loaded;
+
+               if (control instanceof Label) {
+                       if (imgTag == null) {
+                               // IMAGE RETRIEVED HERE
+                               imgTag = getImageTag(node, size);
+                               //
+                               if (imgTag == null)
+                                       imgTag = CmsUiUtils.noImg(size);
+                               else
+                                       loaded = true;
+                       }
+
+                       Label lbl = (Label) control;
+                       lbl.setText(imgTag);
+                       // lbl.setSize(size);
+               } else if (control instanceof FileUpload) {
+                       FileUpload lbl = (FileUpload) control;
+                       lbl.setImage(CmsUiUtils.noImage(size));
+                       lbl.setSize(size);
+                       return loaded;
+               } else
+                       loaded = false;
+
+               return loaded;
+       }
+
+       private Point resizeTo(Point orig, Point constraints) {
+               if (constraints.x != 0 && constraints.y != 0) {
+                       return constraints;
+               } else if (constraints.x == 0 && constraints.y == 0) {
+                       return orig;
+               } else if (constraints.y == 0) {// force width
+                       return new Point(constraints.x, scale(orig.y, orig.x, constraints.x));
+               } else if (constraints.x == 0) {// force height
+                       return new Point(scale(orig.x, orig.y, constraints.y), constraints.y);
+               }
+               throw new CmsException("Cannot resize " + orig + " to " + constraints);
+       }
+
+       private int scale(int origDimension, int otherDimension, int otherConstraint) {
+               return Math.round(origDimension * divide(otherConstraint, otherDimension));
+       }
+
+       private float divide(int a, int b) {
+               return ((float) a) / ((float) b);
+       }
+
+       public Point getImageSize(Node node) throws RepositoryException {
+               // TODO load the SWT image ?
+               return new Point(0, 0);
+       }
+
+       /** @return null if not available */
+       @Override
+       public String getImageTag(Node node) throws RepositoryException {
+               return getImageTag(node, getImageSize(node));
+       }
+
+       private String getImageTag(Node node, Point size) throws RepositoryException {
+               StringBuilder buf = getImageTagBuilder(node, size);
+               if (buf == null)
+                       return null;
+               return buf.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 (file)
index 0000000..50d844f
--- /dev/null
@@ -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 <b>not</b>
+        * 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 (file)
index 0000000..d581436
--- /dev/null
@@ -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 (file)
index 0000000..0f5b079
--- /dev/null
@@ -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<String, Map<String, String>> branding = new HashMap<String, Map<String, String>>();
+       private Map<String, List<String>> styleSheets = new HashMap<String, List<String>>();
+
+       private List<String> resources = new ArrayList<String>();
+
+       private BundleContext bundleContext;
+
+       private Repository repository;
+       private String workspace = null;
+       private String jcrBasePath = "/";
+       private List<String> roPrincipals = Arrays.asList(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER);
+       private List<String> rwPrincipals = Arrays.asList(NodeConstants.ROLE_USER);
+
+       private CmsUiProvider header;
+       private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
+
+       private Integer headerHeight = 40;
+
+       private ServiceRegistration<ApplicationConfiguration> 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<String, String> defaultBranding = null;
+                       if (branding.containsKey("*"))
+                               defaultBranding = branding.get("*");
+                       String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
+
+                       // entry points
+                       for (String page : pages.keySet()) {
+                               Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
+                                               : new HashMap<String, String>();
+                               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<Bundle> 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<String> 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<String, String> props = new Hashtable<String, String>();
+               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<String, CmsUiProvider> pages) {
+               this.pages = pages;
+       }
+
+       public void setJcrBasePath(String basePath) {
+               this.jcrBasePath = basePath;
+       }
+
+       public void setRoPrincipals(List<String> roPrincipals) {
+               this.roPrincipals = roPrincipals;
+       }
+
+       public void setRwPrincipals(List<String> rwPrincipals) {
+               this.rwPrincipals = rwPrincipals;
+       }
+
+       public void setHeaderHeight(Integer headerHeight) {
+               this.headerHeight = headerHeight;
+       }
+
+       public void setBranding(Map<String, Map<String, String>> branding) {
+               this.branding = branding;
+       }
+
+       public void setStyleSheets(Map<String, List<String>> styleSheets) {
+               this.styleSheets = styleSheets;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       public void setResources(List<String> 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<String, String> properties;
+
+               public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
+                               Map<String, String> 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 = "<div"
+                       + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
+                       + "<img src=\"./rwt-resources/" + LOADING_IMAGE
+                       + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
+}
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 (file)
index 0000000..04ce5a2
--- /dev/null
@@ -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<CmsUiProvider> lead = new ArrayList<CmsUiProvider>();
+       private List<CmsUiProvider> center = new ArrayList<CmsUiProvider>();
+       private List<CmsUiProvider> end = new ArrayList<CmsUiProvider>();
+
+       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<CmsUiProvider> 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<CmsUiProvider> lead) {
+               this.lead = lead;
+       }
+
+       public void setCenter(List<CmsUiProvider> center) {
+               this.center = center;
+       }
+
+       public void setEnd(List<CmsUiProvider> end) {
+               this.end = end;
+       }
+
+       public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
+               this.subPartsSameWidth = subPartsSameWidth;
+       }
+
+       public List<CmsUiProvider> getLead() {
+               return lead;
+       }
+
+       public List<CmsUiProvider> getCenter() {
+               return center;
+       }
+
+       public List<CmsUiProvider> 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 (file)
index 0000000..8e0e7c1
--- /dev/null
@@ -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("<b>" + context.getName() + "</b>");
+               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("<i>Children:</i>");
+               // 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("<i>Properties:</i>");
+               // 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 = "<binary>";
+                       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 (file)
index 0000000..92133f5
--- /dev/null
@@ -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<String, String> 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 (file)
index 0000000..ac09b2a
--- /dev/null
@@ -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 (file)
index 0000000..d394f23
--- /dev/null
@@ -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 (file)
index 0000000..f85a1ab
--- /dev/null
@@ -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 (file)
index 0000000..ad1523c
--- /dev/null
@@ -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<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
+
+       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 (file)
index 0000000..f53e552
--- /dev/null
@@ -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("&amp;body=").append(encoded);
+               } catch (UnsupportedEncodingException e) {
+                       mailToUrl.append("&amp;body=").append("Could not encode: ")
+                                       .append(e.getMessage());
+               }
+               Label mailTo = new Label(pane, SWT.NONE);
+               CmsUiUtils.markup(mailTo);
+               mailTo.setText("<a href=\"" + mailToUrl + "\">Send details</a>");
+               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 (file)
index 0000000..90b6840
--- /dev/null
@@ -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<URL> 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 (file)
index 0000000..07b6069
--- /dev/null
@@ -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 (file)
index 0000000..cc470e4
--- /dev/null
@@ -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 (file)
index 0000000..e7dfb4b
--- /dev/null
@@ -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<CmsUiProvider> items = new ArrayList<CmsUiProvider>();
+
+       @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<CmsUiProvider> getItems() {
+               return items;
+       }
+
+       public void setItems(List<CmsUiProvider> 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 (file)
index 0000000..1445fed
--- /dev/null
@@ -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<Void>) () -> {
+                       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 (file)
index 0000000..158beaf
--- /dev/null
@@ -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 (file)
index 0000000..4ca45d1
--- /dev/null
@@ -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<T extends Item> {
+       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 (file)
index 0000000..dfd1438
--- /dev/null
@@ -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 (file)
index 0000000..b51d4fc
--- /dev/null
@@ -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<Node> {
+       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 (file)
index 0000000..793079e
--- /dev/null
@@ -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<Property> {
+       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 (file)
index 0000000..83ea560
--- /dev/null
@@ -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<String, Section> getSubSections() throws RepositoryException {
+               LinkedHashMap<String, Section> result = new LinkedHashMap<String, Section>();
+               for (Control child : getChildren()) {
+                       if (child instanceof Composite) {
+                               collectDirectSubSections((Composite) child, result);
+                       }
+               }
+               return Collections.unmodifiableMap(result);
+       }
+
+       private void collectDirectSubSections(Composite composite, LinkedHashMap<String, Section> 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 (file)
index 0000000..f0b367f
--- /dev/null
@@ -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 (file)
index 0000000..0a327cd
--- /dev/null
@@ -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 (file)
index 0000000..21f4813
--- /dev/null
@@ -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 (file)
index 0000000..055fcf5
--- /dev/null
@@ -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 (file)
index 0000000..d47aba7
--- /dev/null
@@ -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 (file)
index 0000000..5dd00ad
--- /dev/null
@@ -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 (file)
index 0000000..b085fdf
--- /dev/null
@@ -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 (file)
index 0000000..e461ed0
--- /dev/null
@@ -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 (file)
index 0000000..1d34b9d
--- /dev/null
@@ -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 (file)
index 0000000..4533f64
--- /dev/null
@@ -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<Locale> 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<Locale> 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 (file)
index 0000000..5460ffd
--- /dev/null
@@ -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 (file)
index 0000000..974b676
--- /dev/null
@@ -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:<br>
+ * <ul>
+ * <li>{@link PasswordCallback}</li>
+ * <li>{@link NameCallback}</li>
+ * <li>{@link TextOutputCallback}</li>
+ * </ul>
+ * Supported Argeo {@link Callback}s are:<br>
+ * <ul>
+ * <li>{@link LocaleChoice}</li>
+ * </ul>
+ */
+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 (file)
index 0000000..10edbf3
--- /dev/null
@@ -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 (file)
index 0000000..e470bda
--- /dev/null
@@ -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 (file)
index 0000000..0c8031b
--- /dev/null
@@ -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<Locale> locales;
+
+       private Integer selectedIndex = null;
+       private final Integer defaultIndex;
+
+       public LocaleChoice(List<Locale> 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<Locale> 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 (file)
index 7342e10..0000000
+++ /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 (file)
index b18770f..0000000
+++ /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("<a style='color:inherit;text-decoration:inherit;' href=\"");
-                       if (loggedInTarget.equals("")) {
-                               try {
-                                       Node homeNode = NodeUtils.getUserHome(context.getSession());
-                                       String homePath = homeNode.getPath();
-                                       labelText.append("/#" + homePath);
-                               } catch (RepositoryException e) {
-                                       throw new CmsException("Cannot get home path", e);
-                               }
-                       } else {
-                               labelText.append(loggedInTarget);
-                       }
-                       labelText.append("\">");
-               } else if (target != null) {
-                       labelText.append("<a style='color:inherit;text-decoration:inherit;' href=\"");
-                       labelText.append(target);
-                       labelText.append("\">");
-               }
-               if (image != null) {
-                       registerImageIfNeeded();
-                       String imageLocation = RWT.getResourceManager().getLocation(image);
-                       labelText.append("<img");
-                       if (imageWidth != null)
-                               labelText.append(" width='").append(imageWidth).append('\'');
-                       if (imageHeight != null)
-                               labelText.append(" height='").append(imageHeight).append('\'');
-                       labelText.append(" src=\"").append(imageLocation).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("</a>");
-
-               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 (file)
index a8d085c..0000000
+++ /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 (file)
index 8bb7098..0000000
+++ /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).<br>
- * 
- * Additional fonts listed in <code>/fonts.txt</code>.<br>
- * Additional (standard CSS) header in <code>/header.css</code>.<br>
- * RAP specific CSS files in <code>/rap/*.css</code>.<br>
- * All images added as additional resources based on extensions
- * <code>/ ** /*.{png,gif,jpeg,...}</code>.<br>
- */
-public class CmsTheme {
-       private final static Log log = LogFactory.getLog(CmsTheme.class);
-
-       private String themeId;
-       private Map<String, ResourceLoader> css = new HashMap<>();
-       private Map<String, ResourceLoader> resources = new HashMap<>();
-
-       private String headerCss;
-       private List<String> 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("<style type='text/css'>\n");
-                       sb.append(headerCss);
-                       sb.append("\n</style>\n");
-               }
-               for (String link : fonts) {
-                       sb.append("<link rel='stylesheet' href='");
-                       sb.append(link);
-                       sb.append("'/>\n");
-               }
-               if (sb.length() == 0)
-                       return null;
-               else
-                       return sb.toString();
-       }
-
-       void addStyleSheets(Bundle themeBundle, ResourceLoader ssRL) {
-               Enumeration<URL> 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<URL> 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 (file)
index f4004e4..0000000
+++ /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 extends Widget> T style(T widget, String style) {
-               widget.setData(CmsConstants.STYLE, style);
-               return widget;
-       }
-
-       /** Enable markups on widget */
-       public static <T extends Widget> 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 extends Widget> 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("<img width='").append(width).append("' height='").append(height)
-                               .append("' src='").append(src).append("'");
-       }
-
-       public static String noImg(Point size) {
-               ResourceManager rm = RWT.getResourceManager();
-               return CmsUtils.img(rm.getLocation(NO_IMAGE), size);
-       }
-
-       public static String noImg() {
-               return noImg(NO_IMAGE_SIZE);
-       }
-
-       public static Image noImage(Point size) {
-               ResourceManager rm = RWT.getResourceManager();
-               InputStream in = null;
-               try {
-                       in = rm.getRegisteredContent(NO_IMAGE);
-                       ImageData id = new ImageData(in);
-                       ImageData scaled = id.scaledTo(size.x, size.y);
-                       Image image = new Image(Display.getCurrent(), scaled);
-                       return image;
-               } finally {
-                       try {
-                               in.close();
-                       } catch (IOException e) {
-                               // silent
-                       }
-               }
-       }
-
-       /** Lorem ipsum text to be used during development. */
-       public final static String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
-                       + " Etiam eleifend hendrerit sem, ac ultricies massa ornare ac."
-                       + " Cras aliquam sodales risus, vitae varius lacus molestie quis."
-                       + " Vivamus consequat, leo id lacinia volutpat, eros diam efficitur urna, finibus interdum risus turpis at nisi."
-                       + " Curabitur vulputate nulla quis scelerisque fringilla. Integer consectetur turpis id lobortis accumsan."
-                       + " Pellentesque commodo turpis ac diam ultricies dignissim."
-                       + " Curabitur sit amet dolor volutpat lacus aliquam ornare quis sed velit."
-                       + " Integer varius quis est et tristique."
-                       + " Suspendisse pharetra porttitor purus, eget condimentum magna."
-                       + " Duis vitae turpis eros. Sed tincidunt lacinia rutrum."
-                       + " Aliquam velit velit, rutrum ut augue sed, condimentum lacinia augue.";
-
-       /** Singleton. */
-       private CmsUtils() {
-       }
-}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/DefaultImageManager.java b/org.argeo.cms.ui/src/org/argeo/cms/util/DefaultImageManager.java
deleted file mode 100644 (file)
index 015ca1c..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-package org.argeo.cms.util;
-
-import static javax.jcr.Node.JCR_CONTENT;
-import static javax.jcr.Property.JCR_DATA;
-import static javax.jcr.nodetype.NodeType.NT_FILE;
-import static javax.jcr.nodetype.NodeType.NT_RESOURCE;
-import static org.argeo.cms.ui.CmsConstants.NO_IMAGE_SIZE;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-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.ui.CmsImageManager;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.service.ResourceManager;
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-
-/** Manages only public images so far. */
-public class DefaultImageManager implements CmsImageManager {
-       private final static Log log = LogFactory.getLog(DefaultImageManager.class);
-//     private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
-
-       public Boolean load(Node node, Control control, Point preferredSize) throws RepositoryException {
-               Point imageSize = getImageSize(node);
-               Point size;
-               String imgTag = null;
-               if (preferredSize == null || imageSize.x == 0 || imageSize.y == 0
-                               || (preferredSize.x == 0 && preferredSize.y == 0)) {
-                       if (imageSize.x != 0 && imageSize.y != 0) {
-                               // actual image size if completely known
-                               size = imageSize;
-                       } else {
-                               // no image if not completely known
-                               size = resizeTo(NO_IMAGE_SIZE, preferredSize != null ? preferredSize : imageSize);
-                               imgTag = CmsUtils.noImg(size);
-                       }
-
-               } else if (preferredSize.x != 0 && preferredSize.y != 0) {
-                       // given size if completely provided
-                       size = preferredSize;
-               } else {
-                       // at this stage :
-                       // image is completely known
-                       assert imageSize.x != 0 && imageSize.y != 0;
-                       // one and only one of the dimension as been specified
-                       assert preferredSize.x == 0 || preferredSize.y == 0;
-                       size = resizeTo(imageSize, preferredSize);
-               }
-
-               boolean loaded = false;
-               if (control == null)
-                       return loaded;
-
-               if (control instanceof Label) {
-                       if (imgTag == null) {
-                               // IMAGE RETRIEVED HERE
-                               imgTag = getImageTag(node, size);
-                               //
-                               if (imgTag == null)
-                                       imgTag = CmsUtils.noImg(size);
-                               else
-                                       loaded = true;
-                       }
-
-                       Label lbl = (Label) control;
-                       lbl.setText(imgTag);
-                       // lbl.setSize(size);
-               } else if (control instanceof FileUpload) {
-                       FileUpload lbl = (FileUpload) control;
-                       lbl.setImage(CmsUtils.noImage(size));
-                       lbl.setSize(size);
-                       return loaded;
-               } else
-                       loaded = false;
-
-               return loaded;
-       }
-
-       private Point resizeTo(Point orig, Point constraints) {
-               if (constraints.x != 0 && constraints.y != 0) {
-                       return constraints;
-               } else if (constraints.x == 0 && constraints.y == 0) {
-                       return orig;
-               } else if (constraints.y == 0) {// force width
-                       return new Point(constraints.x, scale(orig.y, orig.x, constraints.x));
-               } else if (constraints.x == 0) {// force height
-                       return new Point(scale(orig.x, orig.y, constraints.y), constraints.y);
-               }
-               throw new CmsException("Cannot resize " + orig + " to " + constraints);
-       }
-
-       private int scale(int origDimension, int otherDimension, int otherConstraint) {
-               return Math.round(origDimension * divide(otherConstraint, otherDimension));
-       }
-
-       private float divide(int a, int b) {
-               return ((float) a) / ((float) b);
-       }
-
-       public Point getImageSize(Node node) throws RepositoryException {
-               // TODO load the SWT image ?
-               return new Point(0, 0);
-       }
-
-       /** @return null if not available */
-       @Override
-       public String getImageTag(Node node) throws RepositoryException {
-               return getImageTag(node, getImageSize(node));
-       }
-
-       private String getImageTag(Node node, Point size) throws RepositoryException {
-               StringBuilder buf = getImageTagBuilder(node, size);
-               if (buf == null)
-                       return null;
-               return buf.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 (file)
index b7bf910..0000000
+++ /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 <b>not</b>
-        * 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 (file)
index 79fd7cb..0000000
+++ /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 (file)
index 327438b..0000000
+++ /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<String, Map<String, String>> branding = new HashMap<String, Map<String, String>>();
-       private Map<String, List<String>> styleSheets = new HashMap<String, List<String>>();
-
-       private List<String> resources = new ArrayList<String>();
-
-       private BundleContext bundleContext;
-
-       private Repository repository;
-       private String workspace = null;
-       private String jcrBasePath = "/";
-       private List<String> roPrincipals = Arrays.asList(NodeConstants.ROLE_ANONYMOUS, NodeConstants.ROLE_USER);
-       private List<String> rwPrincipals = Arrays.asList(NodeConstants.ROLE_USER);
-
-       private CmsUiProvider header;
-       private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
-
-       private Integer headerHeight = 40;
-
-       private ServiceRegistration<ApplicationConfiguration> 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<String, String> defaultBranding = null;
-                       if (branding.containsKey("*"))
-                               defaultBranding = branding.get("*");
-                       String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
-
-                       // entry points
-                       for (String page : pages.keySet()) {
-                               Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
-                                               : new HashMap<String, String>();
-                               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<Bundle> 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<String> 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<String, String> props = new Hashtable<String, String>();
-               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<String, CmsUiProvider> pages) {
-               this.pages = pages;
-       }
-
-       public void setJcrBasePath(String basePath) {
-               this.jcrBasePath = basePath;
-       }
-
-       public void setRoPrincipals(List<String> roPrincipals) {
-               this.roPrincipals = roPrincipals;
-       }
-
-       public void setRwPrincipals(List<String> rwPrincipals) {
-               this.rwPrincipals = rwPrincipals;
-       }
-
-       public void setHeaderHeight(Integer headerHeight) {
-               this.headerHeight = headerHeight;
-       }
-
-       public void setBranding(Map<String, Map<String, String>> branding) {
-               this.branding = branding;
-       }
-
-       public void setStyleSheets(Map<String, List<String>> styleSheets) {
-               this.styleSheets = styleSheets;
-       }
-
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       public void setResources(List<String> 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<String, String> properties;
-
-               public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
-                               Map<String, String> 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 = "<div"
-                       + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
-                       + "<img src=\"./rwt-resources/" + LOADING_IMAGE
-                       + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
-}
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 (file)
index ae1299d..0000000
+++ /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<CmsUiProvider> lead = new ArrayList<CmsUiProvider>();
-       private List<CmsUiProvider> center = new ArrayList<CmsUiProvider>();
-       private List<CmsUiProvider> end = new ArrayList<CmsUiProvider>();
-
-       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<CmsUiProvider> 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<CmsUiProvider> lead) {
-               this.lead = lead;
-       }
-
-       public void setCenter(List<CmsUiProvider> center) {
-               this.center = center;
-       }
-
-       public void setEnd(List<CmsUiProvider> end) {
-               this.end = end;
-       }
-
-       public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
-               this.subPartsSameWidth = subPartsSameWidth;
-       }
-
-       public List<CmsUiProvider> getLead() {
-               return lead;
-       }
-
-       public List<CmsUiProvider> getCenter() {
-               return center;
-       }
-
-       public List<CmsUiProvider> 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 (file)
index dd95e7f..0000000
+++ /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("<b>" + context.getName() + "</b>");
-               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("<i>Children:</i>");
-               // 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("<i>Properties:</i>");
-               // 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 = "<binary>";
-                       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 (file)
index 3f06d88..0000000
+++ /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<String, String> 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 (file)
index 4e90766..0000000
+++ /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 (file)
index 8ac3e96..0000000
+++ /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 (file)
index bde67e4..0000000
+++ /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 (file)
index face42b..0000000
+++ /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<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
-
-       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 (file)
index 5fa79a3..0000000
+++ /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("&amp;body=").append(encoded);
-               } catch (UnsupportedEncodingException e) {
-                       mailToUrl.append("&amp;body=").append("Could not encode: ")
-                                       .append(e.getMessage());
-               }
-               Label mailTo = new Label(pane, SWT.NONE);
-               CmsUtils.markup(mailTo);
-               mailTo.setText("<a href=\"" + mailToUrl + "\">Send details</a>");
-               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 (file)
index fdc1cb7..0000000
+++ /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<URL> 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 (file)
index 58b470d..0000000
+++ /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 (file)
index 839567f..0000000
+++ /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 (file)
index d0ea610..0000000
+++ /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<CmsUiProvider> items = new ArrayList<CmsUiProvider>();
-
-       @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<CmsUiProvider> getItems() {
-               return items;
-       }
-
-       public void setItems(List<CmsUiProvider> 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 (file)
index eaaf4bc..0000000
+++ /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<Void>) () -> {
-                       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 (file)
index 99f8acf..0000000
+++ /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 (file)
index 52e5a88..0000000
+++ /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<T extends Item> {
-       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 (file)
index bc31663..0000000
+++ /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 (file)
index db9a60a..0000000
+++ /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<Node> {
-       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 (file)
index 50fdd06..0000000
+++ /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<Property> {
-       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 (file)
index e59b57d..0000000
+++ /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<String, Section> getSubSections() throws RepositoryException {
-               LinkedHashMap<String, Section> result = new LinkedHashMap<String, Section>();
-               for (Control child : getChildren()) {
-                       if (child instanceof Composite) {
-                               collectDirectSubSections((Composite) child, result);
-                       }
-               }
-               return Collections.unmodifiableMap(result);
-       }
-
-       private void collectDirectSubSections(Composite composite, LinkedHashMap<String, Section> 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 (file)
index 6cd45c5..0000000
+++ /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 (file)
index 2e70eb8..0000000
+++ /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 (file)
index e7c56ea..0000000
+++ /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 (file)
index 59c4823..0000000
+++ /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 (file)
index 358b453..0000000
+++ /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 (file)
index c36ed20..0000000
+++ /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 (file)
index 4d999d5..0000000
+++ /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 (file)
index be5bc5f..0000000
+++ /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 (file)
index b86fcb0..0000000
+++ /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 (file)
index cc0e766..0000000
+++ /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<Locale> 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<Locale> 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 (file)
index dea632d..0000000
+++ /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 (file)
index 1f72e23..0000000
+++ /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:<br>
- * <ul>
- * <li>{@link PasswordCallback}</li>
- * <li>{@link NameCallback}</li>
- * <li>{@link TextOutputCallback}</li>
- * </ul>
- * Supported Argeo {@link Callback}s are:<br>
- * <ul>
- * <li>{@link LocaleChoice}</li>
- * </ul>
- */
-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 (file)
index b8de34b..0000000
+++ /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 (file)
index b206355..0000000
+++ /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 (file)
index b3ae47d..0000000
+++ /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<Locale> locales;
-
-       private Integer selectedIndex = null;
-       private final Integer defaultIndex;
-
-       public LocaleChoice(List<Locale> 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<Locale> getLocales() {
-               return locales;
-       }
-
-       public Locale getDefaultLocale() {
-               return locales.get(getDefaultIndex());
-       }
-}
index 44ef29d6a6dcf2c81e2e498fe6a5d9879e9fc5d8..893067f6165af18c49df01dd1c9c24c8a2e5f78c 100644 (file)
@@ -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"));
index 69c833e9c0afd5990eb0a93747d31432d52398a7..78cfb65bd27c507cc50acf2bbe3091eb0e9c0120 100644 (file)
@@ -13,7 +13,7 @@
        <dependencies>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.node.api</artifactId>
+                       <artifactId>org.argeo.api</artifactId>
                        <version>2.1.88-SNAPSHOT</version>
                </dependency>
                <dependency>
index ab40e720c2eaf58b2b3d1a9457ee86c8dedbb47a..4d1fceb58cd5b0c9ea44aebe7a72a796d2a1312c 100644 (file)
@@ -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;
index 43bddaf8d002a727a9000e93cd056010b8994349..e1425015c1838965df660d6d982238bdacb65ea6 100644 (file)
@@ -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;
 
 /**
index 09fece03aa560fb9e6e350b2ae811b3e47b40e1e..4a4151edc47146b37d36b87fe01a3a85ea352e5b 100644 (file)
@@ -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 */
index 4d2cc33390183f406a8fd1a71b2552ae479936d5..e3da327e6e2e4e1db08c6c004507828d0082005a 100644 (file)
@@ -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 {
index 692d214bcccc23ce5fe51303ef9e66336bccc3a4..f7d426e26c31036baf4a678e74438c536e4b1b59 100644 (file)
@@ -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;
index 326b0f4da3bc62ee6486e3b556397473b8479433..3dbc7ad52fa03a7792afecaf6cd53757604bba7e 100644 (file)
@@ -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 (file)
index 78d717a..0000000
+++ /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<String> getKeys() {
-               Vector<String> res = new Vector<String>();
-               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 (file)
index 71a3141..0000000
+++ /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<Locale> 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 (file)
index 7d32193..0000000
+++ /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 {
-}
index cb3a3484a87cb1c96efb966809c20da7321cb58d..5bc13526c64f94363a15ec0c314dab96af3c10cb 100644 (file)
@@ -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;
index 50967391c8c6d42e3e66fd71e7681a1d425f8d5a..bc37b5f4f4e8b3f6a473b963bd53b5df5a081955 100644 (file)
@@ -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 {
index c968d779d49e0d1ddbb284102d054cec5de43edd..4c7c8997af3f2dfff96dfd81bd8e2451206f7ba3 100644 (file)
@@ -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;
index b2e5a7ed3733423b78ccdee06be50ff3a3177efb..11a6944b29efc2fc739d3dc52e653e193fb4f55b 100644 (file)
@@ -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;
index ce38cf0ee94514ed445661707bb9d27e180e6563..f8d5863e69321f530ca818fc89c86b23b5f9ed64 100644 (file)
@@ -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;
index 869dbb23c9a2e76989091de5432563a56687ac8e..d9b8f42d1c4a35b8c108d9933a402dd2fb510b3a 100644 (file)
@@ -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;
index 93f63530e3edfa9709f13ec4b089633230ceb9b2..df469524dece5d5d5fafef54d03280956cd8e24f 100644 (file)
@@ -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;
index f9e17a107ea9d3f17f7e6ce722ef5f084055bb52..f81f26f7a2e2d22fbd298b6ddfdaea06bcdb3a1a 100644 (file)
@@ -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;
index b3ab11a3357f0c4d0ff3e1c38590f511dc6919e1..872d8ae531e977d3ea58a16859f184e3692813f7 100644 (file)
@@ -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. */
index 5728d4fa56386bbddc938604dc315511dbd25ba1..d2d7d25b7cae3602728a03771af0872269bd0e41 100644 (file)
@@ -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;
index dc64590874f5d50358a9d067be381391065efd35..b81a81b3b770901a14448f2c8740809981e627b1 100644 (file)
@@ -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;
index 3304bb41d28a434dc227ab68c1ee7a9cb8b4b6fc..dea21a2e7d2d76c6e076b2f785f19859d81001f5 100644 (file)
@@ -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;
 
index c972f725ce48ddbb2d7124145c576c87643bfa45..1af4095ce268de20a41c3b1feafa96306f330805 100644 (file)
@@ -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;
index 6c97fa14132c6628129e63affccac7c1adb10faa..ca469f0f5b08116c78658fdc8d15154a282aa7b4 100644 (file)
@@ -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;
index 19117a892b2d5bb414e83e664a38f92ab1bf16f2..acf0dbf7cbf9dbd97e4aa95d26df1c068d7586a1 100644 (file)
@@ -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;
index d52fe663bdea694c360ecadb622fa4d02401c09f..9d2925948ceef806191c82f9aa508697ad755da3 100644 (file)
@@ -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;
index 9c5e5f5f78587bd9954ebfd5eeb5968fc4d66712..375175bb1b8475ce225ff34dc0bef80e7406e2c6 100644 (file)
@@ -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.
index 6eaebeb6499c612449bc82543faee65392264cff..390131aa76b19e5ad9d37258028fb907a1d11ae5 100644 (file)
@@ -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;
 
 /**
index c59a599f101fa9941a86a38d33e2087426158258..35575de1da27526725e89bce78fc62868836f03b 100644 (file)
@@ -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);
index 13ff4b8b2780eec7d309dfd3231b289d8be3cf40..8fa7779f9d5d00c3d1f1601af87e6c973aa36f00 100644 (file)
@@ -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" };
index 86289f0ff648544fffebe643241b123153237db2..bf8e8d38e462a58a6821e5a41c4bed69b53a092a 100644 (file)
@@ -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;
index 4356d18ff3b43b3e6debc5e938b2dcf45f0f15a7..fd085e2145606ac6996fd17b35e24e5f362d4dee 100644 (file)
@@ -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;
index 3174285dbfdf93658a53014f744d66407449e302..ea764c3fb2e400fa08d62c13cc11453a55a65796 100644 (file)
@@ -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;
index 947d4d89e6e90a0199cb8dd1ea84bfaceeaf0bf1..1d296c5b9cc45f2a9a057bbf827ddfa08d36ccf2 100644 (file)
@@ -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;
index ae6623826d21651064bd16c3405b1d2c29cd931d..47e8b5cd5e538d01497dd4fa682366daee997c2d 100644 (file)
@@ -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;
index ba4ad836041ae8de7ce8093f14f3bb055a9dd0ec..cb80f7c7f9f446794604a0b0ecc6b9d5f63cdac4 100644 (file)
@@ -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;
index be99bf20e07a2109a5e3ac13c1597efd8cbc9e38..a06b12bbd1852491de6a6f0f90a43167eabc645b 100644 (file)
@@ -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;
index 9e6e3b96bd341145a4d92995e121bdaf8ec17a63..127cb9a8a6207928f3b93f2475629c7e4ef951e4 100644 (file)
@@ -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;
index 1d43afd9af8fe59b87f33b0f46cbc3b0035f1c25..b9f05a4abfb5deea7fc5a71a293eb3344585a280 100644 (file)
@@ -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 {
index e54277a3c73fcb1b9dbcc2a4fe5c1908db9f3ad1..9a59613e9d944610c829943d2408cc6562666172 100644 (file)
@@ -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 {
index 779406a1dabc043172eabc4ab58682794d727bda..0e356edf7e9d82db076313627bb111e1db728da0 100644 (file)
@@ -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 {
index c75d38fc88f54c18fc80414ccba8b29acd076a09..45b7c4beb65ffb1b16b1110a04ecc53bfcf96c81 100644 (file)
@@ -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 {
index d22f44e735cbd66d08863b224f85debd569f863c..f4b53f875eab79cc617088dfc38990ed38a95fc4 100644 (file)
@@ -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}. */
index 04dce92c809c620586c1e001a7d90dabe74161a8..c13d6224490ed12df1adb2d235bebd7992655a30 100644 (file)
@@ -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. */
index a6e1e286c87b2e60399bbe05e44e15fce47f4d86..e581e2b3510db2b3f5aef1ccdf14ea203e192a41 100644 (file)
@@ -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. */
index f72527af18dee1768d926867ccd27e389eb11113..652f25298b0e3e4e2c7fd54f4f6353f1de975959 100644 (file)
@@ -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. */
index 0b6fdb5ef499f50084db111d6fdf0f1319d96465..fb50c88ef8ac0c659ade648dcdb097e6ba6b5fc3 100644 (file)
@@ -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,\
 *
index efc07337d6b6e15e4b36beb87db50197fbc2b9cf..3a12c522b67d44685cfc58d744e36db5ae7f466d 100644 (file)
@@ -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;
index a181a29936324a2fe75a15173179bba163246b2b..4a75cc1f6eb20c11f91bf1a8df11f3c798dd2e48 100644 (file)
@@ -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 (file)
index 0000000..88006ff
--- /dev/null
@@ -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 (file)
index 0000000..3fb1308
--- /dev/null
@@ -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<Item> {
+       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 (file)
index 0000000..5111f7a
--- /dev/null
@@ -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 (file)
index 0000000..16f4d9a
--- /dev/null
@@ -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
+ * <code> JcrFileProvider </code>, 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 (file)
index 472101f..0000000
+++ /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 (file)
index 015670b..0000000
+++ /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<Item> {
-       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 (file)
index db8ca08..0000000
+++ /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 (file)
index 05754d0..0000000
+++ /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
- * <code> JcrFileProvider </code>, 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);
-               }
-       }
-}
index d6dc82cef7326f22d338b5fa3bb48fd6da5517f8..57139056c0e320f02427d4e115877d3e1b34a1c8 100644 (file)
@@ -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 (file)
index 0000000..b99f37a
--- /dev/null
@@ -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 (file)
index 0000000..dbebb79
--- /dev/null
@@ -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 (file)
index a45eeda..0000000
+++ /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 (file)
index 3c029d3..0000000
+++ /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;
-       }
-}
index 3422e55e7ca7844e14d184fa234d663a98eeb42c..c114be661acba0b9d7ebb7cba0c2f0f238cb94a4 100644 (file)
@@ -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
index 3dbc51f9c9a8ed9874ca03dfe0635b8ee33570a4..7893b81d946b926dd6ab0a2d7fcda38949cb6175 100644 (file)
@@ -1,3 +1,3 @@
 Fragment-Host: org.apache.jackrabbit.core
-Import-Package: org.argeo.node,\
+Import-Package: org.argeo.api,\
 *
index 65d5134bc8a8423afce3ab9c7185efe5072495f5..d8a529de887b9e8ad310cbb1f8b486b6f33ea087 100644 (file)
@@ -12,7 +12,7 @@
        <dependencies>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.node.api</artifactId>
+                       <artifactId>org.argeo.api</artifactId>
                        <version>2.1.88-SNAPSHOT</version>
                </dependency>
                <dependency>
index 36401596f0fb38192c1d319af5abcc82f7e9275c..3976f3ba97e21c6f37c49a2c8143f2c9aa0cd2f1 100644 (file)
@@ -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;
 
index f7de8d003f73cb68562fc6b968a8882e90973cb1..4b7734623963a3dc578041aa853b2b9a7cbae3af 100644 (file)
@@ -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<org.argeo.node.security.AnonymousPrincipal> anonPrincipal = subject.getPrincipals(org.argeo.node.security.AnonymousPrincipal.class);
+               Set<org.argeo.api.security.AnonymousPrincipal> anonPrincipal = subject.getPrincipals(org.argeo.api.security.AnonymousPrincipal.class);
                if (!anonPrincipal.isEmpty()) {
                        subject.getPrincipals().add(new AnonymousPrincipal());
                        return true;
index a51b73c2b9b01330eaa73220201360b41b14c9aa..b65148668060708a09c44545d8d7d0e54c9c59bb 100644 (file)
@@ -23,7 +23,7 @@
                </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.node.api</artifactId>
+                       <artifactId>org.argeo.api</artifactId>
                        <version>2.1.88-SNAPSHOT</version>
                </dependency>
        </dependencies>
index 6755a647ab83e2666b873392d9e091b9fb60e349..c756bd8e048447bba544d725ca39f0e61972cad6 100644 (file)
@@ -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;
index 6d1016a06d0ec88865783e2b4b1cd4dcc1bee629..a292dcf23fb5d7d7e69d18cdcd0561608a9f14bd 100644 (file)
@@ -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 (file)
index eca7bdb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.argeo.node.api/.gitignore b/org.argeo.node.api/.gitignore
deleted file mode 100644 (file)
index 09e3bc9..0000000
+++ /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 (file)
index 9573f0c..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.node.api</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/org.argeo.node.api/META-INF/.gitignore b/org.argeo.node.api/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /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 (file)
index f0bd0ce..0000000
+++ /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 (file)
index 34d2e4d..0000000
+++ /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 (file)
index c148f2a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <parent>
-               <groupId>org.argeo.commons</groupId>
-               <artifactId>argeo-commons</artifactId>
-               <version>2.1.88-SNAPSHOT</version>
-               <relativePath>..</relativePath>
-       </parent>
-       <artifactId>org.argeo.node.api</artifactId>
-       <name>Argeo Node API</name>
-       <packaging>jar</packaging>
-       <dependencies>
-       </dependencies>
-</project>
\ 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 (file)
index 303bbef..0000000
+++ /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 (file)
index 213286d..0000000
+++ /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 (file)
index 3074748..0000000
+++ /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<String, ?> sharedState,
-                       Map<String, ?> 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 (file)
index 58e4a64..0000000
+++ /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 (file)
index 4c52b20..0000000
+++ /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<V, M, W> extends BiFunction<V, M, W> {
-       /**
-        * 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 (file)
index c639c75..0000000
+++ /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 (file)
index 8e5558d..0000000
+++ /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 (file)
index aa1b5ce..0000000
+++ /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 (file)
index 7ded111..0000000
+++ /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 (file)
index 387d511..0000000
+++ /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 (file)
index d7148c6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.node;
-
-import java.util.List;
-import java.util.Locale;
-
-public interface NodeState {
-       Locale getDefaultLocale();
-
-       List<Locale> 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 (file)
index 891f546..0000000
+++ /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 (file)
index a735e18..0000000
+++ /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<String, String> parameters = new HashMap<String, String>();
-                       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<String, String> parameters = new HashMap<String, String>();
-                       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<Session>() {
-
-                       @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 (file)
index a2306c6..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<ldap = 'http://www.argeo.org/ns/ldap'>
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 (file)
index d8a26b6..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<node = 'http://www.argeo.org/ns/node'>
-
-[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 (file)
index fda3bae..0000000
+++ /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 (file)
index 141f9d1..0000000
+++ /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 (file)
index dd34022..0000000
+++ /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 (file)
index 53d2ced..0000000
+++ /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 (file)
index fe054c3..0000000
+++ /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. <b>Experimental. This API may
- * change.</b>
- */
-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 (file)
index 7c784b0..0000000
+++ /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<LdapName> 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 (file)
index f03ba9d..0000000
+++ /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 (file)
index 97bf025..0000000
+++ /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 (file)
index 5cd11d1..0000000
+++ /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 (file)
index a5aa9f6..0000000
+++ /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 <code>null</code> is none available. */
-       public List<TabularColumn> 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 (file)
index f652df9..0000000
+++ /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 (file)
index 98a04a6..0000000
+++ /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<TabularRow> {
-       /**
-        * 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 (file)
index b7febee..0000000
+++ /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 (file)
index 0000000..31a2be4
--- /dev/null
@@ -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<String, TesterStatus> 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<Method> 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<Method> findMethods(Class<?> clss) {
+               List<Method> methods = new ArrayList<Method>();
+//             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<String, TesterStatus> 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 (file)
index 0000000..d1d14ed
--- /dev/null
@@ -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 (file)
index 36e27de..0000000
+++ /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<String, TesterStatus> 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<Method> 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<Method> findMethods(Class<?> clss) {
-               List<Method> methods = new ArrayList<Method>();
-//             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<String, TesterStatus> 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 (file)
index d533e94..0000000
+++ /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 713a3916b24cff979c6db11b3287025938a2f18a..ed1b7d1671414d950811b389ede0d1a665b8a73f 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
                <module>org.argeo.eclipse.ui</module>
                <module>org.argeo.eclipse.ui.rap</module>
                <!-- CMS -->
-               <module>org.argeo.node.api</module>
+               <module>org.argeo.api</module>
                <module>org.argeo.maintenance</module>
                <module>org.argeo.cms</module>
                <module>org.argeo.cms.ui.theme</module>
@@ -195,7 +195,7 @@ limitations under the License.
                                <configuration>
                                        <failOnError>false</failOnError>
                                        <additionalJOption>-Xdoclint:none</additionalJOption>
-                                       <excludePackageNames>*.internal.*,org.eclipse.*</excludePackageNames>
+                                       <excludePackageNames>*.internal.*,org.eclipse.*,org.argeo.cms.ui.eclipse.forms.*</excludePackageNames>
                                        <encoding>UTF-8</encoding>
                                        <detectLinks>true</detectLinks>
                                        <links>