Split CMS and CMS UI
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 14 Sep 2016 16:18:54 +0000 (16:18 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 14 Sep 2016 16:18:54 +0000 (16:18 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@9141 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

184 files changed:
dep/org.argeo.dep.cms.node/pom.xml
dep/org.argeo.dep.cms.platform/pom.xml
org.argeo.cms.ui.workbench.rap/pom.xml
org.argeo.cms.ui.workbench/pom.xml
org.argeo.cms.ui/.classpath [new file with mode: 0644]
org.argeo.cms.ui/.project [new file with mode: 0644]
org.argeo.cms.ui/bnd.bnd [new file with mode: 0644]
org.argeo.cms.ui/build.properties [new file with mode: 0644]
org.argeo.cms.ui/pom.xml [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditableLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditableMultiStringProperty.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyDate.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/EditablePropertyString.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormConstants.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormEditorHeader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormPageViewer.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormStyle.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/forms/FormUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/Browse.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/DataDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/LogDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceStyles.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/MaintenanceUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/NonAdminPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/CustomTextEditor.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/Img.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/Paragraph.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/text/WikiPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/SectionTitle.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/BundleResourceLoader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/CmsUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/LoginEntryPoint.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/MenuLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleApp.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleCmsHeader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleDynamicPages.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleErgonomics.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleStaticPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/StyleSheetResourceLoader.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/SystemNotifications.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/UserMenuLink.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/AbstractPageViewer.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/EditablePart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/ItemPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/NodePart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/PropertyPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/Section.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/viewers/SectionPart.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableImage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/EditableText.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/ScrolledPage.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/StyledControl.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLogin.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CmsLoginShell.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/security/core/AbstractSystemExecution.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java [new file with mode: 0644]
org.argeo.cms/bnd.bnd
org.argeo.cms/src/org/argeo/cms/CmsView.java
org.argeo.cms/src/org/argeo/cms/DataMigration.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/UxContext.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/auth/CurrentUser.java
org.argeo.cms/src/org/argeo/cms/forms/EditableLink.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/EditableMultiStringProperty.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyDate.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/EditablePropertyString.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/FormConstants.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/FormEditorHeader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/FormPageViewer.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/FormStyle.java [deleted file]
org.argeo.cms/src/org/argeo/cms/forms/FormUtils.java [deleted file]
org.argeo.cms/src/org/argeo/cms/i18n/LocaleUtils.java
org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/auth/ConsoleCallbackHandler.java
org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/backup/RepositoryMigration.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeHttp.java
org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/text/SectionTitle.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java [deleted file]
org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/Browse.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/DataDeploymentUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/DataMigration.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/LogDeploymentUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceStyles.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/NonAdminPage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/CustomTextEditor.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/Img.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/Paragraph.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/TextSection.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/TextStyles.java [deleted file]
org.argeo.cms/src/org/argeo/cms/text/WikiPage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/ui/UxContext.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/CmsLink.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/LoginEntryPoint.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/MenuLink.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleCmsHeader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleErgonomics.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SimpleUxContext.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/SystemNotifications.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/UserMenu.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java [deleted file]
org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/EditablePart.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/ItemPart.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/NodePart.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/PropertyPart.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/Section.java [deleted file]
org.argeo.cms/src/org/argeo/cms/viewers/SectionPart.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/EditableImage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/EditableText.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/JcrComposite.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/ScrolledPage.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/StyledControl.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java [deleted file]
org.argeo.cms/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java [deleted file]
org.argeo.cms/src/org/argeo/security/core/AbstractSystemExecution.java [deleted file]
org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java [deleted file]
org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java [deleted file]
org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java [deleted file]
pom.xml

index 1c08089650ccee69ae15a979cc0cb5a516538fab..21c8c429711054af9c7d666ccdb084c8046b9ea9 100644 (file)
                        <artifactId>org.argeo.cms</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms.ui</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
                        <artifactId>org.argeo.enterprise</artifactId>
index 91a35419897a6dc2f87ca7275688ed4dcc321c98..25590aa82887f2ccfa6fade475852616654272e5 100644 (file)
                        <artifactId>org.argeo.dep.cms.node</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
                        <artifactId>org.argeo.eclipse.ui.workbench</artifactId>
index 2822051e272ef707e901959c42ee1d7a6544fd18..7fcce46ba825420a793930f461ef5750be388f41 100644 (file)
        <dependencies>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.util</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.security.ui</artifactId>
+                       <artifactId>org.argeo.cms.ui.workbench</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <artifactId>org.argeo.eclipse.ui.rap</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms</artifactId>
-                       <version>2.1.46-SNAPSHOT</version>
-               </dependency>
        </dependencies>
 </project>
\ No newline at end of file
index 2dccd6a3f6437510862e4460540abcf7d54beb14..1a2cd342926fb6d6bcc0c2f3b322b22523c84f85 100644 (file)
@@ -13,7 +13,7 @@
        <dependencies>
                <dependency>
                        <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms</artifactId>
+                       <artifactId>org.argeo.cms.ui</artifactId>
                        <version>2.1.46-SNAPSHOT</version>
                </dependency>
                                <dependency>
diff --git a/org.argeo.cms.ui/.classpath b/org.argeo.cms.ui/.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.cms.ui/.project b/org.argeo.cms.ui/.project
new file mode 100644 (file)
index 0000000..e52eb8e
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.ui</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.cms.ui/bnd.bnd b/org.argeo.cms.ui/bnd.bnd
new file mode 100644 (file)
index 0000000..e472a2c
--- /dev/null
@@ -0,0 +1,11 @@
+Bundle-Activator: org.argeo.cms.ui.internal.Activator
+Bundle-ActivationPolicy: lazy
+
+Import-Package: org.eclipse.swt,\
+org.eclipse.jface.window,\
+org.eclipse.core.commands,\
+javax.jcr.security,\
+org.argeo.eclipse.ui.dialogs,\
+org.springframework.context,\
+org.springframework.core.io,\
+*
\ No newline at end of file
diff --git a/org.argeo.cms.ui/build.properties b/org.argeo.cms.ui/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.cms.ui/pom.xml b/org.argeo.cms.ui/pom.xml
new file mode 100644 (file)
index 0000000..90fffa5
--- /dev/null
@@ -0,0 +1,26 @@
+<?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.46-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.cms.ui</artifactId>
+       <name>Argeo CMS UI</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rap</artifactId>
+                       <version>2.1.46-SNAPSHOT</version>
+                       <scope>provided</scope>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..ece0be3
--- /dev/null
@@ -0,0 +1,75 @@
+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
new file mode 100644 (file)
index 0000000..fdaa036
--- /dev/null
@@ -0,0 +1,267 @@
+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 legal 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 = possibleValues;
+               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
new file mode 100644 (file)
index 0000000..928a285
--- /dev/null
@@ -0,0 +1,304 @@
+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
new file mode 100644 (file)
index 0000000..dd3ff29
--- /dev/null
@@ -0,0 +1,80 @@
+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
new file mode 100644 (file)
index 0000000..18df3e4
--- /dev/null
@@ -0,0 +1,7 @@
+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
new file mode 100644 (file)
index 0000000..c12e2f0
--- /dev/null
@@ -0,0 +1,114 @@
+package org.argeo.cms.forms;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.jcr.Node;
+
+import org.argeo.cms.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
new file mode 100644 (file)
index 0000000..8526fca
--- /dev/null
@@ -0,0 +1,641 @@
+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.CmsEditable;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.ui.internal.text.MarkupValidatorCopy;
+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.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 implements
+                       CmsNames {
+
+               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-.]", "_");
+
+                       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
+       protected Node getOrCreateNode(Node parent, String nodeType, String nodeName)
+                       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
new file mode 100644 (file)
index 0000000..1b9880d
--- /dev/null
@@ -0,0 +1,26 @@
+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,
+       // Date
+       popupCalendar,
+       // Buttons
+       starred, unstarred, starOverlay, 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
new file mode 100644 (file)
index 0000000..f8d08b0
--- /dev/null
@@ -0,0 +1,197 @@
+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.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 '&' by its HTML encoding '&#38;' to
+        * avoid <code>SAXParseException</code> while rendering HTML with RWT
+        */
+       public static String replaceAmpersand(String value) {
+               value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
+               return value;
+       }
+
+       // Prevents instantiation
+       private FormUtils() {
+       }
+}
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
new file mode 100644 (file)
index 0000000..2494d90
--- /dev/null
@@ -0,0 +1,43 @@
+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.layout.GridLayout;
+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
new file mode 100644 (file)
index 0000000..7dafd72
--- /dev/null
@@ -0,0 +1,616 @@
+package org.argeo.cms.maintenance;
+
+import static javax.jcr.Node.JCR_CONTENT;
+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.CmsTypes;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.util.CmsLink;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.widgets.EditableImage;
+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 {
+               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
new file mode 100644 (file)
index 0000000..f4f3079
--- /dev/null
@@ -0,0 +1,48 @@
+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
new file mode 100644 (file)
index 0000000..efcbaa9
--- /dev/null
@@ -0,0 +1,139 @@
+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.jcr.ArgeoJcrConstants;
+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,
+                               "(" + ArgeoJcrConstants.JCR_REPOSITORY_ALIAS + "=*)");
+               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(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS).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
new file mode 100644 (file)
index 0000000..e21974c
--- /dev/null
@@ -0,0 +1,94 @@
+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) {
+               // 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
new file mode 100644 (file)
index 0000000..8fb9643
--- /dev/null
@@ -0,0 +1,74 @@
+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
new file mode 100644 (file)
index 0000000..fef25d7
--- /dev/null
@@ -0,0 +1,10 @@
+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
new file mode 100644 (file)
index 0000000..538379f
--- /dev/null
@@ -0,0 +1,13 @@
+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
new file mode 100644 (file)
index 0000000..b27d506
--- /dev/null
@@ -0,0 +1,30 @@
+package org.argeo.cms.maintenance;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.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
new file mode 100644 (file)
index 0000000..9fcdaf9
--- /dev/null
@@ -0,0 +1,85 @@
+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/text/CustomTextEditor.java b/org.argeo.cms.ui/src/org/argeo/cms/text/CustomTextEditor.java
new file mode 100644 (file)
index 0000000..c7491e0
--- /dev/null
@@ -0,0 +1,35 @@
+package org.argeo.cms.text;
+
+import static org.argeo.cms.util.CmsUtils.fillWidth;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.ui.internal.text.AbstractTextViewer;
+import org.argeo.cms.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Manages hardcoded sections as an arbitrary hierarchy under the main section,
+ * which contains no text and no title.
+ */
+public class CustomTextEditor extends AbstractTextViewer {
+       private static final long serialVersionUID = 5277789504209413500L;
+
+       public CustomTextEditor(Composite parent, int style, Node textNode,
+                       CmsEditable cmsEditable) throws RepositoryException {
+               this(new Section(parent, style, textNode), style, cmsEditable);
+       }
+
+       public CustomTextEditor(Section mainSection, int style,
+                       CmsEditable cmsEditable) throws RepositoryException {
+               super(mainSection, style, cmsEditable);
+               mainSection.setLayoutData(fillWidth());
+       }
+
+       @Override
+       public Section getMainSection() {
+               return super.getMainSection();
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java b/org.argeo.cms.ui/src/org/argeo/cms/text/IdentityTextInterpreter.java
new file mode 100644 (file)
index 0000000..79f6ede
--- /dev/null
@@ -0,0 +1,95 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+
+/** Based on HTML with a few Wiki-like shortcuts. */
+public class IdentityTextInterpreter implements TextInterpreter, CmsNames {
+
+       @Override
+       public void write(Item item, String content) {
+               try {
+                       if (item instanceof Node) {
+                               Node node = (Node) item;
+                               if (node.isNodeType(CmsTypes.CMS_STYLED)) {
+                                       String raw = convertToStorage(node, content);
+                                       validateBeforeStoring(raw);
+                                       node.setProperty(CMS_CONTENT, raw);
+                               } else {
+                                       throw new CmsException("Don't know how to interpret "
+                                                       + node);
+                               }
+                       } else {// property
+                               Property property = (Property) item;
+                               property.setValue(content);
+                       }
+                       // item.getSession().save();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot set content on " + item, e);
+               }
+       }
+
+       @Override
+       public String read(Item item) {
+               try {
+                       String raw = raw(item);
+                       return convertFromStorage(item, raw);
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get " + item + " for edit", e);
+               }
+       }
+
+       @Override
+       public String raw(Item item) {
+               try {
+                       item.getSession().refresh(true);
+                       if (item instanceof Node) {
+                               Node node = (Node) item;
+                               if (node.isNodeType(CmsTypes.CMS_STYLED)) {
+                                       // WORKAROUND FOR BROKEN PARARAPHS
+                                       if (!node.hasProperty(CMS_CONTENT)) {
+                                               node.setProperty(CMS_CONTENT, "");
+                                               node.getSession().save();
+                                       }
+
+                                       return node.getProperty(CMS_CONTENT).getString();
+                               } else {
+                                       throw new CmsException("Don't know how to interpret "
+                                                       + node);
+                               }
+                       } else {// property
+                               Property property = (Property) item;
+                               return property.getString();
+                       }
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get " + item + " content", e);
+               }
+       }
+
+       // EXTENSIBILITY
+       /**
+        * To be overridden, in order to make sure that only valid strings are being
+        * stored.
+        */
+       protected void validateBeforeStoring(String raw) {
+       }
+
+       /** To be overridden, in order to support additional formatting. */
+       protected String convertToStorage(Item item, String content)
+                       throws RepositoryException {
+               return content;
+
+       }
+
+       /** To be overridden, in order to support additional formatting. */
+       protected String convertFromStorage(Item item, String content)
+                       throws RepositoryException {
+               return content;
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/Img.java b/org.argeo.cms.ui/src/org/argeo/cms/text/Img.java
new file mode 100644 (file)
index 0000000..e0cf216
--- /dev/null
@@ -0,0 +1,151 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.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.argeo.cms.widgets.EditableImage;
+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/text/Paragraph.java b/org.argeo.cms.ui/src/org/argeo/cms/text/Paragraph.java
new file mode 100644 (file)
index 0000000..a7a7964
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableText;
+
+public class Paragraph extends EditableText implements SectionPart {
+       private static final long serialVersionUID = 3746457776229542887L;
+
+       private final TextSection section;
+
+       public Paragraph(TextSection section, int style, Node node)
+                       throws RepositoryException {
+               super(section, style, node);
+               this.section = section;
+               CmsUtils.style(this, TextStyles.TEXT_PARAGRAPH);
+       }
+
+       public Section getSection() {
+               return section;
+       }
+
+       @Override
+       public String getPartId() {
+               return getNodeId();
+       }
+
+       @Override
+       public Node getItem() throws RepositoryException {
+               return getNode();
+       }
+
+       @Override
+       public String toString() {
+               return "Paragraph #" + getPartId();
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java b/org.argeo.cms.ui/src/org/argeo/cms/text/StandardTextEditor.java
new file mode 100644 (file)
index 0000000..3d1dc3f
--- /dev/null
@@ -0,0 +1,48 @@
+package org.argeo.cms.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.ui.internal.text.AbstractTextViewer;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+/** Text editor where sections and subsections can be managed by the user. */
+public class StandardTextEditor extends AbstractTextViewer {
+       private static final long serialVersionUID = 6049661610883342325L;
+
+       public StandardTextEditor(Composite parent, int style, Node textNode,
+                       CmsEditable cmsEditable) throws RepositoryException {
+               super(new TextSection(parent, style, textNode), style, cmsEditable);
+               refresh();
+               getMainSection().setLayoutData(CmsUtils.fillWidth());
+       }
+
+       @Override
+       protected void initModel(Node textNode) throws RepositoryException {
+               if (isFlat())
+                       textNode.addNode(CMS_P).addMixin(CmsTypes.CMS_STYLED);
+               else
+                       textNode.setProperty(JCR_TITLE, textNode.getName());
+       }
+
+       @Override
+       protected Boolean isModelInitialized(Node textNode)
+                       throws RepositoryException {
+               return textNode.hasProperty(Property.JCR_TITLE)
+                               || textNode.hasNode(CMS_P)
+                               || (!isFlat() && textNode.hasNode(CMS_H));
+       }
+
+       @Override
+       public Section getMainSection() {
+               // TODO Auto-generated method stub
+               return super.getMainSection();
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextEditorHeader.java
new file mode 100644 (file)
index 0000000..e70a4d0
--- /dev/null
@@ -0,0 +1,90 @@
+package org.argeo.cms.text;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import org.argeo.cms.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;
+
+/** Adds editing capabilities to a page editing text */
+public class TextEditorHeader implements SelectionListener, Observer {
+       private static final long serialVersionUID = 4186756396045701253L;
+
+       private final CmsEditable cmsEditable;
+       private Button publish;
+
+       private Composite parent;
+       private Composite display;
+       private Object layoutData;
+
+       public TextEditorHeader(CmsEditable cmsEditable, Composite parent, int style) {
+               this.cmsEditable = cmsEditable;
+               this.parent = parent;
+               if (this.cmsEditable instanceof Observable)
+                       ((Observable) this.cmsEditable).addObserver(this);
+               refresh();
+       }
+
+       protected void refresh() {
+               if (display != null && !display.isDisposed())
+                       display.dispose();
+               display = null;
+               publish = null;
+               if (cmsEditable.isEditing()) {
+                       display = new Composite(parent, SWT.NONE);
+                       // display.setBackgroundMode(SWT.INHERIT_NONE);
+                       display.setLayoutData(layoutData);
+                       display.setLayout(CmsUtils.noSpaceGridLayout());
+                       CmsUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
+                       publish = new Button(display, SWT.FLAT | SWT.PUSH);
+                       publish.setText(getPublishButtonLabel());
+                       CmsUtils.style(publish, TextStyles.TEXT_EDITOR_HEADER);
+                       publish.addSelectionListener(this);
+                       display.moveAbove(null);
+               }
+               parent.layout();
+       }
+
+       private String getPublishButtonLabel() {
+               if (cmsEditable.isEditing())
+                       return "Publish";
+               else
+                       return "Edit";
+       }
+
+       @Override
+       public void widgetSelected(SelectionEvent e) {
+               if (e.getSource() == publish) {
+                       if (cmsEditable.isEditing()) {
+                               cmsEditable.stopEditing();
+                       } else {
+                               cmsEditable.startEditing();
+                       }
+                       // publish.setText(getPublishButtonLabel());
+               }
+       }
+
+       @Override
+       public void widgetDefaultSelected(SelectionEvent e) {
+       }
+
+       @Override
+       public void update(Observable o, Object arg) {
+               if (o == cmsEditable) {
+                       // publish.setText(getPublishButtonLabel());
+                       refresh();
+               }
+       }
+
+       public void setLayoutData(Object layoutData) {
+               this.layoutData = layoutData;
+               if (display != null && !display.isDisposed())
+                       display.setLayoutData(layoutData);
+       }
+
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextInterpreter.java
new file mode 100644 (file)
index 0000000..f39a2b3
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Item;
+
+/** Convert from/to data layer to/from presentation layer. */
+public interface TextInterpreter {
+       public String raw(Item item);
+
+       public String read(Item item);
+
+       public void write(Item item, String content);
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextSection.java
new file mode 100644 (file)
index 0000000..ac93e4b
--- /dev/null
@@ -0,0 +1,52 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.Section;
+import org.eclipse.swt.widgets.Composite;
+
+public class TextSection extends Section implements CmsNames {
+       private static final long serialVersionUID = -8625209546243220689L;
+       private String defaultTextStyle = TextStyles.TEXT_DEFAULT;
+       private String titleStyle;
+
+       public TextSection(Composite parent, int style, Node node)
+                       throws RepositoryException {
+               this(parent, findSection(parent), style, node);
+       }
+
+       public TextSection(TextSection section, int style, Node node)
+                       throws RepositoryException {
+               this(section, section.getParentSection(), style, node);
+       }
+
+       private TextSection(Composite parent, Section parentSection, int style,
+                       Node node) throws RepositoryException {
+               super(parent, parentSection, style, node);
+               CmsUtils.style(this, TextStyles.TEXT_SECTION);
+       }
+
+       public String getDefaultTextStyle() {
+               return defaultTextStyle;
+       }
+
+       public String getTitleStyle() {
+               if (titleStyle != null)
+                       return titleStyle;
+               // TODO make base H styles configurable
+               Integer relativeDepth = getRelativeDepth();
+               return relativeDepth == 0 ? TextStyles.TEXT_TITLE : TextStyles.TEXT_H
+                               + relativeDepth;
+       }
+
+       public void setDefaultTextStyle(String defaultTextStyle) {
+               this.defaultTextStyle = defaultTextStyle;
+       }
+
+       public void setTitleStyle(String titleStyle) {
+               this.titleStyle = titleStyle;
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/text/TextStyles.java
new file mode 100644 (file)
index 0000000..44c3ad0
--- /dev/null
@@ -0,0 +1,37 @@
+package org.argeo.cms.text;
+
+/** 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/text/WikiPage.java b/org.argeo.cms.ui/src/org/argeo/cms/text/WikiPage.java
new file mode 100644 (file)
index 0000000..38af381
--- /dev/null
@@ -0,0 +1,67 @@
+package org.argeo.cms.text;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.util.CmsLink;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.JcrVersionCmsEditable;
+import org.argeo.cms.widgets.ScrolledPage;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Display the text of the context, and provide an editor if the user can edit. */
+public class WikiPage implements CmsUiProvider, CmsNames {
+       @Override
+       public Control createUi(Composite parent, Node context)
+                       throws RepositoryException {
+               CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
+               if (cmsEditable.canEdit())
+                       new TextEditorHeader(cmsEditable, parent, SWT.NONE)
+                                       .setLayoutData(CmsUtils.fillWidth());
+
+               ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
+               page.setLayout(CmsUtils.noSpaceGridLayout());
+               GridData textGd = CmsUtils.fillAll();
+               page.setLayoutData(textGd);
+
+               if (context.isNodeType(CmsTypes.CMS_TEXT)) {
+                       new StandardTextEditor(page, SWT.NONE, context, cmsEditable);
+               } else if (context.isNodeType(NodeType.NT_FOLDER)
+                               || context.getPath().equals("/")) {
+                       parent.setBackgroundMode(SWT.INHERIT_NONE);
+                       if (context.getSession().hasPermission(context.getPath(),
+                                       Session.ACTION_ADD_NODE)) {
+                               Node indexNode = JcrUtils.getOrAdd(context, CMS_INDEX,
+                                               CmsTypes.CMS_TEXT);
+                               new StandardTextEditor(page, SWT.NONE, indexNode, cmsEditable);
+                               textGd.heightHint = 400;
+
+                               for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+                                       Node textNode = ni.nextNode();
+                                       if (textNode.isNodeType(NodeType.NT_FOLDER))
+                                               new CmsLink(textNode.getName() + "/",
+                                                               textNode.getPath()).createUi(parent, textNode);
+                               }
+                               for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
+                                       Node textNode = ni.nextNode();
+                                       if (textNode.isNodeType(CmsTypes.CMS_TEXT)
+                                                       && !textNode.getName().equals(CMS_INDEX))
+                                               new CmsLink(textNode.getName(), textNode.getPath())
+                                                               .createUi(parent, textNode);
+                               }
+                       }
+               }
+               return page;
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java
new file mode 100644 (file)
index 0000000..ffc162b
--- /dev/null
@@ -0,0 +1,42 @@
+package org.argeo.cms.ui.internal;
+
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.maintenance.MaintenanceUi;
+import org.argeo.cms.ui.internal.rwt.UserUi;
+import org.argeo.node.NodeState;
+import org.argeo.util.LangUtils;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+       // avoid dependency to RWT OSGi
+       private final static String CONTEXT_NAME_PROP = "contextName";
+
+       private static ServiceTracker<NodeState, NodeState> nodeState;
+
+       // @Override
+       public void start(BundleContext bc) throws Exception {
+               // UI
+               bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
+                               LangUtils.init(CONTEXT_NAME_PROP, "system"));
+               bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.init(CONTEXT_NAME_PROP, "user"));
+
+               nodeState = new ServiceTracker<>(bc, NodeState.class, null);
+               nodeState.open();
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception {
+               if (nodeState != null) {
+                       nodeState.close();
+                       nodeState = null;
+               }
+       }
+
+       public static NodeState getNodeState() {
+               return nodeState.getService();
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/ImageManagerImpl.java
new file mode 100644 (file)
index 0000000..92f237a
--- /dev/null
@@ -0,0 +1,263 @@
+package org.argeo.cms.ui.internal;
+
+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.CmsConstants.NO_IMAGE_SIZE;
+import static org.argeo.cms.CmsTypes.CMS_STYLED;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.activation.MimetypesFileTypeMap;
+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.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.util.CmsUtils;
+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 ImageManagerImpl implements CmsImageManager, CmsNames {
+       private final static Log log = LogFactory.getLog(ImageManagerImpl.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 {
+               return new Point(node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node
+                               .getProperty(CMS_IMAGE_WIDTH).getLong() : 0,
+                               node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node.getProperty(
+                                               CMS_IMAGE_HEIGHT).getLong() : 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 if (node.isNodeType(CMS_STYLED) && node.hasProperty(CMS_DATA)) {
+                       return node.getProperty(CMS_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);
+                       fileNode.addMixin(CmsTypes.CMS_IMAGE);
+
+                       inputStream = new ByteArrayInputStream(arr);
+                       ImageData id = new ImageData(inputStream);
+                       fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
+                       fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
+                       fileNode.setProperty(Property.JCR_MIMETYPE,
+                                       fileTypeMap.getContentType(fileName));
+                       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);
+               }
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java
new file mode 100644 (file)
index 0000000..3892f94
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.cms.ui.internal;
+
+import java.util.ArrayList;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+@Deprecated
+class JcrContentProvider implements ITreeContentProvider {
+       private static final long serialVersionUID = -1333678161322488674L;
+
+       @Override
+       public void dispose() {
+       }
+
+       @Override
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               if (newInput == null)
+                       return;
+               if (!(newInput instanceof Node))
+                       throw new CmsException("Input " + newInput + " must be a node");
+       }
+
+       @Override
+       public Object[] getElements(Object inputElement) {
+               try {
+                       Node node = (Node) inputElement;
+                       ArrayList<Node> arr = new ArrayList<Node>();
+                       NodeIterator nit = node.getNodes();
+                       while (nit.hasNext()) {
+                               arr.add(nit.nextNode());
+                       }
+                       return arr.toArray();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get elements", e);
+               }
+       }
+
+       @Override
+       public Object[] getChildren(Object parentElement) {
+               try {
+                       Node node = (Node) parentElement;
+                       ArrayList<Node> arr = new ArrayList<Node>();
+                       NodeIterator nit = node.getNodes();
+                       while (nit.hasNext()) {
+                               arr.add(nit.nextNode());
+                       }
+                       return arr.toArray();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get elements", e);
+               }
+       }
+
+       @Override
+       public Object getParent(Object element) {
+               try {
+                       Node node = (Node) element;
+                       if (node.getName().equals(""))
+                               return null;
+                       else
+                               return node.getParent();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get elements", e);
+               }
+       }
+
+       @Override
+       public boolean hasChildren(Object element) {
+               try {
+                       Node node = (Node) element;
+                       return node.hasNodes();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot get elements", e);
+               }
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java
new file mode 100644 (file)
index 0000000..2b157eb
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.cms.ui.internal;
+
+import static javax.jcr.nodetype.NodeType.NT_FILE;
+
+import java.io.IOException;
+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.FilenameUtils;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadReceiver;
+
+public class JcrFileUploadReceiver extends FileUploadReceiver implements
+               CmsNames {
+       private final Node parentNode;
+       private final String nodeName;
+       private final CmsImageManager imageManager;
+
+       /** If nodeName is null, use the uploaded file name */
+       public JcrFileUploadReceiver(Node parentNode, String nodeName,
+                       CmsImageManager imageManager) {
+               super();
+               this.parentNode = parentNode;
+               this.nodeName = nodeName;
+               this.imageManager = imageManager;
+       }
+
+       @Override
+       public void receive(InputStream stream, FileDetails details)
+                       throws IOException {
+               try {
+                       String fileName = nodeName != null ? nodeName : details
+                                       .getFileName();
+                       String contentType = details.getContentType();
+                       if (isImage(details.getFileName(), contentType)) {
+                               imageManager.uploadImage(parentNode, fileName, stream);
+                               return;
+                               // InputStream inputStream = new ByteArrayInputStream(arr);
+                               // ImageData id = new ImageData(inputStream);
+                               // fileNode.addMixin(CmsTypes.CMS_IMAGE);
+                               // fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
+                               // fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
+                       }
+
+                       Node fileNode;
+                       if (parentNode.hasNode(fileName)) {
+                               fileNode = parentNode.getNode(fileName);
+                               if (!fileNode.isNodeType(NT_FILE))
+                                       fileNode.remove();
+                       }
+                       fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream);
+
+                       if (contentType != null) {
+                               fileNode.addMixin(NodeType.MIX_MIMETYPE);
+                               fileNode.setProperty(Property.JCR_MIMETYPE, contentType);
+                       }
+                       processNewFile(fileNode);
+                       fileNode.getSession().save();
+               } catch (RepositoryException e) {
+                       throw new CmsException("cannot receive " + details, e);
+               }
+       }
+
+       protected Boolean isImage(String fileName, String contentType) {
+               String ext = FilenameUtils.getExtension(fileName);
+               return ext != null
+                               && (ext.equals("png") || ext.equalsIgnoreCase("jpg"));
+       }
+
+       protected void processNewFile(Node node) {
+
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java
new file mode 100644 (file)
index 0000000..3bb1fdf
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.cms.ui.internal;
+
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.widgets.EditableImage;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** NOT working yet. */
+public class SimpleEditableImage extends EditableImage {
+       private static final long serialVersionUID = -5689145523114022890L;
+
+       private String src;
+       private Point imageSize;
+
+       public SimpleEditableImage(Composite parent, int swtStyle) {
+               super(parent, swtStyle);
+               // load(getControl());
+               getParent().layout();
+       }
+
+       public SimpleEditableImage(Composite parent, int swtStyle, String src,
+                       Point imageSize) {
+               super(parent, swtStyle);
+               this.src = src;
+               this.imageSize = imageSize;
+       }
+
+       @Override
+       protected Control createControl(Composite box, String style) {
+               if (isEditing()) {
+                       return createText(box, style);
+               } else {
+                       return createLabel(box, style);
+               }
+       }
+
+       protected String createImgTag() throws RepositoryException {
+               String imgTag;
+               if (src != null)
+                       imgTag = CmsUtils.img(src, imageSize);
+               else
+                       imgTag = CmsUtils.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);
+               return text;
+       }
+
+       public String getSrc() {
+               return src;
+       }
+
+       public void setSrc(String src) {
+               this.src = src;
+       }
+
+       public Point getImageSize() {
+               return imageSize;
+       }
+
+       public void setImageSize(Point imageSize) {
+               this.imageSize = imageSize;
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/rwt/UserUi.java
new file mode 100644 (file)
index 0000000..88cd17b
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.cms.ui.internal.rwt;
+
+import org.argeo.cms.util.LoginEntryPoint;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+
+public class UserUi implements ApplicationConfiguration {
+       @Override
+       public void configure(Application application) {
+               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+               application.addEntryPoint("/login", LoginEntryPoint.class, null);
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/AbstractTextViewer.java
new file mode 100644 (file)
index 0000000..c4b6060
--- /dev/null
@@ -0,0 +1,892 @@
+package org.argeo.cms.ui.internal.text;
+
+import static javax.jcr.Property.JCR_TITLE;
+import static org.argeo.cms.util.CmsUtils.fillWidth;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Observer;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.CmsTypes;
+import org.argeo.cms.text.Img;
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.text.TextInterpreter;
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.cms.viewers.AbstractPageViewer;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.NodePart;
+import org.argeo.cms.viewers.PropertyPart;
+import org.argeo.cms.viewers.Section;
+import org.argeo.cms.viewers.SectionPart;
+import org.argeo.cms.widgets.EditableImage;
+import org.argeo.cms.widgets.EditableText;
+import org.argeo.cms.widgets.StyledControl;
+import org.argeo.jcr.JcrUtils;
+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.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+
+/** Base class for text viewers and editors. */
+public abstract class AbstractTextViewer extends AbstractPageViewer implements
+               CmsNames, KeyListener, Observer {
+       private static final long serialVersionUID = -2401274679492339668L;
+       private final static Log log = LogFactory.getLog(AbstractTextViewer.class);
+
+       private final Section mainSection;
+
+       private TextInterpreter textInterpreter = new TextInterpreterImpl();
+       private CmsImageManager imageManager = CmsUtils.getCmsView()
+                       .getImageManager();
+
+       private FileUploadListener fileUploadListener;
+       private TextContextMenu styledTools;
+
+       private final boolean flat;
+
+       protected AbstractTextViewer(Section parent, int style,
+                       CmsEditable cmsEditable) {
+               super(parent, style, cmsEditable);
+               flat = SWT.FLAT == (style & SWT.FLAT);
+
+               if (getCmsEditable().canEdit()) {
+                       fileUploadListener = new FUL();
+                       styledTools = new TextContextMenu(this, parent.getDisplay());
+               }
+               this.mainSection = parent;
+               initModelIfNeeded(mainSection.getNode());
+               // layout(this.mainSection);
+       }
+
+       @Override
+       public Control getControl() {
+               return mainSection;
+       }
+
+       protected void refresh(Control control) throws RepositoryException {
+               if (!(control instanceof Section))
+                       return;
+               Section section = (Section) control;
+               if (section instanceof TextSection) {
+                       CmsUtils.clear(section);
+                       Node node = section.getNode();
+                       TextSection textSection = (TextSection) section;
+                       if (node.hasProperty(Property.JCR_TITLE)) {
+                               if (section.getHeader() == null)
+                                       section.createHeader();
+                               if (node.hasProperty(Property.JCR_TITLE)) {
+                                       SectionTitle title = newSectionTitle(textSection, node);
+                                       title.setLayoutData(CmsUtils.fillWidth());
+                                       updateContent(title);
+                               }
+                       }
+
+                       for (NodeIterator ni = node.getNodes(CMS_P); ni.hasNext();) {
+                               Node child = ni.nextNode();
+                               final SectionPart sectionPart;
+                               if (child.isNodeType(CmsTypes.CMS_IMAGE)
+                                               || child.isNodeType(NodeType.NT_FILE)) {
+                                       sectionPart = newImg(textSection, child);
+                               } else if (child.isNodeType(CmsTypes.CMS_STYLED)) {
+                                       sectionPart = newParagraph(textSection, child);
+                               } else {
+                                       sectionPart = newSectionPart(textSection, child);
+                                       if (sectionPart == null)
+                                               throw new CmsException("Unsupported node " + child);
+                                       // TODO list node types in exception
+                               }
+                               if (sectionPart instanceof Control)
+                                       ((Control) sectionPart).setLayoutData(CmsUtils.fillWidth());
+                       }
+
+                       if (!flat)
+                               for (NodeIterator ni = section.getNode().getNodes(CMS_H); ni
+                                               .hasNext();) {
+                                       Node child = ni.nextNode();
+                                       if (child.isNodeType(CmsTypes.CMS_SECTION)) {
+                                               TextSection newSection = new TextSection(section,
+                                                               SWT.NONE, child);
+                                               newSection.setLayoutData(CmsUtils.fillWidth());
+                                               refresh(newSection);
+                                       }
+                               }
+               } else {
+                       for (Section s : section.getSubSections().values())
+                               refresh(s);
+               }
+               // section.layout();
+       }
+
+       /** To be overridden in order to provide additional SectionPart types */
+       protected SectionPart newSectionPart(TextSection textSection, Node node) {
+               return null;
+       }
+
+       // CRUD
+       protected Paragraph newParagraph(TextSection parent, Node node)
+                       throws RepositoryException {
+               Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
+               updateContent(paragraph);
+               paragraph.setLayoutData(fillWidth());
+               paragraph.setMouseListener(getMouseListener());
+               return paragraph;
+       }
+
+       protected Img newImg(TextSection parent, Node node)
+                       throws RepositoryException {
+               Img img = new Img(parent, parent.getStyle(), node) {
+                       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);
+               img.setMouseListener(getMouseListener());
+               return img;
+       }
+
+       protected SectionTitle newSectionTitle(TextSection parent, Node node)
+                       throws RepositoryException {
+               SectionTitle title = new SectionTitle(parent.getHeader(),
+                               parent.getStyle(), node.getProperty(JCR_TITLE));
+               updateContent(title);
+               title.setMouseListener(getMouseListener());
+               return title;
+       }
+
+       protected SectionTitle prepareSectionTitle(Section newSection,
+                       String titleText) throws RepositoryException {
+               Node sectionNode = newSection.getNode();
+               if (!sectionNode.hasProperty(JCR_TITLE))
+                       sectionNode.setProperty(Property.JCR_TITLE, "");
+               getTextInterpreter().write(sectionNode.getProperty(Property.JCR_TITLE),
+                               titleText);
+               if (newSection.getHeader() == null)
+                       newSection.createHeader();
+               SectionTitle sectionTitle = newSectionTitle((TextSection) newSection,
+                               sectionNode);
+               return sectionTitle;
+       }
+
+       protected void updateContent(EditablePart part) throws RepositoryException {
+               if (part instanceof SectionPart) {
+                       SectionPart sectionPart = (SectionPart) part;
+                       Node partNode = sectionPart.getNode();
+
+                       if (part instanceof StyledControl
+                                       && (sectionPart.getSection() instanceof TextSection)) {
+                               TextSection section = (TextSection) sectionPart.getSection();
+                               StyledControl styledControl = (StyledControl) part;
+                               if (partNode.isNodeType(CmsTypes.CMS_STYLED)) {
+                                       String style = partNode.hasProperty(CMS_STYLE) ? partNode
+                                                       .getProperty(CMS_STYLE).getString() : section
+                                                       .getDefaultTextStyle();
+                                       styledControl.setStyle(style);
+                               }
+                       }
+                       // use control AFTER setting style, since it may have been reset
+
+                       if (part instanceof EditableText) {
+                               EditableText paragraph = (EditableText) part;
+                               if (paragraph == getEdited())
+                                       paragraph.setText(textInterpreter.read(partNode));
+                               else
+                                       paragraph.setText(textInterpreter.raw(partNode));
+                       } else if (part instanceof EditableImage) {
+                               EditableImage editableImage = (EditableImage) part;
+                               imageManager.load(partNode, part.getControl(),
+                                               editableImage.getPreferredImageSize());
+                       }
+               } else if (part instanceof SectionTitle) {
+                       SectionTitle title = (SectionTitle) part;
+                       title.setStyle(title.getSection().getTitleStyle());
+                       // use control AFTER setting style
+                       if (title == getEdited())
+                               title.setText(textInterpreter.read(title.getProperty()));
+                       else
+                               title.setText(textInterpreter.raw(title.getProperty()));
+               }
+       }
+
+       // OVERRIDDEN FROM PARENT VIEWER
+       @Override
+       protected void save(EditablePart part) throws RepositoryException {
+               if (part instanceof EditableText) {
+                       EditableText et = (EditableText) part;
+                       String text = ((Text) et.getControl()).getText();
+
+                       String[] lines = text.split("[\r\n]+");
+                       assert lines.length != 0;
+                       saveLine(part, lines[0]);
+                       if (lines.length > 1) {
+                               ArrayList<Control> toLayout = new ArrayList<Control>();
+                               if (part instanceof Paragraph) {
+                                       Paragraph currentParagraph = (Paragraph) et;
+                                       Section section = currentParagraph.getSection();
+                                       Node sectionNode = section.getNode();
+                                       Node currentParagraphN = currentParagraph.getNode();
+                                       for (int i = 1; i < lines.length; i++) {
+                                               Node newNode = sectionNode.addNode(CMS_P);
+                                               newNode.addMixin(CmsTypes.CMS_STYLED);
+                                               saveLine(newNode, lines[i]);
+                                               // second node was create as last, if it is not the next
+                                               // one, it
+                                               // means there are some in between and we can take the
+                                               // one at
+                                               // index+1 for the re-order
+                                               if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
+                                                       sectionNode.orderBefore(p(newNode.getIndex()),
+                                                                       p(currentParagraphN.getIndex() + 1));
+                                               }
+                                               Paragraph newParagraph = newParagraph(
+                                                               (TextSection) section, newNode);
+                                               newParagraph.moveBelow(currentParagraph);
+                                               toLayout.add(newParagraph);
+
+                                               currentParagraph = newParagraph;
+                                               currentParagraphN = newNode;
+                                       }
+                                       persistChanges(sectionNode);
+                               }
+                               // TODO or rather return the created paragarphs?
+                               layout(toLayout.toArray(new Control[toLayout.size()]));
+                       }
+               }
+       }
+
+       protected void saveLine(EditablePart part, String line) {
+               if (part instanceof NodePart) {
+                       saveLine(((NodePart) part).getNode(), line);
+               } else if (part instanceof PropertyPart) {
+                       saveLine(((PropertyPart) part).getProperty(), line);
+               } else {
+                       throw new CmsException("Unsupported part " + part);
+               }
+       }
+
+       protected void saveLine(Item item, String line) {
+               line = line.trim();
+               textInterpreter.write(item, line);
+       }
+
+       @Override
+       protected void prepare(EditablePart part, Object caretPosition) {
+               Control control = part.getControl();
+               if (control instanceof Text) {
+                       Text text = (Text) control;
+                       if (caretPosition != null)
+                               if (caretPosition instanceof Integer)
+                                       text.setSelection((Integer) caretPosition);
+                               else if (caretPosition instanceof Point) {
+                                       // TODO find a way to position the caret at the right place
+                               }
+                       text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC",
+                                       "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT",
+                                       "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN",
+                                       "ENTER", "DELETE" });
+                       text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN",
+                                       "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
+                       text.addKeyListener(this);
+               } else if (part instanceof Img) {
+                       ((Img) part).setFileUploadListener(fileUploadListener);
+               }
+       }
+
+       // REQUIRED BY CONTEXT MENU
+       void setParagraphStyle(Paragraph paragraph, String style) {
+               try {
+                       Node paragraphNode = paragraph.getNode();
+                       paragraphNode.setProperty(CMS_STYLE, style);
+                       persistChanges(paragraphNode);
+                       updateContent(paragraph);
+                       layout(paragraph);
+               } catch (RepositoryException e1) {
+                       throw new CmsException("Cannot set style " + style + " on "
+                                       + paragraph, e1);
+               }
+       }
+
+       void deletePart(SectionPart paragraph) {
+               try {
+                       Node paragraphNode = paragraph.getNode();
+                       Section section = paragraph.getSection();
+                       Session session = paragraphNode.getSession();
+                       paragraphNode.remove();
+                       session.save();
+                       if (paragraph instanceof Control)
+                               ((Control) paragraph).dispose();
+                       layout(section);
+               } catch (RepositoryException e1) {
+                       throw new CmsException("Cannot delete " + paragraph, e1);
+               }
+       }
+
+       String getRawParagraphText(Paragraph paragraph) {
+               return textInterpreter.raw(paragraph.getNode());
+       }
+
+       // COMMANDS
+       protected void splitEdit() {
+               checkEdited();
+               try {
+                       if (getEdited() instanceof Paragraph) {
+                               Paragraph paragraph = (Paragraph) getEdited();
+                               Text text = (Text) paragraph.getControl();
+                               int caretPosition = text.getCaretPosition();
+                               String txt = text.getText();
+                               String first = txt.substring(0, caretPosition);
+                               String second = txt.substring(caretPosition);
+                               Node firstNode = paragraph.getNode();
+                               Node sectionNode = firstNode.getParent();
+                               firstNode.setProperty(CMS_CONTENT, first);
+                               Node secondNode = sectionNode.addNode(CMS_P);
+                               secondNode.addMixin(CmsTypes.CMS_STYLED);
+                               // second node was create as last, if it is not the next one, it
+                               // means there are some in between and we can take the one at
+                               // index+1 for the re-order
+                               if (secondNode.getIndex() > firstNode.getIndex() + 1) {
+                                       sectionNode.orderBefore(p(secondNode.getIndex()),
+                                                       p(firstNode.getIndex() + 1));
+                               }
+
+                               // if we die in between, at least we still have the whole text
+                               // in the first node
+                               try {
+                                       textInterpreter.write(secondNode, second);
+                                       textInterpreter.write(firstNode, first);
+                               } catch (Exception e) {
+                                       // so that no additional nodes are created:
+                                       JcrUtils.discardUnderlyingSessionQuietly(firstNode);
+                                       throw e;
+                               }
+
+                               persistChanges(firstNode);
+
+                               Paragraph secondParagraph = paragraphSplitted(paragraph,
+                                               secondNode);
+                               edit(secondParagraph, 0);
+                       } else if (getEdited() instanceof SectionTitle) {
+                               SectionTitle sectionTitle = (SectionTitle) getEdited();
+                               Text text = (Text) sectionTitle.getControl();
+                               String txt = text.getText();
+                               int caretPosition = text.getCaretPosition();
+                               Section section = sectionTitle.getSection();
+                               Node sectionNode = section.getNode();
+                               Node paragraphNode = sectionNode.addNode(CMS_P);
+                               paragraphNode.addMixin(CmsTypes.CMS_STYLED);
+                               textInterpreter.write(paragraphNode,
+                                               txt.substring(caretPosition));
+                               textInterpreter.write(
+                                               sectionNode.getProperty(Property.JCR_TITLE),
+                                               txt.substring(0, caretPosition));
+                               sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
+                               persistChanges(sectionNode);
+
+                               Paragraph paragraph = sectionTitleSplitted(sectionTitle,
+                                               paragraphNode);
+                               // section.layout();
+                               edit(paragraph, 0);
+                       }
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot split " + getEdited(), e);
+               }
+       }
+
+       protected void mergeWithPrevious() {
+               checkEdited();
+               try {
+                       Paragraph paragraph = (Paragraph) getEdited();
+                       Text text = (Text) paragraph.getControl();
+                       String txt = text.getText();
+                       Node paragraphNode = paragraph.getNode();
+                       if (paragraphNode.getIndex() == 1)
+                               return;// do nothing
+                       Node sectionNode = paragraphNode.getParent();
+                       Node previousNode = sectionNode
+                                       .getNode(p(paragraphNode.getIndex() - 1));
+                       String previousTxt = textInterpreter.read(previousNode);
+                       textInterpreter.write(previousNode, previousTxt + txt);
+                       paragraphNode.remove();
+                       persistChanges(sectionNode);
+
+                       Paragraph previousParagraph = paragraphMergedWithPrevious(
+                                       paragraph, previousNode);
+                       edit(previousParagraph, previousTxt.length());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot stop editing", e);
+               }
+       }
+
+       protected void mergeWithNext() {
+               checkEdited();
+               try {
+                       Paragraph paragraph = (Paragraph) getEdited();
+                       Text text = (Text) paragraph.getControl();
+                       String txt = text.getText();
+                       Node paragraphNode = paragraph.getNode();
+                       Node sectionNode = paragraphNode.getParent();
+                       NodeIterator paragraphNodes = sectionNode.getNodes(CMS_P);
+                       long size = paragraphNodes.getSize();
+                       if (paragraphNode.getIndex() == size)
+                               return;// do nothing
+                       Node nextNode = sectionNode
+                                       .getNode(p(paragraphNode.getIndex() + 1));
+                       String nextTxt = textInterpreter.read(nextNode);
+                       textInterpreter.write(paragraphNode, txt + nextTxt);
+
+                       Section section = paragraph.getSection();
+                       Paragraph removed = (Paragraph) section.getSectionPart(nextNode
+                                       .getIdentifier());
+
+                       nextNode.remove();
+                       persistChanges(sectionNode);
+
+                       paragraphMergedWithNext(paragraph, removed);
+                       edit(paragraph, txt.length());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot stop editing", e);
+               }
+       }
+
+       protected synchronized void upload(EditablePart part) {
+               try {
+                       if (part instanceof SectionPart) {
+                               SectionPart sectionPart = (SectionPart) part;
+                               Node partNode = sectionPart.getNode();
+                               int partIndex = partNode.getIndex();
+                               Section section = sectionPart.getSection();
+                               Node sectionNode = section.getNode();
+
+                               if (part instanceof Paragraph) {
+                                       Node newNode = sectionNode.addNode(CMS_P, NodeType.NT_FILE);
+                                       newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+                                       JcrUtils.copyBytesAsFile(sectionNode,
+                                                       p(newNode.getIndex()), new byte[0]);
+                                       if (partIndex < newNode.getIndex() - 1) {
+                                               // was not last
+                                               sectionNode.orderBefore(p(newNode.getIndex()),
+                                                               p(partIndex - 1));
+                                       }
+                                       // sectionNode.orderBefore(p(partNode.getIndex()),
+                                       // p(newNode.getIndex()));
+                                       persistChanges(sectionNode);
+                                       Img img = newImg((TextSection) section, newNode);
+                                       edit(img, null);
+                                       layout(img.getControl());
+                               } else if (part instanceof Img) {
+                                       if (getEdited() == part)
+                                               return;
+                                       edit(part, null);
+                                       layout(part.getControl());
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot upload", e);
+               }
+       }
+
+       protected void deepen() {
+               if (flat)
+                       return;
+               checkEdited();
+               try {
+                       if (getEdited() instanceof Paragraph) {
+                               Paragraph paragraph = (Paragraph) getEdited();
+                               Text text = (Text) paragraph.getControl();
+                               String txt = text.getText();
+                               Node paragraphNode = paragraph.getNode();
+                               Section section = paragraph.getSection();
+                               Node sectionNode = section.getNode();
+                               // main title
+                               if (section == mainSection && section instanceof TextSection
+                                               && paragraphNode.getIndex() == 1
+                                               && !sectionNode.hasProperty(JCR_TITLE)) {
+                                       SectionTitle sectionTitle = prepareSectionTitle(section,
+                                                       txt);
+                                       edit(sectionTitle, 0);
+                                       return;
+                               }
+                               Node newSectionNode = sectionNode.addNode(CMS_H,
+                                               CmsTypes.CMS_SECTION);
+                               sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
+
+                               int paragraphIndex = paragraphNode.getIndex();
+                               String sectionPath = sectionNode.getPath();
+                               String newSectionPath = newSectionNode.getPath();
+                               while (sectionNode.hasNode(p(paragraphIndex + 1))) {
+                                       Node parag = sectionNode.getNode(p(paragraphIndex + 1));
+                                       sectionNode.getSession().move(
+                                                       sectionPath + '/' + p(paragraphIndex + 1),
+                                                       newSectionPath + '/' + CMS_P);
+                                       SectionPart sp = section.getSectionPart(parag
+                                                       .getIdentifier());
+                                       if (sp instanceof Control)
+                                               ((Control) sp).dispose();
+                               }
+                               // create property
+                               newSectionNode.setProperty(Property.JCR_TITLE, "");
+                               getTextInterpreter().write(
+                                               newSectionNode.getProperty(Property.JCR_TITLE), txt);
+
+                               TextSection newSection = new TextSection(section,
+                                               section.getStyle(), newSectionNode);
+                               newSection.setLayoutData(CmsUtils.fillWidth());
+                               newSection.moveBelow(paragraph);
+
+                               // dispose
+                               paragraphNode.remove();
+                               paragraph.dispose();
+
+                               refresh(newSection);
+                               newSection.getParent().layout();
+                               layout(newSection);
+                               persistChanges(sectionNode);
+                       } else if (getEdited() instanceof SectionTitle) {
+                               SectionTitle sectionTitle = (SectionTitle) getEdited();
+                               Section section = sectionTitle.getSection();
+                               Section parentSection = section.getParentSection();
+                               if (parentSection == null)
+                                       return;// cannot deepen main section
+                               Node sectionN = section.getNode();
+                               Node parentSectionN = parentSection.getNode();
+                               if (sectionN.getIndex() == 1)
+                                       return;// cannot deepen first section
+                               Node previousSectionN = parentSectionN.getNode(h(sectionN
+                                               .getIndex() - 1));
+                               NodeIterator subSections = previousSectionN.getNodes(CMS_H);
+                               int subsectionsCount = (int) subSections.getSize();
+                               previousSectionN.getSession().move(
+                                               sectionN.getPath(),
+                                               previousSectionN.getPath() + "/"
+                                                               + h(subsectionsCount + 1));
+                               section.dispose();
+                               TextSection newSection = new TextSection(section,
+                                               section.getStyle(), sectionN);
+                               refresh(newSection);
+                               persistChanges(previousSectionN);
+                       }
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot deepen " + getEdited(), e);
+               }
+       }
+
+       protected void undeepen() {
+               if (flat)
+                       return;
+               checkEdited();
+               try {
+                       if (getEdited() instanceof Paragraph) {
+                               upload(getEdited());
+                       } else if (getEdited() instanceof SectionTitle) {
+                               SectionTitle sectionTitle = (SectionTitle) getEdited();
+                               Section section = sectionTitle.getSection();
+                               Node sectionNode = section.getNode();
+                               Section parentSection = section.getParentSection();
+                               if (parentSection == null)
+                                       return;// cannot undeepen main section
+
+                               // choose in which section to merge
+                               Section mergedSection;
+                               if (sectionNode.getIndex() == 1)
+                                       mergedSection = section.getParentSection();
+                               else {
+                                       Map<String, Section> parentSubsections = parentSection
+                                                       .getSubSections();
+                                       ArrayList<Section> lst = new ArrayList<Section>(
+                                                       parentSubsections.values());
+                                       mergedSection = lst.get(sectionNode.getIndex() - 1);
+                               }
+                               Node mergedNode = mergedSection.getNode();
+                               boolean mergedHasSubSections = mergedNode.hasNode(CMS_H);
+
+                               // title as paragraph
+                               Node newParagrapheNode = mergedNode.addNode(CMS_P);
+                               newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
+                               if (mergedHasSubSections)
+                                       mergedNode.orderBefore(p(newParagrapheNode.getIndex()),
+                                                       h(1));
+                               String txt = getTextInterpreter().read(
+                                               sectionNode.getProperty(Property.JCR_TITLE));
+                               getTextInterpreter().write(newParagrapheNode, txt);
+                               // move
+                               NodeIterator paragraphs = sectionNode.getNodes(CMS_P);
+                               while (paragraphs.hasNext()) {
+                                       Node p = paragraphs.nextNode();
+                                       SectionPart sp = section.getSectionPart(p.getIdentifier());
+                                       if (sp instanceof Control)
+                                               ((Control) sp).dispose();
+                                       mergedNode.getSession().move(p.getPath(),
+                                                       mergedNode.getPath() + '/' + CMS_P);
+                                       if (mergedHasSubSections)
+                                               mergedNode.orderBefore(p(p.getIndex()), h(1));
+                               }
+
+                               Iterator<Section> subsections = section.getSubSections()
+                                               .values().iterator();
+                               // NodeIterator sections = sectionNode.getNodes(CMS_H);
+                               while (subsections.hasNext()) {
+                                       Section subsection = subsections.next();
+                                       Node s = subsection.getNode();
+                                       mergedNode.getSession().move(s.getPath(),
+                                                       mergedNode.getPath() + '/' + CMS_H);
+                                       subsection.dispose();
+                               }
+
+                               // remove section
+                               section.getNode().remove();
+                               section.dispose();
+
+                               refresh(mergedSection);
+                               mergedSection.getParent().layout();
+                               layout(mergedSection);
+                               persistChanges(mergedNode);
+                       }
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot undeepen " + getEdited(), e);
+               }
+       }
+
+       // UI CHANGES
+       protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode)
+                       throws RepositoryException {
+               Section section = paragraph.getSection();
+               updateContent(paragraph);
+               Paragraph newParagraph = newParagraph((TextSection) section, newNode);
+               newParagraph.setLayoutData(CmsUtils.fillWidth());
+               newParagraph.moveBelow(paragraph);
+               layout(paragraph.getControl(), newParagraph.getControl());
+               return newParagraph;
+       }
+
+       protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle,
+                       Node newNode) throws RepositoryException {
+               updateContent(sectionTitle);
+               Paragraph newParagraph = newParagraph(sectionTitle.getSection(),
+                               newNode);
+               // we assume beforeFirst is not null since there was a sectionTitle
+               newParagraph.moveBelow(sectionTitle.getSection().getHeader());
+               layout(sectionTitle.getControl(), newParagraph.getControl());
+               return newParagraph;
+       }
+
+       protected Paragraph paragraphMergedWithPrevious(Paragraph removed,
+                       Node remaining) throws RepositoryException {
+               Section section = removed.getSection();
+               removed.dispose();
+
+               Paragraph paragraph = (Paragraph) section.getSectionPart(remaining
+                               .getIdentifier());
+               updateContent(paragraph);
+               layout(paragraph.getControl());
+               return paragraph;
+       }
+
+       protected void paragraphMergedWithNext(Paragraph remaining,
+                       Paragraph removed) throws RepositoryException {
+               removed.dispose();
+               updateContent(remaining);
+               layout(remaining.getControl());
+       }
+
+       // UTILITIES
+       protected String p(Integer index) {
+               StringBuilder sb = new StringBuilder(6);
+               sb.append(CMS_P).append('[').append(index).append(']');
+               return sb.toString();
+       }
+
+       protected String h(Integer index) {
+               StringBuilder sb = new StringBuilder(5);
+               sb.append(CMS_H).append('[').append(index).append(']');
+               return sb.toString();
+       }
+
+       // GETTERS / SETTERS
+       public Section getMainSection() {
+               return mainSection;
+       }
+
+       public boolean isFlat() {
+               return flat;
+       }
+
+       public TextInterpreter getTextInterpreter() {
+               return textInterpreter;
+       }
+
+       // KEY LISTENER
+       @Override
+       public void keyPressed(KeyEvent ke) {
+               if (log.isTraceEnabled())
+                       log.trace(ke);
+
+               if (getEdited() == null)
+                       return;
+               boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
+               boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
+               boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
+
+               try {
+                       // Common
+                       if (ke.keyCode == SWT.ESC) {
+                               cancelEdit();
+                       } else if (ke.character == '\r') {
+                               splitEdit();
+                       } else if (ke.character == 'S') {
+                               if (ctrlPressed)
+                                       saveEdit();
+                       } else if (ke.character == '\t') {
+                               if (!shiftPressed) {
+                                       deepen();
+                               } else if (shiftPressed) {
+                                       undeepen();
+                               }
+                       } else {
+                               if (getEdited() instanceof Paragraph) {
+                                       Paragraph paragraph = (Paragraph) getEdited();
+                                       Section section = paragraph.getSection();
+                                       if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
+                                               edit(section.nextSectionPart(paragraph), 0);
+                                       } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
+                                               edit(section.previousSectionPart(paragraph), 0);
+                                       } else if (ke.character == SWT.BS) {
+                                               Text text = (Text) paragraph.getControl();
+                                               int caretPosition = text.getCaretPosition();
+                                               if (caretPosition == 0) {
+                                                       mergeWithPrevious();
+                                               }
+                                       } else if (ke.character == SWT.DEL) {
+                                               Text text = (Text) paragraph.getControl();
+                                               int caretPosition = text.getCaretPosition();
+                                               int charcount = text.getCharCount();
+                                               if (caretPosition == charcount) {
+                                                       mergeWithNext();
+                                               }
+                                       }
+                               }
+                       }
+               } catch (Exception e) {
+                       ke.doit = false;
+                       notifyEditionException(e);
+               }
+       }
+
+       @Override
+       public void keyReleased(KeyEvent e) {
+       }
+
+       // 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));
+                               }
+                       }
+               }
+
+               @Override
+               public void mouseUp(MouseEvent e) {
+               }
+       }
+
+       // FILE UPLOAD LISTENER
+       private class FUL implements FileUploadListener {
+               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();
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/MarkupValidatorCopy.java
new file mode 100644 (file)
index 0000000..9bced0d
--- /dev/null
@@ -0,0 +1,184 @@
+package org.argeo.cms.ui.internal.text;
+
+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.argeo.cms.forms.FormPageViewer;
+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.
+ * 
+ * FIXME made public to enable validation from the {@link FormPageViewer}
+ */
+public 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/ui/internal/text/SectionTitle.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/SectionTitle.java
new file mode 100644 (file)
index 0000000..24861ee
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.cms.ui.internal.text;
+
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.text.TextSection;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.PropertyPart;
+import org.argeo.cms.widgets.EditableText;
+import org.eclipse.swt.widgets.Composite;
+
+/** The title of a section. */
+public class SectionTitle extends EditableText implements EditablePart,
+               PropertyPart {
+       private static final long serialVersionUID = -1787983154946583171L;
+
+       private final TextSection section;
+
+       public SectionTitle(Composite parent, int swtStyle, Property title)
+                       throws RepositoryException {
+               super(parent, swtStyle, title);
+               section = (TextSection) TextSection.findSection(this);
+       }
+
+       public TextSection getSection() {
+               return section;
+       }
+
+       // @Override
+       // public Property getProperty() throws RepositoryException {
+       // return getSection().getNode().getProperty(Property.JCR_TITLE);
+       // }
+
+       @Override
+       public Property getItem() throws RepositoryException {
+               return getProperty();
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextContextMenu.java
new file mode 100644 (file)
index 0000000..4868b76
--- /dev/null
@@ -0,0 +1,135 @@
+package org.argeo.cms.ui.internal.text;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.cms.CmsNames;
+import org.argeo.cms.text.Paragraph;
+import org.argeo.cms.text.TextStyles;
+import org.argeo.cms.viewers.EditablePart;
+import org.argeo.cms.viewers.SectionPart;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+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.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Dialog to edit a text part. */
+class TextContextMenu extends Shell implements CmsNames, TextStyles {
+       private final static String[] DEFAULT_TEXT_STYLES = {
+                       TextStyles.TEXT_DEFAULT, TextStyles.TEXT_PRE, TextStyles.TEXT_QUOTE };
+
+       private final AbstractTextViewer textViewer;
+
+       private static final long serialVersionUID = -3826246895162050331L;
+       private List<StyleButton> styleButtons = new ArrayList<TextContextMenu.StyleButton>();
+
+       private Label deleteButton, publishButton, editButton;
+
+       private EditablePart currentTextPart;
+
+       public TextContextMenu(AbstractTextViewer textViewer, Display display) {
+               super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+               this.textViewer = textViewer;
+               setLayout(new GridLayout());
+               setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
+
+               StyledToolMouseListener stml = new StyledToolMouseListener();
+               if (textViewer.getCmsEditable().isEditing()) {
+                       for (String style : DEFAULT_TEXT_STYLES) {
+                               StyleButton styleButton = new StyleButton(this, SWT.WRAP);
+                               styleButton.setData(RWT.CUSTOM_VARIANT, style);
+                               styleButton.setData(RWT.MARKUP_ENABLED, true);
+                               styleButton.addMouseListener(stml);
+                               styleButtons.add(styleButton);
+                       }
+
+                       // Delete
+                       deleteButton = new Label(this, SWT.NONE);
+                       deleteButton.setText("Delete");
+                       deleteButton.addMouseListener(stml);
+
+                       // Publish
+                       publishButton = new Label(this, SWT.NONE);
+                       publishButton.setText("Publish");
+                       publishButton.addMouseListener(stml);
+               } else if (textViewer.getCmsEditable().canEdit()) {
+                       // Edit
+                       editButton = new Label(this, SWT.NONE);
+                       editButton.setText("Edit");
+                       editButton.addMouseListener(stml);
+               }
+               addShellListener(new ToolsShellListener());
+       }
+
+       public void show(EditablePart source, Point location) {
+               if (isVisible())
+                       setVisible(false);
+
+               this.currentTextPart = source;
+
+               if (currentTextPart instanceof Paragraph) {
+                       final int size = 32;
+                       String text = textViewer
+                                       .getRawParagraphText((Paragraph) currentTextPart);
+                       String textToShow = text.length() > size ? text.substring(0,
+                                       size - 3) + "..." : text;
+                       for (StyleButton styleButton : styleButtons) {
+                               styleButton.setText(textToShow);
+                       }
+               }
+               pack();
+               layout();
+               if (source instanceof Control)
+                       setLocation(((Control) source).toDisplay(location.x, location.y));
+               open();
+       }
+
+       class StyleButton extends Label {
+               private static final long serialVersionUID = 7731102609123946115L;
+
+               public StyleButton(Composite parent, int swtStyle) {
+                       super(parent, swtStyle);
+               }
+
+       }
+
+       class StyledToolMouseListener extends MouseAdapter {
+               private static final long serialVersionUID = 8516297091549329043L;
+
+               @Override
+               public void mouseDown(MouseEvent e) {
+                       Object eventSource = e.getSource();
+                       if (eventSource instanceof StyleButton) {
+                               StyleButton sb = (StyleButton) e.getSource();
+                               String style = sb.getData(RWT.CUSTOM_VARIANT).toString();
+                               textViewer
+                                               .setParagraphStyle((Paragraph) currentTextPart, style);
+                       } else if (eventSource == deleteButton) {
+                               textViewer.deletePart((SectionPart) currentTextPart);
+                       } else if (eventSource == editButton) {
+                               textViewer.getCmsEditable().startEditing();
+                       } else if (eventSource == publishButton) {
+                               textViewer.getCmsEditable().stopEditing();
+                       }
+                       setVisible(false);
+               }
+       }
+
+       class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
+               private static final long serialVersionUID = 8432350564023247241L;
+
+               @Override
+               public void shellDeactivated(ShellEvent e) {
+                       setVisible(false);
+               }
+
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/text/TextInterpreterImpl.java
new file mode 100644 (file)
index 0000000..4a646c3
--- /dev/null
@@ -0,0 +1,33 @@
+package org.argeo.cms.ui.internal.text;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.text.IdentityTextInterpreter;
+
+/**
+ * Text interpreter that sanitise and validates before saving, and support CMS
+ * specific formatting and integration.
+ */
+class TextInterpreterImpl extends IdentityTextInterpreter {
+       private MarkupValidatorCopy markupValidator = MarkupValidatorCopy
+                       .getInstance();
+
+       @Override
+       protected void validateBeforeStoring(String raw) {
+               markupValidator.validate(raw);
+       }
+
+       @Override
+       protected String convertToStorage(Item item, String content)
+                       throws RepositoryException {
+               return super.convertToStorage(item, content);
+       }
+
+       @Override
+       protected String convertFromStorage(Item item, String content)
+                       throws RepositoryException {
+               return super.convertFromStorage(item, content);
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..cda00ef
--- /dev/null
@@ -0,0 +1,47 @@
+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;
+import org.osgi.framework.BundleContext;
+
+/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
+public class BundleResourceLoader implements ResourceLoader {
+       private final BundleContext bundleContext;
+
+       public BundleResourceLoader(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       @Override
+       public InputStream getResourceAsStream(String resourceName)
+                       throws IOException {
+               // 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 = bundle.getResource(resourceName);
+               if (res == null)
+                       throw new CmsException("Resource " + resourceName
+                                       + " not found in bundle " + bundle.getSymbolicName());
+               return res.openStream();
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..14f3755
--- /dev/null
@@ -0,0 +1,274 @@
+package org.argeo.cms.util;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jcr.Node;
+
+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.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.eclipse.gemini.blueprint.context.BundleContextAware;
+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;
+import org.springframework.beans.factory.InitializingBean;
+
+/** A link to an internal or external location. */
+public class CmsLink implements CmsUiProvider, InitializingBean,
+               BundleContextAware {
+       private final static Log log = LogFactory.getLog(CmsLink.class);
+
+       private String label;
+       private String custom;
+       private String target;
+       private String image;
+       private MouseListener mouseListener;
+
+       private int verticalAlignment = SWT.CENTER;
+
+       // internal
+       // private Boolean isUrl = false;
+       private Integer imageWidth, imageHeight;
+
+       private BundleContext bundleContext;
+
+       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;
+               afterPropertiesSet();
+       }
+
+       @Override
+       public void afterPropertiesSet() {
+               // if (target != null) {
+               // if (target.startsWith("/")) {
+               // isUrl = true;
+               // } else {
+               // try {
+               // new URL(target);
+               // isUrl = true;
+               // } catch (MalformedURLException e1) {
+               // isUrl = false;
+               // }
+               // }
+               // }
+
+               if (image != null) {
+                       ImageData image = loadImage();
+                       imageWidth = image.width;
+                       imageHeight = image.height;
+               }
+       }
+
+       /** @return {@link Composite} with a single {@link Label} child. */
+       @Override
+       public Control createUi(final Composite parent, Node context) {
+               Composite comp = new Composite(parent, SWT.BOTTOM);
+               comp.setLayout(CmsUtils.noSpaceGridLayout());
+
+               Label link = new Label(comp, SWT.NONE);
+               link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
+               GridData layoutData = new GridData(SWT.CENTER, verticalAlignment, true,
+                               true);
+               if (image != null) {
+                       layoutData.heightHint = imageHeight;
+                       if (label == 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 (target != null) {
+                       labelText
+                                       .append("<a style='color:inherit;text-decoration:inherit;' href=\"");
+                       // if (!isUrl)
+                       // labelText.append('#');
+                       labelText.append(target);
+                       labelText.append("\">");
+               }
+               if (image != null) {
+                       registerImageIfNeeded();
+                       String imageLocation = RWT.getResourceManager().getLocation(image);
+                       labelText.append("<img width='").append(imageWidth)
+                                       .append("' height='").append(imageHeight)
+                                       .append("' src=\"").append(imageLocation).append("\"/>");
+
+                       // final Image img = loadImage(parent.getDisplay());
+                       // link.setImage(img);
+                       // link.addDisposeListener(new DListener(img));
+               }
+
+               if (label != null) {
+                       // link.setText(label);
+                       labelText.append(' ').append(label);
+               }
+
+               if (target != null)
+                       labelText.append("</a>");
+
+               link.setText(labelText.toString());
+
+               // link.setCursor(link.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+               // CmsSession cmsSession = (CmsSession) parent.getDisplay().getData(
+               // CmsSession.KEY);
+               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) {
+                       // in OSGi bundle
+                       if (bundleContext == null)
+                               throw new CmsException("No bundle context available");
+                       url = bundleContext.getBundle().getResource(image);
+               }
+
+               if (url == null)
+                       throw new CmsException("No image " + image + " available.");
+
+               return url;
+       }
+
+       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;
+       }
+
+       @Override
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       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)");
+               }
+       }
+
+       // private class MListener extends MouseAdapter {
+       // private static final long serialVersionUID = 3634864186295639792L;
+       //
+       // @Override
+       // public void mouseDown(MouseEvent e) {
+       // if (e.button == 1) {
+       // }
+       // }
+       // }
+       //
+       // private class DListener implements DisposeListener {
+       // private static final long serialVersionUID = -3808587499269394812L;
+       // private final Image img;
+       //
+       // public DListener(Image img) {
+       // super();
+       // this.img = img;
+       // }
+       //
+       // @Override
+       // public void widgetDisposed(DisposeEvent event) {
+       // img.dispose();
+       // }
+       //
+       // }
+}
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
new file mode 100644 (file)
index 0000000..78279cd
--- /dev/null
@@ -0,0 +1,248 @@
+package org.argeo.cms.util;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsConstants;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.argeo.jcr.ArgeoJcrConstants;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Table;
+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);
+               }
+       }
+// FIXME
+       private final static String PATH_DATA = "/data";
+       private final static String WEBDAV_PUBLIC = PATH_DATA + "/public";
+       private final static String WEBDAV_PRIVATE = PATH_DATA + "/files";
+       public static String getDataPath(Node node) throws RepositoryException {
+               assert node != null;
+               String userId = node.getSession().getUserID();
+               if (log.isTraceEnabled())
+                       log.trace(userId + " : " + node.getPath());
+               StringBuilder buf = new StringBuilder();
+               boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+               if (isAnonymous)
+                       buf.append(WEBDAV_PUBLIC);
+               else
+                       buf.append(WEBDAV_PRIVATE);
+               // TODO convey repo alias vie repository properties
+               return buf.append('/').append(ArgeoJcrConstants.ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
+                               .append(node.getPath()).toString();
+       }
+//
+//     public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
+//             try {
+//                     StringBuilder buf = getServerBaseUrl(request);
+//                     buf.append('/').append('!').append(node.getPath());
+//                     return new URL(buf.toString()).toString();
+//             } catch (MalformedURLException e) {
+//                     throw new CmsException("Cannot build data URL for " + node, e);
+//             }
+//             // return request.getRequestURL().append('!').append(node.getPath())
+//             // .toString();
+//     }
+
+       /** @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(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 void style(Widget widget, String style) {
+               widget.setData(CmsConstants.STYLE, style);
+       }
+
+       /** Enable markups on widget */
+       public static void markup(Widget widget) {
+               widget.setData(CmsConstants.MARKUP, true);
+       }
+
+       public static void setItemHeight(Table table, int height) {
+               table.setData(CmsConstants.ITEM_HEIGHT, height);
+       }
+
+       /** @return the path or null if not instrumented */
+       public static String getDataPath(Widget widget) {
+               // JCR item
+               Object data = widget.getData();
+               if (data != null && data instanceof Item) {
+                       try {
+                               return ((Item) data).getPath();
+                       } catch (RepositoryException e) {
+                               throw new CmsException("Cannot find data path of " + data + " for " + widget);
+                       }
+               }
+
+               // JCR path
+               data = widget.getData(Property.JCR_PATH);
+               if (data != null)
+                       return data.toString();
+
+               return null;
+       }
+
+       /** 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 {
+                       IOUtils.closeQuietly(in);
+               }
+       }
+
+       private CmsUtils() {
+       }
+}
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
new file mode 100644 (file)
index 0000000..07a1397
--- /dev/null
@@ -0,0 +1,159 @@
+package org.argeo.cms.util;
+
+import java.util.Locale;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.CredentialNotFoundException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.UxContext;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.HttpRequestCallbackHandler;
+import org.argeo.cms.widgets.auth.CmsLogin;
+import org.argeo.cms.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 {
+       // private final static Log log = LogFactory.getLog(WorkbenchLogin.class);
+       private final Subject subject = new Subject();
+       private LoginContext loginContext;
+       private UxContext uxContext = null;
+
+       @Override
+       public int createUI() {
+               final Display display = createDisplay();
+               UiContext.setData(CmsView.KEY, this);
+               try {
+                       // try pre-auth
+                       loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_USER,
+                                       subject, new HttpRequestCallbackHandler(getRequest()));
+                       loginContext.login();
+               } catch (CredentialNotFoundException e) {
+                       CmsLoginShell loginShell = createCmsLoginShell();
+                       loginShell.open();
+                       while (!loginShell.getShell().isDisposed()) {
+                               // try {
+                               if (!display.readAndDispatch())
+                                       display.sleep();
+                               // } catch (Exception e1) {
+                               // try {
+                               // Thread.sleep(3000);
+                               // } catch (InterruptedException e2) {
+                               // // silent
+                               // }
+                               // ErrorFeedback.show("Login failed", e1);
+                               // return -1;
+                               // }
+                       }
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot log in", e);
+               }
+
+               if (CurrentUser.getUsername() == 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) {
+               this.loginContext = loginContext;
+       }
+
+       @Override
+       public void logout() {
+               if (loginContext == null)
+                       throw new CmsException("Login context should not bet null");
+               try {
+                       loginContext.logout();
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot log out", e);
+               }
+       }
+
+       @Override
+       public final Subject getSubject() {
+               return subject;
+       }
+
+       @Override
+       public void exception(Throwable e) {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public CmsImageManager getImageManager() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..d491d3c
--- /dev/null
@@ -0,0 +1,13 @@
+package org.argeo.cms.util;
+
+import org.argeo.cms.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);
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/OpenUserMenu.java
new file mode 100644 (file)
index 0000000..bb2bb39
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.cms.util;
+
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.widgets.Control;
+
+/** Open the user menu when clicked */
+public class OpenUserMenu extends MouseAdapter {
+       private static final long serialVersionUID = 3634864186295639792L;
+
+       @Override
+       public void mouseDown(MouseEvent e) {
+               if (e.button == 1) {
+                       new UserMenu((Control) e.getSource());
+               }
+       }
+}
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..39e7507
--- /dev/null
@@ -0,0 +1,376 @@
+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.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+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.CmsConstants;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.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.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("anonymous", "everyone");
+       private List<String> rwPrincipals = Arrays.asList("everyone");
+
+       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 {
+                       StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
+                                       bundleContext);
+                       BundleResourceLoader bundleRL = new BundleResourceLoader(
+                                       bundleContext);
+
+                       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("*");
+
+                       // 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 faviconRelPath = properties.get(WebClient.FAVICON);
+                                       application.addResource(faviconRelPath,
+                                                       new BundleResourceLoader(bundleContext));
+                                       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
+                       for (String themeId : styleSheets.keySet()) {
+                               List<String> cssLst = styleSheets.get(themeId);
+                               if (log.isDebugEnabled())
+                                       log.debug("Theme " + themeId);
+                               for (String css : cssLst) {
+                                       application.addStyleSheet(themeId, css, styleSheetRL);
+                                       if (log.isTraceEnabled())
+                                               log.trace(" CSS " + css);
+                               }
+
+                       }
+               } 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 = JcrUtils.loginOrCreateWorkspace(repository, workspace);
+                       VersionManager vm = session.getWorkspace().getVersionManager();
+                       if (!vm.isCheckedOut("/"))
+                               vm.checkout("/");
+                       JcrUtils.mkdirs(session, 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) {
+
+                               @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
new file mode 100644 (file)
index 0000000..aa1bb73
--- /dev/null
@@ -0,0 +1,88 @@
+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.CmsStyles;
+import org.argeo.cms.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;
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..b6155cf
--- /dev/null
@@ -0,0 +1,118 @@
+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.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
new file mode 100644 (file)
index 0000000..8ada61f
--- /dev/null
@@ -0,0 +1,140 @@
+package org.argeo.cms.util;
+
+import java.util.Map;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.AbstractCmsEntryPoint;
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsImageManager;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.CmsUiProvider;
+import org.argeo.cms.UxContext;
+import org.argeo.cms.ui.internal.ImageManagerImpl;
+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.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Simple header/body ergonomics. */
+public class SimpleErgonomics extends AbstractCmsEntryPoint {
+       private final static Log log = LogFactory.getLog(SimpleErgonomics.class);
+
+       private boolean uiInitialized = false;
+       private Composite headerArea;
+       private Composite bodyArea;
+       private final CmsUiProvider uiProvider;
+
+       private CmsUiProvider header;
+       private Integer headerHeight = 40;
+
+       private CmsImageManager imageManager = new ImageManagerImpl();
+       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());
+
+               // createAdminArea(parent);
+               headerArea = new Composite(parent, SWT.NONE);
+               headerArea.setLayout(new FillLayout());
+               GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false);
+               headerData.heightHint = headerHeight;
+               headerArea.setLayoutData(headerData);
+
+               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());
+               uxContext = new SimpleUxContext();
+               uiInitialized = true;
+               refresh();
+       }
+
+       @Override
+       protected void refresh() {
+               if (!uiInitialized)
+                       return;
+               if (getState() == null)
+                       setState("");
+               refreshHeader();
+               refreshBody();
+               if (log.isTraceEnabled())
+                       log.trace("UI refreshed " + getNode());
+       }
+
+       protected void createAdminArea(Composite parent) {
+       }
+
+       protected void refreshHeader() {
+               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 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 {
+                       uiProvider.createUi(bodyArea, getNode());
+               } 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;
+       }
+}
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
new file mode 100644 (file)
index 0000000..6e09000
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.cms.util;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.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
new file mode 100644 (file)
index 0000000..c5a6e6f
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.cms.util;
+
+import org.argeo.cms.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;
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..a7e3b6e
--- /dev/null
@@ -0,0 +1,73 @@
+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;
+import org.osgi.framework.BundleContext;
+
+/** {@link ResourceLoader} caching stylesheets. */
+public class StyleSheetResourceLoader implements ResourceLoader {
+       private final BundleContext bundleContext;
+
+       private Map<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
+
+       public StyleSheetResourceLoader(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       @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 = bundle.getResource(resourceName);
+                       if (res == null)
+                               throw new CmsException("Resource " + resourceName
+                                               + " not found in bundle " + bundle.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
new file mode 100644 (file)
index 0000000..54e8eb5
--- /dev/null
@@ -0,0 +1,128 @@
+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.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/UserMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/util/UserMenu.java
new file mode 100644 (file)
index 0000000..a654ddd
--- /dev/null
@@ -0,0 +1,48 @@
+package org.argeo.cms.util;
+
+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;
+
+       public UserMenu(Control source) {
+               super(CmsUtils.getCmsView());
+               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();
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..1b7ca03
--- /dev/null
@@ -0,0 +1,84 @@
+package org.argeo.cms.util;
+
+import javax.jcr.Node;
+import javax.security.auth.Subject;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.CmsStyles;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.CurrentUser;
+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) {
+               Subject subject = CmsUtils.getCmsView().getSubject();
+               String username = CurrentUser.getUsername(subject);
+               if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
+                       setLabel(CmsMsg.login.lead());
+               else {
+                       setLabel(CurrentUser.getDisplayName(subject));
+               }
+               Label link = (Label) ((Composite) super.createUi(parent, context))
+                               .getChildren()[0];
+               link.addMouseListener(new UserMenuLinkController());
+               return link.getParent();
+       }
+
+       protected UserMenu createUserMenu(Control source) {
+               return new UserMenu(source.getParent());
+       }
+
+       private class UserMenuLinkController implements MouseListener,
+                       DisposeListener {
+               private static final long serialVersionUID = 3634864186295639792L;
+
+               private UserMenu userMenu = null;
+               private long lastDisposeTS = 0l;
+
+               //
+               // 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);
+                                               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();
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
new file mode 100644 (file)
index 0000000..9ccc305
--- /dev/null
@@ -0,0 +1,242 @@
+package org.argeo.cms.util.useradmin;
+
+import java.security.AccessController;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.osgi.useradmin.LdifName;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/** Centralise common patterns to manage roles with a user admin */
+public class UserAdminUtils {
+
+       /** Retrieves a {@link Role} given a LDAP name */
+       public final static Role getRole(UserAdmin userAdmin, LdapName dn) {
+               Role role = userAdmin.getRole(dn.toString());
+               return role;
+       }
+
+       /** Retrieves the unique local username given a {@link User}. */
+       public final static String getUsername(User user) {
+               String username = null;
+               if (user instanceof Group)
+                       username = getProperty(user, LdifName.cn.name());
+               else
+                       username = getProperty(user, LdifName.uid.name());
+               return username;
+       }
+
+       /**
+        * Easily retrieves one of the {@link Role}'s property or an empty String if
+        * the requested property is not defined
+        */
+       public final static String getProperty(Role role, String key) {
+               Object obj = role.getProperties().get(key);
+               if (obj != null)
+                       return (String) obj;
+               else
+                       return "";
+       }
+
+       // CENTRALIZE SOME METHODS UNTIL API IS STABLE
+       /** Simply checks if current user is registered */
+       public static boolean isRegistered() {
+               return !CurrentUser.isAnonymous();
+       }
+
+       /** Simply checks if current user as a home */
+       public static boolean hasHome() {
+               return isRegistered();
+       }
+
+       // SELF HELPERS
+       /** Simply retrieves the current logged-in user display name. */
+       public static User getCurrentUser(UserAdmin userAdmin) {
+               return (User) getRole(userAdmin, getCurrentUserLdapName());
+       }
+
+       /** Simply retrieves the current logged-in user display name. */
+       public static String getCurrentUserDisplayName(UserAdmin userAdmin) {
+               String username = getCurrentUsername();
+               return getUserDisplayName(userAdmin, username);
+       }
+
+       /** Simply retrieves the current logged-in user display name. */
+       public static String getCurrentUserMail(UserAdmin userAdmin) {
+               String username = getCurrentUsername();
+               return getUserMail(userAdmin, username);
+       }
+
+       /** Returns the local name of the current connected user */
+       public final static String getUsername(UserAdmin userAdmin) {
+               LdapName dn = getCurrentUserLdapName();
+               return getUsername((User) getRole(userAdmin, dn));
+       }
+
+       /** Returns true if the current user is in the specified role */
+       public static boolean isUserInRole(String role) {
+               Set<String> roles = CurrentUser.roles();
+               return roles.contains(role);
+       }
+
+       /** Simply checks if current user is the same as the passed one */
+       public static boolean isCurrentUser(User user) {
+               String userName = getProperty(user, LdifName.dn.name());
+               try {
+                       LdapName selfUserName = getCurrentUserLdapName();
+                       LdapName userLdapName = new LdapName(userName);
+                       if (userLdapName.equals(selfUserName))
+                               return true;
+                       else
+                               return false;
+               } catch (InvalidNameException e) {
+                       throw new CmsException("User " + user + " has an unvalid dn: "
+                                       + userName, e);
+               }
+       }
+
+       public final static LdapName getCurrentUserLdapName() {
+               String name = getCurrentUsername();
+               return getLdapName(name);
+       }
+
+       /** Simply retrieves username for current user, generally a LDAP dn */
+       public static String getCurrentUsername() {
+               Subject subject = currentSubject();
+               String name = subject.getPrincipals(X500Principal.class).iterator()
+                               .next().toString();
+               return name;
+       }
+
+       /**
+        * Fork of the {@link CurrentUser#currentSubject} method that is private.
+        * TODO Enhance and factorize
+        */
+       private static Subject currentSubject() {
+               CmsView cmsView = CmsUtils.getCmsView();
+               if (cmsView != null)
+                       return cmsView.getSubject();
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject != null)
+                       return subject;
+               throw new RuntimeException("Cannot find related subject");
+       }
+
+       // HOME MANAGEMENT
+       /**
+        * Simply retrieves the *relative* path to the current user home node from
+        * the base home node
+        */
+       public static String getCurrentUserHomeRelPath() {
+               return getHomeRelPath(getCurrentUsername());
+       }
+
+       /**
+        * Simply retrieves the *relative* path to the home node of a user given its
+        * userName
+        */
+       public static String getHomeRelPath(String userName) {
+               String id = getUserUid(userName);
+               String currHomePath = JcrUtils.firstCharsToPath(id, 2) + "/" + id;
+               return currHomePath;
+       }
+
+       // HELPERS TO RETRIEVE REMARKABLE PROPERTIES
+       /** Simply retrieves the user uid from his dn with no useradmin */
+       public static String getUserUid(String dn) {
+               LdapName ldapName = getLdapName(dn);
+               Rdn last = ldapName.getRdn(ldapName.size() - 1);
+               if (last.getType().toLowerCase().equals(LdifName.uid.name())
+                               || last.getType().toLowerCase().equals(LdifName.cn.name()))
+                       return (String) last.getValue();
+               else
+                       throw new CmsException("Cannot retrieve user uid, "
+                                       + "non valid dn: " + dn);
+       }
+
+       /**
+        * Returns the local username if no user with this dn is found or if the
+        * found user has no defined display name
+        */
+       public static String getUserDisplayName(UserAdmin userAdmin, String dn) {
+               Role user = getRole(userAdmin, getLdapName(dn));
+               if (user == null)
+                       return getUserUid(dn);
+               String displayName = getProperty(user, LdifName.displayName.name());
+               if (EclipseUiUtils.isEmpty(displayName))
+                       displayName = getProperty(user, LdifName.cn.name());
+               if (EclipseUiUtils.isEmpty(displayName))
+                       return getUserUid(dn);
+               else
+                       return displayName;
+       }
+
+       /**
+        * Returns null if no user with this dn is found or if the found user has no
+        * defined mail
+        */
+       public static String getUserMail(UserAdmin userAdmin, String dn) {
+               Role user = getRole(userAdmin, getLdapName(dn));
+               if (user == null)
+                       return null;
+               else
+                       return getProperty(user, LdifName.mail.name());
+       }
+
+       // VARIOUS UI HELPERS
+       public final static String buildDefaultCn(String firstName, String lastName) {
+               return (firstName.trim() + " " + lastName.trim() + " ").trim();
+       }
+
+       /** Simply retrieves a display name of the relevant domain */
+       public final static String getDomainName(User user) {
+               String dn = user.getName();
+               if (dn.endsWith(AuthConstants.ROLES_BASEDN))
+                       return "System roles";
+               try {
+                       LdapName name = new LdapName(dn);
+                       List<Rdn> rdns = name.getRdns();
+                       String dname = null;
+                       int i = 0;
+                       loop: while (i < rdns.size()) {
+                               Rdn currrRdn = rdns.get(i);
+                               if (!LdifName.dc.name().equals(currrRdn.getType()))
+                                       break loop;
+                               else {
+                                       String currVal = (String) currrRdn.getValue();
+                                       dname = dname == null ? currVal : currVal + "." + dname;
+                               }
+                               i++;
+                       }
+                       return dname;
+               } catch (InvalidNameException e) {
+                       throw new CmsException("Unable to get domain name for " + dn, e);
+               }
+       }
+
+       // Local Helpers
+       /** Simply retrieves a LDAP name from a dn with no exception */
+       public static LdapName getLdapName(String dn) {
+               try {
+                       return new LdapName(dn);
+               } catch (InvalidNameException e) {
+                       throw new CmsException("Cannot parse LDAP name " + dn, e);
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java b/org.argeo.cms.ui/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
new file mode 100644 (file)
index 0000000..aa764d5
--- /dev/null
@@ -0,0 +1,105 @@
+package org.argeo.cms.util.useradmin;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.auth.AuthConstants;
+import org.argeo.osgi.useradmin.UserAdminConf;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/**
+ * Base useradmin wrapper. Implementing application might extends to add
+ * business specific behaviour
+ */
+public abstract class UserAdminWrapper {
+       // private Log log = LogFactory.getLog(UserAdminWrapper.class);
+
+       private UserAdmin userAdmin;
+       private ServiceReference<UserAdmin> userAdminServiceReference;
+       private UserTransaction userTransaction;
+
+       /* USER ADMIN LISTENER MANAGEMENT */
+       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
+
+       // TODO implement safer mechanism
+       public void addListener(UserAdminListener userAdminListener) {
+               if (!listeners.contains(userAdminListener))
+                       listeners.add(userAdminListener);
+       }
+
+       /**
+        * Starts a transaction if none already exists and notify the userAdmin
+        * listeners.Must be called from the UI Thread.
+        */
+       public UserTransaction beginTransactionIfNeeded() {
+               try {
+                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
+                               userTransaction.begin();
+                       }
+                       return userTransaction;
+               } catch (Exception e) {
+                       throw new CmsException("Unable to begin transaction", e);
+               }
+       }
+
+       // Expose this?
+       public void removeListener(UserAdminListener userAdminListener) {
+               if (listeners.contains(userAdminListener))
+                       listeners.remove(userAdminListener);
+       }
+
+       public void notifyListeners(UserAdminEvent event) {
+               for (UserAdminListener listener : listeners)
+                       listener.roleChanged(event);
+       }
+
+       public Map<String, String> getKnownBaseDns(boolean onlyWritable) {
+               Map<String, String> dns = new HashMap<String, String>();
+               for (String uri : userAdminServiceReference.getPropertyKeys()) {
+                       if (!uri.startsWith("/"))
+                               continue;
+                       Dictionary<String, ?> props = UserAdminConf.uriAsProperties(uri);
+                       String readOnly = UserAdminConf.readOnly.getValue(props);
+                       String baseDn = UserAdminConf.baseDn.getValue(props);
+
+                       if (onlyWritable && "true".equals(readOnly))
+                               continue;
+                       if (baseDn.equalsIgnoreCase(AuthConstants.ROLES_BASEDN))
+                               continue;
+                       dns.put(baseDn, uri);
+               }
+               return dns;
+       }
+
+       public UserAdmin getUserAdmin() {
+               return userAdmin;
+       }
+
+       public UserTransaction getUserTransaction() {
+               return userTransaction;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdmin(UserAdmin userAdmin) {
+               this.userAdmin = userAdmin;
+       }
+
+       public void setUserTransaction(UserTransaction userTransaction) {
+               this.userTransaction = userTransaction;
+       }
+
+       public void setUserAdminServiceReference(
+                       ServiceReference<UserAdmin> userAdminServiceReference) {
+               this.userAdminServiceReference = userAdminServiceReference;
+       }
+}
\ No newline at end of file
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
new file mode 100644 (file)
index 0000000..b52f76b
--- /dev/null
@@ -0,0 +1,309 @@
+package org.argeo.cms.viewers;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsEditable;
+import org.argeo.cms.CmsException;
+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;
+
+       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);
+       }
+
+       /**
+        * 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() {
+               try {
+                       if (cmsEditable.canEdit() && !readOnly)
+                               mouseListener = createMouseListener();
+                       else
+                               mouseListener = null;
+                       refresh(getControl());
+                       layout(getControl());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot refresh", e);
+               }
+       }
+
+       @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());
+               if (log.isTraceEnabled())
+                       log.trace("Full stack of " + eToLog.getMessage(), e);
+               // TODO Light error notification popup
+       }
+
+       // 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
new file mode 100644 (file)
index 0000000..99f8acf
--- /dev/null
@@ -0,0 +1,11 @@
+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
new file mode 100644 (file)
index 0000000..52e5a88
--- /dev/null
@@ -0,0 +1,9 @@
+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
new file mode 100644 (file)
index 0000000..2d7daae
--- /dev/null
@@ -0,0 +1,101 @@
+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.CmsEditable;
+import org.argeo.cms.CmsEditionEvent;
+import org.argeo.cms.CmsException;
+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
new file mode 100644 (file)
index 0000000..db9a60a
--- /dev/null
@@ -0,0 +1,8 @@
+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
new file mode 100644 (file)
index 0000000..50fdd06
--- /dev/null
@@ -0,0 +1,8 @@
+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
new file mode 100644 (file)
index 0000000..af7fd87
--- /dev/null
@@ -0,0 +1,155 @@
+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.CmsNames;
+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 implements CmsNames {
+       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
new file mode 100644 (file)
index 0000000..6cd45c5
--- /dev/null
@@ -0,0 +1,9 @@
+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
new file mode 100644 (file)
index 0000000..2e70eb8
--- /dev/null
@@ -0,0 +1,112 @@
+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
new file mode 100644 (file)
index 0000000..e7c56ea
--- /dev/null
@@ -0,0 +1,76 @@
+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/JcrComposite.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/JcrComposite.java
new file mode 100644 (file)
index 0000000..358b453
--- /dev/null
@@ -0,0 +1,175 @@
+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
new file mode 100644 (file)
index 0000000..c36ed20
--- /dev/null
@@ -0,0 +1,60 @@
+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
new file mode 100644 (file)
index 0000000..ec1dbe7
--- /dev/null
@@ -0,0 +1,138 @@
+package org.argeo.cms.widgets;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.CmsConstants;
+import org.argeo.cms.CmsNames;
+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, CmsNames {
+       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/auth/AbstractLoginDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java
new file mode 100644 (file)
index 0000000..b86fcb0
--- /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.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
new file mode 100644 (file)
index 0000000..21f101a
--- /dev/null
@@ -0,0 +1,296 @@
+package org.argeo.cms.widgets.auth;
+
+import static org.argeo.cms.CmsMsg.password;
+import static org.argeo.cms.CmsMsg.username;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS;
+import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER;
+
+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.FailedLoginException;
+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.CmsStyles;
+import org.argeo.cms.CmsView;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.HttpRequestCallback;
+import org.argeo.cms.i18n.LocaleUtils;
+import org.argeo.cms.ui.internal.Activator;
+import org.argeo.cms.util.CmsUtils;
+import org.eclipse.rap.rwt.RWT;
+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;
+
+       public CmsLogin(CmsView cmsView) {
+               this.cmsView = cmsView;
+               defaultLocale = Activator.getNodeState().getDefaultLocale();
+               List<Locale> locales = Activator.getNodeState().getLocales();
+               if (locales != null)
+                       localeChoice = new LocaleChoice(locales, defaultLocale);
+               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 CurrentUser.isAnonymous(cmsView.getSubject());
+       }
+
+       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);
+               l.setData(RWT.CUSTOM_VARIANT, 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());
+
+               Integer textWidth = 120;
+               parent.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
+
+               // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
+               usernameT = new Text(credentialsBlock, SWT.BORDER);
+               usernameT.setMessage(username.lead(locale));
+               usernameT.setData(RWT.CUSTOM_VARIANT, 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));
+               passwordT.setData(RWT.CUSTOM_VARIANT, 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 });
+               // credentialsBlock.setFocus();
+
+               extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener);
+               if (localeChoice != null)
+                       createLocalesBlock(credentialsBlock);
+               return credentialsBlock;
+       }
+
+       /**
+        * To be overridden in order to provide custome 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);
+               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);
+                       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() {
+               Subject subject = cmsView.getSubject();
+               LoginContext loginContext;
+               try {
+                       //
+                       // LOGIN
+                       //
+                       new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
+                       loginContext = new LoginContext(LOGIN_CONTEXT_USER, subject, this);
+                       loginContext.login();
+               } catch (FailedLoginException e) {
+                       log.warn(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;
+               }
+               cmsView.authChange(loginContext);
+               return true;
+       }
+
+       protected void logout() {
+               cmsView.logout();
+               cmsView.navigateTo("~");
+       }
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+               for (Callback callback : callbacks) {
+                       if (callback instanceof NameCallback)
+                               ((NameCallback) callback).setName(usernameT.getText());
+                       else if (callback instanceof PasswordCallback)
+                               ((PasswordCallback) callback).setPassword(passwordT.getTextChars());
+                       else if (callback instanceof HttpRequestCallback)
+                               ((HttpRequestCallback) callback).setRequest(RWT.getRequest());
+                       else if (callback instanceof LanguageCallback && localeChoice != null)
+                               ((LanguageCallback) callback).setLocale(localeChoice.getSelectedLocale());
+               }
+       }
+
+}
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
new file mode 100644 (file)
index 0000000..29a3f54
--- /dev/null
@@ -0,0 +1,70 @@
+package org.argeo.cms.widgets.auth;
+
+import org.argeo.cms.CmsView;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+/** The site-related user menu */
+public class CmsLoginShell extends CmsLogin {
+       private final Shell shell;
+
+       public CmsLoginShell(CmsView cmsView) {
+               super(cmsView);
+               shell = createShell();
+               shell.setData(RWT.CUSTOM_VARIANT, 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;
+       }
+}
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
new file mode 100644 (file)
index 0000000..4cb85b7
--- /dev/null
@@ -0,0 +1,290 @@
+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[])}.
+ * <p>
+ * Supported standard {@link Callback}s are:<br>
+ * <ul>
+ * <li>{@link PasswordCallback}</li>
+ * <li>{@link NameCallback}</li>
+ * <li>{@link TextOutputCallback}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Supported Argeo {@link Callback}s are:<br>
+ * <ul>
+ * <li>{@link LocaleChoice}</li>
+ * </ul>
+ * </p>
+ */
+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
new file mode 100644 (file)
index 0000000..b8de34b
--- /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.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/LocaleChoice.java b/org.argeo.cms.ui/src/org/argeo/cms/widgets/auth/LocaleChoice.java
new file mode 100644 (file)
index 0000000..009c372
--- /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.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.i18n.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/security/core/AbstractSystemExecution.java b/org.argeo.cms.ui/src/org/argeo/security/core/AbstractSystemExecution.java
new file mode 100644 (file)
index 0000000..9c3e5cd
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.security.core;
+
+import javax.security.auth.Subject;
+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.CmsException;
+
+/** Provides base method for executing code with system authorization. */
+public abstract class AbstractSystemExecution {
+       private final static Log log = LogFactory.getLog(AbstractSystemExecution.class);
+       private final Subject subject = new Subject();
+
+       private final String loginModule = "SYSTEM";
+
+       /**
+        * Authenticate the calling thread to the underlying
+        * {@link AuthenticationManager}
+        */
+       protected void authenticateAsSystem() {
+               ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
+               Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+               try {
+                       LoginContext lc = new LoginContext(loginModule, subject);
+                       lc.login();
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot login as system", e);
+               } finally {
+                       Thread.currentThread().setContextClassLoader(origClassLoader);
+               }
+               if (log.isTraceEnabled())
+                       log.trace("System authenticated");
+       }
+
+       protected void deauthenticateAsSystem() {
+               ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
+               Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+               try {
+                       LoginContext lc = new LoginContext(loginModule, subject);
+                       lc.logout();
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot logout as system", e);
+               } finally {
+                       Thread.currentThread().setContextClassLoader(origClassLoader);
+               }
+       }
+
+       protected Subject getSubject() {
+               return subject;
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.cms.ui/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
new file mode 100644 (file)
index 0000000..aa3827c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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.security.core;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.AbstractBeanFactory;
+import org.springframework.beans.factory.support.SecurityContextProvider;
+import org.springframework.beans.factory.support.SimpleSecurityContextProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ * Executes with a system authentication the instantiation and initialization
+ * methods of the application context where it has been defined.
+ */
+public class AuthenticatedApplicationContextInitialization extends
+               AbstractSystemExecution implements
+               DependencyInitializationAwareBeanPostProcessor, ApplicationContextAware {
+       /** If non empty, restricts to these beans */
+       private List<String> beanNames = new ArrayList<String>();
+
+       public Object postProcessBeforeInitialization(Object bean, String beanName)
+                       throws BeansException {
+               if (beanNames.size() == 0 || beanNames.contains(beanName))
+                       authenticateAsSystem();
+               return bean;
+       }
+
+       public Object postProcessAfterInitialization(Object bean, String beanName)
+                       throws BeansException {
+               if (beanNames.size() == 0 || beanNames.contains(beanName))
+                       deauthenticateAsSystem();
+               return bean;
+       }
+
+       public void setBeanNames(List<String> beanNames) {
+               this.beanNames = beanNames;
+       }
+
+       @Override
+       public void setApplicationContext(ApplicationContext applicationContext)
+                       throws BeansException {
+               if (applicationContext.getAutowireCapableBeanFactory() instanceof AbstractBeanFactory) {
+                       final AbstractBeanFactory beanFactory = ((AbstractBeanFactory) applicationContext
+                                       .getAutowireCapableBeanFactory());
+                       // retrieve subject's access control context
+                       // and set it as the bean factory security context
+                       Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
+                               @Override
+                               public Void run() {
+                                       SecurityContextProvider scp = new SimpleSecurityContextProvider(
+                                                       AccessController.getContext());
+                                       beanFactory.setSecurityContextProvider(scp);
+                                       return null;
+                               }
+                       });
+               }
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.cms.ui/src/org/argeo/security/core/OsgiModuleLabel.java
new file mode 100644 (file)
index 0000000..45c9e16
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.security.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Logs the name and version of an OSGi bundle based on its
+ * {@link BundleContext}.
+ */
+public class OsgiModuleLabel {
+       private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
+
+       private Bundle bundle;
+
+       public OsgiModuleLabel() {
+       }
+
+       /** Sets without logging. */
+       public OsgiModuleLabel(Bundle bundle) {
+               this.bundle = bundle;
+       }
+
+       /**
+        * Retrieved bundle from a bundle context and logs it. Typically to be set
+        * as a Spring bean.
+        */
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundle = bundleContext.getBundle();
+               log.info(msg());
+       }
+
+       public String msg() {
+               String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
+               String symbolicName = bundle.getSymbolicName();
+               String version = bundle.getVersion().toString();
+               return name + " v" + version + " (" + symbolicName + ")";
+       }
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.cms.ui/src/org/argeo/security/core/SimpleRoleRegistration.java
new file mode 100644 (file)
index 0000000..58f6686
--- /dev/null
@@ -0,0 +1,89 @@
+package org.argeo.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.CmsException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Register one or many roles via a user admin service. Does nothing if the role
+ * is already registered.
+ */
+public class SimpleRoleRegistration implements Runnable {
+       private final static Log log = LogFactory
+                       .getLog(SimpleRoleRegistration.class);
+
+       private String role;
+       private List<String> roles = new ArrayList<String>();
+       private UserAdmin userAdmin;
+       private UserTransaction userTransaction;
+
+       @Override
+       public void run() {
+               try {
+                       userTransaction.begin();
+                       if (role != null && !roleExists(role))
+                               newRole(toDn(role));
+
+                       for (String r : roles)
+                               if (!roleExists(r))
+                                       newRole(toDn(r));
+                       userTransaction.commit();
+               } catch (Exception e) {
+                       try {
+                               userTransaction.rollback();
+                       } catch (Exception e1) {
+                               log.error("Cannot rollback", e1);
+                       }
+                       throw new CmsException("Cannot add roles", e);
+               }
+       }
+
+       private boolean roleExists(String role) {
+               return userAdmin.getRole(toDn(role).toString()) != null;
+       }
+
+       protected void newRole(LdapName r) {
+               userAdmin.createRole(r.toString(), Role.GROUP);
+               log.info("Added role " + r + " required by application.");
+       }
+
+       public void register(UserAdmin userAdminService, Map<?, ?> properties) {
+               this.userAdmin = userAdminService;
+               run();
+       }
+
+       protected LdapName toDn(String name) {
+               try {
+                       return new LdapName("cn=" + name + ",ou=roles,ou=node");
+               } catch (InvalidNameException e) {
+                       throw new CmsException("Badly formatted role name " + name, e);
+               }
+       }
+
+       public void setRole(String role) {
+               this.role = role;
+       }
+
+       public void setRoles(List<String> roles) {
+               this.roles = roles;
+       }
+
+       public void setUserAdmin(UserAdmin userAdminService) {
+               this.userAdmin = userAdminService;
+       }
+
+       public void setUserTransaction(UserTransaction userTransaction) {
+               this.userTransaction = userTransaction;
+       }
+
+}
index 8b20808988c4e1d1fc83c508a555b65c2555406a..b0994c6a2a0f109636408b0f6ceb9248a30557a4 100644 (file)
@@ -1,27 +1,10 @@
 Bundle-SymbolicName: org.argeo.cms;singleton:=true
 Bundle-Activator: org.argeo.cms.internal.kernel.Activator
 Import-Package: javax.jcr.security,\
-org.xml.sax,\
-org.argeo.jcr,\
-org.argeo.jcr.docbook,\
-org.apache.jackrabbit.api,\
-org.apache.jackrabbit.commons,\
-org.apache.jackrabbit.core.security.user,\
-org.argeo.eclipse.ui.dialogs;resolution:=optional,\
-org.eclipse.*;resolution:=optional,\
-org.eclipse.core.commands;resolution:=optional,\
-org.eclipse.swt;resolution:=optional,\
-org.eclipse.jface.window;resolution:=optional,\
 org.h2;resolution:=optional,\
 org.postgresql;resolution:=optional,\
-org.apache.commons.vfs2.*;resolution:=optional,\
-org.apache.jackrabbit.*;resolution:=optional,\
-org.joda.time.*;resolution:=optional,\
-org.springframework.context,\
-org.springframework.core.io,\
 org.apache.jackrabbit.webdav.server,\
 org.apache.jackrabbit.webdav.jcr,\
-org.bouncycastle.asn1.x509,\
 org.eclipse.equinox.http.jetty,\
 *
 Provide-Capability: cms.datamodel;name=cms;cnd=/org/argeo/cms/cms.cnd
\ No newline at end of file
index 8b56c137b99cab9ac35b0c64cdb3d419a4659c27..75cefd1c03687aad342dbc26159c465703427484 100644 (file)
@@ -3,8 +3,6 @@ package org.argeo.cms;
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginContext;
 
-import org.argeo.cms.ui.UxContext;
-
 /** Provides interaction with the CMS system. UNSTABLE API at this stage. */
 public interface CmsView {
        public final static String KEY = "org.argeo.cms.view";
diff --git a/org.argeo.cms/src/org/argeo/cms/DataMigration.java b/org.argeo.cms/src/org/argeo/cms/DataMigration.java
new file mode 100644 (file)
index 0000000..f75e66b
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.cms;
+
+import java.util.Map;
+
+import javax.jcr.Session;
+
+public interface DataMigration {
+       /** Migrate data between two workspaces, at JCR level. */
+       Boolean migrate(Session source, Session target);
+
+       /**
+        * Keys are the source workspaces and values the target workspaces. If null
+        * is returned, only the default workspace will be migrated, to the default
+        * workspace of the target repository.
+        */
+       Map<String, String> workspacesToMigrate();
+
+}
diff --git a/org.argeo.cms/src/org/argeo/cms/UxContext.java b/org.argeo.cms/src/org/argeo/cms/UxContext.java
new file mode 100644 (file)
index 0000000..b6674b9
--- /dev/null
@@ -0,0 +1,9 @@
+package org.argeo.cms;
+
+public interface UxContext {
+       boolean isPortrait();
+       boolean isLandscape();
+       boolean isSquare();
+       
+       boolean isSmall();
+}
index 8aa08728b79846eace2b4e970c6223de4ef5fa34..1ec52f057523cbe9aacade8a5ca55e1362acf390 100644 (file)
@@ -26,7 +26,7 @@ import javax.security.auth.x500.X500Principal;
 
 import org.argeo.cms.CmsException;
 import org.argeo.cms.CmsView;
-import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
 import org.osgi.service.useradmin.Authorization;
 
 /** Static utilities */
@@ -49,12 +49,11 @@ public final class CurrentUser {
 
        public static boolean isAnonymous(Subject subject) {
                String username = getUsername(subject);
-               return username == null
-                               || username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+               return username == null || username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
        }
 
        private static Subject currentSubject() {
-               CmsView cmsView = CmsUtils.getCmsView();
+               CmsView cmsView = getCmsView();
                if (cmsView != null)
                        return cmsView.getSubject();
                Subject subject = Subject.getSubject(AccessController.getContext());
@@ -63,14 +62,21 @@ public final class CurrentUser {
                throw new CmsException("Cannot find related subject");
        }
 
+       /**
+        * The CMS view related to this display, or null if none is available from
+        * this call.
+        */
+       private static CmsView getCmsView() {
+               return UiContext.getData(CmsView.KEY);
+       }
+
        public final static String getUsername(Subject subject) {
                // Subject subject = Subject.getSubject(AccessController.getContext());
                // if (subject == null)
                // return null;
                if (subject.getPrincipals(X500Principal.class).size() != 1)
                        return null;
-               Principal principal = subject.getPrincipals(X500Principal.class)
-                               .iterator().next();
+               Principal principal = subject.getPrincipals(X500Principal.class).iterator().next();
                return principal.getName();
 
        }
@@ -80,8 +86,7 @@ public final class CurrentUser {
        }
 
        private static Authorization getAuthorization(Subject subject) {
-               return subject.getPrivateCredentials(Authorization.class).iterator()
-                               .next();
+               return subject.getPrivateCredentials(Authorization.class).iterator().next();
        }
 
        public final static Set<String> roles() {
@@ -90,8 +95,7 @@ public final class CurrentUser {
 
        public final static Set<String> roles(Subject subject) {
                Set<String> roles = new HashSet<String>();
-               X500Principal userPrincipal = subject
-                               .getPrincipals(X500Principal.class).iterator().next();
+               X500Principal userPrincipal = subject.getPrincipals(X500Principal.class).iterator().next();
                roles.add(userPrincipal.getName());
                for (Principal group : subject.getPrincipals(Group.class)) {
                        roles.add(group.getName());
diff --git a/org.argeo.cms/src/org/argeo/cms/forms/EditableLink.java b/org.argeo.cms/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/src/org/argeo/cms/forms/EditableMultiStringProperty.java b/org.argeo.cms/src/org/argeo/cms/forms/EditableMultiStringProperty.java
deleted file mode 100644 (file)
index fdaa036..0000000
+++ /dev/null
@@ -1,267 +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 legal 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 = possibleValues;
-               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/src/org/argeo/cms/forms/EditablePropertyDate.java b/org.argeo.cms/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/src/org/argeo/cms/forms/EditablePropertyString.java b/org.argeo.cms/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/src/org/argeo/cms/forms/FormConstants.java b/org.argeo.cms/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/src/org/argeo/cms/forms/FormEditorHeader.java b/org.argeo.cms/src/org/argeo/cms/forms/FormEditorHeader.java
deleted file mode 100644 (file)
index c12e2f0..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.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/src/org/argeo/cms/forms/FormPageViewer.java b/org.argeo.cms/src/org/argeo/cms/forms/FormPageViewer.java
deleted file mode 100644 (file)
index b4d24bf..0000000
+++ /dev/null
@@ -1,641 +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.CmsEditable;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.internal.text.MarkupValidatorCopy;
-import org.argeo.cms.text.Img;
-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.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 implements
-                       CmsNames {
-
-               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-.]", "_");
-
-                       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
-       protected Node getOrCreateNode(Node parent, String nodeType, String nodeName)
-                       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/src/org/argeo/cms/forms/FormStyle.java b/org.argeo.cms/src/org/argeo/cms/forms/FormStyle.java
deleted file mode 100644 (file)
index 1b9880d..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,
-       // Date
-       popupCalendar,
-       // Buttons
-       starred, unstarred, starOverlay, 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/src/org/argeo/cms/forms/FormUtils.java b/org.argeo.cms/src/org/argeo/cms/forms/FormUtils.java
deleted file mode 100644 (file)
index f8d08b0..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.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 '&' by its HTML encoding '&#38;' to
-        * avoid <code>SAXParseException</code> while rendering HTML with RWT
-        */
-       public static String replaceAmpersand(String value) {
-               value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND);
-               return value;
-       }
-
-       // Prevents instantiation
-       private FormUtils() {
-       }
-}
index e422e21fea8212608520fd7d935d4288522d973a..6253e585beeb50b25420f967157fc6d0e92718f9 100644 (file)
@@ -1,5 +1,7 @@
 package org.argeo.cms.i18n;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.ResourceBundle;
 
@@ -41,110 +43,27 @@ public class LocaleUtils {
        static Locale getCurrentLocale() {
                return UiContext.getLocale();
        }
-       // private String id;
-       // private ClassLoader classLoader;
-       // private final Object defaultLocal;
-       //
-       // public Msg() {
-       // defaultLocal = null;
-       // }
-       //
-       // public Msg(Object defaultMessage) {
-       // this.defaultLocal = defaultMessage;
-       // }
-       //
-       // public String getId() {
-       // return id;
-       // }
-       //
-       // public void setId(String id) {
-       // this.id = id;
-       // }
-       //
-       // public ClassLoader getClassLoader() {
-       // return classLoader;
-       // }
-       //
-       // public void setClassLoader(ClassLoader classLoader) {
-       // this.classLoader = classLoader;
-       // }
-       //
-       // public Object getDefault() {
-       // return defaultLocal;
-       // }
-       //
-       // public String toString() {
-       // return local().toString();
-       // }
-       //
-       // /** When used as the first word of a sentence. */
-       // public String lead() {
-       // return lead(UiContext.getLocale());
-       // }
-       //
-       // public String lead(Locale locale) {
-       // return lead(this, locale);
-       // }
 
-       // private static String lead(Msg msg, Locale locale) {
-       // String raw = msg.local(locale).toString();
-       // return lead(raw, locale);
-       // }
-
-       // public Object local() {
-       // Object local = local(this);
-       // if (local == null)
-       // local = getDefault();
-       // if (local == null)
-       // throw new CmsException("No translation found for " + id);
-       // return local;
-       // }
-       //
-       // public Object local(Locale locale) {
-       // Object local = local(this, locale);
-       // if (local == null)
-       // local = getDefault();
-       // if (local == null)
-       // throw new CmsException("No translation found for " + id);
-       // return local;
-       // }
-       //
-       // private static Object local(Msg msg) {
-       // Locale locale = UiContext.getLocale();
-       // return local(msg, locale);
-       // }
-       //
-       // public static Object local(Msg msg, Locale locale) {
-       // String key = msg.getId();
-       // int lastDot = key.lastIndexOf('.');
-       // String className = key.substring(0, lastDot);
-       // String fieldName = key.substring(lastDot + 1);
-       // ResourceBundle rb = ResourceBundle.getBundle("/OSGI-INF/l10n/bundle",
-       // locale, msg.getClassLoader());
-       // // ResourceBundle rb = ResourceBundle.getBundle(className, locale,
-       // // msg.getClassLoader());
-       // return rb.getString(fieldName);
-       // }
-
-       // public static void init(Class<?> clss) {
-       // final Field[] fieldArray = clss.getDeclaredFields();
-       // ClassLoader loader = clss.getClassLoader();
-       //
-       // for (Field field : fieldArray) {
-       // if (Modifier.isStatic(field.getModifiers())
-       // && field.getType().isAssignableFrom(Msg.class)) {
-       // try {
-       // Object obj = field.get(null);
-       // String id = clss.getCanonicalName() + "." + field.getName();
-       // obj.getClass().getMethod("setId", String.class)
-       // .invoke(obj, id);
-       // obj.getClass()
-       // .getMethod("setClassLoader", ClassLoader.class)
-       // .invoke(obj, loader);
-       // } catch (Exception e) {
-       // throw new CmsException("Cannot prepare field " + field);
-       // }
-       // }
-       // }
-       // }
+       /** Returns null if argument is null. */
+       public static List<Locale> asLocaleList(Object locales) {
+               if (locales == null)
+                       return null;
+               ArrayList<Locale> availableLocales = new ArrayList<Locale>();
+               String[] codes = locales.toString().split(",");
+               for (int i = 0; i < codes.length; i++) {
+                       String code = codes[i];
+                       // variant not supported
+                       int indexUnd = code.indexOf("_");
+                       Locale locale;
+                       if (indexUnd > 0) {
+                               String language = code.substring(0, indexUnd);
+                               String country = code.substring(indexUnd + 1);
+                               locale = new Locale(language, country);
+                       } else {
+                               locale = new Locale(code);
+                       }
+                       availableLocales.add(locale);
+               }
+               return availableLocales;
+       }
 }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/ImageManagerImpl.java
deleted file mode 100644 (file)
index 122c24e..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-package org.argeo.cms.internal;
-
-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.CmsConstants.NO_IMAGE_SIZE;
-import static org.argeo.cms.CmsTypes.CMS_STYLED;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.activation.MimetypesFileTypeMap;
-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.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.util.CmsUtils;
-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 ImageManagerImpl implements CmsImageManager, CmsNames {
-       private final static Log log = LogFactory.getLog(ImageManagerImpl.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 {
-               return new Point(node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node
-                               .getProperty(CMS_IMAGE_WIDTH).getLong() : 0,
-                               node.hasProperty(CMS_IMAGE_WIDTH) ? (int) node.getProperty(
-                                               CMS_IMAGE_HEIGHT).getLong() : 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 if (node.isNodeType(CMS_STYLED) && node.hasProperty(CMS_DATA)) {
-                       return node.getProperty(CMS_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);
-                       fileNode.addMixin(CmsTypes.CMS_IMAGE);
-
-                       inputStream = new ByteArrayInputStream(arr);
-                       ImageData id = new ImageData(inputStream);
-                       fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
-                       fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
-                       fileNode.setProperty(Property.JCR_MIMETYPE,
-                                       fileTypeMap.getContentType(fileName));
-                       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);
-               }
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java b/org.argeo.cms/src/org/argeo/cms/internal/JcrContentProvider.java
deleted file mode 100644 (file)
index fd8eb2c..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.internal;
-
-import java.util.ArrayList;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.Viewer;
-
-@Deprecated
-class JcrContentProvider implements ITreeContentProvider {
-       private static final long serialVersionUID = -1333678161322488674L;
-
-       @Override
-       public void dispose() {
-       }
-
-       @Override
-       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               if (newInput == null)
-                       return;
-               if (!(newInput instanceof Node))
-                       throw new CmsException("Input " + newInput + " must be a node");
-       }
-
-       @Override
-       public Object[] getElements(Object inputElement) {
-               try {
-                       Node node = (Node) inputElement;
-                       ArrayList<Node> arr = new ArrayList<Node>();
-                       NodeIterator nit = node.getNodes();
-                       while (nit.hasNext()) {
-                               arr.add(nit.nextNode());
-                       }
-                       return arr.toArray();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get elements", e);
-               }
-       }
-
-       @Override
-       public Object[] getChildren(Object parentElement) {
-               try {
-                       Node node = (Node) parentElement;
-                       ArrayList<Node> arr = new ArrayList<Node>();
-                       NodeIterator nit = node.getNodes();
-                       while (nit.hasNext()) {
-                               arr.add(nit.nextNode());
-                       }
-                       return arr.toArray();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get elements", e);
-               }
-       }
-
-       @Override
-       public Object getParent(Object element) {
-               try {
-                       Node node = (Node) element;
-                       if (node.getName().equals(""))
-                               return null;
-                       else
-                               return node.getParent();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get elements", e);
-               }
-       }
-
-       @Override
-       public boolean hasChildren(Object element) {
-               try {
-                       Node node = (Node) element;
-                       return node.hasNodes();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get elements", e);
-               }
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java b/org.argeo.cms/src/org/argeo/cms/internal/JcrFileUploadReceiver.java
deleted file mode 100644 (file)
index a2ff496..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.internal;
-
-import static javax.jcr.nodetype.NodeType.NT_FILE;
-
-import java.io.IOException;
-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.FilenameUtils;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.fileupload.FileDetails;
-import org.eclipse.rap.fileupload.FileUploadReceiver;
-
-public class JcrFileUploadReceiver extends FileUploadReceiver implements
-               CmsNames {
-       private final Node parentNode;
-       private final String nodeName;
-       private final CmsImageManager imageManager;
-
-       /** If nodeName is null, use the uploaded file name */
-       public JcrFileUploadReceiver(Node parentNode, String nodeName,
-                       CmsImageManager imageManager) {
-               super();
-               this.parentNode = parentNode;
-               this.nodeName = nodeName;
-               this.imageManager = imageManager;
-       }
-
-       @Override
-       public void receive(InputStream stream, FileDetails details)
-                       throws IOException {
-               try {
-                       String fileName = nodeName != null ? nodeName : details
-                                       .getFileName();
-                       String contentType = details.getContentType();
-                       if (isImage(details.getFileName(), contentType)) {
-                               imageManager.uploadImage(parentNode, fileName, stream);
-                               return;
-                               // InputStream inputStream = new ByteArrayInputStream(arr);
-                               // ImageData id = new ImageData(inputStream);
-                               // fileNode.addMixin(CmsTypes.CMS_IMAGE);
-                               // fileNode.setProperty(CMS_IMAGE_WIDTH, id.width);
-                               // fileNode.setProperty(CMS_IMAGE_HEIGHT, id.height);
-                       }
-
-                       Node fileNode;
-                       if (parentNode.hasNode(fileName)) {
-                               fileNode = parentNode.getNode(fileName);
-                               if (!fileNode.isNodeType(NT_FILE))
-                                       fileNode.remove();
-                       }
-                       fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream);
-
-                       if (contentType != null) {
-                               fileNode.addMixin(NodeType.MIX_MIMETYPE);
-                               fileNode.setProperty(Property.JCR_MIMETYPE, contentType);
-                       }
-                       processNewFile(fileNode);
-                       fileNode.getSession().save();
-               } catch (RepositoryException e) {
-                       throw new CmsException("cannot receive " + details, e);
-               }
-       }
-
-       protected Boolean isImage(String fileName, String contentType) {
-               String ext = FilenameUtils.getExtension(fileName);
-               return ext != null
-                               && (ext.equals("png") || ext.equalsIgnoreCase("jpg"));
-       }
-
-       protected void processNewFile(Node node) {
-
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java b/org.argeo.cms/src/org/argeo/cms/internal/SimpleEditableImage.java
deleted file mode 100644 (file)
index 67e500f..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.cms.internal;
-
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.widgets.EditableImage;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Text;
-
-/** NOT working yet. */
-public class SimpleEditableImage extends EditableImage {
-       private static final long serialVersionUID = -5689145523114022890L;
-
-       private String src;
-       private Point imageSize;
-
-       public SimpleEditableImage(Composite parent, int swtStyle) {
-               super(parent, swtStyle);
-               // load(getControl());
-               getParent().layout();
-       }
-
-       public SimpleEditableImage(Composite parent, int swtStyle, String src,
-                       Point imageSize) {
-               super(parent, swtStyle);
-               this.src = src;
-               this.imageSize = imageSize;
-       }
-
-       @Override
-       protected Control createControl(Composite box, String style) {
-               if (isEditing()) {
-                       return createText(box, style);
-               } else {
-                       return createLabel(box, style);
-               }
-       }
-
-       protected String createImgTag() throws RepositoryException {
-               String imgTag;
-               if (src != null)
-                       imgTag = CmsUtils.img(src, imageSize);
-               else
-                       imgTag = CmsUtils.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);
-               return text;
-       }
-
-       public String getSrc() {
-               return src;
-       }
-
-       public void setSrc(String src) {
-               this.src = src;
-       }
-
-       public Point getImageSize() {
-               return imageSize;
-       }
-
-       public void setImageSize(Point imageSize) {
-               this.imageSize = imageSize;
-       }
-
-}
index 44bc331592160ebc3a79f0fefdcc40790d71a421..4f1d3637bad56c79ef1b8fc343eb36e1cf34bd72 100644 (file)
@@ -4,7 +4,6 @@ import java.io.Console;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Arrays;
-import java.util.Locale;
 
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
@@ -48,21 +47,22 @@ public class ConsoleCallbackHandler implements CallbackHandler {
                                char[] answer = console.readPassword();
                                callback.setPassword(answer);
                                Arrays.fill(answer, ' ');
-                       } else if (callbacks[i] instanceof LocaleChoice) {
-                               LocaleChoice callback = (LocaleChoice) callbacks[i];
-                               writer.write("Language");
-                               writer.write("\n");
-                               for (int j = 0; j < callback.getLocales().size(); j++) {
-                                       Locale locale = callback.getLocales().get(j);
-                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
-                               }
-                               writer.write("(" + callback.getDefaultIndex() + ") : ");
-                               String answer = console.readLine();
-                               if (answer.trim().equals(""))
-                                       callback.setSelectedIndex(callback.getDefaultIndex());
-                               else
-                                       callback.setSelectedIndex(new Integer(answer.trim()));
                        }
+//                     else if (callbacks[i] instanceof LocaleChoice) {
+//                             LocaleChoice callback = (LocaleChoice) callbacks[i];
+//                             writer.write("Language");
+//                             writer.write("\n");
+//                             for (int j = 0; j < callback.getLocales().size(); j++) {
+//                                     Locale locale = callback.getLocales().get(j);
+//                                     writer.print(j + " : " + locale.getDisplayName() + "\n");
+//                             }
+//                             writer.write("(" + callback.getDefaultIndex() + ") : ");
+//                             String answer = console.readLine();
+//                             if (answer.trim().equals(""))
+//                                     callback.setSelectedIndex(callback.getDefaultIndex());
+//                             else
+//                                     callback.setSelectedIndex(new Integer(answer.trim()));
+//                     }
                }
        }
 
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/LocaleChoice.java
deleted file mode 100644 (file)
index a23348f..0000000
+++ /dev/null
@@ -1,132 +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.internal.auth;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.callback.LanguageCallback;
-
-import org.argeo.cms.CmsException;
-
-/** 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(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());
-       }
-
-       /** Returns null if argument is null. */
-       public static List<Locale> asLocaleList(Object locales) {
-               if (locales == null)
-                       return null;
-               ArrayList<Locale> availableLocales = new ArrayList<Locale>();
-               String[] codes = locales.toString().split(",");
-               for (int i = 0; i < codes.length; i++) {
-                       String code = codes[i];
-                       // variant not supported
-                       int indexUnd = code.indexOf("_");
-                       Locale locale;
-                       if (indexUnd > 0) {
-                               String language = code.substring(0, indexUnd);
-                               String country = code.substring(indexUnd + 1);
-                               locale = new Locale(language, country);
-                       } else {
-                               locale = new Locale(code);
-                       }
-                       availableLocales.add(locale);
-               }
-               return availableLocales;
-       }
-
-       public static void main(String[] args) {
-               for (String isoL : Locale.getISOLanguages()) {
-                       Locale locale = new Locale(isoL);
-                       System.out.println(isoL + "\t" + locale.getDisplayLanguage() + "\t" + locale.getDisplayLanguage(locale));
-               }
-       }
-
-}
index 82f6fef5b973020c8cfaf5b5591407cfd5bed843..d708f90cbef1aa50288368ba48884f02010127f1 100644 (file)
@@ -8,7 +8,7 @@ import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
-import org.argeo.cms.maintenance.DataMigration;
+import org.argeo.cms.DataMigration;
 import org.argeo.jcr.JcrUtils;
 
 /** Migrate data between two workspaces, at JCR level. */
index 63aeeac299a42b257ded84f05a6ec3a90e414feb..7906c28faf99473f6d3190f78f4a64193a59ea14 100644 (file)
@@ -3,7 +3,6 @@ package org.argeo.cms.internal.kernel;
 import static bitronix.tm.TransactionManagerServices.getTransactionManager;
 import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry;
 import static java.util.Locale.ENGLISH;
-import static org.argeo.cms.internal.auth.LocaleChoice.asLocaleList;
 
 import java.io.File;
 import java.net.InetAddress;
@@ -23,11 +22,10 @@ import javax.transaction.UserTransaction;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.maintenance.MaintenanceUi;
+import org.argeo.cms.i18n.LocaleUtils;
 import org.argeo.node.NodeConstants;
 import org.argeo.node.NodeState;
 import org.argeo.util.LangUtils;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -85,7 +83,7 @@ public class CmsState implements NodeState {
                Object defaultLocaleValue = KernelUtils.getFrameworkProp(NodeConstants.I18N_DEFAULT_LOCALE);
                defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString())
                                : new Locale(ENGLISH.getLanguage());
-               locales = asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES));
+               locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(NodeConstants.I18N_LOCALES));
        }
 
        private void initServices() {
@@ -107,12 +105,6 @@ public class CmsState implements NodeState {
                Dictionary<String, Object> props = new Hashtable<>();
                props.put(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID);
                bc.registerService(ManagedServiceFactory.class, userAdmin, props);
-
-               // UI
-               bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(),
-                               LangUtils.init(KernelConstants.CONTEXT_NAME_PROP, "system"));
-               bc.registerService(ApplicationConfiguration.class, new UserUi(),
-                               LangUtils.init(KernelConstants.CONTEXT_NAME_PROP, "user"));
        }
 
        private void initTransactionManager() {
index cb22e2790c055f7e021cd9a3dbdfd419e5dd095e..50f7ef3227fac04c5ac7b2c0027f80fef108f568 100644 (file)
@@ -1,9 +1,14 @@
 package org.argeo.cms.internal.kernel;
 
+import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PRIVATE;
+import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PUBLIC;
+
 import java.io.File;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.PrivilegedAction;
@@ -13,6 +18,7 @@ import java.util.Hashtable;
 import java.util.Properties;
 import java.util.TreeSet;
 
+import javax.jcr.Node;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -24,6 +30,7 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.logging.Log;
 import org.argeo.cms.CmsException;
 import org.argeo.cms.auth.AuthConstants;
+import org.argeo.jcr.ArgeoJcrConstants;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
@@ -207,6 +214,59 @@ class KernelUtils implements KernelConstants {
                }
        }
 
+       // DATA
+       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);
+               }
+       }
+
+       public static String getDataPath(Node node) throws RepositoryException {
+               assert node != null;
+               String userId = node.getSession().getUserID();
+//             if (log.isTraceEnabled())
+//                     log.trace(userId + " : " + node.getPath());
+               StringBuilder buf = new StringBuilder();
+               boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
+               if (isAnonymous)
+                       buf.append(WEBDAV_PUBLIC);
+               else
+                       buf.append(WEBDAV_PRIVATE);
+               // TODO convey repo alias vie repository properties
+               return buf.append('/').append(ArgeoJcrConstants.ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
+                               .append(node.getPath()).toString();
+       }
+
+       public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
+               try {
+                       StringBuilder buf = getServerBaseUrl(request);
+                       buf.append('/').append('!').append(node.getPath());
+                       return new URL(buf.toString()).toString();
+               } catch (MalformedURLException e) {
+                       throw new CmsException("Cannot build data URL for " + node, e);
+               }
+               // return request.getRequestURL().append('!').append(node.getPath())
+               // .toString();
+       }
+
+       
        private KernelUtils() {
 
        }
index 97e912edddf372ea6e06afd9fafea4b69ee53b4d..4a6ad233799246036f73afd3748c538f81dde16c 100644 (file)
@@ -29,7 +29,6 @@ import javax.servlet.http.HttpSession;
 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.jcr.ArgeoJcrConstants;
 import org.argeo.jcr.JcrUtils;
 import org.osgi.framework.BundleContext;
@@ -128,13 +127,13 @@ class NodeHttp implements KernelConstants, ArgeoJcrConstants {
                                String desc = node.hasProperty(JCR_DESCRIPTION) ? node.getProperty(JCR_DESCRIPTION).getString() : null;
                                Calendar lastUpdate = node.hasProperty(JCR_LAST_MODIFIED)
                                                ? node.getProperty(JCR_LAST_MODIFIED).getDate() : null;
-                               String url = CmsUtils.getCanonicalUrl(node, request);
+                               String url = KernelUtils.getCanonicalUrl(node, request);
                                String imgUrl = null;
                                loop: for (NodeIterator it = node.getNodes(); it.hasNext();) {
                                        // Takes the first found cms:image
                                        Node child = it.nextNode();
                                        if (child.isNodeType(CMS_IMAGE)) {
-                                               imgUrl = CmsUtils.getDataUrl(child, request);
+                                               imgUrl = KernelUtils.getDataUrl(child, request);
                                                break loop;
                                        }
                                }
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/UserUi.java
deleted file mode 100644 (file)
index fd332ee..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.cms.internal.kernel;
-
-import org.argeo.cms.util.LoginEntryPoint;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-
-public class UserUi implements ApplicationConfiguration {
-       @Override
-       public void configure(Application application) {
-               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-               application.addEntryPoint("/login", LoginEntryPoint.class, null);
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java b/org.argeo.cms/src/org/argeo/cms/internal/text/AbstractTextViewer.java
deleted file mode 100644 (file)
index cb0b3a9..0000000
+++ /dev/null
@@ -1,892 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import static javax.jcr.Property.JCR_TITLE;
-import static org.argeo.cms.util.CmsUtils.fillWidth;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Observer;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.text.Img;
-import org.argeo.cms.text.Paragraph;
-import org.argeo.cms.text.TextInterpreter;
-import org.argeo.cms.text.TextSection;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.AbstractPageViewer;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.NodePart;
-import org.argeo.cms.viewers.PropertyPart;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableImage;
-import org.argeo.cms.widgets.EditableText;
-import org.argeo.cms.widgets.StyledControl;
-import org.argeo.jcr.JcrUtils;
-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.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Text;
-
-/** Base class for text viewers and editors. */
-public abstract class AbstractTextViewer extends AbstractPageViewer implements
-               CmsNames, KeyListener, Observer {
-       private static final long serialVersionUID = -2401274679492339668L;
-       private final static Log log = LogFactory.getLog(AbstractTextViewer.class);
-
-       private final Section mainSection;
-
-       private TextInterpreter textInterpreter = new TextInterpreterImpl();
-       private CmsImageManager imageManager = CmsUtils.getCmsView()
-                       .getImageManager();
-
-       private FileUploadListener fileUploadListener;
-       private TextContextMenu styledTools;
-
-       private final boolean flat;
-
-       protected AbstractTextViewer(Section parent, int style,
-                       CmsEditable cmsEditable) {
-               super(parent, style, cmsEditable);
-               flat = SWT.FLAT == (style & SWT.FLAT);
-
-               if (getCmsEditable().canEdit()) {
-                       fileUploadListener = new FUL();
-                       styledTools = new TextContextMenu(this, parent.getDisplay());
-               }
-               this.mainSection = parent;
-               initModelIfNeeded(mainSection.getNode());
-               // layout(this.mainSection);
-       }
-
-       @Override
-       public Control getControl() {
-               return mainSection;
-       }
-
-       protected void refresh(Control control) throws RepositoryException {
-               if (!(control instanceof Section))
-                       return;
-               Section section = (Section) control;
-               if (section instanceof TextSection) {
-                       CmsUtils.clear(section);
-                       Node node = section.getNode();
-                       TextSection textSection = (TextSection) section;
-                       if (node.hasProperty(Property.JCR_TITLE)) {
-                               if (section.getHeader() == null)
-                                       section.createHeader();
-                               if (node.hasProperty(Property.JCR_TITLE)) {
-                                       SectionTitle title = newSectionTitle(textSection, node);
-                                       title.setLayoutData(CmsUtils.fillWidth());
-                                       updateContent(title);
-                               }
-                       }
-
-                       for (NodeIterator ni = node.getNodes(CMS_P); ni.hasNext();) {
-                               Node child = ni.nextNode();
-                               final SectionPart sectionPart;
-                               if (child.isNodeType(CmsTypes.CMS_IMAGE)
-                                               || child.isNodeType(NodeType.NT_FILE)) {
-                                       sectionPart = newImg(textSection, child);
-                               } else if (child.isNodeType(CmsTypes.CMS_STYLED)) {
-                                       sectionPart = newParagraph(textSection, child);
-                               } else {
-                                       sectionPart = newSectionPart(textSection, child);
-                                       if (sectionPart == null)
-                                               throw new CmsException("Unsupported node " + child);
-                                       // TODO list node types in exception
-                               }
-                               if (sectionPart instanceof Control)
-                                       ((Control) sectionPart).setLayoutData(CmsUtils.fillWidth());
-                       }
-
-                       if (!flat)
-                               for (NodeIterator ni = section.getNode().getNodes(CMS_H); ni
-                                               .hasNext();) {
-                                       Node child = ni.nextNode();
-                                       if (child.isNodeType(CmsTypes.CMS_SECTION)) {
-                                               TextSection newSection = new TextSection(section,
-                                                               SWT.NONE, child);
-                                               newSection.setLayoutData(CmsUtils.fillWidth());
-                                               refresh(newSection);
-                                       }
-                               }
-               } else {
-                       for (Section s : section.getSubSections().values())
-                               refresh(s);
-               }
-               // section.layout();
-       }
-
-       /** To be overridden in order to provide additional SectionPart types */
-       protected SectionPart newSectionPart(TextSection textSection, Node node) {
-               return null;
-       }
-
-       // CRUD
-       protected Paragraph newParagraph(TextSection parent, Node node)
-                       throws RepositoryException {
-               Paragraph paragraph = new Paragraph(parent, parent.getStyle(), node);
-               updateContent(paragraph);
-               paragraph.setLayoutData(fillWidth());
-               paragraph.setMouseListener(getMouseListener());
-               return paragraph;
-       }
-
-       protected Img newImg(TextSection parent, Node node)
-                       throws RepositoryException {
-               Img img = new Img(parent, parent.getStyle(), node) {
-                       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);
-               img.setMouseListener(getMouseListener());
-               return img;
-       }
-
-       protected SectionTitle newSectionTitle(TextSection parent, Node node)
-                       throws RepositoryException {
-               SectionTitle title = new SectionTitle(parent.getHeader(),
-                               parent.getStyle(), node.getProperty(JCR_TITLE));
-               updateContent(title);
-               title.setMouseListener(getMouseListener());
-               return title;
-       }
-
-       protected SectionTitle prepareSectionTitle(Section newSection,
-                       String titleText) throws RepositoryException {
-               Node sectionNode = newSection.getNode();
-               if (!sectionNode.hasProperty(JCR_TITLE))
-                       sectionNode.setProperty(Property.JCR_TITLE, "");
-               getTextInterpreter().write(sectionNode.getProperty(Property.JCR_TITLE),
-                               titleText);
-               if (newSection.getHeader() == null)
-                       newSection.createHeader();
-               SectionTitle sectionTitle = newSectionTitle((TextSection) newSection,
-                               sectionNode);
-               return sectionTitle;
-       }
-
-       protected void updateContent(EditablePart part) throws RepositoryException {
-               if (part instanceof SectionPart) {
-                       SectionPart sectionPart = (SectionPart) part;
-                       Node partNode = sectionPart.getNode();
-
-                       if (part instanceof StyledControl
-                                       && (sectionPart.getSection() instanceof TextSection)) {
-                               TextSection section = (TextSection) sectionPart.getSection();
-                               StyledControl styledControl = (StyledControl) part;
-                               if (partNode.isNodeType(CmsTypes.CMS_STYLED)) {
-                                       String style = partNode.hasProperty(CMS_STYLE) ? partNode
-                                                       .getProperty(CMS_STYLE).getString() : section
-                                                       .getDefaultTextStyle();
-                                       styledControl.setStyle(style);
-                               }
-                       }
-                       // use control AFTER setting style, since it may have been reset
-
-                       if (part instanceof EditableText) {
-                               EditableText paragraph = (EditableText) part;
-                               if (paragraph == getEdited())
-                                       paragraph.setText(textInterpreter.read(partNode));
-                               else
-                                       paragraph.setText(textInterpreter.raw(partNode));
-                       } else if (part instanceof EditableImage) {
-                               EditableImage editableImage = (EditableImage) part;
-                               imageManager.load(partNode, part.getControl(),
-                                               editableImage.getPreferredImageSize());
-                       }
-               } else if (part instanceof SectionTitle) {
-                       SectionTitle title = (SectionTitle) part;
-                       title.setStyle(title.getSection().getTitleStyle());
-                       // use control AFTER setting style
-                       if (title == getEdited())
-                               title.setText(textInterpreter.read(title.getProperty()));
-                       else
-                               title.setText(textInterpreter.raw(title.getProperty()));
-               }
-       }
-
-       // OVERRIDDEN FROM PARENT VIEWER
-       @Override
-       protected void save(EditablePart part) throws RepositoryException {
-               if (part instanceof EditableText) {
-                       EditableText et = (EditableText) part;
-                       String text = ((Text) et.getControl()).getText();
-
-                       String[] lines = text.split("[\r\n]+");
-                       assert lines.length != 0;
-                       saveLine(part, lines[0]);
-                       if (lines.length > 1) {
-                               ArrayList<Control> toLayout = new ArrayList<Control>();
-                               if (part instanceof Paragraph) {
-                                       Paragraph currentParagraph = (Paragraph) et;
-                                       Section section = currentParagraph.getSection();
-                                       Node sectionNode = section.getNode();
-                                       Node currentParagraphN = currentParagraph.getNode();
-                                       for (int i = 1; i < lines.length; i++) {
-                                               Node newNode = sectionNode.addNode(CMS_P);
-                                               newNode.addMixin(CmsTypes.CMS_STYLED);
-                                               saveLine(newNode, lines[i]);
-                                               // second node was create as last, if it is not the next
-                                               // one, it
-                                               // means there are some in between and we can take the
-                                               // one at
-                                               // index+1 for the re-order
-                                               if (newNode.getIndex() > currentParagraphN.getIndex() + 1) {
-                                                       sectionNode.orderBefore(p(newNode.getIndex()),
-                                                                       p(currentParagraphN.getIndex() + 1));
-                                               }
-                                               Paragraph newParagraph = newParagraph(
-                                                               (TextSection) section, newNode);
-                                               newParagraph.moveBelow(currentParagraph);
-                                               toLayout.add(newParagraph);
-
-                                               currentParagraph = newParagraph;
-                                               currentParagraphN = newNode;
-                                       }
-                                       persistChanges(sectionNode);
-                               }
-                               // TODO or rather return the created paragarphs?
-                               layout(toLayout.toArray(new Control[toLayout.size()]));
-                       }
-               }
-       }
-
-       protected void saveLine(EditablePart part, String line) {
-               if (part instanceof NodePart) {
-                       saveLine(((NodePart) part).getNode(), line);
-               } else if (part instanceof PropertyPart) {
-                       saveLine(((PropertyPart) part).getProperty(), line);
-               } else {
-                       throw new CmsException("Unsupported part " + part);
-               }
-       }
-
-       protected void saveLine(Item item, String line) {
-               line = line.trim();
-               textInterpreter.write(item, line);
-       }
-
-       @Override
-       protected void prepare(EditablePart part, Object caretPosition) {
-               Control control = part.getControl();
-               if (control instanceof Text) {
-                       Text text = (Text) control;
-                       if (caretPosition != null)
-                               if (caretPosition instanceof Integer)
-                                       text.setSelection((Integer) caretPosition);
-                               else if (caretPosition instanceof Point) {
-                                       // TODO find a way to position the caret at the right place
-                               }
-                       text.setData(RWT.ACTIVE_KEYS, new String[] { "BACKSPACE", "ESC",
-                                       "TAB", "SHIFT+TAB", "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT",
-                                       "ALT+ARROW_UP", "ALT+ARROW_DOWN", "RETURN", "CTRL+RETURN",
-                                       "ENTER", "DELETE" });
-                       text.setData(RWT.CANCEL_KEYS, new String[] { "RETURN",
-                                       "ALT+ARROW_LEFT", "ALT+ARROW_RIGHT" });
-                       text.addKeyListener(this);
-               } else if (part instanceof Img) {
-                       ((Img) part).setFileUploadListener(fileUploadListener);
-               }
-       }
-
-       // REQUIRED BY CONTEXT MENU
-       void setParagraphStyle(Paragraph paragraph, String style) {
-               try {
-                       Node paragraphNode = paragraph.getNode();
-                       paragraphNode.setProperty(CMS_STYLE, style);
-                       persistChanges(paragraphNode);
-                       updateContent(paragraph);
-                       layout(paragraph);
-               } catch (RepositoryException e1) {
-                       throw new CmsException("Cannot set style " + style + " on "
-                                       + paragraph, e1);
-               }
-       }
-
-       void deletePart(SectionPart paragraph) {
-               try {
-                       Node paragraphNode = paragraph.getNode();
-                       Section section = paragraph.getSection();
-                       Session session = paragraphNode.getSession();
-                       paragraphNode.remove();
-                       session.save();
-                       if (paragraph instanceof Control)
-                               ((Control) paragraph).dispose();
-                       layout(section);
-               } catch (RepositoryException e1) {
-                       throw new CmsException("Cannot delete " + paragraph, e1);
-               }
-       }
-
-       String getRawParagraphText(Paragraph paragraph) {
-               return textInterpreter.raw(paragraph.getNode());
-       }
-
-       // COMMANDS
-       protected void splitEdit() {
-               checkEdited();
-               try {
-                       if (getEdited() instanceof Paragraph) {
-                               Paragraph paragraph = (Paragraph) getEdited();
-                               Text text = (Text) paragraph.getControl();
-                               int caretPosition = text.getCaretPosition();
-                               String txt = text.getText();
-                               String first = txt.substring(0, caretPosition);
-                               String second = txt.substring(caretPosition);
-                               Node firstNode = paragraph.getNode();
-                               Node sectionNode = firstNode.getParent();
-                               firstNode.setProperty(CMS_CONTENT, first);
-                               Node secondNode = sectionNode.addNode(CMS_P);
-                               secondNode.addMixin(CmsTypes.CMS_STYLED);
-                               // second node was create as last, if it is not the next one, it
-                               // means there are some in between and we can take the one at
-                               // index+1 for the re-order
-                               if (secondNode.getIndex() > firstNode.getIndex() + 1) {
-                                       sectionNode.orderBefore(p(secondNode.getIndex()),
-                                                       p(firstNode.getIndex() + 1));
-                               }
-
-                               // if we die in between, at least we still have the whole text
-                               // in the first node
-                               try {
-                                       textInterpreter.write(secondNode, second);
-                                       textInterpreter.write(firstNode, first);
-                               } catch (Exception e) {
-                                       // so that no additional nodes are created:
-                                       JcrUtils.discardUnderlyingSessionQuietly(firstNode);
-                                       throw e;
-                               }
-
-                               persistChanges(firstNode);
-
-                               Paragraph secondParagraph = paragraphSplitted(paragraph,
-                                               secondNode);
-                               edit(secondParagraph, 0);
-                       } else if (getEdited() instanceof SectionTitle) {
-                               SectionTitle sectionTitle = (SectionTitle) getEdited();
-                               Text text = (Text) sectionTitle.getControl();
-                               String txt = text.getText();
-                               int caretPosition = text.getCaretPosition();
-                               Section section = sectionTitle.getSection();
-                               Node sectionNode = section.getNode();
-                               Node paragraphNode = sectionNode.addNode(CMS_P);
-                               paragraphNode.addMixin(CmsTypes.CMS_STYLED);
-                               textInterpreter.write(paragraphNode,
-                                               txt.substring(caretPosition));
-                               textInterpreter.write(
-                                               sectionNode.getProperty(Property.JCR_TITLE),
-                                               txt.substring(0, caretPosition));
-                               sectionNode.orderBefore(p(paragraphNode.getIndex()), p(1));
-                               persistChanges(sectionNode);
-
-                               Paragraph paragraph = sectionTitleSplitted(sectionTitle,
-                                               paragraphNode);
-                               // section.layout();
-                               edit(paragraph, 0);
-                       }
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot split " + getEdited(), e);
-               }
-       }
-
-       protected void mergeWithPrevious() {
-               checkEdited();
-               try {
-                       Paragraph paragraph = (Paragraph) getEdited();
-                       Text text = (Text) paragraph.getControl();
-                       String txt = text.getText();
-                       Node paragraphNode = paragraph.getNode();
-                       if (paragraphNode.getIndex() == 1)
-                               return;// do nothing
-                       Node sectionNode = paragraphNode.getParent();
-                       Node previousNode = sectionNode
-                                       .getNode(p(paragraphNode.getIndex() - 1));
-                       String previousTxt = textInterpreter.read(previousNode);
-                       textInterpreter.write(previousNode, previousTxt + txt);
-                       paragraphNode.remove();
-                       persistChanges(sectionNode);
-
-                       Paragraph previousParagraph = paragraphMergedWithPrevious(
-                                       paragraph, previousNode);
-                       edit(previousParagraph, previousTxt.length());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot stop editing", e);
-               }
-       }
-
-       protected void mergeWithNext() {
-               checkEdited();
-               try {
-                       Paragraph paragraph = (Paragraph) getEdited();
-                       Text text = (Text) paragraph.getControl();
-                       String txt = text.getText();
-                       Node paragraphNode = paragraph.getNode();
-                       Node sectionNode = paragraphNode.getParent();
-                       NodeIterator paragraphNodes = sectionNode.getNodes(CMS_P);
-                       long size = paragraphNodes.getSize();
-                       if (paragraphNode.getIndex() == size)
-                               return;// do nothing
-                       Node nextNode = sectionNode
-                                       .getNode(p(paragraphNode.getIndex() + 1));
-                       String nextTxt = textInterpreter.read(nextNode);
-                       textInterpreter.write(paragraphNode, txt + nextTxt);
-
-                       Section section = paragraph.getSection();
-                       Paragraph removed = (Paragraph) section.getSectionPart(nextNode
-                                       .getIdentifier());
-
-                       nextNode.remove();
-                       persistChanges(sectionNode);
-
-                       paragraphMergedWithNext(paragraph, removed);
-                       edit(paragraph, txt.length());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot stop editing", e);
-               }
-       }
-
-       protected synchronized void upload(EditablePart part) {
-               try {
-                       if (part instanceof SectionPart) {
-                               SectionPart sectionPart = (SectionPart) part;
-                               Node partNode = sectionPart.getNode();
-                               int partIndex = partNode.getIndex();
-                               Section section = sectionPart.getSection();
-                               Node sectionNode = section.getNode();
-
-                               if (part instanceof Paragraph) {
-                                       Node newNode = sectionNode.addNode(CMS_P, NodeType.NT_FILE);
-                                       newNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
-                                       JcrUtils.copyBytesAsFile(sectionNode,
-                                                       p(newNode.getIndex()), new byte[0]);
-                                       if (partIndex < newNode.getIndex() - 1) {
-                                               // was not last
-                                               sectionNode.orderBefore(p(newNode.getIndex()),
-                                                               p(partIndex - 1));
-                                       }
-                                       // sectionNode.orderBefore(p(partNode.getIndex()),
-                                       // p(newNode.getIndex()));
-                                       persistChanges(sectionNode);
-                                       Img img = newImg((TextSection) section, newNode);
-                                       edit(img, null);
-                                       layout(img.getControl());
-                               } else if (part instanceof Img) {
-                                       if (getEdited() == part)
-                                               return;
-                                       edit(part, null);
-                                       layout(part.getControl());
-                               }
-                       }
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot upload", e);
-               }
-       }
-
-       protected void deepen() {
-               if (flat)
-                       return;
-               checkEdited();
-               try {
-                       if (getEdited() instanceof Paragraph) {
-                               Paragraph paragraph = (Paragraph) getEdited();
-                               Text text = (Text) paragraph.getControl();
-                               String txt = text.getText();
-                               Node paragraphNode = paragraph.getNode();
-                               Section section = paragraph.getSection();
-                               Node sectionNode = section.getNode();
-                               // main title
-                               if (section == mainSection && section instanceof TextSection
-                                               && paragraphNode.getIndex() == 1
-                                               && !sectionNode.hasProperty(JCR_TITLE)) {
-                                       SectionTitle sectionTitle = prepareSectionTitle(section,
-                                                       txt);
-                                       edit(sectionTitle, 0);
-                                       return;
-                               }
-                               Node newSectionNode = sectionNode.addNode(CMS_H,
-                                               CmsTypes.CMS_SECTION);
-                               sectionNode.orderBefore(h(newSectionNode.getIndex()), h(1));
-
-                               int paragraphIndex = paragraphNode.getIndex();
-                               String sectionPath = sectionNode.getPath();
-                               String newSectionPath = newSectionNode.getPath();
-                               while (sectionNode.hasNode(p(paragraphIndex + 1))) {
-                                       Node parag = sectionNode.getNode(p(paragraphIndex + 1));
-                                       sectionNode.getSession().move(
-                                                       sectionPath + '/' + p(paragraphIndex + 1),
-                                                       newSectionPath + '/' + CMS_P);
-                                       SectionPart sp = section.getSectionPart(parag
-                                                       .getIdentifier());
-                                       if (sp instanceof Control)
-                                               ((Control) sp).dispose();
-                               }
-                               // create property
-                               newSectionNode.setProperty(Property.JCR_TITLE, "");
-                               getTextInterpreter().write(
-                                               newSectionNode.getProperty(Property.JCR_TITLE), txt);
-
-                               TextSection newSection = new TextSection(section,
-                                               section.getStyle(), newSectionNode);
-                               newSection.setLayoutData(CmsUtils.fillWidth());
-                               newSection.moveBelow(paragraph);
-
-                               // dispose
-                               paragraphNode.remove();
-                               paragraph.dispose();
-
-                               refresh(newSection);
-                               newSection.getParent().layout();
-                               layout(newSection);
-                               persistChanges(sectionNode);
-                       } else if (getEdited() instanceof SectionTitle) {
-                               SectionTitle sectionTitle = (SectionTitle) getEdited();
-                               Section section = sectionTitle.getSection();
-                               Section parentSection = section.getParentSection();
-                               if (parentSection == null)
-                                       return;// cannot deepen main section
-                               Node sectionN = section.getNode();
-                               Node parentSectionN = parentSection.getNode();
-                               if (sectionN.getIndex() == 1)
-                                       return;// cannot deepen first section
-                               Node previousSectionN = parentSectionN.getNode(h(sectionN
-                                               .getIndex() - 1));
-                               NodeIterator subSections = previousSectionN.getNodes(CMS_H);
-                               int subsectionsCount = (int) subSections.getSize();
-                               previousSectionN.getSession().move(
-                                               sectionN.getPath(),
-                                               previousSectionN.getPath() + "/"
-                                                               + h(subsectionsCount + 1));
-                               section.dispose();
-                               TextSection newSection = new TextSection(section,
-                                               section.getStyle(), sectionN);
-                               refresh(newSection);
-                               persistChanges(previousSectionN);
-                       }
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot deepen " + getEdited(), e);
-               }
-       }
-
-       protected void undeepen() {
-               if (flat)
-                       return;
-               checkEdited();
-               try {
-                       if (getEdited() instanceof Paragraph) {
-                               upload(getEdited());
-                       } else if (getEdited() instanceof SectionTitle) {
-                               SectionTitle sectionTitle = (SectionTitle) getEdited();
-                               Section section = sectionTitle.getSection();
-                               Node sectionNode = section.getNode();
-                               Section parentSection = section.getParentSection();
-                               if (parentSection == null)
-                                       return;// cannot undeepen main section
-
-                               // choose in which section to merge
-                               Section mergedSection;
-                               if (sectionNode.getIndex() == 1)
-                                       mergedSection = section.getParentSection();
-                               else {
-                                       Map<String, Section> parentSubsections = parentSection
-                                                       .getSubSections();
-                                       ArrayList<Section> lst = new ArrayList<Section>(
-                                                       parentSubsections.values());
-                                       mergedSection = lst.get(sectionNode.getIndex() - 1);
-                               }
-                               Node mergedNode = mergedSection.getNode();
-                               boolean mergedHasSubSections = mergedNode.hasNode(CMS_H);
-
-                               // title as paragraph
-                               Node newParagrapheNode = mergedNode.addNode(CMS_P);
-                               newParagrapheNode.addMixin(CmsTypes.CMS_STYLED);
-                               if (mergedHasSubSections)
-                                       mergedNode.orderBefore(p(newParagrapheNode.getIndex()),
-                                                       h(1));
-                               String txt = getTextInterpreter().read(
-                                               sectionNode.getProperty(Property.JCR_TITLE));
-                               getTextInterpreter().write(newParagrapheNode, txt);
-                               // move
-                               NodeIterator paragraphs = sectionNode.getNodes(CMS_P);
-                               while (paragraphs.hasNext()) {
-                                       Node p = paragraphs.nextNode();
-                                       SectionPart sp = section.getSectionPart(p.getIdentifier());
-                                       if (sp instanceof Control)
-                                               ((Control) sp).dispose();
-                                       mergedNode.getSession().move(p.getPath(),
-                                                       mergedNode.getPath() + '/' + CMS_P);
-                                       if (mergedHasSubSections)
-                                               mergedNode.orderBefore(p(p.getIndex()), h(1));
-                               }
-
-                               Iterator<Section> subsections = section.getSubSections()
-                                               .values().iterator();
-                               // NodeIterator sections = sectionNode.getNodes(CMS_H);
-                               while (subsections.hasNext()) {
-                                       Section subsection = subsections.next();
-                                       Node s = subsection.getNode();
-                                       mergedNode.getSession().move(s.getPath(),
-                                                       mergedNode.getPath() + '/' + CMS_H);
-                                       subsection.dispose();
-                               }
-
-                               // remove section
-                               section.getNode().remove();
-                               section.dispose();
-
-                               refresh(mergedSection);
-                               mergedSection.getParent().layout();
-                               layout(mergedSection);
-                               persistChanges(mergedNode);
-                       }
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot undeepen " + getEdited(), e);
-               }
-       }
-
-       // UI CHANGES
-       protected Paragraph paragraphSplitted(Paragraph paragraph, Node newNode)
-                       throws RepositoryException {
-               Section section = paragraph.getSection();
-               updateContent(paragraph);
-               Paragraph newParagraph = newParagraph((TextSection) section, newNode);
-               newParagraph.setLayoutData(CmsUtils.fillWidth());
-               newParagraph.moveBelow(paragraph);
-               layout(paragraph.getControl(), newParagraph.getControl());
-               return newParagraph;
-       }
-
-       protected Paragraph sectionTitleSplitted(SectionTitle sectionTitle,
-                       Node newNode) throws RepositoryException {
-               updateContent(sectionTitle);
-               Paragraph newParagraph = newParagraph(sectionTitle.getSection(),
-                               newNode);
-               // we assume beforeFirst is not null since there was a sectionTitle
-               newParagraph.moveBelow(sectionTitle.getSection().getHeader());
-               layout(sectionTitle.getControl(), newParagraph.getControl());
-               return newParagraph;
-       }
-
-       protected Paragraph paragraphMergedWithPrevious(Paragraph removed,
-                       Node remaining) throws RepositoryException {
-               Section section = removed.getSection();
-               removed.dispose();
-
-               Paragraph paragraph = (Paragraph) section.getSectionPart(remaining
-                               .getIdentifier());
-               updateContent(paragraph);
-               layout(paragraph.getControl());
-               return paragraph;
-       }
-
-       protected void paragraphMergedWithNext(Paragraph remaining,
-                       Paragraph removed) throws RepositoryException {
-               removed.dispose();
-               updateContent(remaining);
-               layout(remaining.getControl());
-       }
-
-       // UTILITIES
-       protected String p(Integer index) {
-               StringBuilder sb = new StringBuilder(6);
-               sb.append(CMS_P).append('[').append(index).append(']');
-               return sb.toString();
-       }
-
-       protected String h(Integer index) {
-               StringBuilder sb = new StringBuilder(5);
-               sb.append(CMS_H).append('[').append(index).append(']');
-               return sb.toString();
-       }
-
-       // GETTERS / SETTERS
-       public Section getMainSection() {
-               return mainSection;
-       }
-
-       public boolean isFlat() {
-               return flat;
-       }
-
-       public TextInterpreter getTextInterpreter() {
-               return textInterpreter;
-       }
-
-       // KEY LISTENER
-       @Override
-       public void keyPressed(KeyEvent ke) {
-               if (log.isTraceEnabled())
-                       log.trace(ke);
-
-               if (getEdited() == null)
-                       return;
-               boolean altPressed = (ke.stateMask & SWT.ALT) != 0;
-               boolean shiftPressed = (ke.stateMask & SWT.SHIFT) != 0;
-               boolean ctrlPressed = (ke.stateMask & SWT.CTRL) != 0;
-
-               try {
-                       // Common
-                       if (ke.keyCode == SWT.ESC) {
-                               cancelEdit();
-                       } else if (ke.character == '\r') {
-                               splitEdit();
-                       } else if (ke.character == 'S') {
-                               if (ctrlPressed)
-                                       saveEdit();
-                       } else if (ke.character == '\t') {
-                               if (!shiftPressed) {
-                                       deepen();
-                               } else if (shiftPressed) {
-                                       undeepen();
-                               }
-                       } else {
-                               if (getEdited() instanceof Paragraph) {
-                                       Paragraph paragraph = (Paragraph) getEdited();
-                                       Section section = paragraph.getSection();
-                                       if (altPressed && ke.keyCode == SWT.ARROW_RIGHT) {
-                                               edit(section.nextSectionPart(paragraph), 0);
-                                       } else if (altPressed && ke.keyCode == SWT.ARROW_LEFT) {
-                                               edit(section.previousSectionPart(paragraph), 0);
-                                       } else if (ke.character == SWT.BS) {
-                                               Text text = (Text) paragraph.getControl();
-                                               int caretPosition = text.getCaretPosition();
-                                               if (caretPosition == 0) {
-                                                       mergeWithPrevious();
-                                               }
-                                       } else if (ke.character == SWT.DEL) {
-                                               Text text = (Text) paragraph.getControl();
-                                               int caretPosition = text.getCaretPosition();
-                                               int charcount = text.getCharCount();
-                                               if (caretPosition == charcount) {
-                                                       mergeWithNext();
-                                               }
-                                       }
-                               }
-                       }
-               } catch (Exception e) {
-                       ke.doit = false;
-                       notifyEditionException(e);
-               }
-       }
-
-       @Override
-       public void keyReleased(KeyEvent e) {
-       }
-
-       // 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));
-                               }
-                       }
-               }
-
-               @Override
-               public void mouseUp(MouseEvent e) {
-               }
-       }
-
-       // FILE UPLOAD LISTENER
-       private class FUL implements FileUploadListener {
-               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();
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java b/org.argeo.cms/src/org/argeo/cms/internal/text/MarkupValidatorCopy.java
deleted file mode 100644 (file)
index e28d370..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-package org.argeo.cms.internal.text;
-
-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.argeo.cms.forms.FormPageViewer;
-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.
- * 
- * FIXME made public to enable validation from the {@link FormPageViewer}
- */
-public 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/src/org/argeo/cms/internal/text/SectionTitle.java b/org.argeo.cms/src/org/argeo/cms/internal/text/SectionTitle.java
deleted file mode 100644 (file)
index 160060e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.text.TextSection;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.PropertyPart;
-import org.argeo.cms.widgets.EditableText;
-import org.eclipse.swt.widgets.Composite;
-
-/** The title of a section. */
-public class SectionTitle extends EditableText implements EditablePart,
-               PropertyPart {
-       private static final long serialVersionUID = -1787983154946583171L;
-
-       private final TextSection section;
-
-       public SectionTitle(Composite parent, int swtStyle, Property title)
-                       throws RepositoryException {
-               super(parent, swtStyle, title);
-               section = (TextSection) TextSection.findSection(this);
-       }
-
-       public TextSection getSection() {
-               return section;
-       }
-
-       // @Override
-       // public Property getProperty() throws RepositoryException {
-       // return getSection().getNode().getProperty(Property.JCR_TITLE);
-       // }
-
-       @Override
-       public Property getItem() throws RepositoryException {
-               return getProperty();
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java b/org.argeo.cms/src/org/argeo/cms/internal/text/TextContextMenu.java
deleted file mode 100644 (file)
index 8afad16..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.text.Paragraph;
-import org.argeo.cms.text.TextStyles;
-import org.argeo.cms.viewers.EditablePart;
-import org.argeo.cms.viewers.SectionPart;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.graphics.Point;
-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.Label;
-import org.eclipse.swt.widgets.Shell;
-
-/** Dialog to edit a text part. */
-class TextContextMenu extends Shell implements CmsNames, TextStyles {
-       private final static String[] DEFAULT_TEXT_STYLES = {
-                       TextStyles.TEXT_DEFAULT, TextStyles.TEXT_PRE, TextStyles.TEXT_QUOTE };
-
-       private final AbstractTextViewer textViewer;
-
-       private static final long serialVersionUID = -3826246895162050331L;
-       private List<StyleButton> styleButtons = new ArrayList<TextContextMenu.StyleButton>();
-
-       private Label deleteButton, publishButton, editButton;
-
-       private EditablePart currentTextPart;
-
-       public TextContextMenu(AbstractTextViewer textViewer, Display display) {
-               super(display, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
-               this.textViewer = textViewer;
-               setLayout(new GridLayout());
-               setData(RWT.CUSTOM_VARIANT, TEXT_STYLED_TOOLS_DIALOG);
-
-               StyledToolMouseListener stml = new StyledToolMouseListener();
-               if (textViewer.getCmsEditable().isEditing()) {
-                       for (String style : DEFAULT_TEXT_STYLES) {
-                               StyleButton styleButton = new StyleButton(this, SWT.WRAP);
-                               styleButton.setData(RWT.CUSTOM_VARIANT, style);
-                               styleButton.setData(RWT.MARKUP_ENABLED, true);
-                               styleButton.addMouseListener(stml);
-                               styleButtons.add(styleButton);
-                       }
-
-                       // Delete
-                       deleteButton = new Label(this, SWT.NONE);
-                       deleteButton.setText("Delete");
-                       deleteButton.addMouseListener(stml);
-
-                       // Publish
-                       publishButton = new Label(this, SWT.NONE);
-                       publishButton.setText("Publish");
-                       publishButton.addMouseListener(stml);
-               } else if (textViewer.getCmsEditable().canEdit()) {
-                       // Edit
-                       editButton = new Label(this, SWT.NONE);
-                       editButton.setText("Edit");
-                       editButton.addMouseListener(stml);
-               }
-               addShellListener(new ToolsShellListener());
-       }
-
-       public void show(EditablePart source, Point location) {
-               if (isVisible())
-                       setVisible(false);
-
-               this.currentTextPart = source;
-
-               if (currentTextPart instanceof Paragraph) {
-                       final int size = 32;
-                       String text = textViewer
-                                       .getRawParagraphText((Paragraph) currentTextPart);
-                       String textToShow = text.length() > size ? text.substring(0,
-                                       size - 3) + "..." : text;
-                       for (StyleButton styleButton : styleButtons) {
-                               styleButton.setText(textToShow);
-                       }
-               }
-               pack();
-               layout();
-               if (source instanceof Control)
-                       setLocation(((Control) source).toDisplay(location.x, location.y));
-               open();
-       }
-
-       class StyleButton extends Label {
-               private static final long serialVersionUID = 7731102609123946115L;
-
-               public StyleButton(Composite parent, int swtStyle) {
-                       super(parent, swtStyle);
-               }
-
-       }
-
-       class StyledToolMouseListener extends MouseAdapter {
-               private static final long serialVersionUID = 8516297091549329043L;
-
-               @Override
-               public void mouseDown(MouseEvent e) {
-                       Object eventSource = e.getSource();
-                       if (eventSource instanceof StyleButton) {
-                               StyleButton sb = (StyleButton) e.getSource();
-                               String style = sb.getData(RWT.CUSTOM_VARIANT).toString();
-                               textViewer
-                                               .setParagraphStyle((Paragraph) currentTextPart, style);
-                       } else if (eventSource == deleteButton) {
-                               textViewer.deletePart((SectionPart) currentTextPart);
-                       } else if (eventSource == editButton) {
-                               textViewer.getCmsEditable().startEditing();
-                       } else if (eventSource == publishButton) {
-                               textViewer.getCmsEditable().stopEditing();
-                       }
-                       setVisible(false);
-               }
-       }
-
-       class ToolsShellListener extends org.eclipse.swt.events.ShellAdapter {
-               private static final long serialVersionUID = 8432350564023247241L;
-
-               @Override
-               public void shellDeactivated(ShellEvent e) {
-                       setVisible(false);
-               }
-
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/text/TextInterpreterImpl.java
deleted file mode 100644 (file)
index c252efe..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.argeo.cms.internal.text;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.text.IdentityTextInterpreter;
-
-/**
- * Text interpreter that sanitise and validates before saving, and support CMS
- * specific formatting and integration.
- */
-class TextInterpreterImpl extends IdentityTextInterpreter {
-       private MarkupValidatorCopy markupValidator = MarkupValidatorCopy
-                       .getInstance();
-
-       @Override
-       protected void validateBeforeStoring(String raw) {
-               markupValidator.validate(raw);
-       }
-
-       @Override
-       protected String convertToStorage(Item item, String content)
-                       throws RepositoryException {
-               return super.convertToStorage(item, content);
-       }
-
-       @Override
-       protected String convertFromStorage(Item item, String content)
-                       throws RepositoryException {
-               return super.convertFromStorage(item, content);
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java b/org.argeo.cms/src/org/argeo/cms/maintenance/AbstractOsgiComposite.java
deleted file mode 100644 (file)
index 2494d90..0000000
+++ /dev/null
@@ -1,43 +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.layout.GridLayout;
-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/src/org/argeo/cms/maintenance/Browse.java b/org.argeo.cms/src/org/argeo/cms/maintenance/Browse.java
deleted file mode 100644 (file)
index 7dafd72..0000000
+++ /dev/null
@@ -1,616 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import static javax.jcr.Node.JCR_CONTENT;
-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.CmsTypes;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.text.Img;
-import org.argeo.cms.util.CmsLink;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.widgets.EditableImage;
-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 {
-               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/src/org/argeo/cms/maintenance/ConnectivityDeploymentUi.java b/org.argeo.cms/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/src/org/argeo/cms/maintenance/DataDeploymentUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DataDeploymentUi.java
deleted file mode 100644 (file)
index 49f3fc3..0000000
+++ /dev/null
@@ -1,149 +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.CmsException;
-import org.argeo.cms.internal.kernel.JackrabbitType;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.node.NodeConstants;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-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.Group;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-
-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,
-                               "(" + ArgeoJcrConstants.JCR_REPOSITORY_ALIAS + "=*)");
-               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(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS).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/src/org/argeo/cms/maintenance/DataMigration.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DataMigration.java
deleted file mode 100644 (file)
index 3aa6120..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import java.util.Map;
-
-import javax.jcr.Session;
-
-public interface DataMigration {
-       /** Migrate data between two workspaces, at JCR level. */
-       Boolean migrate(Session source, Session target);
-
-       /**
-        * Keys are the source workspaces and values the target workspaces. If null
-        * is returned, only the default workspace will be migrated, to the default
-        * workspace of the target repository.
-        */
-       Map<String, String> workspacesToMigrate();
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java b/org.argeo.cms/src/org/argeo/cms/maintenance/DeploymentEntryPoint.java
deleted file mode 100644 (file)
index e21974c..0000000
+++ /dev/null
@@ -1,94 +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) {
-               // 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/src/org/argeo/cms/maintenance/LogDeploymentUi.java b/org.argeo.cms/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/src/org/argeo/cms/maintenance/MaintenanceStyles.java b/org.argeo.cms/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/src/org/argeo/cms/maintenance/MaintenanceUi.java b/org.argeo.cms/src/org/argeo/cms/maintenance/MaintenanceUi.java
deleted file mode 100644 (file)
index 538379f..0000000
+++ /dev/null
@@ -1,13 +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/src/org/argeo/cms/maintenance/NonAdminPage.java b/org.argeo.cms/src/org/argeo/cms/maintenance/NonAdminPage.java
deleted file mode 100644 (file)
index b27d506..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.argeo.cms.maintenance;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.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/src/org/argeo/cms/maintenance/SecurityDeploymentUi.java b/org.argeo.cms/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/src/org/argeo/cms/text/CustomTextEditor.java b/org.argeo.cms/src/org/argeo/cms/text/CustomTextEditor.java
deleted file mode 100644 (file)
index 900dce2..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.argeo.cms.text;
-
-import static org.argeo.cms.util.CmsUtils.fillWidth;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.internal.text.AbstractTextViewer;
-import org.argeo.cms.viewers.Section;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * Manages hardcoded sections as an arbitrary hierarchy under the main section,
- * which contains no text and no title.
- */
-public class CustomTextEditor extends AbstractTextViewer {
-       private static final long serialVersionUID = 5277789504209413500L;
-
-       public CustomTextEditor(Composite parent, int style, Node textNode,
-                       CmsEditable cmsEditable) throws RepositoryException {
-               this(new Section(parent, style, textNode), style, cmsEditable);
-       }
-
-       public CustomTextEditor(Section mainSection, int style,
-                       CmsEditable cmsEditable) throws RepositoryException {
-               super(mainSection, style, cmsEditable);
-               mainSection.setLayoutData(fillWidth());
-       }
-
-       @Override
-       public Section getMainSection() {
-               return super.getMainSection();
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java b/org.argeo.cms/src/org/argeo/cms/text/IdentityTextInterpreter.java
deleted file mode 100644 (file)
index 79f6ede..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-
-/** Based on HTML with a few Wiki-like shortcuts. */
-public class IdentityTextInterpreter implements TextInterpreter, CmsNames {
-
-       @Override
-       public void write(Item item, String content) {
-               try {
-                       if (item instanceof Node) {
-                               Node node = (Node) item;
-                               if (node.isNodeType(CmsTypes.CMS_STYLED)) {
-                                       String raw = convertToStorage(node, content);
-                                       validateBeforeStoring(raw);
-                                       node.setProperty(CMS_CONTENT, raw);
-                               } else {
-                                       throw new CmsException("Don't know how to interpret "
-                                                       + node);
-                               }
-                       } else {// property
-                               Property property = (Property) item;
-                               property.setValue(content);
-                       }
-                       // item.getSession().save();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot set content on " + item, e);
-               }
-       }
-
-       @Override
-       public String read(Item item) {
-               try {
-                       String raw = raw(item);
-                       return convertFromStorage(item, raw);
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get " + item + " for edit", e);
-               }
-       }
-
-       @Override
-       public String raw(Item item) {
-               try {
-                       item.getSession().refresh(true);
-                       if (item instanceof Node) {
-                               Node node = (Node) item;
-                               if (node.isNodeType(CmsTypes.CMS_STYLED)) {
-                                       // WORKAROUND FOR BROKEN PARARAPHS
-                                       if (!node.hasProperty(CMS_CONTENT)) {
-                                               node.setProperty(CMS_CONTENT, "");
-                                               node.getSession().save();
-                                       }
-
-                                       return node.getProperty(CMS_CONTENT).getString();
-                               } else {
-                                       throw new CmsException("Don't know how to interpret "
-                                                       + node);
-                               }
-                       } else {// property
-                               Property property = (Property) item;
-                               return property.getString();
-                       }
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot get " + item + " content", e);
-               }
-       }
-
-       // EXTENSIBILITY
-       /**
-        * To be overridden, in order to make sure that only valid strings are being
-        * stored.
-        */
-       protected void validateBeforeStoring(String raw) {
-       }
-
-       /** To be overridden, in order to support additional formatting. */
-       protected String convertToStorage(Item item, String content)
-                       throws RepositoryException {
-               return content;
-
-       }
-
-       /** To be overridden, in order to support additional formatting. */
-       protected String convertFromStorage(Item item, String content)
-                       throws RepositoryException {
-               return content;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/Img.java b/org.argeo.cms/src/org/argeo/cms/text/Img.java
deleted file mode 100644 (file)
index 8c23109..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.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.argeo.cms.widgets.EditableImage;
-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/src/org/argeo/cms/text/Paragraph.java b/org.argeo.cms/src/org/argeo/cms/text/Paragraph.java
deleted file mode 100644 (file)
index a7a7964..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.Section;
-import org.argeo.cms.viewers.SectionPart;
-import org.argeo.cms.widgets.EditableText;
-
-public class Paragraph extends EditableText implements SectionPart {
-       private static final long serialVersionUID = 3746457776229542887L;
-
-       private final TextSection section;
-
-       public Paragraph(TextSection section, int style, Node node)
-                       throws RepositoryException {
-               super(section, style, node);
-               this.section = section;
-               CmsUtils.style(this, TextStyles.TEXT_PARAGRAPH);
-       }
-
-       public Section getSection() {
-               return section;
-       }
-
-       @Override
-       public String getPartId() {
-               return getNodeId();
-       }
-
-       @Override
-       public Node getItem() throws RepositoryException {
-               return getNode();
-       }
-
-       @Override
-       public String toString() {
-               return "Paragraph #" + getPartId();
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java b/org.argeo.cms/src/org/argeo/cms/text/StandardTextEditor.java
deleted file mode 100644 (file)
index 4ce8d1f..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.cms.text;
-
-import static javax.jcr.Property.JCR_TITLE;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.internal.text.AbstractTextViewer;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.Section;
-import org.eclipse.swt.widgets.Composite;
-
-/** Text editor where sections and subsections can be managed by the user. */
-public class StandardTextEditor extends AbstractTextViewer {
-       private static final long serialVersionUID = 6049661610883342325L;
-
-       public StandardTextEditor(Composite parent, int style, Node textNode,
-                       CmsEditable cmsEditable) throws RepositoryException {
-               super(new TextSection(parent, style, textNode), style, cmsEditable);
-               refresh();
-               getMainSection().setLayoutData(CmsUtils.fillWidth());
-       }
-
-       @Override
-       protected void initModel(Node textNode) throws RepositoryException {
-               if (isFlat())
-                       textNode.addNode(CMS_P).addMixin(CmsTypes.CMS_STYLED);
-               else
-                       textNode.setProperty(JCR_TITLE, textNode.getName());
-       }
-
-       @Override
-       protected Boolean isModelInitialized(Node textNode)
-                       throws RepositoryException {
-               return textNode.hasProperty(Property.JCR_TITLE)
-                               || textNode.hasNode(CMS_P)
-                               || (!isFlat() && textNode.hasNode(CMS_H));
-       }
-
-       @Override
-       public Section getMainSection() {
-               // TODO Auto-generated method stub
-               return super.getMainSection();
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java b/org.argeo.cms/src/org/argeo/cms/text/TextEditorHeader.java
deleted file mode 100644 (file)
index e70a4d0..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.argeo.cms.text;
-
-import java.util.Observable;
-import java.util.Observer;
-
-import org.argeo.cms.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;
-
-/** Adds editing capabilities to a page editing text */
-public class TextEditorHeader implements SelectionListener, Observer {
-       private static final long serialVersionUID = 4186756396045701253L;
-
-       private final CmsEditable cmsEditable;
-       private Button publish;
-
-       private Composite parent;
-       private Composite display;
-       private Object layoutData;
-
-       public TextEditorHeader(CmsEditable cmsEditable, Composite parent, int style) {
-               this.cmsEditable = cmsEditable;
-               this.parent = parent;
-               if (this.cmsEditable instanceof Observable)
-                       ((Observable) this.cmsEditable).addObserver(this);
-               refresh();
-       }
-
-       protected void refresh() {
-               if (display != null && !display.isDisposed())
-                       display.dispose();
-               display = null;
-               publish = null;
-               if (cmsEditable.isEditing()) {
-                       display = new Composite(parent, SWT.NONE);
-                       // display.setBackgroundMode(SWT.INHERIT_NONE);
-                       display.setLayoutData(layoutData);
-                       display.setLayout(CmsUtils.noSpaceGridLayout());
-                       CmsUtils.style(display, TextStyles.TEXT_EDITOR_HEADER);
-                       publish = new Button(display, SWT.FLAT | SWT.PUSH);
-                       publish.setText(getPublishButtonLabel());
-                       CmsUtils.style(publish, TextStyles.TEXT_EDITOR_HEADER);
-                       publish.addSelectionListener(this);
-                       display.moveAbove(null);
-               }
-               parent.layout();
-       }
-
-       private String getPublishButtonLabel() {
-               if (cmsEditable.isEditing())
-                       return "Publish";
-               else
-                       return "Edit";
-       }
-
-       @Override
-       public void widgetSelected(SelectionEvent e) {
-               if (e.getSource() == publish) {
-                       if (cmsEditable.isEditing()) {
-                               cmsEditable.stopEditing();
-                       } else {
-                               cmsEditable.startEditing();
-                       }
-                       // publish.setText(getPublishButtonLabel());
-               }
-       }
-
-       @Override
-       public void widgetDefaultSelected(SelectionEvent e) {
-       }
-
-       @Override
-       public void update(Observable o, Object arg) {
-               if (o == cmsEditable) {
-                       // publish.setText(getPublishButtonLabel());
-                       refresh();
-               }
-       }
-
-       public void setLayoutData(Object layoutData) {
-               this.layoutData = layoutData;
-               if (display != null && !display.isDisposed())
-                       display.setLayoutData(layoutData);
-       }
-
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java b/org.argeo.cms/src/org/argeo/cms/text/TextInterpreter.java
deleted file mode 100644 (file)
index f39a2b3..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Item;
-
-/** Convert from/to data layer to/from presentation layer. */
-public interface TextInterpreter {
-       public String raw(Item item);
-
-       public String read(Item item);
-
-       public void write(Item item, String content);
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextSection.java b/org.argeo.cms/src/org/argeo/cms/text/TextSection.java
deleted file mode 100644 (file)
index ac93e4b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.Section;
-import org.eclipse.swt.widgets.Composite;
-
-public class TextSection extends Section implements CmsNames {
-       private static final long serialVersionUID = -8625209546243220689L;
-       private String defaultTextStyle = TextStyles.TEXT_DEFAULT;
-       private String titleStyle;
-
-       public TextSection(Composite parent, int style, Node node)
-                       throws RepositoryException {
-               this(parent, findSection(parent), style, node);
-       }
-
-       public TextSection(TextSection section, int style, Node node)
-                       throws RepositoryException {
-               this(section, section.getParentSection(), style, node);
-       }
-
-       private TextSection(Composite parent, Section parentSection, int style,
-                       Node node) throws RepositoryException {
-               super(parent, parentSection, style, node);
-               CmsUtils.style(this, TextStyles.TEXT_SECTION);
-       }
-
-       public String getDefaultTextStyle() {
-               return defaultTextStyle;
-       }
-
-       public String getTitleStyle() {
-               if (titleStyle != null)
-                       return titleStyle;
-               // TODO make base H styles configurable
-               Integer relativeDepth = getRelativeDepth();
-               return relativeDepth == 0 ? TextStyles.TEXT_TITLE : TextStyles.TEXT_H
-                               + relativeDepth;
-       }
-
-       public void setDefaultTextStyle(String defaultTextStyle) {
-               this.defaultTextStyle = defaultTextStyle;
-       }
-
-       public void setTitleStyle(String titleStyle) {
-               this.titleStyle = titleStyle;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/text/TextStyles.java b/org.argeo.cms/src/org/argeo/cms/text/TextStyles.java
deleted file mode 100644 (file)
index 44c3ad0..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.argeo.cms.text;
-
-/** 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/src/org/argeo/cms/text/WikiPage.java b/org.argeo.cms/src/org/argeo/cms/text/WikiPage.java
deleted file mode 100644 (file)
index 38af381..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.argeo.cms.text;
-
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsNames;
-import org.argeo.cms.CmsTypes;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.util.CmsLink;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.cms.viewers.JcrVersionCmsEditable;
-import org.argeo.cms.widgets.ScrolledPage;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Display the text of the context, and provide an editor if the user can edit. */
-public class WikiPage implements CmsUiProvider, CmsNames {
-       @Override
-       public Control createUi(Composite parent, Node context)
-                       throws RepositoryException {
-               CmsEditable cmsEditable = new JcrVersionCmsEditable(context);
-               if (cmsEditable.canEdit())
-                       new TextEditorHeader(cmsEditable, parent, SWT.NONE)
-                                       .setLayoutData(CmsUtils.fillWidth());
-
-               ScrolledPage page = new ScrolledPage(parent, SWT.NONE);
-               page.setLayout(CmsUtils.noSpaceGridLayout());
-               GridData textGd = CmsUtils.fillAll();
-               page.setLayoutData(textGd);
-
-               if (context.isNodeType(CmsTypes.CMS_TEXT)) {
-                       new StandardTextEditor(page, SWT.NONE, context, cmsEditable);
-               } else if (context.isNodeType(NodeType.NT_FOLDER)
-                               || context.getPath().equals("/")) {
-                       parent.setBackgroundMode(SWT.INHERIT_NONE);
-                       if (context.getSession().hasPermission(context.getPath(),
-                                       Session.ACTION_ADD_NODE)) {
-                               Node indexNode = JcrUtils.getOrAdd(context, CMS_INDEX,
-                                               CmsTypes.CMS_TEXT);
-                               new StandardTextEditor(page, SWT.NONE, indexNode, cmsEditable);
-                               textGd.heightHint = 400;
-
-                               for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
-                                       Node textNode = ni.nextNode();
-                                       if (textNode.isNodeType(NodeType.NT_FOLDER))
-                                               new CmsLink(textNode.getName() + "/",
-                                                               textNode.getPath()).createUi(parent, textNode);
-                               }
-                               for (NodeIterator ni = context.getNodes(); ni.hasNext();) {
-                                       Node textNode = ni.nextNode();
-                                       if (textNode.isNodeType(CmsTypes.CMS_TEXT)
-                                                       && !textNode.getName().equals(CMS_INDEX))
-                                               new CmsLink(textNode.getName(), textNode.getPath())
-                                                               .createUi(parent, textNode);
-                               }
-                       }
-               }
-               return page;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/ui/UxContext.java b/org.argeo.cms/src/org/argeo/cms/ui/UxContext.java
deleted file mode 100644 (file)
index f03b88b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.cms.ui;
-
-public interface UxContext {
-       boolean isPortrait();
-       boolean isLandscape();
-       boolean isSquare();
-       
-       boolean isSmall();
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java b/org.argeo.cms/src/org/argeo/cms/util/BundleResourceLoader.java
deleted file mode 100644 (file)
index cda00ef..0000000
+++ /dev/null
@@ -1,47 +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;
-import org.osgi.framework.BundleContext;
-
-/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
-public class BundleResourceLoader implements ResourceLoader {
-       private final BundleContext bundleContext;
-
-       public BundleResourceLoader(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       @Override
-       public InputStream getResourceAsStream(String resourceName)
-                       throws IOException {
-               // 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 = bundle.getResource(resourceName);
-               if (res == null)
-                       throw new CmsException("Resource " + resourceName
-                                       + " not found in bundle " + bundle.getSymbolicName());
-               return res.openStream();
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/CmsLink.java b/org.argeo.cms/src/org/argeo/cms/util/CmsLink.java
deleted file mode 100644 (file)
index 14f3755..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 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.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.eclipse.gemini.blueprint.context.BundleContextAware;
-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;
-import org.springframework.beans.factory.InitializingBean;
-
-/** A link to an internal or external location. */
-public class CmsLink implements CmsUiProvider, InitializingBean,
-               BundleContextAware {
-       private final static Log log = LogFactory.getLog(CmsLink.class);
-
-       private String label;
-       private String custom;
-       private String target;
-       private String image;
-       private MouseListener mouseListener;
-
-       private int verticalAlignment = SWT.CENTER;
-
-       // internal
-       // private Boolean isUrl = false;
-       private Integer imageWidth, imageHeight;
-
-       private BundleContext bundleContext;
-
-       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;
-               afterPropertiesSet();
-       }
-
-       @Override
-       public void afterPropertiesSet() {
-               // if (target != null) {
-               // if (target.startsWith("/")) {
-               // isUrl = true;
-               // } else {
-               // try {
-               // new URL(target);
-               // isUrl = true;
-               // } catch (MalformedURLException e1) {
-               // isUrl = false;
-               // }
-               // }
-               // }
-
-               if (image != null) {
-                       ImageData image = loadImage();
-                       imageWidth = image.width;
-                       imageHeight = image.height;
-               }
-       }
-
-       /** @return {@link Composite} with a single {@link Label} child. */
-       @Override
-       public Control createUi(final Composite parent, Node context) {
-               Composite comp = new Composite(parent, SWT.BOTTOM);
-               comp.setLayout(CmsUtils.noSpaceGridLayout());
-
-               Label link = new Label(comp, SWT.NONE);
-               link.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
-               GridData layoutData = new GridData(SWT.CENTER, verticalAlignment, true,
-                               true);
-               if (image != null) {
-                       layoutData.heightHint = imageHeight;
-                       if (label == 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 (target != null) {
-                       labelText
-                                       .append("<a style='color:inherit;text-decoration:inherit;' href=\"");
-                       // if (!isUrl)
-                       // labelText.append('#');
-                       labelText.append(target);
-                       labelText.append("\">");
-               }
-               if (image != null) {
-                       registerImageIfNeeded();
-                       String imageLocation = RWT.getResourceManager().getLocation(image);
-                       labelText.append("<img width='").append(imageWidth)
-                                       .append("' height='").append(imageHeight)
-                                       .append("' src=\"").append(imageLocation).append("\"/>");
-
-                       // final Image img = loadImage(parent.getDisplay());
-                       // link.setImage(img);
-                       // link.addDisposeListener(new DListener(img));
-               }
-
-               if (label != null) {
-                       // link.setText(label);
-                       labelText.append(' ').append(label);
-               }
-
-               if (target != null)
-                       labelText.append("</a>");
-
-               link.setText(labelText.toString());
-
-               // link.setCursor(link.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
-               // CmsSession cmsSession = (CmsSession) parent.getDisplay().getData(
-               // CmsSession.KEY);
-               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) {
-                       // in OSGi bundle
-                       if (bundleContext == null)
-                               throw new CmsException("No bundle context available");
-                       url = bundleContext.getBundle().getResource(image);
-               }
-
-               if (url == null)
-                       throw new CmsException("No image " + image + " available.");
-
-               return url;
-       }
-
-       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;
-       }
-
-       @Override
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       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)");
-               }
-       }
-
-       // private class MListener extends MouseAdapter {
-       // private static final long serialVersionUID = 3634864186295639792L;
-       //
-       // @Override
-       // public void mouseDown(MouseEvent e) {
-       // if (e.button == 1) {
-       // }
-       // }
-       // }
-       //
-       // private class DListener implements DisposeListener {
-       // private static final long serialVersionUID = -3808587499269394812L;
-       // private final Image img;
-       //
-       // public DListener(Image img) {
-       // super();
-       // this.img = img;
-       // }
-       //
-       // @Override
-       // public void widgetDisposed(DisposeEvent event) {
-       // img.dispose();
-       // }
-       //
-       // }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java b/org.argeo.cms/src/org/argeo/cms/util/CmsUtils.java
deleted file mode 100644 (file)
index e5d3026..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-package org.argeo.cms.util;
-
-import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PRIVATE;
-import static org.argeo.cms.internal.kernel.KernelConstants.WEBDAV_PUBLIC;
-import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE;
-
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import javax.jcr.Item;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsConstants;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.AuthConstants;
-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.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Table;
-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);
-               }
-       }
-
-       public static String getDataPath(Node node) throws RepositoryException {
-               assert node != null;
-               String userId = node.getSession().getUserID();
-               if (log.isTraceEnabled())
-                       log.trace(userId + " : " + node.getPath());
-               StringBuilder buf = new StringBuilder();
-               boolean isAnonymous = userId.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS);
-               if (isAnonymous)
-                       buf.append(WEBDAV_PUBLIC);
-               else
-                       buf.append(WEBDAV_PRIVATE);
-               // TODO convey repo alias vie repository properties
-               return buf.append('/').append(ALIAS_NODE).append('/').append(node.getSession().getWorkspace().getName())
-                               .append(node.getPath()).toString();
-       }
-
-       public static String getCanonicalUrl(Node node, HttpServletRequest request) throws RepositoryException {
-               try {
-                       StringBuilder buf = getServerBaseUrl(request);
-                       buf.append('/').append('!').append(node.getPath());
-                       return new URL(buf.toString()).toString();
-               } catch (MalformedURLException e) {
-                       throw new CmsException("Cannot build data URL for " + node, e);
-               }
-               // return request.getRequestURL().append('!').append(node.getPath())
-               // .toString();
-       }
-
-       /** @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(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 void style(Widget widget, String style) {
-               widget.setData(CmsConstants.STYLE, style);
-       }
-
-       /** Enable markups on widget */
-       public static void markup(Widget widget) {
-               widget.setData(CmsConstants.MARKUP, true);
-       }
-
-       public static void setItemHeight(Table table, int height) {
-               table.setData(CmsConstants.ITEM_HEIGHT, height);
-       }
-
-       /** @return the path or null if not instrumented */
-       public static String getDataPath(Widget widget) {
-               // JCR item
-               Object data = widget.getData();
-               if (data != null && data instanceof Item) {
-                       try {
-                               return ((Item) data).getPath();
-                       } catch (RepositoryException e) {
-                               throw new CmsException("Cannot find data path of " + data + " for " + widget);
-                       }
-               }
-
-               // JCR path
-               data = widget.getData(Property.JCR_PATH);
-               if (data != null)
-                       return data.toString();
-
-               return null;
-       }
-
-       /** 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 {
-                       IOUtils.closeQuietly(in);
-               }
-       }
-
-       private CmsUtils() {
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/LoginEntryPoint.java b/org.argeo.cms/src/org/argeo/cms/util/LoginEntryPoint.java
deleted file mode 100644 (file)
index 2ccb3ca..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-package org.argeo.cms.util;
-
-import java.util.Locale;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.CredentialNotFoundException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.HttpRequestCallbackHandler;
-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.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 {
-       // private final static Log log = LogFactory.getLog(WorkbenchLogin.class);
-       private final Subject subject = new Subject();
-       private LoginContext loginContext;
-       private UxContext uxContext = null;
-
-       @Override
-       public int createUI() {
-               final Display display = createDisplay();
-               UiContext.setData(CmsView.KEY, this);
-               try {
-                       // try pre-auth
-                       loginContext = new LoginContext(AuthConstants.LOGIN_CONTEXT_USER,
-                                       subject, new HttpRequestCallbackHandler(getRequest()));
-                       loginContext.login();
-               } catch (CredentialNotFoundException e) {
-                       CmsLoginShell loginShell = createCmsLoginShell();
-                       loginShell.open();
-                       while (!loginShell.getShell().isDisposed()) {
-                               // try {
-                               if (!display.readAndDispatch())
-                                       display.sleep();
-                               // } catch (Exception e1) {
-                               // try {
-                               // Thread.sleep(3000);
-                               // } catch (InterruptedException e2) {
-                               // // silent
-                               // }
-                               // ErrorFeedback.show("Login failed", e1);
-                               // return -1;
-                               // }
-                       }
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot log in", e);
-               }
-
-               if (CurrentUser.getUsername() == 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) {
-               this.loginContext = loginContext;
-       }
-
-       @Override
-       public void logout() {
-               if (loginContext == null)
-                       throw new CmsException("Login context should not bet null");
-               try {
-                       loginContext.logout();
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot log out", e);
-               }
-       }
-
-       @Override
-       public final Subject getSubject() {
-               return subject;
-       }
-
-       @Override
-       public void exception(Throwable e) {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public CmsImageManager getImageManager() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/MenuLink.java b/org.argeo.cms/src/org/argeo/cms/util/MenuLink.java
deleted file mode 100644 (file)
index d491d3c..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.cms.util;
-
-import org.argeo.cms.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);
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java b/org.argeo.cms/src/org/argeo/cms/util/OpenUserMenu.java
deleted file mode 100644 (file)
index bb2bb39..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.cms.util;
-
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.widgets.Control;
-
-/** Open the user menu when clicked */
-public class OpenUserMenu extends MouseAdapter {
-       private static final long serialVersionUID = 3634864186295639792L;
-
-       @Override
-       public void mouseDown(MouseEvent e) {
-               if (e.button == 1) {
-                       new UserMenu((Control) e.getSource());
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleApp.java
deleted file mode 100644 (file)
index 39e7507..0000000
+++ /dev/null
@@ -1,376 +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.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-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.CmsConstants;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.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.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("anonymous", "everyone");
-       private List<String> rwPrincipals = Arrays.asList("everyone");
-
-       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 {
-                       StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
-                                       bundleContext);
-                       BundleResourceLoader bundleRL = new BundleResourceLoader(
-                                       bundleContext);
-
-                       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("*");
-
-                       // 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 faviconRelPath = properties.get(WebClient.FAVICON);
-                                       application.addResource(faviconRelPath,
-                                                       new BundleResourceLoader(bundleContext));
-                                       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
-                       for (String themeId : styleSheets.keySet()) {
-                               List<String> cssLst = styleSheets.get(themeId);
-                               if (log.isDebugEnabled())
-                                       log.debug("Theme " + themeId);
-                               for (String css : cssLst) {
-                                       application.addStyleSheet(themeId, css, styleSheetRL);
-                                       if (log.isTraceEnabled())
-                                               log.trace(" CSS " + css);
-                               }
-
-                       }
-               } 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 = JcrUtils.loginOrCreateWorkspace(repository, workspace);
-                       VersionManager vm = session.getWorkspace().getVersionManager();
-                       if (!vm.isCheckedOut("/"))
-                               vm.checkout("/");
-                       JcrUtils.mkdirs(session, 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) {
-
-                               @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/src/org/argeo/cms/util/SimpleCmsHeader.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleCmsHeader.java
deleted file mode 100644 (file)
index aa1bb73..0000000
+++ /dev/null
@@ -1,88 +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.CmsStyles;
-import org.argeo.cms.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;
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleDynamicPages.java
deleted file mode 100644 (file)
index b6155cf..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.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/src/org/argeo/cms/util/SimpleErgonomics.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleErgonomics.java
deleted file mode 100644 (file)
index e7192af..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.argeo.cms.util;
-
-import java.util.Map;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.AbstractCmsEntryPoint;
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsImageManager;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.CmsUiProvider;
-import org.argeo.cms.internal.ImageManagerImpl;
-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.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Simple header/body ergonomics. */
-public class SimpleErgonomics extends AbstractCmsEntryPoint {
-       private final static Log log = LogFactory.getLog(SimpleErgonomics.class);
-
-       private boolean uiInitialized = false;
-       private Composite headerArea;
-       private Composite bodyArea;
-       private final CmsUiProvider uiProvider;
-
-       private CmsUiProvider header;
-       private Integer headerHeight = 40;
-
-       private CmsImageManager imageManager = new ImageManagerImpl();
-       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());
-
-               // createAdminArea(parent);
-               headerArea = new Composite(parent, SWT.NONE);
-               headerArea.setLayout(new FillLayout());
-               GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false);
-               headerData.heightHint = headerHeight;
-               headerArea.setLayoutData(headerData);
-
-               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());
-               uxContext = new SimpleUxContext();
-               uiInitialized = true;
-               refresh();
-       }
-
-       @Override
-       protected void refresh() {
-               if (!uiInitialized)
-                       return;
-               if (getState() == null)
-                       setState("");
-               refreshHeader();
-               refreshBody();
-               if (log.isTraceEnabled())
-                       log.trace("UI refreshed " + getNode());
-       }
-
-       protected void createAdminArea(Composite parent) {
-       }
-
-       protected void refreshHeader() {
-               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 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 {
-                       uiProvider.createUi(bodyArea, getNode());
-               } 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;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleStaticPage.java
deleted file mode 100644 (file)
index 6e09000..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.cms.util;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.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/src/org/argeo/cms/util/SimpleUxContext.java b/org.argeo.cms/src/org/argeo/cms/util/SimpleUxContext.java
deleted file mode 100644 (file)
index 6cb37bd..0000000
+++ /dev/null
@@ -1,44 +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;
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java b/org.argeo.cms/src/org/argeo/cms/util/StyleSheetResourceLoader.java
deleted file mode 100644 (file)
index a7e3b6e..0000000
+++ /dev/null
@@ -1,73 +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;
-import org.osgi.framework.BundleContext;
-
-/** {@link ResourceLoader} caching stylesheets. */
-public class StyleSheetResourceLoader implements ResourceLoader {
-       private final BundleContext bundleContext;
-
-       private Map<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
-
-       public StyleSheetResourceLoader(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       @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 = bundle.getResource(resourceName);
-                       if (res == null)
-                               throw new CmsException("Resource " + resourceName
-                                               + " not found in bundle " + bundle.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/src/org/argeo/cms/util/SystemNotifications.java b/org.argeo.cms/src/org/argeo/cms/util/SystemNotifications.java
deleted file mode 100644 (file)
index 54e8eb5..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.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/src/org/argeo/cms/util/UserMenu.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenu.java
deleted file mode 100644 (file)
index a654ddd..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.cms.util;
-
-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;
-
-       public UserMenu(Control source) {
-               super(CmsUtils.getCmsView());
-               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();
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java b/org.argeo.cms/src/org/argeo/cms/util/UserMenuLink.java
deleted file mode 100644 (file)
index 1b7ca03..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.argeo.cms.util;
-
-import javax.jcr.Node;
-import javax.security.auth.Subject;
-
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.CmsStyles;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.CurrentUser;
-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) {
-               Subject subject = CmsUtils.getCmsView().getSubject();
-               String username = CurrentUser.getUsername(subject);
-               if (username.equalsIgnoreCase(AuthConstants.ROLE_ANONYMOUS))
-                       setLabel(CmsMsg.login.lead());
-               else {
-                       setLabel(CurrentUser.getDisplayName(subject));
-               }
-               Label link = (Label) ((Composite) super.createUi(parent, context))
-                               .getChildren()[0];
-               link.addMouseListener(new UserMenuLinkController());
-               return link.getParent();
-       }
-
-       protected UserMenu createUserMenu(Control source) {
-               return new UserMenu(source.getParent());
-       }
-
-       private class UserMenuLinkController implements MouseListener,
-                       DisposeListener {
-               private static final long serialVersionUID = 3634864186295639792L;
-
-               private UserMenu userMenu = null;
-               private long lastDisposeTS = 0l;
-
-               //
-               // 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);
-                                               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();
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminUtils.java
deleted file mode 100644 (file)
index 9ccc305..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-package org.argeo.cms.util.useradmin;
-
-import java.security.AccessController;
-import java.util.List;
-import java.util.Set;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-import javax.security.auth.Subject;
-import javax.security.auth.x500.X500Principal;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.osgi.useradmin.LdifName;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/** Centralise common patterns to manage roles with a user admin */
-public class UserAdminUtils {
-
-       /** Retrieves a {@link Role} given a LDAP name */
-       public final static Role getRole(UserAdmin userAdmin, LdapName dn) {
-               Role role = userAdmin.getRole(dn.toString());
-               return role;
-       }
-
-       /** Retrieves the unique local username given a {@link User}. */
-       public final static String getUsername(User user) {
-               String username = null;
-               if (user instanceof Group)
-                       username = getProperty(user, LdifName.cn.name());
-               else
-                       username = getProperty(user, LdifName.uid.name());
-               return username;
-       }
-
-       /**
-        * Easily retrieves one of the {@link Role}'s property or an empty String if
-        * the requested property is not defined
-        */
-       public final static String getProperty(Role role, String key) {
-               Object obj = role.getProperties().get(key);
-               if (obj != null)
-                       return (String) obj;
-               else
-                       return "";
-       }
-
-       // CENTRALIZE SOME METHODS UNTIL API IS STABLE
-       /** Simply checks if current user is registered */
-       public static boolean isRegistered() {
-               return !CurrentUser.isAnonymous();
-       }
-
-       /** Simply checks if current user as a home */
-       public static boolean hasHome() {
-               return isRegistered();
-       }
-
-       // SELF HELPERS
-       /** Simply retrieves the current logged-in user display name. */
-       public static User getCurrentUser(UserAdmin userAdmin) {
-               return (User) getRole(userAdmin, getCurrentUserLdapName());
-       }
-
-       /** Simply retrieves the current logged-in user display name. */
-       public static String getCurrentUserDisplayName(UserAdmin userAdmin) {
-               String username = getCurrentUsername();
-               return getUserDisplayName(userAdmin, username);
-       }
-
-       /** Simply retrieves the current logged-in user display name. */
-       public static String getCurrentUserMail(UserAdmin userAdmin) {
-               String username = getCurrentUsername();
-               return getUserMail(userAdmin, username);
-       }
-
-       /** Returns the local name of the current connected user */
-       public final static String getUsername(UserAdmin userAdmin) {
-               LdapName dn = getCurrentUserLdapName();
-               return getUsername((User) getRole(userAdmin, dn));
-       }
-
-       /** Returns true if the current user is in the specified role */
-       public static boolean isUserInRole(String role) {
-               Set<String> roles = CurrentUser.roles();
-               return roles.contains(role);
-       }
-
-       /** Simply checks if current user is the same as the passed one */
-       public static boolean isCurrentUser(User user) {
-               String userName = getProperty(user, LdifName.dn.name());
-               try {
-                       LdapName selfUserName = getCurrentUserLdapName();
-                       LdapName userLdapName = new LdapName(userName);
-                       if (userLdapName.equals(selfUserName))
-                               return true;
-                       else
-                               return false;
-               } catch (InvalidNameException e) {
-                       throw new CmsException("User " + user + " has an unvalid dn: "
-                                       + userName, e);
-               }
-       }
-
-       public final static LdapName getCurrentUserLdapName() {
-               String name = getCurrentUsername();
-               return getLdapName(name);
-       }
-
-       /** Simply retrieves username for current user, generally a LDAP dn */
-       public static String getCurrentUsername() {
-               Subject subject = currentSubject();
-               String name = subject.getPrincipals(X500Principal.class).iterator()
-                               .next().toString();
-               return name;
-       }
-
-       /**
-        * Fork of the {@link CurrentUser#currentSubject} method that is private.
-        * TODO Enhance and factorize
-        */
-       private static Subject currentSubject() {
-               CmsView cmsView = CmsUtils.getCmsView();
-               if (cmsView != null)
-                       return cmsView.getSubject();
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               if (subject != null)
-                       return subject;
-               throw new RuntimeException("Cannot find related subject");
-       }
-
-       // HOME MANAGEMENT
-       /**
-        * Simply retrieves the *relative* path to the current user home node from
-        * the base home node
-        */
-       public static String getCurrentUserHomeRelPath() {
-               return getHomeRelPath(getCurrentUsername());
-       }
-
-       /**
-        * Simply retrieves the *relative* path to the home node of a user given its
-        * userName
-        */
-       public static String getHomeRelPath(String userName) {
-               String id = getUserUid(userName);
-               String currHomePath = JcrUtils.firstCharsToPath(id, 2) + "/" + id;
-               return currHomePath;
-       }
-
-       // HELPERS TO RETRIEVE REMARKABLE PROPERTIES
-       /** Simply retrieves the user uid from his dn with no useradmin */
-       public static String getUserUid(String dn) {
-               LdapName ldapName = getLdapName(dn);
-               Rdn last = ldapName.getRdn(ldapName.size() - 1);
-               if (last.getType().toLowerCase().equals(LdifName.uid.name())
-                               || last.getType().toLowerCase().equals(LdifName.cn.name()))
-                       return (String) last.getValue();
-               else
-                       throw new CmsException("Cannot retrieve user uid, "
-                                       + "non valid dn: " + dn);
-       }
-
-       /**
-        * Returns the local username if no user with this dn is found or if the
-        * found user has no defined display name
-        */
-       public static String getUserDisplayName(UserAdmin userAdmin, String dn) {
-               Role user = getRole(userAdmin, getLdapName(dn));
-               if (user == null)
-                       return getUserUid(dn);
-               String displayName = getProperty(user, LdifName.displayName.name());
-               if (EclipseUiUtils.isEmpty(displayName))
-                       displayName = getProperty(user, LdifName.cn.name());
-               if (EclipseUiUtils.isEmpty(displayName))
-                       return getUserUid(dn);
-               else
-                       return displayName;
-       }
-
-       /**
-        * Returns null if no user with this dn is found or if the found user has no
-        * defined mail
-        */
-       public static String getUserMail(UserAdmin userAdmin, String dn) {
-               Role user = getRole(userAdmin, getLdapName(dn));
-               if (user == null)
-                       return null;
-               else
-                       return getProperty(user, LdifName.mail.name());
-       }
-
-       // VARIOUS UI HELPERS
-       public final static String buildDefaultCn(String firstName, String lastName) {
-               return (firstName.trim() + " " + lastName.trim() + " ").trim();
-       }
-
-       /** Simply retrieves a display name of the relevant domain */
-       public final static String getDomainName(User user) {
-               String dn = user.getName();
-               if (dn.endsWith(AuthConstants.ROLES_BASEDN))
-                       return "System roles";
-               try {
-                       LdapName name = new LdapName(dn);
-                       List<Rdn> rdns = name.getRdns();
-                       String dname = null;
-                       int i = 0;
-                       loop: while (i < rdns.size()) {
-                               Rdn currrRdn = rdns.get(i);
-                               if (!LdifName.dc.name().equals(currrRdn.getType()))
-                                       break loop;
-                               else {
-                                       String currVal = (String) currrRdn.getValue();
-                                       dname = dname == null ? currVal : currVal + "." + dname;
-                               }
-                               i++;
-                       }
-                       return dname;
-               } catch (InvalidNameException e) {
-                       throw new CmsException("Unable to get domain name for " + dn, e);
-               }
-       }
-
-       // Local Helpers
-       /** Simply retrieves a LDAP name from a dn with no exception */
-       public static LdapName getLdapName(String dn) {
-               try {
-                       return new LdapName(dn);
-               } catch (InvalidNameException e) {
-                       throw new CmsException("Cannot parse LDAP name " + dn, e);
-               }
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java b/org.argeo.cms/src/org/argeo/cms/util/useradmin/UserAdminWrapper.java
deleted file mode 100644 (file)
index aa764d5..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-package org.argeo.cms.util.useradmin;
-
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.transaction.Status;
-import javax.transaction.UserTransaction;
-
-import org.argeo.cms.CmsException;
-import org.argeo.cms.auth.AuthConstants;
-import org.argeo.osgi.useradmin.UserAdminConf;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/**
- * Base useradmin wrapper. Implementing application might extends to add
- * business specific behaviour
- */
-public abstract class UserAdminWrapper {
-       // private Log log = LogFactory.getLog(UserAdminWrapper.class);
-
-       private UserAdmin userAdmin;
-       private ServiceReference<UserAdmin> userAdminServiceReference;
-       private UserTransaction userTransaction;
-
-       /* USER ADMIN LISTENER MANAGEMENT */
-       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
-
-       // TODO implement safer mechanism
-       public void addListener(UserAdminListener userAdminListener) {
-               if (!listeners.contains(userAdminListener))
-                       listeners.add(userAdminListener);
-       }
-
-       /**
-        * Starts a transaction if none already exists and notify the userAdmin
-        * listeners.Must be called from the UI Thread.
-        */
-       public UserTransaction beginTransactionIfNeeded() {
-               try {
-                       if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
-                               userTransaction.begin();
-                       }
-                       return userTransaction;
-               } catch (Exception e) {
-                       throw new CmsException("Unable to begin transaction", e);
-               }
-       }
-
-       // Expose this?
-       public void removeListener(UserAdminListener userAdminListener) {
-               if (listeners.contains(userAdminListener))
-                       listeners.remove(userAdminListener);
-       }
-
-       public void notifyListeners(UserAdminEvent event) {
-               for (UserAdminListener listener : listeners)
-                       listener.roleChanged(event);
-       }
-
-       public Map<String, String> getKnownBaseDns(boolean onlyWritable) {
-               Map<String, String> dns = new HashMap<String, String>();
-               for (String uri : userAdminServiceReference.getPropertyKeys()) {
-                       if (!uri.startsWith("/"))
-                               continue;
-                       Dictionary<String, ?> props = UserAdminConf.uriAsProperties(uri);
-                       String readOnly = UserAdminConf.readOnly.getValue(props);
-                       String baseDn = UserAdminConf.baseDn.getValue(props);
-
-                       if (onlyWritable && "true".equals(readOnly))
-                               continue;
-                       if (baseDn.equalsIgnoreCase(AuthConstants.ROLES_BASEDN))
-                               continue;
-                       dns.put(baseDn, uri);
-               }
-               return dns;
-       }
-
-       public UserAdmin getUserAdmin() {
-               return userAdmin;
-       }
-
-       public UserTransaction getUserTransaction() {
-               return userTransaction;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdmin(UserAdmin userAdmin) {
-               this.userAdmin = userAdmin;
-       }
-
-       public void setUserTransaction(UserTransaction userTransaction) {
-               this.userTransaction = userTransaction;
-       }
-
-       public void setUserAdminServiceReference(
-                       ServiceReference<UserAdmin> userAdminServiceReference) {
-               this.userAdminServiceReference = userAdminServiceReference;
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java b/org.argeo.cms/src/org/argeo/cms/viewers/AbstractPageViewer.java
deleted file mode 100644 (file)
index b52f76b..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-package org.argeo.cms.viewers;
-
-import java.util.Observable;
-import java.util.Observer;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsEditable;
-import org.argeo.cms.CmsException;
-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;
-
-       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);
-       }
-
-       /**
-        * 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() {
-               try {
-                       if (cmsEditable.canEdit() && !readOnly)
-                               mouseListener = createMouseListener();
-                       else
-                               mouseListener = null;
-                       refresh(getControl());
-                       layout(getControl());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot refresh", e);
-               }
-       }
-
-       @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());
-               if (log.isTraceEnabled())
-                       log.trace("Full stack of " + eToLog.getMessage(), e);
-               // TODO Light error notification popup
-       }
-
-       // 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/src/org/argeo/cms/viewers/EditablePart.java b/org.argeo.cms/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/src/org/argeo/cms/viewers/ItemPart.java b/org.argeo.cms/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/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java b/org.argeo.cms/src/org/argeo/cms/viewers/JcrVersionCmsEditable.java
deleted file mode 100644 (file)
index 2d7daae..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.CmsEditable;
-import org.argeo.cms.CmsEditionEvent;
-import org.argeo.cms.CmsException;
-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/src/org/argeo/cms/viewers/NodePart.java b/org.argeo.cms/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/src/org/argeo/cms/viewers/PropertyPart.java b/org.argeo.cms/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/src/org/argeo/cms/viewers/Section.java b/org.argeo.cms/src/org/argeo/cms/viewers/Section.java
deleted file mode 100644 (file)
index af7fd87..0000000
+++ /dev/null
@@ -1,155 +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.CmsNames;
-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 implements CmsNames {
-       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/src/org/argeo/cms/viewers/SectionPart.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/EditableImage.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/EditableText.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/JcrComposite.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/ScrolledPage.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/StyledControl.java b/org.argeo.cms/src/org/argeo/cms/widgets/StyledControl.java
deleted file mode 100644 (file)
index ec1dbe7..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.argeo.cms.widgets;
-
-import javax.jcr.Item;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.CmsConstants;
-import org.argeo.cms.CmsNames;
-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, CmsNames {
-       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/src/org/argeo/cms/widgets/auth/AbstractLoginDialog.java b/org.argeo.cms/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/src/org/argeo/cms/widgets/auth/CmsLogin.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLogin.java
deleted file mode 100644 (file)
index 8f00c45..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-package org.argeo.cms.widgets.auth;
-
-import static org.argeo.cms.CmsMsg.password;
-import static org.argeo.cms.CmsMsg.username;
-import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_ANONYMOUS;
-import static org.argeo.cms.auth.AuthConstants.LOGIN_CONTEXT_USER;
-import static org.argeo.cms.internal.kernel.Activator.getNodeState;
-
-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.FailedLoginException;
-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.CmsStyles;
-import org.argeo.cms.CmsView;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.HttpRequestCallback;
-import org.argeo.cms.i18n.LocaleUtils;
-import org.argeo.cms.internal.auth.LocaleChoice;
-import org.argeo.cms.util.CmsUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.eclipse.rap.rwt.RWT;
-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;
-
-       public CmsLogin(CmsView cmsView) {
-               this.cmsView = cmsView;
-               defaultLocale = getNodeState().getDefaultLocale();
-               List<Locale> locales = getNodeState().getLocales();
-               if (locales != null)
-                       localeChoice = new LocaleChoice(locales, defaultLocale);
-               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 CurrentUser.isAnonymous(cmsView.getSubject());
-       }
-
-       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);
-               l.setData(RWT.CUSTOM_VARIANT, 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());
-
-               Integer textWidth = 120;
-               parent.setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU);
-
-               // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
-               usernameT = new Text(credentialsBlock, SWT.BORDER);
-               usernameT.setMessage(username.lead(locale));
-               usernameT.setData(RWT.CUSTOM_VARIANT, 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));
-               passwordT.setData(RWT.CUSTOM_VARIANT, 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 });
-               // credentialsBlock.setFocus();
-
-               extendsCredentialsBlock(credentialsBlock, locale,
-                               loginSelectionListener);
-               if (localeChoice != null)
-                       createLocalesBlock(credentialsBlock);
-               return credentialsBlock;
-       }
-
-       /**
-        * To be overridden in order to provide custome 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);
-               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);
-                       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() {
-               Subject subject = cmsView.getSubject();
-               LoginContext loginContext;
-               try {
-                       //
-                       // LOGIN
-                       //
-                       new LoginContext(LOGIN_CONTEXT_ANONYMOUS, subject).logout();
-                       loginContext = new LoginContext(LOGIN_CONTEXT_USER, subject, this);
-                       loginContext.login();
-               } catch (FailedLoginException e) {
-                       log.warn(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;
-               }
-               cmsView.authChange(loginContext);
-               return true;
-       }
-
-       protected void logout() {
-               cmsView.logout();
-               cmsView.navigateTo("~");
-       }
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException,
-                       UnsupportedCallbackException {
-               for (Callback callback : callbacks) {
-                       if (callback instanceof NameCallback)
-                               ((NameCallback) callback).setName(usernameT.getText());
-                       else if (callback instanceof PasswordCallback)
-                               ((PasswordCallback) callback).setPassword(passwordT
-                                               .getTextChars());
-                       else if (callback instanceof HttpRequestCallback)
-                               ((HttpRequestCallback) callback).setRequest(RWT.getRequest());
-                       else if (callback instanceof LanguageCallback
-                                       && localeChoice != null)
-                               ((LanguageCallback) callback).setLocale(localeChoice
-                                               .getSelectedLocale());
-               }
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CmsLoginShell.java
deleted file mode 100644 (file)
index 29a3f54..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.argeo.cms.widgets.auth;
-
-import org.argeo.cms.CmsView;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Widget;
-
-/** The site-related user menu */
-public class CmsLoginShell extends CmsLogin {
-       private final Shell shell;
-
-       public CmsLoginShell(CmsView cmsView) {
-               super(cmsView);
-               shell = createShell();
-               shell.setData(RWT.CUSTOM_VARIANT, 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;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java b/org.argeo.cms/src/org/argeo/cms/widgets/auth/CompositeCallbackHandler.java
deleted file mode 100644 (file)
index 55190d3..0000000
+++ /dev/null
@@ -1,291 +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.argeo.cms.internal.auth.LocaleChoice;
-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[])}.
- * <p>
- * Supported standard {@link Callback}s are:<br>
- * <ul>
- * <li>{@link PasswordCallback}</li>
- * <li>{@link NameCallback}</li>
- * <li>{@link TextOutputCallback}</li>
- * </ul>
- * </p>
- * <p>
- * Supported Argeo {@link Callback}s are:<br>
- * <ul>
- * <li>{@link LocaleChoice}</li>
- * </ul>
- * </p>
- */
-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/src/org/argeo/cms/widgets/auth/DefaultLoginDialog.java b/org.argeo.cms/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/src/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.cms/src/org/argeo/security/core/AbstractSystemExecution.java
deleted file mode 100644 (file)
index 9c3e5cd..0000000
+++ /dev/null
@@ -1,68 +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.security.core;
-
-import javax.security.auth.Subject;
-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.CmsException;
-
-/** Provides base method for executing code with system authorization. */
-public abstract class AbstractSystemExecution {
-       private final static Log log = LogFactory.getLog(AbstractSystemExecution.class);
-       private final Subject subject = new Subject();
-
-       private final String loginModule = "SYSTEM";
-
-       /**
-        * Authenticate the calling thread to the underlying
-        * {@link AuthenticationManager}
-        */
-       protected void authenticateAsSystem() {
-               ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
-               Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-               try {
-                       LoginContext lc = new LoginContext(loginModule, subject);
-                       lc.login();
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot login as system", e);
-               } finally {
-                       Thread.currentThread().setContextClassLoader(origClassLoader);
-               }
-               if (log.isTraceEnabled())
-                       log.trace("System authenticated");
-       }
-
-       protected void deauthenticateAsSystem() {
-               ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader();
-               Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-               try {
-                       LoginContext lc = new LoginContext(loginModule, subject);
-                       lc.logout();
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot logout as system", e);
-               } finally {
-                       Thread.currentThread().setContextClassLoader(origClassLoader);
-               }
-       }
-
-       protected Subject getSubject() {
-               return subject;
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.cms/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
deleted file mode 100644 (file)
index aa3827c..0000000
+++ /dev/null
@@ -1,80 +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.security.core;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.security.auth.Subject;
-
-import org.eclipse.gemini.blueprint.context.DependencyInitializationAwareBeanPostProcessor;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.support.AbstractBeanFactory;
-import org.springframework.beans.factory.support.SecurityContextProvider;
-import org.springframework.beans.factory.support.SimpleSecurityContextProvider;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-
-/**
- * Executes with a system authentication the instantiation and initialization
- * methods of the application context where it has been defined.
- */
-public class AuthenticatedApplicationContextInitialization extends
-               AbstractSystemExecution implements
-               DependencyInitializationAwareBeanPostProcessor, ApplicationContextAware {
-       /** If non empty, restricts to these beans */
-       private List<String> beanNames = new ArrayList<String>();
-
-       public Object postProcessBeforeInitialization(Object bean, String beanName)
-                       throws BeansException {
-               if (beanNames.size() == 0 || beanNames.contains(beanName))
-                       authenticateAsSystem();
-               return bean;
-       }
-
-       public Object postProcessAfterInitialization(Object bean, String beanName)
-                       throws BeansException {
-               if (beanNames.size() == 0 || beanNames.contains(beanName))
-                       deauthenticateAsSystem();
-               return bean;
-       }
-
-       public void setBeanNames(List<String> beanNames) {
-               this.beanNames = beanNames;
-       }
-
-       @Override
-       public void setApplicationContext(ApplicationContext applicationContext)
-                       throws BeansException {
-               if (applicationContext.getAutowireCapableBeanFactory() instanceof AbstractBeanFactory) {
-                       final AbstractBeanFactory beanFactory = ((AbstractBeanFactory) applicationContext
-                                       .getAutowireCapableBeanFactory());
-                       // retrieve subject's access control context
-                       // and set it as the bean factory security context
-                       Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
-                               @Override
-                               public Void run() {
-                                       SecurityContextProvider scp = new SimpleSecurityContextProvider(
-                                                       AccessController.getContext());
-                                       beanFactory.setSecurityContextProvider(scp);
-                                       return null;
-                               }
-                       });
-               }
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.cms/src/org/argeo/security/core/OsgiModuleLabel.java
deleted file mode 100644 (file)
index 45c9e16..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.security.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-
-/**
- * Logs the name and version of an OSGi bundle based on its
- * {@link BundleContext}.
- */
-public class OsgiModuleLabel {
-       private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
-
-       private Bundle bundle;
-
-       public OsgiModuleLabel() {
-       }
-
-       /** Sets without logging. */
-       public OsgiModuleLabel(Bundle bundle) {
-               this.bundle = bundle;
-       }
-
-       /**
-        * Retrieved bundle from a bundle context and logs it. Typically to be set
-        * as a Spring bean.
-        */
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundle = bundleContext.getBundle();
-               log.info(msg());
-       }
-
-       public String msg() {
-               String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
-               String symbolicName = bundle.getSymbolicName();
-               String version = bundle.getVersion().toString();
-               return name + " v" + version + " (" + symbolicName + ")";
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.cms/src/org/argeo/security/core/SimpleRoleRegistration.java
deleted file mode 100644 (file)
index 58f6686..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.argeo.security.core;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.transaction.UserTransaction;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.CmsException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdmin;
-
-/**
- * Register one or many roles via a user admin service. Does nothing if the role
- * is already registered.
- */
-public class SimpleRoleRegistration implements Runnable {
-       private final static Log log = LogFactory
-                       .getLog(SimpleRoleRegistration.class);
-
-       private String role;
-       private List<String> roles = new ArrayList<String>();
-       private UserAdmin userAdmin;
-       private UserTransaction userTransaction;
-
-       @Override
-       public void run() {
-               try {
-                       userTransaction.begin();
-                       if (role != null && !roleExists(role))
-                               newRole(toDn(role));
-
-                       for (String r : roles)
-                               if (!roleExists(r))
-                                       newRole(toDn(r));
-                       userTransaction.commit();
-               } catch (Exception e) {
-                       try {
-                               userTransaction.rollback();
-                       } catch (Exception e1) {
-                               log.error("Cannot rollback", e1);
-                       }
-                       throw new CmsException("Cannot add roles", e);
-               }
-       }
-
-       private boolean roleExists(String role) {
-               return userAdmin.getRole(toDn(role).toString()) != null;
-       }
-
-       protected void newRole(LdapName r) {
-               userAdmin.createRole(r.toString(), Role.GROUP);
-               log.info("Added role " + r + " required by application.");
-       }
-
-       public void register(UserAdmin userAdminService, Map<?, ?> properties) {
-               this.userAdmin = userAdminService;
-               run();
-       }
-
-       protected LdapName toDn(String name) {
-               try {
-                       return new LdapName("cn=" + name + ",ou=roles,ou=node");
-               } catch (InvalidNameException e) {
-                       throw new CmsException("Badly formatted role name " + name, e);
-               }
-       }
-
-       public void setRole(String role) {
-               this.role = role;
-       }
-
-       public void setRoles(List<String> roles) {
-               this.roles = roles;
-       }
-
-       public void setUserAdmin(UserAdmin userAdminService) {
-               this.userAdmin = userAdminService;
-       }
-
-       public void setUserTransaction(UserTransaction userTransaction) {
-               this.userTransaction = userTransaction;
-       }
-
-}
diff --git a/pom.xml b/pom.xml
index 3f8cc338452170399f01f02cbf213cce2c2c785f..9c1f8aec746ecb33c27020f2e95416d7b017c1f5 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -32,8 +32,8 @@
                <!-- CMS -->
                <module>org.argeo.node.api</module>
                <module>org.argeo.cms</module>
+               <module>org.argeo.cms.ui</module>
                <!-- CMS Workbench -->
-               <module>org.argeo.security.ui</module>
                <module>org.argeo.cms.ui.workbench</module>
                <module>org.argeo.cms.ui.workbench.rap</module>
                <!-- Third Parties Extensions -->