Refactor SWT directory structure.
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 10 Jul 2022 05:46:29 +0000 (07:46 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 10 Jul 2022 05:46:29 +0000 (07:46 +0200)
757 files changed:
Makefile
Makefile-rcp.mk
eclipse/org.argeo.cms.e4/.classpath [deleted file]
eclipse/org.argeo.cms.e4/.project [deleted file]
eclipse/org.argeo.cms.e4/META-INF/.gitignore [deleted file]
eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml [deleted file]
eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml [deleted file]
eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml [deleted file]
eclipse/org.argeo.cms.e4/bnd.bnd [deleted file]
eclipse/org.argeo.cms.e4/build.properties [deleted file]
eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi [deleted file]
eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java [deleted file]
eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/.classpath [deleted file]
eclipse/org.argeo.cms.swt/.project [deleted file]
eclipse/org.argeo.cms.swt/META-INF/.gitignore [deleted file]
eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml [deleted file]
eclipse/org.argeo.cms.swt/bnd.bnd [deleted file]
eclipse/org.argeo.cms.swt/build.properties [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/add.png [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/close-all.png [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/delete.png [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/edit.png [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/save-all.png [deleted file]
eclipse/org.argeo.cms.swt/icons/actions/save.png [deleted file]
eclipse/org.argeo.cms.swt/icons/active.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/add.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/add.png [deleted file]
eclipse/org.argeo.cms.swt/icons/addFolder.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/addPrivileges.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/addRepo.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/addWorkspace.png [deleted file]
eclipse/org.argeo.cms.swt/icons/adminLog.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/batch.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/begin.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/binary.png [deleted file]
eclipse/org.argeo.cms.swt/icons/browser.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/bundles.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/changePassword.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/clear.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/close-all.png [deleted file]
eclipse/org.argeo.cms.swt/icons/commit.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/delete.png [deleted file]
eclipse/org.argeo.cms.swt/icons/dumpNode.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/file.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/folder.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/getSize.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/group.png [deleted file]
eclipse/org.argeo.cms.swt/icons/home.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/home.png [deleted file]
eclipse/org.argeo.cms.swt/icons/import_fs.png [deleted file]
eclipse/org.argeo.cms.swt/icons/installed.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/log.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/logout.png [deleted file]
eclipse/org.argeo.cms.swt/icons/maintenance.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/node.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/nodes.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/password.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/person-logged-in.png [deleted file]
eclipse/org.argeo.cms.swt/icons/person.png [deleted file]
eclipse/org.argeo.cms.swt/icons/query.png [deleted file]
eclipse/org.argeo.cms.swt/icons/refresh.png [deleted file]
eclipse/org.argeo.cms.swt/icons/remote_connected.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/remove.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/removePrivileges.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/rename.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/repositories.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/repository_connected.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/resolved.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/role.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/rollback.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/save-all.png [deleted file]
eclipse/org.argeo.cms.swt/icons/save.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/save.png [deleted file]
eclipse/org.argeo.cms.swt/icons/save_security.png [deleted file]
eclipse/org.argeo.cms.swt/icons/save_security_disabled.png [deleted file]
eclipse/org.argeo.cms.swt/icons/security.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/service_published.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/service_referenced.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/sort.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/starting.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/sync.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/user.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/users.gif [deleted file]
eclipse/org.argeo.cms.swt/icons/workgroup.png [deleted file]
eclipse/org.argeo.cms.swt/icons/workgroup.xcf [deleted file]
eclipse/org.argeo.cms.swt/icons/workspace_connected.png [deleted file]
eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java [deleted file]
eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java [deleted file]
org.argeo.api.cli/.classpath [new file with mode: 0644]
org.argeo.api.cli/.project [new file with mode: 0644]
org.argeo.api.cli/bnd.bnd [new file with mode: 0644]
org.argeo.api.cli/build.properties [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java [new file with mode: 0644]
org.argeo.api.cli/src/org/argeo/api/cli/package-info.java [new file with mode: 0644]
org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java [new file with mode: 0644]
org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/acr/fs/FileSync.java
org.argeo.cms/src/org/argeo/cms/acr/fs/FsCommands.java
org.argeo.cms/src/org/argeo/cms/auth/AnonymousLoginModule.java
org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java [deleted file]
org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java [deleted file]
org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java [deleted file]
org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java [deleted file]
org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java [deleted file]
org.argeo.cms/src/org/argeo/cms/cli/package-info.java [deleted file]
org.argeo.cms/src/org/argeo/cms/runtime/StaticCms.java
org.argeo.util/src/org/argeo/util/register/ComponentRegister.java
rap/org.argeo.cms.e4.rap/.classpath [deleted file]
rap/org.argeo.cms.e4.rap/.project [deleted file]
rap/org.argeo.cms.e4.rap/META-INF/.gitignore [deleted file]
rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml [deleted file]
rap/org.argeo.cms.e4.rap/bnd.bnd [deleted file]
rap/org.argeo.cms.e4.rap/build.properties [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java [deleted file]
rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java [deleted file]
rap/org.argeo.cms.ui.rap/.classpath [deleted file]
rap/org.argeo.cms.ui.rap/.project [deleted file]
rap/org.argeo.cms.ui.rap/META-INF/.gitignore [deleted file]
rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml [deleted file]
rap/org.argeo.cms.ui.rap/bnd.bnd [deleted file]
rap/org.argeo.cms.ui.rap/build.properties [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java [deleted file]
rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java [deleted file]
rap/org.argeo.swt.specific.rap/.classpath [deleted file]
rap/org.argeo.swt.specific.rap/.project [deleted file]
rap/org.argeo.swt.specific.rap/META-INF/.gitignore [deleted file]
rap/org.argeo.swt.specific.rap/bnd.bnd [deleted file]
rap/org.argeo.swt.specific.rap/build.properties [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java [deleted file]
rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java [deleted file]
rcp/org.argeo.cms.e4.rcp/.classpath [deleted file]
rcp/org.argeo.cms.e4.rcp/.gitignore [deleted file]
rcp/org.argeo.cms.e4.rcp/.project [deleted file]
rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs [deleted file]
rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs [deleted file]
rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore [deleted file]
rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi [deleted file]
rcp/org.argeo.cms.e4.rcp/argeo-companion.properties [deleted file]
rcp/org.argeo.cms.e4.rcp/bnd.bnd [deleted file]
rcp/org.argeo.cms.e4.rcp/build.properties [deleted file]
rcp/org.argeo.cms.e4.rcp/log4j.properties [deleted file]
rcp/org.argeo.cms.e4.rcp/plugin.xml [deleted file]
rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java [deleted file]
rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java [deleted file]
rcp/org.argeo.cms.ui.rcp/.classpath [deleted file]
rcp/org.argeo.cms.ui.rcp/.gitignore [deleted file]
rcp/org.argeo.cms.ui.rcp/.project [deleted file]
rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore [deleted file]
rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml [deleted file]
rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml [deleted file]
rcp/org.argeo.cms.ui.rcp/bnd.bnd [deleted file]
rcp/org.argeo.cms.ui.rcp/build.properties [deleted file]
rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java [deleted file]
rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java [deleted file]
rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java [deleted file]
rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java [deleted file]
rcp/org.argeo.swt.specific.rcp/.classpath [deleted file]
rcp/org.argeo.swt.specific.rcp/.gitignore [deleted file]
rcp/org.argeo.swt.specific.rcp/.project [deleted file]
rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore [deleted file]
rcp/org.argeo.swt.specific.rcp/bnd.bnd [deleted file]
rcp/org.argeo.swt.specific.rcp/build.properties [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java [deleted file]
rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java [deleted file]
swt/org.argeo.cms.e4/.classpath [new file with mode: 0644]
swt/org.argeo.cms.e4/.project [new file with mode: 0644]
swt/org.argeo.cms.e4/META-INF/.gitignore [new file with mode: 0644]
swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml [new file with mode: 0644]
swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml [new file with mode: 0644]
swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml [new file with mode: 0644]
swt/org.argeo.cms.e4/bnd.bnd [new file with mode: 0644]
swt/org.argeo.cms.e4/build.properties [new file with mode: 0644]
swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi [new file with mode: 0644]
swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java [new file with mode: 0644]
swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/.classpath [new file with mode: 0644]
swt/org.argeo.cms.swt/.project [new file with mode: 0644]
swt/org.argeo.cms.swt/META-INF/.gitignore [new file with mode: 0644]
swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml [new file with mode: 0644]
swt/org.argeo.cms.swt/bnd.bnd [new file with mode: 0644]
swt/org.argeo.cms.swt/build.properties [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/add.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/close-all.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/delete.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/edit.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/save-all.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/actions/save.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/active.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/add.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/add.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/addFolder.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/addPrivileges.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/addRepo.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/addWorkspace.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/adminLog.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/batch.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/begin.gif [new file with mode: 0755]
swt/org.argeo.cms.swt/icons/binary.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/browser.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/bundles.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/changePassword.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/clear.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/close-all.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/commit.gif [new file with mode: 0755]
swt/org.argeo.cms.swt/icons/delete.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/dumpNode.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/file.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/folder.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/getSize.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/group.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/home.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/home.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/import_fs.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/installed.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/log.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/logout.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/maintenance.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/node.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/nodes.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/osgi_explorer.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/password.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/person-logged-in.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/person.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/query.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/refresh.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/remote_connected.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/remote_disconnected.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/remove.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/removePrivileges.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/rename.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/repositories.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/repository_connected.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/repository_disconnected.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/resolved.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/role.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/rollback.gif [new file with mode: 0755]
swt/org.argeo.cms.swt/icons/save-all.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/save.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/save.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/save_security.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/save_security_disabled.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/security.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/service_published.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/service_referenced.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/sort.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/starting.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/sync.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/user.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/users.gif [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/workgroup.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/workgroup.xcf [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/workspace_connected.png [new file with mode: 0644]
swt/org.argeo.cms.swt/icons/workspace_disconnected.png [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java [new file with mode: 0644]
swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/.classpath [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/.project [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/bnd.bnd [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/build.properties [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java [new file with mode: 0644]
swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/.classpath [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/.project [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/build.properties [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/.classpath [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/.project [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/bnd.bnd [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/build.properties [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/.classpath [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/.project [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/bnd.bnd [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/build.properties [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java [new file with mode: 0644]
swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/.classpath [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/.project [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/build.properties [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/log4j.properties [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/plugin.xml [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp.cli/.project [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp.cli/bnd.bnd [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp.cli/build.properties [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/.classpath [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/.project [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/build.properties [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java [new file with mode: 0644]
swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/.classpath [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/.project [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/build.properties [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java [new file with mode: 0644]
swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java [new file with mode: 0644]

index 8688ce849c46b46f241bd47ba8b3f3a42315e17e..ee06046301d15b77b5b8f3cca23d090bd2a80cfe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ org.argeo.init \
 org.argeo.util \
 org.argeo.api.uuid \
 org.argeo.api.acr \
+org.argeo.api.cli \
 org.argeo.api.cms \
 org.argeo.cms \
 org.argeo.cms.sql \
@@ -23,11 +24,12 @@ org.argeo.cms.ssh \
 org.argeo.cms.ux \
 eclipse/org.argeo.ext.equinox.jetty \
 eclipse/org.argeo.cms.servlet \
-eclipse/org.argeo.cms.swt \
-eclipse/org.argeo.cms.e4 \
-rap/org.argeo.cms.ui.rap \
-rap/org.argeo.swt.specific.rap \
-rap/org.argeo.cms.e4.rap \
+swt/org.argeo.cms.swt \
+swt/org.argeo.cms.e4 \
+swt/rap/org.argeo.swt.specific.rap \
+swt/rap/org.argeo.cms.swt.rap \
+swt/rap/org.argeo.cms.swt.rap.cli \
+swt/rap/org.argeo.cms.e4.rap \
 jcr/org.argeo.cms.jcr \
 jcr/org.argeo.cms.ui \
 
@@ -44,7 +46,7 @@ org.argeo.api.cms
 A2_OUTPUT = $(SDK_BUILD_BASE)/a2
 A2_BASE = $(A2_OUTPUT)
 
-VPATH = .:eclipse:rap:jcr
+VPATH = .:eclipse:rap:jcr:swt:swt/rap
 
 DEP_CATEGORIES = \
 org.argeo.tp \
@@ -64,4 +66,16 @@ clean:
        $(MAKE) -C jni clean
        $(MAKE) -f Makefile-rcp.mk clean
 
+A2_BUNDLES_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_BUNDLES)))
+
+native-image:
+       mkdir -p $(A2_OUTPUT)/libexec/$(A2_CATEGORY)
+       cd $(A2_OUTPUT)/libexec/$(A2_CATEGORY) && /opt/graalvm-ce/bin/native-image \
+               -cp $(A2_CLASSPATH):$(A2_BUNDLES_CLASSPATH) org.argeo.eclipse.ui.jetty.CmsRapCli \
+               --enable-url-protocols=http,https \
+               -H:AdditionalSecurityProviders=sun.security.jgss.SunProvider,org.bouncycastle.jce.provider.BouncyCastleProvider,net.i2p.crypto.eddsa.EdDSASecurityProvider \
+               --initialize-at-build-time=org.argeo.init.logging.ThinLogging,org.slf4j.LoggerFactory \
+               --no-fallback 
+
+
 include  $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
\ No newline at end of file
index 02ca5296230633f39962df7dc3e2aee5b2a07559..8b96683fc389da57f1eda4e0e39664630f3b748e 100644 (file)
@@ -6,10 +6,11 @@ all: osgi
 A2_CATEGORY = org.argeo.cms.eclipse.rcp
 
 BUNDLES = \
-rcp/org.argeo.cms.e4.rcp \
-rcp/org.argeo.cms.ui.rcp \
 rcp/org.argeo.swt.minidesktop \
-rcp/org.argeo.swt.specific.rcp \
+swt/rcp/org.argeo.swt.specific.rcp \
+swt/rcp/org.argeo.cms.swt.rcp \
+swt/rcp/org.argeo.cms.swt.rcp.cli \
+swt/rcp/org.argeo.cms.e4.rcp \
 
 A2_OUTPUT = $(SDK_BUILD_BASE)/a2
 A2_BASE = $(A2_OUTPUT)
@@ -30,6 +31,6 @@ org.argeo.tp.jcr
 clean:
        rm -rf $(BUILD_BASE)
 
-VPATH = .:rcp
+VPATH = .:rcp:swt/rcp
 
 include  $(SDK_SRC_BASE)/sdk/argeo-build/osgi.mk
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/.classpath b/eclipse/org.argeo.cms.e4/.classpath
deleted file mode 100644 (file)
index e801ebf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/eclipse/org.argeo.cms.e4/.project b/eclipse/org.argeo.cms.e4/.project
deleted file mode 100644 (file)
index 0c04069..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.e4</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/eclipse/org.argeo.cms.e4/META-INF/.gitignore b/eclipse/org.argeo.cms.e4/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml
deleted file mode 100644 (file)
index fcd3ae5..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default CallbackHandler">
-   <implementation class="org.argeo.cms.swt.auth.DynamicCallbackHandler"/>
-   <service>
-      <provide interface="javax.security.auth.callback.CallbackHandler"/>
-   </service>
-</scr:component>
diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/homeRepository.xml
deleted file mode 100644 (file)
index 65690f2..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="Home Repository">
-   <implementation class="org.argeo.cms.e4.OsgiFilterContextFunction"/>
-   <property name="service.context.key" type="String" value="(cn=home)"/>
-   <service>
-      <provide interface="org.eclipse.e4.core.contexts.IContextFunction"/>
-   </service>
-</scr:component>
diff --git a/eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/eclipse/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml
deleted file mode 100644 (file)
index cc7087b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="User Admin Wrapper">
-   <implementation class="org.argeo.cms.e4.users.UserAdminWrapper"/>
-   <reference bind="setUserTransaction" cardinality="1..1" interface="org.argeo.util.transaction.WorkTransaction" name="UserTransaction" policy="static"/>
-   <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
-   <service>
-      <provide interface="org.argeo.cms.e4.users.UserAdminWrapper"/>
-   </service>
-   <reference bind="addUserDirectory" cardinality="0..n" interface="org.argeo.osgi.useradmin.UserDirectory" name="UserDirectory" policy="static" unbind="removeUserDirectory"/>
-</scr:component>
diff --git a/eclipse/org.argeo.cms.e4/bnd.bnd b/eclipse/org.argeo.cms.e4/bnd.bnd
deleted file mode 100644 (file)
index 8026f98..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Service-Component: OSGI-INF/homeRepository.xml,\
-OSGI-INF/userAdminWrapper.xml,\
-OSGI-INF/defaultCallbackHandler.xml
-Bundle-ActivationPolicy: lazy
-
-Import-Package: \
-org.argeo.api.acr,\
-org.eclipse.swt,\
-org.eclipse.swt.widgets;version="0.0.0",\
-org.eclipse.e4.ui.model.application.ui,\
-org.eclipse.e4.ui.model.application,\
-javax.jcr.nodetype,\
-org.argeo.cms,\
-org.eclipse.core.commands.common,\
-org.eclipse.jface.window,\
-org.argeo.cms.swt.auth,\
-org.apache.jackrabbit.*;version="[2,3)",\
-javax.servlet.*;version="[3,5)",\
-*
diff --git a/eclipse/org.argeo.cms.e4/build.properties b/eclipse/org.argeo.cms.e4/build.properties
deleted file mode 100644 (file)
index e46a7ba..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               OSGI-INF/,\
-               .,\
-               OSGI-INF/homeRepository.xml,\
-               OSGI-INF/userAdminWrapper.xml,\
-               OSGI-INF/defaultCallbackHandler.xml,\
-               e4xmi/cms-demo.e4xmi
-source.. = src/
diff --git a/eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi b/eclipse/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi
deleted file mode 100644 (file)
index 89bcc37..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="ASCII"?>
-<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:advanced="http://www.eclipse.org/ui/2010/UIModel/application/ui/advanced" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_XqkCQKknEeObFrG_clJBYA" elementId="">
-  <children xsi:type="basic:TrimmedWindow" xmi:id="_Zdy6cKknEeObFrG_clJBYA" elementId="org.argeo.cms.e4.apps.admin.trimmedwindow.0" label="" x="10" y="10" width="500" height="500">
-    <persistedState key="styleOverride" value="8"/>
-    <tags>shellMaximized</tags>
-    <tags>auth.cn=admin,ou=roles,ou=node</tags>
-    <children xsi:type="advanced:PerspectiveStack" xmi:id="_jXVqsCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.perspectivestack.0" selectedElement="_xOVlsDvOEeiF1foPJZSZkw">
-      <children xsi:type="advanced:Perspective" xmi:id="_xOVlsDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.perspective.users" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png">
-        <tags>auth.cn=admin,ou=roles,ou=node</tags>
-        <children xsi:type="basic:PartSashContainer" xmi:id="_1tQoEDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partsashcontainer.2" horizontal="true">
-          <children xsi:type="basic:PartStack" xmi:id="_vtbKkDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.4" containerData="4000" selectedElement="_9gukYDvOEeiF1foPJZSZkw">
-            <children xsi:type="basic:Part" xmi:id="_9gukYDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.part.users" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.UsersView" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person.png">
-              <handlers xmi:id="_0mN68DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.4" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.NewUser" command="_uL5i4DvjEeiF1foPJZSZkw"/>
-              <handlers xmi:id="_ODLdgDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.5" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.DeleteUsers" command="_xkcMADvjEeiF1foPJZSZkw"/>
-              <toolbar xmi:id="_jLWmkDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.toolbar.1">
-                <children xsi:type="menu:HandledToolItem" xmi:id="_jy_OUDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.new" label="New" iconURI="platform:/plugin/org.argeo.cms.swt/icons/add.png" command="_uL5i4DvjEeiF1foPJZSZkw"/>
-                <children xsi:type="menu:HandledToolItem" xmi:id="_9qszMDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/delete.png" command="_xkcMADvjEeiF1foPJZSZkw"/>
-              </toolbar>
-            </children>
-          </children>
-          <children xsi:type="basic:PartStack" xmi:id="__g1a8DvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.3" containerData="4000">
-            <tags>usersEditorArea</tags>
-          </children>
-          <children xsi:type="basic:PartStack" xmi:id="_-mFn8DvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.5" containerData="2000">
-            <children xsi:type="basic:Part" xmi:id="_6etk4DvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.part.groups" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.GroupsView" label="Groups" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png">
-              <handlers xmi:id="_cmShoDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.6" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.NewGroup" command="_uL5i4DvjEeiF1foPJZSZkw"/>
-              <handlers xmi:id="_fbYfcDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.7" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.DeleteGroups" command="_xkcMADvjEeiF1foPJZSZkw"/>
-              <toolbar xmi:id="_Us0rADvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.toolbar.2">
-                <children xsi:type="menu:HandledToolItem" xmi:id="_VQTLgDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.new" label="New" iconURI="platform:/plugin/org.argeo.cms.swt/icons/add.png" command="_uL5i4DvjEeiF1foPJZSZkw"/>
-                <children xsi:type="menu:HandledToolItem" xmi:id="_XfME8DvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/delete.png" command="_xkcMADvjEeiF1foPJZSZkw"/>
-              </toolbar>
-            </children>
-          </children>
-        </children>
-      </children>
-      <children xsi:type="advanced:Perspective" xmi:id="_jvjWYCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.perspective.data" label="Data" iconURI="platform:/plugin/org.argeo.cms.swt/icons/nodes.gif">
-        <children xsi:type="basic:PartSashContainer" xmi:id="_h3tvMCkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partsashcontainer.0" selectedElement="_0B9SECkxEein5vuhpK-Dew" horizontal="true">
-          <children xsi:type="basic:PartStack" xmi:id="_0B9SECkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.0" containerData="4000" selectedElement="_WAjPkCkTEein5vuhpK-Dew">
-            <children xsi:type="basic:Part" xmi:id="_WAjPkCkTEein5vuhpK-Dew" elementId="org.argeo.cms.e4.jcrbrowser" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR" iconURI="platform:/plugin/org.argeo.cms.swt/icons/browser.gif">
-              <menus xsi:type="menu:PopupMenu" xmi:id="_eXiUECqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.popupmenu.nodeViewer">
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_GVeO8CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.swt/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_fU238CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_U4o9cCqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.swt/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_Ncxo0CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.remove" label="Remove" iconURI="platform:/plugin/org.argeo.cms.swt/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-              </menus>
-              <menus xmi:id="_oRg_ACqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.menu.0">
-                <tags>ViewMenu</tags>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_yJR8ECqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.swt/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_o6HQECqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_5D7aACqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.swt/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_7rR2wCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-                <children xsi:type="menu:HandledMenuItem" xmi:id="_XsHLgFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledmenuitem.0" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addRepo.gif" command="_ZWpasFgQEeiknZQLx-vtnA"/>
-              </menus>
-            </children>
-          </children>
-          <children xsi:type="basic:PartStack" xmi:id="_mHrEUCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.1" containerData="6000">
-            <tags>dataExplorer</tags>
-          </children>
-        </children>
-      </children>
-      <children xsi:type="advanced:Perspective" xmi:id="_u5ZakFhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.perspective.monitoring" label="Monitoring" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif">
-        <children xsi:type="basic:PartStack" xmi:id="_7i7t8FhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.partstack.6">
-          <children xsi:type="basic:Part" xmi:id="_Z-3cMFhbEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.osgiConfigurations" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.OsgiConfigurationsView" label="OSGi Configurations" iconURI="platform:/plugin/org.argeo.cms.swt/icons/node.gif"/>
-          <children xsi:type="basic:Part" xmi:id="_8dM90FhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.cmsSessions" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.CmsSessionsView" label="CMS Sessions" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person-logged-in.png"/>
-          <children xsi:type="basic:Part" xmi:id="_KqRZIFhNEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.modules" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.ModulesView" label="Modules" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif"/>
-          <children xsi:type="basic:Part" xmi:id="_dXtIoFhNEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.bundles" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.BundlesView" label="Bundles" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif"/>
-        </children>
-      </children>
-      <children xsi:type="advanced:Perspective" xmi:id="_ABK2ADsNEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.perspective.files" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif">
-        <children xsi:type="basic:PartSashContainer" xmi:id="_FPimEDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.partsashcontainer.1" horizontal="true">
-          <children xsi:type="basic:PartStack" xmi:id="_H93NgDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.partstack.2" containerData="4000">
-            <children xsi:type="basic:Part" xmi:id="_Izxh0DsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.part.files" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.files.NodeFsBrowserView" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif"/>
-          </children>
-          <children xsi:type="basic:Part" xmi:id="_TMqBMDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.part.0" containerData="6000"/>
-        </children>
-      </children>
-    </children>
-    <handlers xmi:id="_Vwax0DvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.8" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.OpenPerspective" command="_AF1UsDvrEeiF1foPJZSZkw"/>
-    <trimBars xmi:id="_euVxMCk2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.trimbar.0" side="Left">
-      <children xsi:type="menu:ToolBar" xmi:id="_fotHsCk2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.toolbar.0">
-        <children xsi:type="menu:HandledToolItem" xmi:id="_jCSQgDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.users" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png" command="_AF1UsDvrEeiF1foPJZSZkw">
-          <tags>auth.cn=admin,ou=roles,ou=node</tags>
-          <parameters xmi:id="_lu_uYDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.2" name="perspectiveId" value="org.argeo.cms.e4.perspective.users"/>
-        </children>
-        <children xsi:type="menu:HandledToolItem" xmi:id="_jfUM4Ck2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.handledtoolitem.test" label="Data" iconURI="platform:/plugin/org.argeo.cms.swt/icons/nodes.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
-          <parameters xmi:id="_KDlXQDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.0" name="perspectiveId" value="org.argeo.cms.e4.perspective.data"/>
-        </children>
-        <children xsi:type="menu:HandledToolItem" xmi:id="_dhv80FhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledtoolitem.monitoring" label="Monitoring" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
-          <parameters xmi:id="_kjN0cFhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.parameter.3" name="perspectiveId" value="org.argeo.cms.e4.perspective.monitoring"/>
-        </children>
-        <children xsi:type="menu:HandledToolItem" xmi:id="_b0OHUDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.files" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
-          <parameters xmi:id="_fXvRYDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.1" name="perspectiveId" value="org.argeo.cms.e4.perspective.files"/>
-        </children>
-        <children xsi:type="menu:ToolBarSeparator" xmi:id="_wuoL8FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.toolbarseparator.0"/>
-        <children xsi:type="menu:HandledToolItem" xmi:id="_2v8DkFhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledtoolitem.logout" label="Log out" iconURI="platform:/plugin/org.argeo.cms.swt/icons/logout.png" command="_PsWd0FhLEeiknZQLx-vtnA"/>
-      </children>
-    </trimBars>
-  </children>
-  <handlers xmi:id="_Xp-P4CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.0" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddFolderNode" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_jbnNwCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.1" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.DeleteNodes" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_loxB0CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.2" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.Refresh" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_omPfkCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.3" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.RenameNode" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_dUg-cFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.9" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddRemoteRepository" command="_ZWpasFgQEeiknZQLx-vtnA"/>
-  <handlers xmi:id="_RQyFAFhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.10" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.CloseWorkbench" command="_PsWd0FhLEeiknZQLx-vtnA"/>
-  <descriptors xmi:id="_XzfoMCqlEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" label="Node Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/node.gif" allowMultiple="true" category="dataExplorer" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
-  <descriptors xmi:id="_sAdNwDvdEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partdescriptor.userEditor" label="User Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person.png" allowMultiple="true" category="usersEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.UserEditor"/>
-  <descriptors xmi:id="_5nK7EDvdEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partdescriptor.groupEditor" label="Group Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png" allowMultiple="true" category="usersEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.GroupEditor"/>
-  <commands xmi:id="_RgE5cCqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.addFolderNode" commandName="Add folder node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_ChJ-4CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.deleteNodes" commandName="Delete nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_TOKHsCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.refreshNodes" commandName="Refresh nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_ZrcUMCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.renameNode" commandName="Rename node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_uL5i4DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.add" commandName="Add"/>
-  <commands xmi:id="_xkcMADvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.delete" commandName="Delete"/>
-  <commands xmi:id="_AF1UsDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.openPerspective" commandName="Open Perspective">
-    <parameters xmi:id="_F3WAUDvrEeiF1foPJZSZkw" elementId="perspectiveId" name="Perspective Id" optional="false"/>
-  </commands>
-  <commands xmi:id="_ZWpasFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.addRemoteRepository" commandName="Add Remote Repository"/>
-  <commands xmi:id="_PsWd0FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.logout" commandName="Log out"/>
-  <addons xmi:id="_XqkCQaknEeObFrG_clJBYA" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
-  <addons xmi:id="_XqkCQqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
-  <addons xmi:id="_XqkCQ6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
-  <addons xmi:id="_XqkCRKknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
-  <addons xmi:id="_XqkCRaknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
-  <addons xmi:id="_XqkCRqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
-  <addons xmi:id="_XqkCR6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
-  <addons xmi:id="_8VnK8OdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.locale" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.LocaleAddon"/>
-  <addons xmi:id="_-xeJYOdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.auth" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.AuthAddon"/>
-  <categories xmi:id="_MDkwUCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.category.jcrBrowser" name="JCR Browser"/>
-</application:Application>
diff --git a/eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi b/eclipse/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi
deleted file mode 100644 (file)
index ef659fc..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="ASCII"?>
-<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_XqkCQKknEeObFrG_clJBYA" elementId="">
-  <children xsi:type="basic:TrimmedWindow" xmi:id="_Zdy6cKknEeObFrG_clJBYA" elementId="org.argeo.cms.e4.apps.admin.trimmedwindow.0" label="" x="10" y="10" width="500" height="500">
-    <persistedState key="styleOverride" value="8"/>
-    <tags>shellMaximized</tags>
-    <tags>auth.cn=user,ou=roles,ou=node</tags>
-    <children xsi:type="basic:PartSashContainer" xmi:id="_h3tvMCkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partsashcontainer.0" selectedElement="_0B9SECkxEein5vuhpK-Dew" horizontal="true">
-      <children xsi:type="basic:PartStack" xmi:id="_0B9SECkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.0" containerData="4000" selectedElement="_WAjPkCkTEein5vuhpK-Dew">
-        <children xsi:type="basic:Part" xmi:id="_WAjPkCkTEein5vuhpK-Dew" elementId="org.argeo.cms.e4.jcrbrowser" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/browser.gif">
-          <menus xsi:type="menu:PopupMenu" xmi:id="_eXiUECqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.popupmenu.nodeViewer">
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_GVeO8CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_fU238CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_U4o9cCqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_Ncxo0CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.remove" label="Remove" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-          </menus>
-          <menus xmi:id="_oRg_ACqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.menu.0">
-            <tags>ViewMenu</tags>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_yJR8ECqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_o6HQECqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_5D7aACqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_7rR2wCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-            <children xsi:type="menu:HandledMenuItem" xmi:id="_XsHLgFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledmenuitem.0" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addRepo.gif" command="_ZWpasFgQEeiknZQLx-vtnA"/>
-          </menus>
-        </children>
-      </children>
-      <children xsi:type="basic:PartStack" xmi:id="_mHrEUCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.1" containerData="6000">
-        <tags>dataExplorer</tags>
-        <children xsi:type="basic:Part" xmi:id="_LyT80MKKEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.part.egoDashboard" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.parts.EgoDashboard" label="" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/home.png">
-          <toolbar xmi:id="_Ut8wMMKMEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.toolbar.0">
-            <children xsi:type="menu:HandledToolItem" xmi:id="_nElwUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.cms.e4.handledtoolitem.changepassword" label="Change password" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/actions/edit.png" command="_jEjCUMKMEeq1Ytjq4ALs6g"/>
-            <children xsi:type="menu:HandledToolItem" xmi:id="_WAD4UMKMEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.handledtoolitem.logout" label="Log out" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/logout.png" command="_PsWd0FhLEeiknZQLx-vtnA"/>
-          </toolbar>
-        </children>
-      </children>
-    </children>
-  </children>
-  <handlers xmi:id="_Xp-P4CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.0" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddFolderNode" command="_RgE5cCqREeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_jbnNwCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.1" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.DeleteNodes" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_loxB0CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.2" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.Refresh" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_omPfkCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.3" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.RenameNode" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
-  <handlers xmi:id="_dUg-cFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.9" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddRemoteRepository" command="_ZWpasFgQEeiknZQLx-vtnA"/>
-  <handlers xmi:id="_RQyFAFhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.logout" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.CloseWorkbench" command="_PsWd0FhLEeiknZQLx-vtnA"/>
-  <handlers xmi:id="_lN4GUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.suite.e4.handler.changePassword" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangePassword" command="_jEjCUMKMEeq1Ytjq4ALs6g"/>
-  <descriptors xmi:id="_XzfoMCqlEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" label="Node Editor" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/node.gif" allowMultiple="true" category="dataExplorer" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
-  <commands xmi:id="_RgE5cCqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.addFolderNode" commandName="Add folder node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_ChJ-4CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.deleteNodes" commandName="Delete nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_TOKHsCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.refreshNodes" commandName="Refresh nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_ZrcUMCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.renameNode" commandName="Rename node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
-  <commands xmi:id="_uL5i4DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.add" commandName="Add"/>
-  <commands xmi:id="_xkcMADvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.delete" commandName="Delete"/>
-  <commands xmi:id="_AF1UsDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.openPerspective" commandName="Open Perspective">
-    <parameters xmi:id="_F3WAUDvrEeiF1foPJZSZkw" elementId="perspectiveId" name="Perspective Id" optional="false"/>
-  </commands>
-  <commands xmi:id="_ZWpasFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.addRemoteRepository" commandName="Add Remote Repository"/>
-  <commands xmi:id="_PsWd0FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.logout" commandName="Log out"/>
-  <commands xmi:id="_jEjCUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.cms.e4.command.changePassword" commandName="Change Password"/>
-  <addons xmi:id="_XqkCQaknEeObFrG_clJBYA" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
-  <addons xmi:id="_XqkCQqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
-  <addons xmi:id="_XqkCQ6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
-  <addons xmi:id="_XqkCRKknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
-  <addons xmi:id="_XqkCRaknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
-  <addons xmi:id="_XqkCRqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
-  <addons xmi:id="_XqkCR6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
-  <addons xmi:id="_8VnK8OdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.locale" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.LocaleAddon"/>
-  <addons xmi:id="_-xeJYOdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.auth" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.AuthAddon"/>
-  <categories xmi:id="_MDkwUCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.category.jcrBrowser" name="JCR Browser"/>
-</application:Application>
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java
deleted file mode 100644 (file)
index a997de7..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.argeo.cms.e4;
-
-import java.util.List;
-
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.e4.ui.model.application.MApplication;
-import org.eclipse.e4.ui.model.application.commands.MCommand;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
-import org.eclipse.e4.ui.model.application.ui.menu.MHandledMenuItem;
-import org.eclipse.e4.ui.workbench.modeling.EModelService;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkUtil;
-
-/** Static utilities simplifying recurring Eclipse 4 patterns. */
-public class CmsE4Utils {
-       /** Open an editor based on its id. */
-       public static void openEditor(EPartService partService, String editorId, String key, String state) {
-               for (MPart part : partService.getParts()) {
-                       String id = part.getPersistedState().get(key);
-                       if (id != null && state.equals(id)) {
-                               partService.showPart(part, PartState.ACTIVATE);
-                               return;
-                       }
-               }
-
-               // new part
-               MPart part = partService.createPart(editorId);
-               if (part == null)
-                       throw new CmsException("No editor found with id " + editorId);
-               part.getPersistedState().put(key, state);
-               partService.showPart(part, PartState.ACTIVATE);
-       }
-
-       /** Dynamically creates an handled menu item from a command ID. */
-       public static MHandledMenuItem createHandledMenuItem(EModelService modelService, MApplication app,
-                       String commandId) {
-               MCommand command = findCommand(modelService, app, commandId);
-               if (command == null)
-                       return null;
-               MHandledMenuItem handledItem = modelService.createModelElement(MHandledMenuItem.class);
-               handledItem.setCommand(command);
-               return handledItem;
-
-       }
-
-       /**
-        * Finds a command by ID.
-        * 
-        * @return the {@link MCommand} or <code>null</code> if not found.
-        */
-       public static MCommand findCommand(EModelService modelService, MApplication app, String commandId) {
-               List<MCommand> cmds = modelService.findElements(app, null, MCommand.class, null);
-               for (MCommand cmd : cmds) {
-                       if (cmd.getElementId().equals(commandId)) {
-                               return cmd;
-                       }
-               }
-               return null;
-       }
-
-       /** Dynamically creates a direct menu item from a class. */
-       public static MDirectMenuItem createDirectMenuItem(EModelService modelService, Class<?> clss, String label) {
-               MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class);
-               dynamicItem.setLabel(label);
-               Bundle bundle = FrameworkUtil.getBundle(clss);
-               dynamicItem.setContributionURI("bundleclass://" + bundle.getSymbolicName() + "/" + clss.getName());
-               return dynamicItem;
-       }
-
-       /** Singleton. */
-       private CmsE4Utils() {
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java
deleted file mode 100644 (file)
index 1e3e75c..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.argeo.cms.e4;
-
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.e4.core.contexts.ContextFunction;
-import org.eclipse.e4.core.contexts.IEclipseContext;
-import org.eclipse.e4.core.di.IInjector;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-/** An Eclipse 4 {@link ContextFunction} based on an OSGi filter. */
-public class OsgiFilterContextFunction extends ContextFunction {
-
-       private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext();
-
-       @Override
-       public Object compute(IEclipseContext context, String contextKey) {
-               ServiceReference<?>[] srs;
-               try {
-                       srs = bc.getServiceReferences((String) null, contextKey);
-               } catch (InvalidSyntaxException e) {
-                       throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e);
-               }
-               if (srs == null || srs.length == 0) {
-                       return IInjector.NOT_A_VALUE;
-               } else {
-                       // return the first one
-                       return bc.getService(srs[0]);
-               }
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java
deleted file mode 100644 (file)
index 89055d2..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.argeo.cms.e4;
-
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.Subject;
-
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.jobs.Job;
-
-/**
- * Propagate authentication to an eclipse job. Typically to execute a privileged
- * action outside the UI thread
- */
-public abstract class PrivilegedJob extends Job {
-       private final Subject subject;
-
-       public PrivilegedJob(String jobName) {
-               this(jobName, AccessController.getContext());
-       }
-
-       public PrivilegedJob(String jobName,
-                       AccessControlContext accessControlContext) {
-               super(jobName);
-               subject = Subject.getSubject(accessControlContext);
-
-               // Must be called *before* the job is scheduled,
-               // it is required for the progress window to appear
-               setUser(true);
-       }
-
-       @Override
-       protected IStatus run(final IProgressMonitor progressMonitor) {
-               PrivilegedAction<IStatus> privilegedAction = new PrivilegedAction<IStatus>() {
-                       public IStatus run() {
-                               return doRun(progressMonitor);
-                       }
-               };
-               return Subject.doAs(subject, privilegedAction);
-       }
-
-       /**
-        * Implement here what should be executed with default context
-        * authentication
-        */
-       protected abstract IStatus doRun(IProgressMonitor progressMonitor);
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java
deleted file mode 100644 (file)
index e84b18c..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.argeo.cms.e4.addons;
-
-import java.security.AccessController;
-import java.util.Iterator;
-
-import javax.annotation.PostConstruct;
-import javax.security.auth.Subject;
-import javax.servlet.http.HttpServletRequest;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.e4.ui.model.application.MApplication;
-import org.eclipse.e4.ui.model.application.ui.MElementContainer;
-import org.eclipse.e4.ui.model.application.ui.MUIElement;
-import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
-import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
-import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
-
-public class AuthAddon {
-       private final static CmsLog log = CmsLog.getLog(AuthAddon.class);
-
-       public final static String AUTH = "auth.";
-
-       @PostConstruct
-       void init(MApplication application) {
-               Iterator<MWindow> windows = application.getChildren().iterator();
-               boolean atLeastOneTopLevelWindowVisible = false;
-               windows: while (windows.hasNext()) {
-                       MWindow window = windows.next();
-                       // main window
-                       boolean windowVisible = process(window);
-                       if (!windowVisible) {
-//                             windows.remove();
-                               continue windows;
-                       }
-                       atLeastOneTopLevelWindowVisible = true;
-                       // trim bars
-                       if (window instanceof MTrimmedWindow) {
-                               Iterator<MTrimBar> trimBars = ((MTrimmedWindow) window).getTrimBars().iterator();
-                               while (trimBars.hasNext()) {
-                                       MTrimBar trimBar = trimBars.next();
-                                       if (!process(trimBar)) {
-                                               trimBars.remove();
-                                       }
-                               }
-                       }
-               }
-
-               if (!atLeastOneTopLevelWindowVisible) {
-                       log.warn("No top-level window is authorized for user " + CurrentUser.getUsername() + ", logging out..");
-                       logout();
-               }
-       }
-
-       protected boolean process(MUIElement element) {
-               for (String tag : element.getTags()) {
-                       if (tag.startsWith(AUTH)) {
-                               String role = tag.substring(AUTH.length(), tag.length());
-                               if (!CurrentUser.isInRole(role)) {
-                                       element.setVisible(false);
-                                       element.setToBeRendered(false);
-                                       return false;
-                               }
-                       }
-               }
-
-               // children
-               if (element instanceof MElementContainer) {
-                       @SuppressWarnings("unchecked")
-                       MElementContainer<? extends MUIElement> container = (MElementContainer<? extends MUIElement>) element;
-                       Iterator<? extends MUIElement> children = container.getChildren().iterator();
-                       while (children.hasNext()) {
-                               MUIElement child = children.next();
-                               boolean visible = process(child);
-                               if (!visible)
-                                       children.remove();
-                       }
-
-                       for (Object child : container.getChildren()) {
-                               if (child instanceof MUIElement) {
-                                       boolean visible = process((MUIElement) child);
-                                       if (!visible)
-                                               container.getChildren().remove(child);
-                               }
-                       }
-               }
-
-               return true;
-       }
-
-       protected void logout() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               try {
-                       CurrentUser.logoutCmsSession(subject);
-               } catch (Exception e) {
-                       throw new CmsException("Cannot log out", e);
-               }
-               HttpServletRequest request = org.argeo.eclipse.ui.specific.UiContext.getHttpRequest();
-               if (request != null)
-                       request.getSession().setMaxInactiveInterval(0);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java
deleted file mode 100644 (file)
index 5bc0d69..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.argeo.cms.e4.addons;
-
-import java.security.AccessController;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
-import javax.annotation.PostConstruct;
-import javax.security.auth.Subject;
-
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.e4.core.services.nls.ILocaleChangeService;
-import org.eclipse.e4.ui.model.application.MApplication;
-import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
-import org.eclipse.e4.ui.workbench.modeling.EModelService;
-import org.eclipse.e4.ui.workbench.modeling.ElementMatcher;
-import org.eclipse.swt.SWT;
-
-/** Integrate workbench with the locale provided at log in. */
-public class LocaleAddon {
-       private final static String STYLE_OVERRIDE = "styleOverride";
-
-       // Right to left languages
-       private final static String ARABIC = "ar";
-       private final static String HEBREW = "he";
-
-       @PostConstruct
-       public void init(ILocaleChangeService localeChangeService, EModelService modelService, MApplication application) {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               Set<Locale> locales = subject.getPublicCredentials(Locale.class);
-               if (!locales.isEmpty()) {
-                       Locale locale = locales.iterator().next();
-                       localeChangeService.changeApplicationLocale(locale);
-                       UiContext.setLocale(locale);
-
-                       if (locale.getLanguage().equals(ARABIC) || locale.getLanguage().equals(HEBREW)) {
-                               List<MWindow> windows = modelService.findElements(application, MWindow.class, EModelService.ANYWHERE,
-                                               new ElementMatcher(null, null, (String) null));
-                               for (MWindow window : windows) {
-                                       String currentStyle = window.getPersistedState().get(STYLE_OVERRIDE);
-                                       int style = 0;
-                                       if (currentStyle != null) {
-                                               style = Integer.parseInt(currentStyle);
-                                       }
-                                       style = style | SWT.RIGHT_TO_LEFT;
-                                       window.getPersistedState().put(STYLE_OVERRIDE, Integer.toString(style));
-                               }
-                       }
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java
deleted file mode 100644 (file)
index 6367b42..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Eclipse 4 addons to integrate with Argeo CMS. */
-package org.argeo.cms.e4.addons;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java
deleted file mode 100644 (file)
index 579d35d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.argeo.cms.e4.files;
-
-import java.net.URI;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.eclipse.ui.fs.AdvancedFsBrowser;
-import org.argeo.eclipse.ui.fs.SimpleFsBrowser;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-/** Browse the node file system. */
-public class NodeFsBrowserView {
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID +
-       // ".nodeFsBrowserView";
-
-       @Inject
-       FileSystemProvider nodeFileSystemProvider;
-
-       @PostConstruct
-       public void createPartControl(Composite parent) {
-               try {
-                       //URI uri = new URI("node://root:demo@localhost:7070/");
-                       URI uri = new URI("node:///");
-                       FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri);
-                       if (fileSystem == null)
-                               fileSystem = nodeFileSystemProvider.newFileSystem(uri, null);
-                       Path nodePath = fileSystem.getPath("/");
-
-                       Path localPath = Paths.get(System.getProperty("user.home"));
-
-                       SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS);
-                       browser.setInput(nodePath, localPath);
-//                     AdvancedFsBrowser browser = new AdvancedFsBrowser();
-//                     browser.createUi(parent, localPath);
-               } catch (Exception e) {
-                       throw new CmsException("Cannot open file system browser", e);
-               }
-       }
-
-       public void setFocus() {
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java
deleted file mode 100644 (file)
index b481dd4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Files browser perspective. */
-package org.argeo.cms.e4.files;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java
deleted file mode 100644 (file)
index 416df7d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import java.util.Locale;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.core.services.nls.ILocaleChangeService;
-
-public class ChangeLanguage {
-       @Execute
-       public void execute(ILocaleChangeService localeChangeService) {
-               localeChangeService.changeApplicationLocale(Locale.FRENCH);
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java
deleted file mode 100644 (file)
index 7ef8c59..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import static org.argeo.cms.CmsMsg.changePassword;
-import static org.argeo.cms.CmsMsg.currentPassword;
-import static org.argeo.cms.CmsMsg.newPassword;
-import static org.argeo.cms.CmsMsg.passwordChanged;
-import static org.argeo.cms.CmsMsg.repeatNewPassword;
-
-import java.util.Arrays;
-
-import javax.inject.Inject;
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.security.CryptoKeyring;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.dialogs.CmsMessageDialog;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.util.transaction.WorkTransaction;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.core.di.annotations.Optional;
-import org.eclipse.jface.dialogs.Dialog;
-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.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/** Change the password of the logged-in user. */
-public class ChangePassword {
-       @Inject
-       private UserAdmin userAdmin;
-       @Inject
-       private WorkTransaction userTransaction;
-       @Inject
-       @Optional
-       private CryptoKeyring keyring = null;
-
-       @Execute
-       public void execute() {
-               ChangePasswordDialog dialog = new ChangePasswordDialog(Display.getCurrent().getActiveShell(), userAdmin);
-               if (dialog.open() == Dialog.OK) {
-                       new CmsMessageDialog(Display.getCurrent().getActiveShell(), passwordChanged.lead(),
-                                       CmsMessageDialog.INFORMATION).open();
-               }
-       }
-
-       protected void changePassword(char[] oldPassword, char[] newPassword) {
-               String name = CurrentUser.getUsername();
-               LdapName dn;
-               try {
-                       dn = new LdapName(name);
-               } catch (InvalidNameException e) {
-                       throw new CmsException("Invalid user dn " + name, e);
-               }
-               User user = (User) userAdmin.getRole(dn.toString());
-               if (!user.hasCredential(null, oldPassword))
-                       throw new CmsException("Invalid password");
-               if (Arrays.equals(newPassword, new char[0]))
-                       throw new CmsException("New password empty");
-               try {
-                       userTransaction.begin();
-                       user.getCredentials().put(null, newPassword);
-                       if (keyring != null) {
-                               keyring.changePassword(oldPassword, newPassword);
-                               // TODO change secret keys in the CMS session
-                       }
-                       userTransaction.commit();
-               } catch (Exception e) {
-                       try {
-                               userTransaction.rollback();
-                       } catch (Exception e1) {
-                               e1.printStackTrace();
-                       }
-                       if (e instanceof RuntimeException)
-                               throw (RuntimeException) e;
-                       else
-                               throw new CmsException("Cannot change password", e);
-               }
-       }
-
-       class ChangePasswordDialog extends CmsMessageDialog {
-               private Text oldPassword, newPassword1, newPassword2;
-
-               public ChangePasswordDialog(Shell parentShell, UserAdmin securityService) {
-                       super(parentShell, changePassword.lead(), CONFIRM);
-               }
-
-//             protected Point getInitialSize() {
-//                     return new Point(400, 450);
-//             }
-
-               protected Control createDialogArea(Composite parent) {
-                       Composite dialogarea = (Composite) super.createDialogArea(parent);
-                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       Composite composite = new Composite(dialogarea, SWT.NONE);
-                       composite.setLayout(new GridLayout(2, false));
-                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-                       oldPassword = createLP(composite, currentPassword.lead());
-                       newPassword1 = createLP(composite, newPassword.lead());
-                       newPassword2 = createLP(composite, repeatNewPassword.lead());
-
-//                     parent.pack();
-                       oldPassword.setFocus();
-                       return composite;
-               }
-
-               @Override
-               protected void okPressed() {
-                       try {
-                               if (!newPassword1.getText().equals(newPassword2.getText()))
-                                       throw new CmsException("New passwords are different");
-                               changePassword(oldPassword.getTextChars(), newPassword1.getTextChars());
-                               closeShell(OK);
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot change password", e);
-                       }
-               }
-
-               /** Creates label and password. */
-               protected Text createLP(Composite parent, String label) {
-                       new Label(parent, SWT.NONE).setText(label);
-                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER);
-                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-                       return text;
-               }
-
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java
deleted file mode 100644 (file)
index d11c041..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-
-public class CloseAllParts {
-
-       @Execute
-       void execute(EPartService partService) {
-               for (MPart part : partService.getParts()) {
-                       if (part.isCloseable()) {
-                               if (part.isDirty()) {
-                                       if (partService.savePart(part, true)) {
-                                               partService.hidePart(part, true);
-                                       }
-                               } else {
-                                       partService.hidePart(part, true);
-                               }
-                       }
-               }
-       }
-
-       @CanExecute
-       boolean canExecute(EPartService partService) {
-               boolean atLeastOnePart = false;
-               for (MPart part : partService.getParts()) {
-                       if (part.isVisible() && part.isCloseable()) {
-                               atLeastOnePart = true;
-                               break;
-                       }
-               }
-               return atLeastOnePart;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java
deleted file mode 100644 (file)
index c2ae4bf..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import java.security.AccessController;
-
-import javax.security.auth.Subject;
-
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.workbench.IWorkbench;
-
-public class CloseWorkbench {
-       @Execute
-       public void execute(IWorkbench workbench) {
-               logout();
-               workbench.close();
-       }
-
-       protected void logout() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               try {
-                       CurrentUser.logoutCmsSession(subject);
-               } catch (Exception e) {
-                       throw new CmsException("Cannot log out", e);
-               }
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java
deleted file mode 100644 (file)
index 358494c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-
-public class DoNothing {
-       @Execute
-       public void execute() {
-
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java
deleted file mode 100644 (file)
index ac825bb..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-
-package org.argeo.cms.e4.handlers;
-
-import java.util.Date;
-import java.util.List;
-
-import org.eclipse.e4.ui.di.AboutToHide;
-import org.eclipse.e4.ui.di.AboutToShow;
-import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
-import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement;
-import org.eclipse.e4.ui.workbench.modeling.EModelService;
-
-public class LanguageMenuContribution {
-       @AboutToShow
-       public void aboutToShow(List<MMenuElement> items, EModelService modelService) {
-               MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class);
-               dynamicItem.setLabel("Dynamic Menu Item (" + new Date() + ")");
-               //dynamicItem.setContributorURI("platform:/plugin/org.argeo.cms.e4");
-               //dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/" + ChangeLanguage.class.getName());
-               dynamicItem.setEnabled(true);
-               dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangeLanguage");
-               items.add(dynamicItem);
-       }
-
-       @AboutToHide
-       public void aboutToHide() {
-               
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java
deleted file mode 100644 (file)
index ac544b1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.MApplication;
-import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
-import org.eclipse.e4.ui.workbench.modeling.EModelService;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-
-public class OpenPerspective {
-       @Inject
-       MApplication application;
-       @Inject
-       EPartService partService;
-       @Inject
-       EModelService modelService;
-
-       @Execute
-       public void execute(@Named("perspectiveId") String perspectiveId) {
-               List<MPerspective> perspectives = modelService.findElements(application, perspectiveId, MPerspective.class,
-                               null);
-               if (perspectives.size() == 0)
-                       return;
-               MPerspective perspective = perspectives.get(0);
-               partService.switchPerspective(perspective);
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java
deleted file mode 100644 (file)
index 3b60abd..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-
-public class SaveAllParts {
-
-       @Execute
-       void execute(EPartService partService) {
-               partService.saveAll(false);
-       }
-
-       @CanExecute
-       boolean canExecute(EPartService partService) {
-               return partService.getDirtyParts().size() > 0;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java
deleted file mode 100644 (file)
index 73486f3..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.argeo.cms.e4.handlers;
-
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-
-public class SavePart {
-       @Execute
-       void execute(EPartService partService, MPart part) {
-               partService.savePart(part, false);
-       }
-
-       @CanExecute
-       boolean canExecute(MPart part) {
-               return part.isDirty();
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java
deleted file mode 100644 (file)
index a44ca90..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic Eclipse 4 handlers. */
-package org.argeo.cms.e4.handlers;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java
deleted file mode 100644 (file)
index e10738e..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import org.argeo.jcr.JcrMonitor;
-import org.eclipse.core.runtime.IProgressMonitor;
-
-/**
- * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to
- * framework agnostic Argeo routines.
- */
-public class EclipseJcrMonitor implements JcrMonitor {
-       private final IProgressMonitor progressMonitor;
-
-       public EclipseJcrMonitor(IProgressMonitor progressMonitor) {
-               this.progressMonitor = progressMonitor;
-       }
-
-       public void beginTask(String name, int totalWork) {
-               progressMonitor.beginTask(name, totalWork);
-       }
-
-       public void done() {
-               progressMonitor.done();
-       }
-
-       public boolean isCanceled() {
-               return progressMonitor.isCanceled();
-       }
-
-       public void setCanceled(boolean value) {
-               progressMonitor.setCanceled(value);
-       }
-
-       public void setTaskName(String name) {
-               progressMonitor.setTaskName(name);
-       }
-
-       public void subTask(String name) {
-               progressMonitor.subTask(name);
-       }
-
-       public void worked(int work) {
-               progressMonitor.worked(work);
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java
deleted file mode 100644 (file)
index e17f17b..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.ui.jcr.PropertyLabelProvider;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.eclipse.jface.layout.TreeColumnLayout;
-import org.eclipse.jface.viewers.ColumnWeightData;
-import org.eclipse.jface.viewers.IBaseLabelProvider;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
-
-/**
- * Generic editor property page. Lists all properties of current node as a
- * complex tree. TODO: enable editing
- */
-public class GenericPropertyPage {
-
-       // Main business Objects
-       private Node currentNode;
-
-       public GenericPropertyPage(Node currentNode) {
-               this.currentNode = currentNode;
-       }
-
-       protected void createFormContent(Composite parent) {
-               Composite innerBox = new Composite(parent, SWT.NONE);
-               // Composite innerBox = new Composite(body, SWT.NO_FOCUS);
-               FillLayout layout = new FillLayout();
-               layout.marginHeight = 5;
-               layout.marginWidth = 5;
-               innerBox.setLayout(layout);
-               createComplexTree(innerBox);
-               // TODO TreeColumnLayout triggers a scroll issue with the form:
-               // The inside body is always to big and a scroll bar is shown
-               // Composite tableCmp = new Composite(body, SWT.NO_FOCUS);
-               // createComplexTree(tableCmp);
-       }
-
-       private TreeViewer createComplexTree(Composite parent) {
-               int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION;
-               Tree tree = new Tree(parent, style);
-               TreeColumnLayout tableColumnLayout = new TreeColumnLayout();
-
-               createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30);
-               createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60);
-               createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10);
-               createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0);
-               // Do not apply the treeColumnLayout it does not work yet
-               // parent.setLayout(tableColumnLayout);
-
-               tree.setLinesVisible(true);
-               tree.setHeaderVisible(true);
-
-               TreeViewer treeViewer = new TreeViewer(tree);
-               treeViewer.setContentProvider(new TreeContentProvider());
-               treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider());
-               treeViewer.setInput(currentNode);
-               treeViewer.expandAll();
-               return treeViewer;
-       }
-
-       private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style,
-                       int width, int weight) {
-               TreeColumn column = new TreeColumn(parent, style);
-               column.setText(name);
-               column.setWidth(width);
-               column.setMoveable(true);
-               column.setResizable(true);
-               tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true));
-               return column;
-       }
-
-       private class TreeContentProvider implements ITreeContentProvider {
-               private static final long serialVersionUID = -6162736530019406214L;
-
-               public Object[] getElements(Object parent) {
-                       Object[] props = null;
-                       try {
-
-                               if (parent instanceof Node) {
-                                       Node node = (Node) parent;
-                                       PropertyIterator pi;
-                                       pi = node.getProperties();
-                                       List<Property> propList = new ArrayList<Property>();
-                                       while (pi.hasNext()) {
-                                               propList.add(pi.nextProperty());
-                                       }
-                                       props = propList.toArray();
-                               }
-                       } catch (RepositoryException e) {
-                               throw new EclipseUiException("Unexpected exception while listing node properties", e);
-                       }
-                       return props;
-               }
-
-               public Object getParent(Object child) {
-                       return null;
-               }
-
-               public Object[] getChildren(Object parent) {
-                       if (parent instanceof Property) {
-                               Property prop = (Property) parent;
-                               try {
-                                       if (prop.isMultiple())
-                                               return prop.getValues();
-                               } catch (RepositoryException e) {
-                                       throw new EclipseUiException("Cannot get multi-prop values on " + prop, e);
-                               }
-                       }
-                       return null;
-               }
-
-               public boolean hasChildren(Object parent) {
-                       try {
-                               return (parent instanceof Property && ((Property) parent).isMultiple());
-                       } catch (RepositoryException e) {
-                               throw new EclipseUiException("Cannot check if property is multiple for " + parent, e);
-                       }
-               }
-
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-
-               public void dispose() {
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java
deleted file mode 100644 (file)
index 0b77c07..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.jcr.Property;
-import javax.jcr.PropertyType;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventListener;
-import javax.jcr.observation.ObservationManager;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.security.CryptoKeyring;
-import org.argeo.cms.security.Keyring;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.jcr.JcrBrowserUtils;
-import org.argeo.cms.ui.jcr.NodeContentProvider;
-import org.argeo.cms.ui.jcr.NodeLabelProvider;
-import org.argeo.cms.ui.jcr.OsgiRepositoryRegister;
-import org.argeo.cms.ui.jcr.PropertiesContentProvider;
-import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
-import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.e4.core.contexts.IEclipseContext;
-import org.eclipse.e4.core.di.annotations.Optional;
-import org.eclipse.e4.ui.services.EMenuService;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.IBaseLabelProvider;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-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.TreeViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-
-/**
- * Basic View to display a sash form to browse a JCR compliant multiple
- * repository environment
- */
-public class JcrBrowserView {
-       final static String ID = "org.argeo.cms.e4.jcrbrowser";
-       final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer";
-
-       private boolean sortChildNodes = true;
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       @Optional
-       private Keyring keyring;
-       @Inject
-       private RepositoryFactory repositoryFactory;
-       @Inject
-       private Repository nodeRepository;
-
-       // Current user session on the home repository default workspace
-       private Session userSession;
-
-       private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister();
-
-       // This page widgets
-       private TreeViewer nodesViewer;
-       private NodeContentProvider nodeContentProvider;
-       private TableViewer propertiesViewer;
-       private EventListener resultsObserver;
-
-       @PostConstruct
-       public void createPartControl(Composite parent, IEclipseContext context, EPartService partService,
-                       ESelectionService selectionService, EMenuService menuService) {
-               repositoryRegister.init();
-
-               parent.setLayout(new FillLayout());
-               SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
-               // sashForm.setSashWidth(4);
-               // sashForm.setLayout(new FillLayout());
-
-               // Create the tree on top of the view
-               Composite top = new Composite(sashForm, SWT.NONE);
-               // GridLayout gl = new GridLayout(1, false);
-               top.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               try {
-                       this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE);
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot open user session", e);
-               }
-
-               nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory,
-                               sortChildNodes);
-
-               // nodes viewer
-               nodesViewer = createNodeViewer(top, nodeContentProvider);
-
-               // context menu : it is completely defined in the plugin.xml file.
-               // MenuManager menuManager = new MenuManager();
-               // Menu menu = menuManager.createContextMenu(nodesViewer.getTree());
-
-               // nodesViewer.getTree().setMenu(menu);
-
-               nodesViewer.setInput("");
-
-               // Create the property viewer on the bottom
-               Composite bottom = new Composite(sashForm, SWT.NONE);
-               bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               propertiesViewer = createPropertiesViewer(bottom);
-
-               sashForm.setWeights(getWeights());
-               nodesViewer.setComparer(new NodeViewerComparer());
-               nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-                       public void selectionChanged(SelectionChangedEvent event) {
-                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
-                               selectionService.setSelection(selection.toList());
-                       }
-               });
-               nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService));
-               menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID);
-               // getSite().registerContextMenu(menuManager, nodesViewer);
-               // getSite().setSelectionProvider(nodesViewer);
-       }
-
-       @PreDestroy
-       public void dispose() {
-               JcrUtils.logoutQuietly(userSession);
-               repositoryRegister.destroy();
-       }
-
-       public void refresh(Object obj) {
-               // Enable full refresh from a command when no element of the tree is
-               // selected
-               if (obj == null) {
-                       Object[] elements = nodeContentProvider.getElements(null);
-                       for (Object el : elements) {
-                               if (el instanceof TreeParent)
-                                       JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el);
-                               getNodeViewer().refresh(el);
-                       }
-               } else
-                       getNodeViewer().refresh(obj);
-       }
-
-       /**
-        * To be overridden to adapt size of form and result frames.
-        */
-       protected int[] getWeights() {
-               return new int[] { 70, 30 };
-       }
-
-       protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) {
-
-               final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
-
-               tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               tmpNodeViewer.setContentProvider(nodeContentProvider);
-               tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider());
-               tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-                       public void selectionChanged(SelectionChangedEvent event) {
-                               if (!event.getSelection().isEmpty()) {
-                                       IStructuredSelection sel = (IStructuredSelection) event.getSelection();
-                                       Object firstItem = sel.getFirstElement();
-                                       if (firstItem instanceof SingleJcrNodeElem)
-                                               propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode());
-                               } else {
-                                       propertiesViewer.setInput("");
-                               }
-                       }
-               });
-
-               resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay());
-               if (keyring != null)
-                       try {
-                               ObservationManager observationManager = userSession.getWorkspace().getObservationManager();
-                               observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/",
-                                               true, null, null, false);
-                       } catch (RepositoryException e) {
-                               throw new EclipseUiException("Cannot register listeners", e);
-                       }
-
-               // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer));
-               return tmpNodeViewer;
-       }
-
-       protected TableViewer createPropertiesViewer(Composite parent) {
-               propertiesViewer = new TableViewer(parent, SWT.NONE);
-               propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               propertiesViewer.getTable().setHeaderVisible(true);
-               propertiesViewer.setContentProvider(new PropertiesContentProvider());
-               TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE);
-               col.getColumn().setText("Name");
-               col.getColumn().setWidth(200);
-               col.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -6684361063107478595L;
-
-                       public String getText(Object element) {
-                               try {
-                                       return ((Property) element).getName();
-                               } catch (RepositoryException e) {
-                                       throw new EclipseUiException("Unexpected exception in label provider", e);
-                               }
-                       }
-               });
-               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
-               col.getColumn().setText("Value");
-               col.getColumn().setWidth(400);
-               col.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -8201994187693336657L;
-
-                       public String getText(Object element) {
-                               try {
-                                       Property property = (Property) element;
-                                       if (property.getType() == PropertyType.BINARY)
-                                               return "<binary>";
-                                       else if (property.isMultiple()) {
-                                               StringBuffer buf = new StringBuffer("[");
-                                               Value[] values = property.getValues();
-                                               for (int i = 0; i < values.length; i++) {
-                                                       if (i != 0)
-                                                               buf.append(", ");
-                                                       buf.append(values[i].getString());
-                                               }
-                                               buf.append(']');
-                                               return buf.toString();
-                                       } else
-                                               return property.getValue().getString();
-                               } catch (RepositoryException e) {
-                                       throw new EclipseUiException("Unexpected exception in label provider", e);
-                               }
-                       }
-               });
-               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
-               col.getColumn().setText("Type");
-               col.getColumn().setWidth(200);
-               col.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -6009599998150286070L;
-
-                       public String getText(Object element) {
-                               return JcrBrowserUtils.getPropertyTypeAsString((Property) element);
-                       }
-               });
-               propertiesViewer.setInput("");
-               return propertiesViewer;
-       }
-
-       protected TreeViewer getNodeViewer() {
-               return nodesViewer;
-       }
-
-       /**
-        * Resets the tree content provider
-        * 
-        * @param sortChildNodes if true the content provider will use a comparer to
-        *                       sort nodes that might slow down the display
-        */
-       public void setSortChildNodes(boolean sortChildNodes) {
-               this.sortChildNodes = sortChildNodes;
-               ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes);
-               nodesViewer.setInput("");
-       }
-
-       /** Notifies the current view that a node has been added */
-       public void nodeAdded(TreeParent parentNode) {
-               // insure that Ui objects have been correctly created:
-               JcrBrowserUtils.forceRefreshIfNeeded(parentNode);
-               getNodeViewer().refresh(parentNode);
-               getNodeViewer().expandToLevel(parentNode, 1);
-       }
-
-       /** Notifies the current view that a node has been removed */
-       public void nodeRemoved(TreeParent parentNode) {
-               IStructuredSelection newSel = new StructuredSelection(parentNode);
-               getNodeViewer().setSelection(newSel, true);
-               // Force refresh
-               IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection();
-               getNodeViewer().refresh(tmpSel.getFirstElement());
-       }
-
-       class TreeObserver extends AsyncUiEventListener {
-
-               public TreeObserver(Display display) {
-                       super(display);
-               }
-
-               @Override
-               protected Boolean willProcessInUiThread(List<Event> events) throws RepositoryException {
-                       for (Event event : events) {
-                               if (getLog().isTraceEnabled())
-                                       getLog().debug("Received event " + event);
-                               String path = event.getPath();
-                               int index = path.lastIndexOf('/');
-                               String propertyName = path.substring(index + 1);
-                               if (getLog().isTraceEnabled())
-                                       getLog().debug("Concerned property " + propertyName);
-                       }
-                       return false;
-               }
-
-               protected void onEventInUiThread(List<Event> events) throws RepositoryException {
-                       if (getLog().isTraceEnabled())
-                               getLog().trace("Refresh result list");
-                       nodesViewer.refresh();
-               }
-
-       }
-
-       public boolean getSortChildNodes() {
-               return sortChildNodes;
-       }
-
-       public void setFocus() {
-               getNodeViewer().getTree().setFocus();
-       }
-
-       /* DEPENDENCY INJECTION */
-       // public void setRepositoryRegister(RepositoryRegister repositoryRegister) {
-       // this.repositoryRegister = repositoryRegister;
-       // }
-
-       public void setKeyring(CryptoKeyring keyring) {
-               this.keyring = keyring;
-       }
-
-       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
-               this.repositoryFactory = repositoryFactory;
-       }
-
-       public void setNodeRepository(Repository nodeRepository) {
-               this.nodeRepository = nodeRepository;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java
deleted file mode 100644 (file)
index f4ee2e8..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.ui.jcr.JcrDClickListener;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
-import org.eclipse.jface.viewers.TreeViewer;
-
-public class JcrE4DClickListener extends JcrDClickListener {
-       EPartService partService;
-
-       public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) {
-               super(nodeViewer);
-               this.partService = partService;
-       }
-
-       @Override
-       protected void openNode(Node node) {
-               MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID);
-               try {
-                       part.setLabel(node.getName());
-                       part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName());
-                       part.getPersistedState().put("nodePath", node.getPath());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot open " + node, e);
-               }
-
-               // the provided part is be shown
-               partService.showPart(part, PartState.ACTIVATE);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java
deleted file mode 100644 (file)
index ae2b325..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.jcr.Node;
-
-import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Composite;
-
-public class JcrNodeEditor {
-       final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor";
-
-       @PostConstruct
-       public void createUi(Composite parent, MPart part, ESelectionService selectionService) {
-               parent.setLayout(new FillLayout());
-               List<?> selection = (List<?>) selectionService.getSelection();
-               Node node = ((SingleJcrNodeElem) selection.get(0)).getNode();
-               GenericPropertyPage propertyPage = new GenericPropertyPage(node);
-               propertyPage.createFormContent(parent);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java
deleted file mode 100644 (file)
index 17d8d2a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.argeo.cms.e4.jcr;
-
-import javax.annotation.PostConstruct;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-
-public class SimplePart {
-
-       @PostConstruct
-       void init(Composite parent) {
-               parent.setLayout(new GridLayout());
-               Label label = new Label(parent, SWT.NONE);
-               label.setText("Hello e4 World");
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java
deleted file mode 100644 (file)
index 09fa760..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.argeo.cms.e4.jcr.handlers;
-
-import java.util.List;
-
-import javax.inject.Named;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.nodetype.NodeType;
-
-import org.argeo.cms.e4.jcr.JcrBrowserView;
-import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
-import org.argeo.cms.ui.jcr.model.WorkspaceElem;
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.eclipse.ui.dialogs.SingleValue;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-
-/**
- * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and
- * {@link WorkspaceElem} TreeObject types.
- * 
- * This handler assumes that a selection provider is available and picks only
- * first selected item. It is UI's job to enable the command only when the
- * selection contains one and only one element. Thus no parameter is passed
- * through the command.
- */
-public class AddFolderNode {
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               List<?> selection = (List<?>) selectionService.getSelection();
-               JcrBrowserView view = (JcrBrowserView) part.getObject();
-
-               if (selection != null && selection.size() == 1) {
-                       TreeParent treeParentNode = null;
-                       Node jcrParentNode = null;
-                       Object obj = selection.get(0);
-
-                       if (obj instanceof SingleJcrNodeElem) {
-                               treeParentNode = (TreeParent) obj;
-                               jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode();
-                       } else if (obj instanceof WorkspaceElem) {
-                               treeParentNode = (TreeParent) obj;
-                               jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode();
-                       } else
-                               return;
-
-                       String folderName = SingleValue.ask("Folder name", "Enter folder name");
-                       if (folderName != null) {
-                               try {
-                                       jcrParentNode.addNode(folderName, NodeType.NT_FOLDER);
-                                       jcrParentNode.getSession().save();
-                                       view.nodeAdded(treeParentNode);
-                               } catch (RepositoryException e) {
-                                       ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e);
-                               }
-                       }
-               } else {
-                       // ErrorFeedback.show(WorkbenchUiPlugin
-                       // .getMessage("errorUnvalidNtFolderNodeType"));
-                       ErrorFeedback.show("Invalid NT folder node type");
-               }
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java
deleted file mode 100644 (file)
index dc47f6e..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-package org.argeo.cms.e4.jcr.handlers;
-
-import java.net.URI;
-import java.util.Hashtable;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.ArgeoNames;
-import org.argeo.cms.ArgeoTypes;
-import org.argeo.cms.e4.jcr.JcrBrowserView;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.security.Keyring;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.core.di.annotations.Optional;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.jface.dialogs.Dialog;
-import org.eclipse.jface.dialogs.IMessageProvider;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.TitleAreaDialog;
-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.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.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Connect to a remote repository and, if successful publish it as an OSGi
- * service.
- */
-public class AddRemoteRepository {
-
-       @Inject
-       private RepositoryFactory repositoryFactory;
-       @Inject
-       private Repository nodeRepository;
-       @Inject
-       @Optional
-       private Keyring keyring;
-
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) {
-               JcrBrowserView view = (JcrBrowserView) part.getObject();
-               RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell());
-               if (dlg.open() == Dialog.OK) {
-                       view.refresh(null);
-               }
-       }
-
-       // public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
-       // this.repositoryFactory = repositoryFactory;
-       // }
-       //
-       // public void setKeyring(Keyring keyring) {
-       // this.keyring = keyring;
-       // }
-       //
-       // public void setNodeRepository(Repository nodeRepository) {
-       // this.nodeRepository = nodeRepository;
-       // }
-
-       class RemoteRepositoryLoginDialog extends TitleAreaDialog {
-               private static final long serialVersionUID = 2234006887750103399L;
-               private Text name;
-               private Text uri;
-               private Text username;
-               private Text password;
-               private Button saveInKeyring;
-
-               public RemoteRepositoryLoginDialog(Shell parentShell) {
-                       super(parentShell);
-               }
-
-               protected Point getInitialSize() {
-                       return new Point(600, 400);
-               }
-
-               protected Control createDialogArea(Composite parent) {
-                       Composite dialogarea = (Composite) super.createDialogArea(parent);
-                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       Composite composite = new Composite(dialogarea, SWT.NONE);
-                       composite.setLayout(new GridLayout(2, false));
-                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-                       setMessage("Login to remote repository", IMessageProvider.NONE);
-                       name = createLT(composite, "Name", "remoteRepository");
-                       uri = createLT(composite, "URI", "http://localhost:7070/jcr/node");
-                       username = createLT(composite, "User", "");
-                       password = createLP(composite, "Password");
-
-                       saveInKeyring = createLC(composite, "Remember password", false);
-                       parent.pack();
-                       return composite;
-               }
-
-               @Override
-               protected void createButtonsForButtonBar(Composite parent) {
-                       super.createButtonsForButtonBar(parent);
-                       Button test = createButton(parent, 2, "Test", false);
-                       test.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = -1829962269440419560L;
-
-                               public void widgetSelected(SelectionEvent arg0) {
-                                       testConnection();
-                               }
-                       });
-               }
-
-               void testConnection() {
-                       Session session = null;
-                       try {
-                               URI checkedUri = new URI(uri.getText());
-                               String checkedUriStr = checkedUri.toString();
-
-                               Hashtable<String, String> params = new Hashtable<String, String>();
-                               params.put(CmsConstants.LABELED_URI, checkedUriStr);
-                               Repository repository = repositoryFactory.getRepository(params);
-                               if (username.getText().trim().equals("")) {// anonymous
-                                       // FIXME make it more generic
-                                       session = repository.login(CmsConstants.SYS_WORKSPACE);
-                               } else {
-                                       // FIXME use getTextChars() when upgrading to 3.7
-                                       // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412
-                                       char[] pwd = password.getText().toCharArray();
-                                       SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd);
-                                       session = repository.login(sc, "main");
-                                       MessageDialog.openInformation(getParentShell(), "Success",
-                                                       "Connection to '" + uri.getText() + "' successful");
-                               }
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Connection test failed for " + uri.getText(), e);
-                       } finally {
-                               JcrUtils.logoutQuietly(session);
-                       }
-               }
-
-               @Override
-               protected void okPressed() {
-                       Session nodeSession = null;
-                       try {
-                               nodeSession = nodeRepository.login();
-                               Node home = CmsJcrUtils.getUserHome(nodeSession);
-
-                               Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE)
-                                               : home.addNode(ArgeoNames.ARGEO_REMOTE);
-                               if (remote.hasNode(name.getText()))
-                                       throw new EclipseUiException("There is already a remote repository named " + name.getText());
-                               Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
-                               remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText());
-                               remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText());
-                               nodeSession.save();
-                               if (saveInKeyring.getSelection()) {
-                                       String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD;
-                                       keyring.set(pwdPath, password.getText().toCharArray());
-                               }
-                               nodeSession.save();
-                               MessageDialog.openInformation(getParentShell(), "Repository Added",
-                                               "Remote repository '" + username.getText() + "@" + uri.getText() + "' added");
-
-                               super.okPressed();
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot add remote repository", e);
-                       } finally {
-                               JcrUtils.logoutQuietly(nodeSession);
-                       }
-               }
-
-               /** Creates label and text. */
-               protected Text createLT(Composite parent, String label, String initial) {
-                       new Label(parent, SWT.NONE).setText(label);
-                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
-                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       text.setText(initial);
-                       return text;
-               }
-
-               /** Creates label and check. */
-               protected Button createLC(Composite parent, String label, Boolean initial) {
-                       new Label(parent, SWT.NONE).setText(label);
-                       Button check = new Button(parent, SWT.CHECK);
-                       check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       check.setSelection(initial);
-                       return check;
-               }
-
-               protected Text createLP(Composite parent, String label) {
-                       new Label(parent, SWT.NONE).setText(label);
-                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
-                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       return text;
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java
deleted file mode 100644 (file)
index b8de06b..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.argeo.cms.e4.jcr.handlers;
-
-import java.util.List;
-
-import javax.inject.Named;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.e4.jcr.JcrBrowserView;
-import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
-import org.argeo.cms.ui.jcr.model.WorkspaceElem;
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-
-/**
- * Delete the selected nodes: both in the JCR repository and in the UI view.
- * Warning no check is done, except implementation dependent native checks,
- * handle with care.
- * 
- * This handler is still 'hard linked' to a GenericJcrBrowser view to enable
- * correct tree refresh when a node is added. This must be corrected in future
- * versions.
- */
-public class DeleteNodes {
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               List<?> selection = (List<?>) selectionService.getSelection();
-               if (selection == null)
-                       return;
-
-               JcrBrowserView view = (JcrBrowserView) part.getObject();
-
-               // confirmation
-               StringBuffer buf = new StringBuffer("");
-               for (Object o : selection) {
-                       SingleJcrNodeElem sjn = (SingleJcrNodeElem) o;
-                       buf.append(sjn.getName()).append(' ');
-               }
-               Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion",
-                               "Do you want to delete " + buf + "?");
-
-               // operation
-               if (doRemove) {
-                       SingleJcrNodeElem ancestor = null;
-                       WorkspaceElem rootAncestor = null;
-                       try {
-                               for (Object obj : selection) {
-                                       if (obj instanceof SingleJcrNodeElem) {
-                                               // Cache objects
-                                               SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj;
-                                               TreeParent tp = (TreeParent) sjn.getParent();
-                                               Node node = sjn.getNode();
-
-                                               // Jcr Remove
-                                               node.remove();
-                                               node.getSession().save();
-                                               // UI remove
-                                               tp.removeChild(sjn);
-
-                                               // Check if the parent is the root node
-                                               if (tp instanceof WorkspaceElem)
-                                                       rootAncestor = (WorkspaceElem) tp;
-                                               else
-                                                       ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp);
-                                       }
-                               }
-                               if (rootAncestor != null)
-                                       view.nodeRemoved(rootAncestor);
-                               else if (ancestor != null)
-                                       view.nodeRemoved(ancestor);
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot delete selected node ", e);
-                       }
-               }
-       }
-
-       private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) {
-               try {
-                       if (A == null)
-                               return B == null ? null : B;
-                       // Todo enhanced this method
-                       else
-                               return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B;
-               } catch (RepositoryException re) {
-                       throw new EclipseUiException("Cannot find ancestor", re);
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java
deleted file mode 100644 (file)
index 036e70a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.argeo.cms.e4.jcr.handlers;
-
-import java.util.List;
-
-import javax.inject.Named;
-
-import org.argeo.cms.e4.jcr.JcrBrowserView;
-import org.argeo.cms.ui.jcr.JcrBrowserUtils;
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-
-/**
- * Force the selected objects of the active view to be refreshed doing the
- * following:
- * <ol>
- * <li>The model objects are recomputed</li>
- * <li>the view is refreshed</li>
- * </ol>
- */
-public class Refresh {
-
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService,
-                       ESelectionService selectionService) {
-
-               JcrBrowserView view = (JcrBrowserView) part.getObject();
-               List<?> selection = (List<?>) selectionService.getSelection();
-
-               if (selection != null && !selection.isEmpty()) {
-                       for (Object obj : selection)
-                               if (obj instanceof TreeParent) {
-                                       TreeParent tp = (TreeParent) obj;
-                                       JcrBrowserUtils.forceRefreshIfNeeded(tp);
-                                       view.refresh(obj);
-                               }
-               } else if (view instanceof JcrBrowserView)
-                       view.refresh(null); // force full refresh
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java
deleted file mode 100644 (file)
index 97674ab..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.argeo.cms.e4.jcr.handlers;
-
-import java.util.List;
-
-import javax.inject.Named;
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.cms.e4.jcr.JcrBrowserView;
-import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.dialogs.SingleValue;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-
-/**
- * Canonically call JCR Session#move(String, String) on the first element
- * returned by HandlerUtil#getActiveWorkbenchWindow()
- * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}.
- * The user must then fill a new name in and confirm
- */
-public class RenameNode {
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService,
-                       ESelectionService selectionService) {
-               List<?> selection = (List<?>) selectionService.getSelection();
-               if (selection == null || selection.size() != 1)
-                       return;
-               JcrBrowserView view = (JcrBrowserView) part.getObject();
-
-               Object element = selection.get(0);
-               if (element instanceof SingleJcrNodeElem) {
-                       SingleJcrNodeElem sjn = (SingleJcrNodeElem) element;
-                       Node node = sjn.getNode();
-                       Session session = null;
-                       String newName = null;
-                       String oldPath = null;
-                       try {
-                               newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]");
-                               // TODO sanity check and user feedback
-                               newName = JcrUtils.replaceInvalidChars(newName);
-                               oldPath = node.getPath();
-                               session = node.getSession();
-                               session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName);
-                               session.save();
-
-                               // Manually refresh the browser view. Must be enhanced
-                               view.refresh(sjn);
-                       } catch (RepositoryException e) {
-                               throw new EclipseUiException("Unable to rename " + node + " to " + newName, e);
-                       }
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java
deleted file mode 100644 (file)
index 4e075e2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** JCR browser handlers. */
-package org.argeo.cms.e4.jcr.handlers;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java
deleted file mode 100644 (file)
index 3e92fb0..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** JCR browser perspective. */
-package org.argeo.cms.e4.jcr;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java
deleted file mode 100644 (file)
index 4fd1d68..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import java.util.Collection;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-abstract class AbstractOsgiComposite extends Composite {
-       private static final long serialVersionUID = -4097415973477517137L;
-       protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-       protected final CmsLog log = CmsLog.getLog(getClass());
-
-       public AbstractOsgiComposite(Composite parent, int style) {
-               super(parent, style);
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               setLayout(CmsSwtUtils.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/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java
deleted file mode 100644 (file)
index a536da0..0000000
+++ /dev/null
@@ -1,576 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import static org.eclipse.swt.SWT.RIGHT;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.LinkedHashMap;
-
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-
-import org.argeo.api.cms.ux.Cms2DSize;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.CmsLink;
-import org.argeo.cms.ui.widgets.EditableImage;
-import org.argeo.cms.ui.widgets.Img;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ILazyContentProvider;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.Text;
-
-public class Browse implements CmsUiProvider {
-
-       // Some local constants to experiment. should be cleaned
-       private final static String BROWSE_PREFIX = "browse#";
-       private final static int THUMBNAIL_WIDTH = 400;
-       private final static int COLUMN_WIDTH = 160;
-       private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm");
-
-       // keep a cache of the opened nodes
-       // Key is the path
-       private LinkedHashMap<String, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<String, Browse.FilterEntitiesVirtualTable>();
-       private Composite nodeDisplayParent;
-       private Composite colViewer;
-       private ScrolledComposite scrolledCmp;
-       private Text parentPathTxt;
-       private Text filterTxt;
-       private Node currEdited;
-
-       private String initialPath;
-
-       @Override
-       public Control createUi(Composite parent, Node context) throws RepositoryException {
-               if (context == null)
-                       // return null;
-                       throw new CmsException("Context cannot be null");
-               GridLayout layout = CmsSwtUtils.noSpaceGridLayout();
-               layout.numColumns = 2;
-               parent.setLayout(layout);
-
-               // Left
-               Composite leftCmp = new Composite(parent, SWT.NO_FOCUS);
-               leftCmp.setLayoutData(CmsSwtUtils.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 = CmsSwtUtils.noSpaceGridLayout();
-               parent.setLayout(layout);
-               Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
-               filterCmp.setLayoutData(CmsSwtUtils.fillWidth());
-
-               // top filter
-               addFilterPanel(filterCmp);
-
-               // scrolled composite
-               scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS);
-               scrolledCmp.setLayoutData(CmsSwtUtils.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(CmsSwtUtils.noSpaceGridLayout());
-               createBrowserColumn(parent, context);
-               return null;
-       }
-
-       private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException {
-               // TODO style is not correctly managed.
-               FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context);
-               // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style());
-               table.filterList("*");
-               table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
-               browserCols.put(context.getPath(), table);
-               return null;
-       }
-
-       public void addFilterPanel(Composite parent) {
-
-               parent.setLayout(CmsSwtUtils.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(CmsSwtUtils.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;
-                       CmsSwtUtils.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(CmsSwtUtils.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 Cms2DSize imageWidth = new Cms2DSize(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);
-               CmsSwtUtils.markup(contextL);
-               contextL.setText("<b>" + context.getName() + "</b>");
-               new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName());
-
-               // Children
-               for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
-                       Node child = nIt.nextNode();
-                       new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context);
-                       new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName());
-               }
-
-               // Properties
-               for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
-                       Property property = pIt.nextProperty();
-                       Label label = new Label(parent, SWT.NONE);
-                       label.setText(property.getName());
-                       label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property));
-                       new Label(parent, SWT.NONE).setText(getPropAsString(property));
-               }
-
-               return null;
-       }
-
-       private boolean isImg(Node node) throws RepositoryException {
-               // TODO support images
-               return false;
-//             return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE);
-       }
-
-       private String getPropAsString(Property property) throws RepositoryException {
-               String result = "";
-               if (property.isMultiple()) {
-                       result = getMultiAsString(property, ", ");
-               } else {
-                       Value value = property.getValue();
-                       if (value.getType() == PropertyType.BINARY)
-                               result = "<binary>";
-                       else if (value.getType() == PropertyType.DATE)
-                               result = timeFormatter.format(value.getDate().getTime());
-                       else
-                               result = value.getString();
-               }
-               return result;
-       }
-
-       private String getMultiAsString(Property property, String separator) throws RepositoryException {
-               if (separator == null)
-                       separator = "; ";
-               Value[] values = property.getValues();
-               StringBuilder builder = new StringBuilder();
-               for (Value val : values) {
-                       String currStr = val.getString();
-                       if (!"".equals(currStr.trim()))
-                               builder.append(currStr).append(separator);
-               }
-               if (builder.lastIndexOf(separator) >= 0)
-                       return builder.substring(0, builder.length() - separator.length());
-               else
-                       return builder.toString();
-       }
-
-       /** Almost canonical implementation of a table that display entities */
-       private class FilterEntitiesVirtualTable extends Composite {
-               private static final long serialVersionUID = 8798147431706283824L;
-
-               // Context
-               private Node context;
-
-               // UI Objects
-               private TableViewer entityViewer;
-
-               // enable management of multiple columns
-               Node getNode() {
-                       return context;
-               }
-
-               @Override
-               public boolean setFocus() {
-                       if (entityViewer.getTable().isDisposed())
-                               return false;
-                       if (entityViewer.getSelection().isEmpty()) {
-                               Object first = entityViewer.getElementAt(0);
-                               if (first != null) {
-                                       entityViewer.setSelection(new StructuredSelection(first), true);
-                               }
-                       }
-                       return entityViewer.getTable().setFocus();
-               }
-
-               void filterList(String filter) {
-                       try {
-                               NodeIterator nit = context.getNodes(filter);
-                               refreshFilteredList(nit);
-                       } catch (RepositoryException e) {
-                               throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e);
-                       }
-
-               }
-
-               public FilterEntitiesVirtualTable(Composite parent, int style, Node context) {
-                       super(parent, SWT.NO_FOCUS);
-                       this.context = context;
-                       populate();
-               }
-
-               protected void populate() {
-                       Composite parent = this;
-                       GridLayout layout = CmsSwtUtils.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(CmsSwtUtils.noSpaceGridLayout());
-
-                       entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE);
-                       Table table = entityViewer.getTable();
-
-                       table.setLayoutData(CmsSwtUtils.fillAll());
-                       table.setLinesVisible(true);
-                       table.setHeaderVisible(false);
-                       CmsSwtUtils.markup(table);
-
-                       CmsSwtUtils.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/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java
deleted file mode 100644 (file)
index 97f3e67..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-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));
-               CmsSwtUtils.markup(label);
-               label.setText(text.toString());
-       }
-
-       protected boolean isDeployed() {
-               return bc.getServiceReference(UserAdmin.class) != null;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java
deleted file mode 100644 (file)
index ef95bde..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-
-import org.apache.jackrabbit.core.RepositoryContext;
-import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.swt.CmsSwtUtils;
-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,
-                               "(" + CmsConstants.CN + "=*)");
-               StringBuffer text = new StringBuffer();
-               text.append("<span style='font-variant: small-caps;'>Jackrabbit Repositories</span><br/>");
-               for (ServiceReference<RepositoryContext> sr : contexts) {
-                       RepositoryContext repositoryContext = bc.getService(sr);
-                       String alias = sr.getProperty(CmsConstants.CN).toString();
-                       String rootNodeId = repositoryContext.getRootNodeId().toString();
-                       RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig();
-                       Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath();
-                       // TODO check data store
-
-                       text.append("<b>" + alias + "</b><br/>");
-                       text.append("rootNodeId: " + rootNodeId + "<br/>");
-                       try {
-                               FileStore fileStore = Files.getFileStore(repoHomePath);
-                               text.append("partition: " + fileStore.toString() + "<br/>");
-                               text.append(
-                                               percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)<br/>");
-                       } catch (IOException e) {
-                               log.error("Cannot check fileStore for " + repoHomePath, e);
-                       }
-               }
-               Label label = new Label(parent, SWT.NONE);
-               label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
-               CmsSwtUtils.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/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java
deleted file mode 100644 (file)
index e713f53..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.CmsDeployment;
-import org.argeo.api.cms.CmsState;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-
-class DeploymentEntryPoint {
-       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
-
-       protected void createContents(Composite parent) {
-               // FIXME manage authentication if needed
-               // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN))
-               // return;
-
-               // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               if (isDesktop()) {
-                       parent.setLayout(new GridLayout(2, true));
-               } else {
-                       // TODO add scrolling
-                       parent.setLayout(new GridLayout(1, true));
-               }
-
-               initHighLevelSummary(parent);
-
-               Group securityGroup = createHighLevelGroup(parent, "Security");
-               securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-               new SecurityDeploymentUi(securityGroup, SWT.NONE);
-
-               Group dataGroup = createHighLevelGroup(parent, "Data");
-               dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-               new DataDeploymentUi(dataGroup, SWT.NONE);
-
-               Group logGroup = createHighLevelGroup(parent, "Notifications");
-               logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
-               new LogDeploymentUi(logGroup, SWT.NONE);
-
-               Group connectivityGroup = createHighLevelGroup(parent, "Connectivity");
-               new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE);
-               connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
-
-       }
-
-       private void initHighLevelSummary(Composite parent) {
-               Composite composite = new Composite(parent, SWT.NONE);
-               GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
-               if (isDesktop())
-                       gridData.horizontalSpan = 3;
-               composite.setLayoutData(gridData);
-               composite.setLayout(new FillLayout());
-
-               ServiceReference<CmsState> nodeStateRef = bc.getServiceReference(CmsState.class);
-               if (nodeStateRef == null)
-                       throw new IllegalStateException("No CMS state available");
-               CmsState nodeState = bc.getService(nodeStateRef);
-               ServiceReference<CmsContext> nodeDeploymentRef = bc.getServiceReference(CmsContext.class);
-               Label label = new Label(composite, SWT.WRAP);
-               CmsSwtUtils.markup(label);
-               if (nodeDeploymentRef == null) {
-                       label.setText("Not yet deployed on <br>" + nodeState.getHostname() + "</br>, please configure below.");
-               } else {
-                       Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN);
-                       CmsContext 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);
-               CmsSwtUtils.markup(group);
-               return group;
-       }
-
-       private boolean isDesktop() {
-               return true;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java
deleted file mode 100644 (file)
index fa5d3da..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Enumeration;
-import java.util.GregorianCalendar;
-import java.util.TimeZone;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-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));
-               CmsSwtUtils.markup(logDisplay);
-               Enumeration<LogEntry> logEntries = (Enumeration<LogEntry>) logReader.getLog();
-               while (logEntries.hasMoreElements())
-                       logDisplay.append(printEntry(logEntries.nextElement()));
-       }
-
-       private String printEntry(LogEntry entry) {
-               StringBuilder sb = new StringBuilder();
-               GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault());
-               calendar.setTimeInMillis(entry.getTime());
-               sb.append(dateFormat.format(calendar.getTime())).append(' ');
-               sb.append(entry.getMessage());
-               sb.append('\n');
-               return sb.toString();
-       }
-
-       @Override
-       public void logged(LogEntry entry) {
-               if (display.isDisposed())
-                       return;
-               display.asyncExec(() -> {
-                       if (logDisplay.isDisposed())
-                               return;
-                       logDisplay.append(printEntry(entry));
-               });
-               display.wake();
-       }
-
-       // @Override
-       // public void dispose() {
-       // super.dispose();
-       // getService(LogReaderService.class).removeLogListener(this);
-       // }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java
deleted file mode 100644 (file)
index df1be51..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-/** Specific styles used by the various maintenance pages . */
-public interface MaintenanceStyles {
-       // General
-       public final static String PREFIX = "maintenance_";
-
-       // Browser
-       public final static String BROWSER_COLUMN = "browser_column";
-       }
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java
deleted file mode 100644 (file)
index cb38ce8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.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(CmsSwtUtils.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/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java
deleted file mode 100644 (file)
index 3492c54..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.argeo.cms.e4.maintenance;
-
-import java.net.URI;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-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));
-               CmsSwtUtils.markup(label);
-               label.setText(text.toString());
-       }
-
-       protected boolean isDeployed() {
-               return bc.getServiceReference(UserAdmin.class) != null;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java
deleted file mode 100644 (file)
index e4d2ad4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Maintenance perspective. */
-package org.argeo.cms.e4.maintenance;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java
deleted file mode 100644 (file)
index e953683..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-
-/** A tree element representing a {@link Bundle} */
-class BundleNode extends TreeParent {
-       private final Bundle bundle;
-
-       public BundleNode(Bundle bundle) {
-               this(bundle, false);
-       }
-
-       @SuppressWarnings("rawtypes")
-       public BundleNode(Bundle bundle, boolean hasChildren) {
-               super(bundle.getSymbolicName());
-               this.bundle = bundle;
-
-               if (hasChildren) {
-                       // REFERENCES
-                       ServiceReference[] usedServices = bundle.getServicesInUse();
-                       if (usedServices != null) {
-                               for (ServiceReference sr : usedServices) {
-                                       if (sr != null)
-                                               addChild(new ServiceReferenceNode(sr, false));
-                               }
-                       }
-
-                       // SERVICES
-                       ServiceReference[] registeredServices = bundle
-                                       .getRegisteredServices();
-                       if (registeredServices != null) {
-                               for (ServiceReference sr : registeredServices) {
-                                       if (sr != null)
-                                               addChild(new ServiceReferenceNode(sr, true));
-                               }
-                       }
-               }
-
-       }
-
-       Bundle getBundle() {
-               return bundle;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java
deleted file mode 100644 (file)
index c639255..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-//package org.argeo.eclipse.ui.workbench.osgi;
-//public class BundlesView {}
-
-package org.argeo.cms.e4.monitoring;
-
-import javax.annotation.PostConstruct;
-
-import org.argeo.eclipse.ui.ColumnViewerComparator;
-import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-/**
- * Overview of the bundles as a table. Equivalent to Equinox 'ss' console
- * command.
- */
-public class BundlesView {
-       private final static BundleContext bc = FrameworkUtil.getBundle(BundlesView.class).getBundleContext();
-       private TableViewer viewer;
-
-       @PostConstruct
-       public void createPartControl(Composite parent) {
-               viewer = new TableViewer(parent);
-               viewer.setContentProvider(new BundleContentProvider());
-               viewer.getTable().setHeaderVisible(true);
-
-               EclipseUiSpecificUtils.enableToolTipSupport(viewer);
-
-               // ID
-               TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(30);
-               column.getColumn().setText("ID");
-               column.getColumn().setAlignment(SWT.RIGHT);
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -3122136344359358605L;
-
-                       public String getText(Object element) {
-                               return Long.toString(((Bundle) element).getBundleId());
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // State
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(18);
-               column.getColumn().setText("State");
-               column.setLabelProvider(new StateLabelProvider());
-               new ColumnViewerComparator(column);
-
-               // Symbolic name
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(250);
-               column.getColumn().setText("Symbolic Name");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -4280840684440451080L;
-
-                       public String getText(Object element) {
-                               return ((Bundle) element).getSymbolicName();
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // Version
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(250);
-               column.getColumn().setText("Version");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = 6871926308708629989L;
-
-                       public String getText(Object element) {
-                               Bundle bundle = (org.osgi.framework.Bundle) element;
-                               return bundle.getVersion().toString();
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               viewer.setInput(bc);
-
-       }
-
-       @Focus
-       public void setFocus() {
-               if (viewer != null)
-                       viewer.getControl().setFocus();
-       }
-
-       /** Content provider managing the array of bundles */
-       private static class BundleContentProvider implements IStructuredContentProvider {
-               private static final long serialVersionUID = -8533792785725875977L;
-
-               public Object[] getElements(Object inputElement) {
-                       if (inputElement instanceof BundleContext) {
-                               BundleContext bc = (BundleContext) inputElement;
-                               return bc.getBundles();
-                       }
-                       return null;
-               }
-
-               public void dispose() {
-               }
-
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java
deleted file mode 100644 (file)
index 95b1eb2..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-//package org.argeo.eclipse.ui.workbench.osgi;
-//public class BundlesView {}
-
-package org.argeo.cms.e4.monitoring;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-
-import org.argeo.api.cms.CmsSession;
-import org.argeo.cms.auth.RoleNameUtils;
-import org.argeo.eclipse.ui.ColumnViewerComparator;
-import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
-import org.argeo.util.LangUtils;
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-/**
- * Overview of the active CMS sessions.
- */
-public class CmsSessionsView {
-       private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionsView.class).getBundleContext();
-
-       private TableViewer viewer;
-
-       @PostConstruct
-       public void createPartControl(Composite parent) {
-               viewer = new TableViewer(parent);
-               viewer.setContentProvider(new CmsSessionContentProvider());
-               viewer.getTable().setHeaderVisible(true);
-
-               EclipseUiSpecificUtils.enableToolTipSupport(viewer);
-
-               int longColWidth = 150;
-               int smallColWidth = 100;
-
-               // Display name
-               TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(longColWidth);
-               column.getColumn().setText("User");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -5234573509093747505L;
-
-                       public String getText(Object element) {
-                               return ((CmsSession) element).getDisplayName();
-                       }
-
-                       public String getToolTipText(Object element) {
-                               return ((CmsSession) element).getUserDn().toString();
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // Creation time
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(smallColWidth);
-               column.getColumn().setText("Since");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -5234573509093747505L;
-
-                       public String getText(Object element) {
-                               return LangUtils.since(((CmsSession) element).getCreationTime());
-                       }
-
-                       public String getToolTipText(Object element) {
-                               return ((CmsSession) element).getCreationTime().toString();
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // Username
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(smallColWidth);
-               column.getColumn().setText("Username");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -5234573509093747505L;
-
-                       public String getText(Object element) {
-                               String userDn = ((CmsSession) element).getUserDn();
-                               return RoleNameUtils.getLastRdnValue(userDn);
-                       }
-
-                       public String getToolTipText(Object element) {
-                               return ((CmsSession) element).getUserDn().toString();
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // UUID
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(smallColWidth);
-               column.getColumn().setText("UUID");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -5234573509093747505L;
-
-                       public String getText(Object element) {
-                               return ((CmsSession) element).getUuid().toString();
-                       }
-
-                       public String getToolTipText(Object element) {
-                               return getText(element);
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               // Local ID
-               column = new TableViewerColumn(viewer, SWT.NONE);
-               column.getColumn().setWidth(smallColWidth);
-               column.getColumn().setText("Local ID");
-               column.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = -5234573509093747505L;
-
-                       public String getText(Object element) {
-                               return ((CmsSession) element).getLocalId();
-                       }
-
-                       public String getToolTipText(Object element) {
-                               return getText(element);
-                       }
-               });
-               new ColumnViewerComparator(column);
-
-               viewer.setInput(bc);
-
-       }
-
-       @Focus
-       public void setFocus() {
-               if (viewer != null)
-                       viewer.getControl().setFocus();
-       }
-
-       /** Content provider managing the array of bundles */
-       private static class CmsSessionContentProvider implements IStructuredContentProvider {
-               private static final long serialVersionUID = -8533792785725875977L;
-
-               public Object[] getElements(Object inputElement) {
-                       if (inputElement instanceof BundleContext) {
-                               BundleContext bc = (BundleContext) inputElement;
-                               Collection<ServiceReference<CmsSession>> srs;
-                               try {
-                                       srs = bc.getServiceReferences(CmsSession.class, null);
-                               } catch (InvalidSyntaxException e) {
-                                       throw new IllegalArgumentException("Cannot retrieve CMS sessions", e);
-                               }
-                               List<CmsSession> res = new ArrayList<>();
-                               for (ServiceReference<CmsSession> sr : srs) {
-                                       res.add(bc.getService(sr));
-                               }
-                               return res.toArray();
-                       }
-                       return null;
-               }
-
-               public void dispose() {
-               }
-
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java
deleted file mode 100644 (file)
index 6317882..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-/** The OSGi runtime from a module perspective. */
-public class ModulesView {
-       private final static BundleContext bc = FrameworkUtil.getBundle(ModulesView.class).getBundleContext();
-       private TreeViewer viewer;
-
-       @PostConstruct
-       public void createPartControl(Composite parent) {
-               viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-               viewer.setContentProvider(new ModulesContentProvider());
-               viewer.setLabelProvider(new ModulesLabelProvider());
-               viewer.setInput(bc);
-       }
-
-       @Focus
-       public void setFocus() {
-               viewer.getTree().setFocus();
-       }
-
-       private class ModulesContentProvider implements ITreeContentProvider {
-               private static final long serialVersionUID = 3819934804640641721L;
-
-               public Object[] getElements(Object inputElement) {
-                       return getChildren(inputElement);
-               }
-
-               public Object[] getChildren(Object parentElement) {
-                       if (parentElement instanceof BundleContext) {
-                               BundleContext bundleContext = (BundleContext) parentElement;
-                               Bundle[] bundles = bundleContext.getBundles();
-
-                               List<BundleNode> modules = new ArrayList<BundleNode>();
-                               for (Bundle bundle : bundles) {
-                                       if (bundle.getState() == Bundle.ACTIVE)
-                                               modules.add(new BundleNode(bundle, true));
-                               }
-                               return modules.toArray();
-                       } else if (parentElement instanceof TreeParent) {
-                               return ((TreeParent) parentElement).getChildren();
-                       } else {
-                               return null;
-                       }
-               }
-
-               public Object getParent(Object element) {
-                       // TODO Auto-generated method stub
-                       return null;
-               }
-
-               public boolean hasChildren(Object element) {
-                       if (element instanceof TreeParent) {
-                               return ((TreeParent) element).hasChildren();
-                       }
-                       return false;
-               }
-
-               public void dispose() {
-               }
-
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-       }
-
-       private class ModulesLabelProvider extends StateLabelProvider {
-               private static final long serialVersionUID = 5290046145534824722L;
-
-               @Override
-               public String getText(Object element) {
-                       if (element instanceof BundleNode)
-                               return element.toString() + " [" + ((BundleNode) element).getBundle().getBundleId() + "]";
-                       return element.toString();
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java
deleted file mode 100644 (file)
index 5db8bd1..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Dictionary;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-
-import org.argeo.cms.swt.CmsException;
-import org.argeo.util.LangUtils;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.TreeViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-
-public class OsgiConfigurationsView {
-       private final static BundleContext bc = FrameworkUtil.getBundle(OsgiConfigurationsView.class).getBundleContext();
-
-       @PostConstruct
-       public void createPartControl(Composite parent) {
-               ConfigurationAdmin configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
-
-               TreeViewer viewer = new TreeViewer(parent);
-               // viewer.getTree().setHeaderVisible(true);
-
-               TreeViewerColumn tvc = new TreeViewerColumn(viewer, SWT.NONE);
-               tvc.getColumn().setWidth(400);
-               tvc.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = 835407996597566763L;
-
-                       @Override
-                       public String getText(Object element) {
-                               if (element instanceof Configuration) {
-                                       return ((Configuration) element).getPid();
-                               } else if (element instanceof Prop) {
-                                       return ((Prop) element).key;
-                               }
-                               return super.getText(element);
-                       }
-
-                       @Override
-                       public Image getImage(Object element) {
-                               if (element instanceof Configuration)
-                                       return OsgiExplorerImages.CONFIGURATION;
-                               return null;
-                       }
-
-               });
-
-               tvc = new TreeViewerColumn(viewer, SWT.NONE);
-               tvc.getColumn().setWidth(400);
-               tvc.setLabelProvider(new ColumnLabelProvider() {
-                       private static final long serialVersionUID = 6999659261190014687L;
-
-                       @Override
-                       public String getText(Object element) {
-                               if (element instanceof Configuration) {
-                                       // return ((Configuration) element).getFactoryPid();
-                                       return null;
-                               } else if (element instanceof Prop) {
-                                       return ((Prop) element).value.toString();
-                               }
-                               return super.getText(element);
-                       }
-               });
-
-               viewer.setContentProvider(new ConfigurationsContentProvider());
-               viewer.setInput(configurationAdmin);
-       }
-
-       static class ConfigurationsContentProvider implements ITreeContentProvider {
-               private static final long serialVersionUID = -4892768279440981042L;
-               private ConfigurationComparator configurationComparator = new ConfigurationComparator();
-
-               @Override
-               public void dispose() {
-               }
-
-               @Override
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-
-               @Override
-               public Object[] getElements(Object inputElement) {
-                       ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) inputElement;
-                       try {
-                               Configuration[] configurations = configurationAdmin.listConfigurations(null);
-                               Arrays.sort(configurations, configurationComparator);
-                               return configurations;
-                       } catch (IOException | InvalidSyntaxException e) {
-                               throw new CmsException("Cannot list configurations", e);
-                       }
-               }
-
-               @Override
-               public Object[] getChildren(Object parentElement) {
-                       if (parentElement instanceof Configuration) {
-                               List<Prop> res = new ArrayList<>();
-                               Configuration configuration = (Configuration) parentElement;
-                               Dictionary<String, Object> props = configuration.getProperties();
-                               keys: for (String key : LangUtils.keys(props)) {
-                                       if (Constants.SERVICE_PID.equals(key))
-                                               continue keys;
-                                       if (ConfigurationAdmin.SERVICE_FACTORYPID.equals(key))
-                                               continue keys;
-                                       res.add(new Prop(configuration, key, props.get(key)));
-                               }
-                               return res.toArray(new Prop[res.size()]);
-                       }
-                       return null;
-               }
-
-               @Override
-               public Object getParent(Object element) {
-                       if (element instanceof Prop)
-                               return ((Prop) element).configuration;
-                       return null;
-               }
-
-               @Override
-               public boolean hasChildren(Object element) {
-                       if (element instanceof Configuration)
-                               return true;
-                       return false;
-               }
-
-       }
-
-       static class Prop {
-               final Configuration configuration;
-               final String key;
-               final Object value;
-
-               public Prop(Configuration configuration, String key, Object value) {
-                       this.configuration = configuration;
-                       this.key = key;
-                       this.value = value;
-               }
-
-       }
-
-       static class ConfigurationComparator implements Comparator<Configuration> {
-
-               @Override
-               public int compare(Configuration o1, Configuration o2) {
-                       return o1.getPid().compareTo(o2.getPid());
-               }
-
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java
deleted file mode 100644 (file)
index 7217fe6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import org.argeo.cms.ui.theme.CmsImages;
-import org.eclipse.swt.graphics.Image;
-
-/** Shared icons. */
-public class OsgiExplorerImages extends CmsImages {
-       public final static Image INSTALLED = createIcon("installed.gif");
-       public final static Image RESOLVED = createIcon("resolved.gif");
-       public final static Image STARTING = createIcon("starting.gif");
-       public final static Image ACTIVE = createIcon("active.gif");
-       public final static Image SERVICE_PUBLISHED = createIcon("service_published.gif");
-       public final static Image SERVICE_REFERENCED = createIcon("service_referenced.gif");
-       public final static Image CONFIGURATION = createIcon("node.gif");
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java
deleted file mode 100644 (file)
index 1c60811..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-
-/** A tree element representing a {@link ServiceReference} */
-@SuppressWarnings({ "rawtypes" })
-class ServiceReferenceNode extends TreeParent {
-       private final ServiceReference serviceReference;
-       private final boolean published;
-
-       public ServiceReferenceNode(ServiceReference serviceReference,
-                       boolean published) {
-               super(serviceReference.toString());
-               this.serviceReference = serviceReference;
-               this.published = published;
-
-               if (isPublished()) {
-                       Bundle[] usedBundles = serviceReference.getUsingBundles();
-                       if (usedBundles != null) {
-                               for (Bundle b : usedBundles) {
-                                       if (b != null)
-                                               addChild(new BundleNode(b));
-                               }
-                       }
-               } else {
-                       Bundle provider = serviceReference.getBundle();
-                       addChild(new BundleNode(provider));
-               }
-
-               for (String key : serviceReference.getPropertyKeys()) {
-                       addChild(new TreeParent(key + "="
-                                       + serviceReference.getProperty(key)));
-               }
-
-       }
-
-       public ServiceReference getServiceReference() {
-               return serviceReference;
-       }
-
-       public boolean isPublished() {
-               return published;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java
deleted file mode 100644 (file)
index 5cb5b65..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.e4.monitoring;
-
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.swt.graphics.Image;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-
-/** Label provider showing the sate of bundles */
-class StateLabelProvider extends ColumnLabelProvider {
-       private static final long serialVersionUID = -7885583135316000733L;
-
-       @Override
-       public Image getImage(Object element) {
-               int state;
-               if (element instanceof Bundle)
-                       state = ((Bundle) element).getState();
-               else if (element instanceof BundleNode)
-                       state = ((BundleNode) element).getBundle().getState();
-               else if (element instanceof ServiceReferenceNode)
-                       if (((ServiceReferenceNode) element).isPublished())
-                               return OsgiExplorerImages.SERVICE_PUBLISHED;
-                       else
-                               return OsgiExplorerImages.SERVICE_REFERENCED;
-               else
-                       return null;
-
-               switch (state) {
-               case Bundle.UNINSTALLED:
-                       return OsgiExplorerImages.INSTALLED;
-               case Bundle.INSTALLED:
-                       return OsgiExplorerImages.INSTALLED;
-               case Bundle.RESOLVED:
-                       return OsgiExplorerImages.RESOLVED;
-               case Bundle.STARTING:
-                       return OsgiExplorerImages.STARTING;
-               case Bundle.STOPPING:
-                       return OsgiExplorerImages.STARTING;
-               case Bundle.ACTIVE:
-                       return OsgiExplorerImages.ACTIVE;
-               default:
-                       return null;
-               }
-       }
-
-       @Override
-       public String getText(Object element) {
-               return null;
-       }
-
-       @Override
-       public String getToolTipText(Object element) {
-               Bundle bundle = (Bundle) element;
-               Integer state = bundle.getState();
-               switch (state) {
-               case Bundle.UNINSTALLED:
-                       return "UNINSTALLED";
-               case Bundle.INSTALLED:
-                       return "INSTALLED";
-               case Bundle.RESOLVED:
-                       return "RESOLVED";
-               case Bundle.STARTING:
-                       String activationPolicy = bundle.getHeaders()
-                                       .get(Constants.BUNDLE_ACTIVATIONPOLICY).toString();
-
-                       // .get("Bundle-ActivationPolicy").toString();
-                       // FIXME constant triggers the compilation failure
-                       if (activationPolicy != null
-                                       && activationPolicy.equals(Constants.ACTIVATION_LAZY))
-                               // && activationPolicy.equals("lazy"))
-                               // FIXME constant triggers the compilation failure
-                               // && activationPolicy.equals(Constants.ACTIVATION_LAZY))
-                               return "<<LAZY>>";
-                       return "STARTING";
-               case Bundle.STOPPING:
-                       return "STOPPING";
-               case Bundle.ACTIVE:
-                       return "ACTIVE";
-               default:
-                       return null;
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java
deleted file mode 100644 (file)
index 873bf31..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Monitoring perspective. */
-package org.argeo.cms.e4.monitoring;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java
deleted file mode 100644 (file)
index 233119c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Eclipse 4 user interfaces. */
-package org.argeo.cms.e4;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java
deleted file mode 100644 (file)
index f2a73f2..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.cms.e4.parts;
-
-import java.time.ZonedDateTime;
-
-import javax.annotation.PostConstruct;
-
-import org.argeo.api.cms.CmsSession;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** A canonical view of the logged in user. */
-public class EgoDashboard {
-//     private BundleContext bc = FrameworkUtil.getBundle(EgoDashboard.class).getBundleContext();
-
-       @PostConstruct
-       public void createPartControl(Composite p) {
-               p.setLayout(new GridLayout());
-               String username = CurrentUser.getUsername();
-
-               CmsSwtUtils.lbl(p, "<strong>" + CurrentUser.getDisplayName() + "</strong>");
-               CmsSwtUtils.txt(p, username);
-               CmsSwtUtils.lbl(p, "Roles:");
-               roles: for (String role : CurrentUser.roles()) {
-                       if (username.equals(role))
-                               continue roles;
-                       CmsSwtUtils.txt(p, role);
-               }
-
-//             Subject subject = Subject.getSubject(AccessController.getContext());
-//             if (subject != null) {
-               CmsSession cmsSession = CurrentUser.getCmsSession();
-               ZonedDateTime loggedIndSince = cmsSession.getCreationTime();
-               CmsSwtUtils.lbl(p, "Session:");
-               CmsSwtUtils.txt(p, cmsSession.getUuid().toString());
-               CmsSwtUtils.lbl(p, "Logged in since:");
-               CmsSwtUtils.txt(p, loggedIndSince.toString());
-//             }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java
deleted file mode 100644 (file)
index 137f762..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
-import org.argeo.cms.ui.eclipse.forms.IManagedForm;
-import org.argeo.cms.ui.eclipse.forms.ManagedForm;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.e4.ui.di.Persist;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-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.Text;
-import org.osgi.service.useradmin.Authorization;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Editor for a user, might be a user or a group. */
-public abstract class AbstractRoleEditor {
-
-       // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID +
-       // ".userEditor";
-       // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID +
-       // ".groupEditor";
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       protected UserAdminWrapper userAdminWrapper;
-
-       @Inject
-       private MPart mPart;
-
-       // @Inject
-       // Composite parent;
-
-       private UserAdmin userAdmin;
-
-       // Context
-       private User user;
-       private String username;
-
-       private NameChangeListener listener;
-
-       private ManagedForm managedForm;
-
-       // public void init(IEditorSite site, IEditorInput input) throws
-       // PartInitException {
-       @PostConstruct
-       public void init(Composite parent) {
-               this.userAdmin = userAdminWrapper.getUserAdmin();
-               username = mPart.getPersistedState().get(LdapAttrs.uid.name());
-               user = (User) userAdmin.getRole(username);
-
-               listener = new NameChangeListener(Display.getCurrent());
-               userAdminWrapper.addListener(listener);
-               updateEditorTitle(null);
-
-               managedForm = new ManagedForm(parent) {
-
-                       @Override
-                       public void staleStateChanged() {
-                               refresh();
-                       }
-               };
-               ScrolledComposite scrolled = managedForm.getForm();
-               Composite body = new Composite(scrolled, SWT.NONE);
-               scrolled.setContent(body);
-               createUi(body);
-               managedForm.refresh();
-       }
-
-       abstract void createUi(Composite parent);
-
-       /**
-        * returns the list of all authorizations for the given user or of the current
-        * displayed user if parameter is null
-        */
-       protected List<User> getFlatGroups(User aUser) {
-               Authorization currAuth;
-               if (aUser == null)
-                       currAuth = userAdmin.getAuthorization(this.user);
-               else
-                       currAuth = userAdmin.getAuthorization(aUser);
-
-               String[] roles = currAuth.getRoles();
-
-               List<User> groups = new ArrayList<User>();
-               for (String roleStr : roles) {
-                       User currRole = (User) userAdmin.getRole(roleStr);
-                       if (currRole != null && !groups.contains(currRole))
-                               groups.add(currRole);
-               }
-               return groups;
-       }
-
-       protected IManagedForm getManagedForm() {
-               return managedForm;
-       }
-
-       /** Exposes the user (or group) that is displayed by the current editor */
-       protected User getDisplayedUser() {
-               return user;
-       }
-
-       private void setDisplayedUser(User user) {
-               this.user = user;
-       }
-
-       void updateEditorTitle(String title) {
-               if (title == null) {
-                       String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name());
-                       title = "".equals(commonName) ? user.getName() : commonName;
-               }
-               setPartName(title);
-       }
-
-       protected void setPartName(String name) {
-               mPart.setLabel(name);
-       }
-
-       // protected void addPages() {
-       // try {
-       // if (user.getType() == Role.GROUP)
-       // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance));
-       // else
-       // addPage(new UserMainPage(this, userAdminWrapper));
-       // } catch (Exception e) {
-       // throw new CmsException("Cannot add pages", e);
-       // }
-       // }
-
-       @Persist
-       public void doSave(IProgressMonitor monitor) {
-               userAdminWrapper.beginTransactionIfNeeded();
-               commitPages(true);
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-               // firePropertyChange(PROP_DIRTY);
-               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user));
-       }
-
-       protected void commitPages(boolean b) {
-               managedForm.commit(b);
-       }
-
-       @PreDestroy
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-               managedForm.dispose();
-       }
-
-       // CONTROLERS FOR THIS EDITOR AND ITS PAGES
-
-       class NameChangeListener extends UiUserAdminListener {
-               public NameChangeListener(Display display) {
-                       super(display);
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       Role changedRole = event.getRole();
-                       if (changedRole == null || changedRole.equals(user)) {
-                               updateEditorTitle(null);
-                               User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName());
-                               setDisplayedUser(reloadedUser);
-                       }
-               }
-       }
-
-       class MainInfoListener extends UiUserAdminListener {
-               private final AbstractFormPart part;
-
-               public MainInfoListener(Display display, AbstractFormPart part) {
-                       super(display);
-                       this.part = part;
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       // Rollback
-                       if (event.getRole() == null)
-                               part.markStale();
-               }
-       }
-
-       class GroupChangeListener extends UiUserAdminListener {
-               private final AbstractFormPart part;
-
-               public GroupChangeListener(Display display, AbstractFormPart part) {
-                       super(display);
-                       this.part = part;
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       // always mark as stale
-                       part.markStale();
-               }
-       }
-
-       /** Registers a listener that will notify this part */
-       class FormPartML implements ModifyListener {
-               private static final long serialVersionUID = 6299808129505381333L;
-               private AbstractFormPart formPart;
-
-               public FormPartML(AbstractFormPart generalPart) {
-                       this.formPart = generalPart;
-               }
-
-               public void modifyText(ModifyEvent e) {
-                       // Discard event when the control does not have the focus, typically
-                       // to avoid all editors being marked as dirty during a Rollback
-                       if (((Control) e.widget).isFocusControl())
-                               formPart.markDirty();
-               }
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-
-       /** Creates label and multiline text. */
-       Text createLMT(Composite parent, String label, String value) {
-               Label lbl = new Label(parent, SWT.NONE);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-               Text text = new Text(parent, SWT.NONE);
-               text.setText(value);
-               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true));
-               return text;
-       }
-
-       /** Creates label and password. */
-       Text createLP(Composite parent, String label, String value) {
-               Label lbl = new Label(parent, SWT.NONE);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-               Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER);
-               text.setText(value);
-               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false));
-               return text;
-       }
-
-       /** Creates label and text. */
-       Text createLT(Composite parent, String label, String value) {
-               Label lbl = new Label(parent, SWT.NONE);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-               lbl.setFont(EclipseUiUtils.getBoldFont(parent));
-               Text text = new Text(parent, SWT.BORDER);
-               text.setText(value);
-               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
-               return text;
-       }
-
-       Text createReadOnlyLT(Composite parent, String label, String value) {
-               Label lbl = new Label(parent, SWT.NONE);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-               lbl.setFont(EclipseUiUtils.getBoldFont(parent));
-               Text text = new Text(parent, SWT.NONE);
-               text.setText(value);
-               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false));
-               text.setEditable(false);
-               // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
-               return text;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java
deleted file mode 100644 (file)
index 07df312..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-package org.argeo.cms.e4.users;
-
-/** Centralize the declaration of Workbench specific CSS Styles */
-interface CmsWorkbenchStyles {
-
-       // Specific People layouting
-       String WORKBENCH_FORM_TEXT = "workbench_form_text";
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java
deleted file mode 100644 (file)
index d54f8bc..0000000
+++ /dev/null
@@ -1,566 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import static org.argeo.api.cms.CmsContext.WORKGROUP;
-import static org.argeo.cms.auth.UserAdminUtils.setProperty;
-import static org.argeo.util.naming.LdapAttrs.businessCategory;
-import static org.argeo.util.naming.LdapAttrs.description;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.providers.CommonNameLP;
-import org.argeo.cms.e4.users.providers.MailLP;
-import org.argeo.cms.e4.users.providers.RoleIconLP;
-import org.argeo.cms.e4.users.providers.UserFilter;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
-import org.argeo.cms.ui.eclipse.forms.IManagedForm;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.jcr.JcrException;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.transaction.WorkTransaction;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ToolBarManager;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.ViewerDropAdapter;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-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.eclipse.swt.widgets.Link;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-//import org.eclipse.ui.forms.AbstractFormPart;
-//import org.eclipse.ui.forms.IManagedForm;
-//import org.eclipse.ui.forms.SectionPart;
-//import org.eclipse.ui.forms.editor.FormEditor;
-//import org.eclipse.ui.forms.editor.FormPage;
-//import org.eclipse.ui.forms.widgets.FormToolkit;
-//import org.eclipse.ui.forms.widgets.ScrolledForm;
-//import org.eclipse.ui.forms.widgets.Section;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Display/edit main properties of a given group */
-public class GroupEditor extends AbstractRoleEditor {
-       // final static String ID = "GroupEditor.mainPage";
-
-       @Inject
-       private EPartService partService;
-
-       // private final UserEditor editor;
-       @Inject
-       private Repository repository;
-       @Inject
-       private CmsContext nodeInstance;
-       // private final UserAdminWrapper userAdminWrapper;
-       private Session groupsSession;
-
-       // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper,
-       // Repository repository,
-       // NodeInstance nodeInstance) {
-       // super(editor, ID, "Main");
-       // try {
-       // session = repository.login();
-       // } catch (RepositoryException e) {
-       // throw new CmsException("Cannot retrieve session of in MainGroupPage
-       // constructor", e);
-       // }
-       // this.editor = (UserEditor) editor;
-       // this.userAdminWrapper = userAdminWrapper;
-       // this.nodeInstance = nodeInstance;
-       // }
-
-       // protected void createFormContent(final IManagedForm mf) {
-       // ScrolledForm form = mf.getForm();
-       // Composite body = form.getBody();
-       // GridLayout mainLayout = new GridLayout();
-       // body.setLayout(mainLayout);
-       // Group group = (Group) editor.getDisplayedUser();
-       // appendOverviewPart(body, group);
-       // appendMembersPart(body, group);
-       // }
-
-       @Override
-       protected void createUi(Composite parent) {
-               try {
-                       groupsSession = repository.login(CmsConstants.SRV_WORKSPACE);
-               } catch (RepositoryException e) {
-                       throw new JcrException("Cannot retrieve session", e);
-               }
-               // ScrolledForm form = mf.getForm();
-               // Composite body = form.getBody();
-               // Composite body = new Composite(parent, SWT.NONE);
-               Composite body = parent;
-               GridLayout mainLayout = new GridLayout();
-               body.setLayout(mainLayout);
-               Group group = (Group) getDisplayedUser();
-               appendOverviewPart(body, group);
-               appendMembersPart(body, group);
-       }
-
-       @PreDestroy
-       public void dispose() {
-               JcrUtils.logoutQuietly(groupsSession);
-               super.dispose();
-       }
-
-       /** Creates the general section */
-       protected void appendOverviewPart(final Composite parent, final Group group) {
-               Composite body = new Composite(parent, SWT.NONE);
-               // GridLayout layout = new GridLayout(5, false);
-               GridLayout layout = new GridLayout(2, false);
-               body.setLayout(layout);
-               body.setLayoutData(CmsSwtUtils.fillWidth());
-
-               String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name());
-               createReadOnlyLT(body, "Name", cn);
-               createReadOnlyLT(body, "DN", group.getName());
-               createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group));
-
-               // Description
-               Label descLbl = new Label(body, SWT.LEAD);
-               descLbl.setFont(EclipseUiUtils.getBoldFont(body));
-               descLbl.setText("Description");
-               descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1));
-               final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER);
-               GridData gd = EclipseUiUtils.fillWidth();
-               gd.heightHint = 50;
-               gd.horizontalSpan = 2;
-               descTxt.setLayoutData(gd);
-
-               // Mark as workgroup
-               Link markAsWorkgroupLk = new Link(body, SWT.NONE);
-               markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
-
-               // create form part (controller)
-               final AbstractFormPart part = new AbstractFormPart() {
-
-                       private MainInfoListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = new MainInfoListener(parent.getDisplay(), this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       public void commit(boolean onSave) {
-                               // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText());
-                               setProperty(group, description, descTxt.getText());
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void refresh() {
-                               // dnTxt.setText(group.getName());
-                               // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name()));
-                               descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name()));
-                               Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn);
-                               if (workgroupHome == null)
-                                       markAsWorkgroupLk.setText("<a>Mark as workgroup</a>");
-                               else
-                                       markAsWorkgroupLk.setText("Configured as workgroup");
-                               parent.layout(true, true);
-                               super.refresh();
-                       }
-               };
-
-               markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() {
-                       private static final long serialVersionUID = -6439340898096365078L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-
-                               boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup",
-                                               "Are you sure you want to mark " + cn + " as being a workgroup? ");
-                               if (confirmed) {
-                                       Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn);
-                                       if (workgroupHome != null)
-                                               return; // already marked as workgroup, do nothing
-                                       else {
-                                               // improve transaction management
-                                               userAdminWrapper.beginTransactionIfNeeded();
-                                               nodeInstance.createWorkgroup(group.getName());
-                                               setProperty(group, businessCategory, WORKGROUP);
-                                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
-                                               part.refresh();
-                                       }
-                               }
-                       }
-               });
-
-               ModifyListener defaultListener = new FormPartML(part);
-               descTxt.addModifyListener(defaultListener);
-               getManagedForm().addPart(part);
-       }
-
-       /** Filtered table with members. Has drag and drop ability */
-       protected void appendMembersPart(Composite parent, Group group) {
-               // Section section = tk.createSection(parent, Section.TITLE_BAR);
-               // section.setText("Members");
-               // section.setLayoutData(EclipseUiUtils.fillAll());
-
-               Composite body = new Composite(parent, SWT.BORDER);
-               body.setLayout(new GridLayout());
-               // section.setClient(body);
-               body.setLayoutData(EclipseUiUtils.fillAll());
-
-               // Define the displayed columns
-               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
-               columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150));
-               // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
-               // 240));
-
-               // Create and configure the table
-               LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL,
-                               userAdminWrapper.getUserAdmin());
-
-               userViewerCmp.setColumnDefinitions(columnDefs);
-               userViewerCmp.populate(true, false);
-               userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-
-               // Controllers
-               TableViewer userViewer = userViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDropSupport(operations, tt,
-                               new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser()));
-
-               AbstractFormPart part = new GroupMembersPart(userViewerCmp);
-               getManagedForm().addPart(part);
-
-               // remove button
-               // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group);
-               Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group",
-                               SecurityAdminImages.ICON_REMOVE_DESC);
-
-               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-               ToolBar toolBar = toolBarManager.createControl(body);
-               toolBar.setLayoutData(CmsSwtUtils.fillWidth());
-
-               toolBarManager.add(action);
-               toolBarManager.update(true);
-
-       }
-
-       // private LdifUsersTable createMemberPart(Composite parent, Group group) {
-       //
-       // // Define the displayed columns
-       // List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-       // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
-       // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
-       // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150));
-       // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished
-       // Name",
-       // // 240));
-       //
-       // // Create and configure the table
-       // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI |
-       // SWT.H_SCROLL | SWT.V_SCROLL,
-       // userAdminWrapper.getUserAdmin());
-       //
-       // userViewerCmp.setColumnDefinitions(columnDefs);
-       // userViewerCmp.populate(true, false);
-       // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-       //
-       // // Controllers
-       // TableViewer userViewer = userViewerCmp.getTableViewer();
-       // userViewer.addDoubleClickListener(new
-       // UserTableDefaultDClickListener(partService));
-       // int operations = DND.DROP_COPY | DND.DROP_MOVE;
-       // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-       // userViewer.addDropSupport(operations, tt,
-       // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group)
-       // getDisplayedUser()));
-       //
-       // // userViewerCmp.refresh();
-       // return userViewerCmp;
-       // }
-
-       // Local viewers
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private final UserFilter userFilter;
-
-               public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) {
-                       super(parent, style, true);
-                       userFilter = new UserFilter();
-
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       // reload user and set it in the editor
-                       Group group = (Group) getDisplayedUser();
-                       Role[] roles = group.getMembers();
-                       List<User> users = new ArrayList<User>();
-                       userFilter.setSearchText(filter);
-                       // userFilter.setShowSystemRole(true);
-                       for (Role role : roles)
-                               // if (role.getType() == Role.GROUP)
-                               if (userFilter.select(null, null, role))
-                                       users.add((User) role);
-                       return users;
-               }
-       }
-
-       // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer
-       // userViewer, Group group) {
-       // // Section section = sectionPart.getSection();
-       // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-       // // ToolBar toolbar = toolBarManager.createControl(parent);
-       // // ToolBar toolbar = toolBarManager.getControl();
-       // // final Cursor handCursor = new Cursor(toolbar.getDisplay(),
-       // SWT.CURSOR_HAND);
-       // // toolbar.setCursor(handCursor);
-       // // toolbar.addDisposeListener(new DisposeListener() {
-       // // private static final long serialVersionUID = 3882131405820522925L;
-       // //
-       // // public void widgetDisposed(DisposeEvent e) {
-       // // if ((handCursor != null) && (handCursor.isDisposed() == false)) {
-       // // handCursor.dispose();
-       // // }
-       // // }
-       // // });
-       //
-       // Action action = new RemoveMembershipAction(userViewer, group, "Remove
-       // selected items from this group",
-       // SecurityAdminImages.ICON_REMOVE_DESC);
-       // toolBarManager.add(action);
-       // toolBarManager.update(true);
-       // // section.setTextClient(toolbar);
-       // }
-
-       private class RemoveMembershipAction extends Action {
-               private static final long serialVersionUID = -1337713097184522588L;
-
-               private final TableViewer userViewer;
-               private final Group group;
-
-               RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) {
-                       super(name, img);
-                       this.userViewer = userViewer;
-                       this.group = group;
-               }
-
-               @Override
-               public void run() {
-                       ISelection selection = userViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-
-                       @SuppressWarnings("unchecked")
-                       Iterator<User> it = ((IStructuredSelection) selection).iterator();
-                       List<User> users = new ArrayList<User>();
-                       while (it.hasNext()) {
-                               User currUser = it.next();
-                               users.add(currUser);
-                       }
-
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       for (User user : users) {
-                               group.removeMember(user);
-                       }
-                       userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
-               }
-       }
-
-       // LOCAL CONTROLLERS
-       private class GroupMembersPart extends AbstractFormPart {
-               private final LdifUsersTable userViewer;
-               // private final Group group;
-
-               private GroupChangeListener listener;
-
-               public GroupMembersPart(LdifUsersTable userViewer) {
-                       // super(section);
-                       this.userViewer = userViewer;
-                       // this.group = group;
-               }
-
-               @Override
-               public void initialize(IManagedForm form) {
-                       super.initialize(form);
-                       listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this);
-                       userAdminWrapper.addListener(listener);
-               }
-
-               @Override
-               public void dispose() {
-                       userAdminWrapper.removeListener(listener);
-                       super.dispose();
-               }
-
-               @Override
-               public void refresh() {
-                       userViewer.refresh();
-                       super.refresh();
-               }
-       }
-
-       /**
-        * Defines this table as being a potential target to add group membership
-        * (roles) to this group
-        */
-       private class GroupDropListener extends ViewerDropAdapter {
-               private static final long serialVersionUID = 2893468717831451621L;
-
-               private final UserAdminWrapper userAdminWrapper;
-               // private final LdifUsersTable myUserViewerCmp;
-               private final Group myGroup;
-
-               public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) {
-                       super(userTableViewerCmp.getTableViewer());
-                       this.userAdminWrapper = userAdminWrapper;
-                       this.myGroup = group;
-                       // this.myUserViewerCmp = userTableViewerCmp;
-               }
-
-               @Override
-               public boolean validateDrop(Object target, int operation, TransferData transferType) {
-                       // Target is always OK in a list only view
-                       // TODO check if not a string
-                       boolean validDrop = true;
-                       return validDrop;
-               }
-
-               @Override
-               public void drop(DropTargetEvent event) {
-                       // TODO Is there an opportunity to perform the check before?
-                       String newUserName = (String) event.data;
-                       UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin();
-                       Role role = myUserAdmin.getRole(newUserName);
-                       if (role.getType() == Role.GROUP) {
-                               Group newGroup = (Group) role;
-                               Shell shell = getViewer().getControl().getShell();
-                               // Sanity checks
-                               if (myGroup == newGroup) { // Equality
-                                       MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself.");
-                                       return;
-                               }
-
-                               // Cycle
-                               String myName = myGroup.getName();
-                               List<User> myMemberships = getFlatGroups(myGroup);
-                               if (myMemberships.contains(newGroup)) {
-                                       MessageDialog.openError(shell, "Forbidden addition: cycle",
-                                                       "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle");
-                                       return;
-                               }
-
-                               // Already member
-                               List<User> newGroupMemberships = getFlatGroups(newGroup);
-                               if (newGroupMemberships.contains(myGroup)) {
-                                       MessageDialog.openError(shell, "Forbidden addition",
-                                                       "Cannot add " + newUserName + " to group " + myName + ", this membership already exists");
-                                       return;
-                               }
-                               userAdminWrapper.beginTransactionIfNeeded();
-                               myGroup.addMember(newGroup);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup));
-                       } else if (role.getType() == Role.USER) {
-                               // TODO check if the group is already member of this group
-                               WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded();
-                               User user = (User) role;
-                               myGroup.addMember(user);
-                               if (UserAdminWrapper.COMMIT_ON_SAVE)
-                                       try {
-                                               transaction.commit();
-                                       } catch (Exception e) {
-                                               throw new IllegalStateException(
-                                                               "Cannot commit transaction " + "after user group membership update", e);
-                                       }
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup));
-                       }
-                       super.drop(event);
-               }
-
-               @Override
-               public boolean performDrop(Object data) {
-                       // myUserViewerCmp.refresh();
-                       return true;
-               }
-       }
-
-       // LOCAL HELPERS
-       // private Composite addSection(FormToolkit tk, Composite parent) {
-       // Section section = tk.createSection(parent, SWT.NO_FOCUS);
-       // section.setLayoutData(EclipseUiUtils.fillWidth());
-       // Composite body = tk.createComposite(section, SWT.WRAP);
-       // body.setLayoutData(EclipseUiUtils.fillAll());
-       // section.setClient(body);
-       // return body;
-       // }
-
-       /** Creates label and text. */
-       // private Text createLT(Composite parent, String label, String value) {
-       // FormToolkit toolkit = getManagedForm().getToolkit();
-       // Label lbl = toolkit.createLabel(parent, label);
-       // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-       // lbl.setFont(EclipseUiUtils.getBoldFont(parent));
-       // Text text = toolkit.createText(parent, value, SWT.BORDER);
-       // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-       // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
-       // return text;
-       // }
-       //
-       // Text createReadOnlyLT(Composite parent, String label, String value) {
-       // FormToolkit toolkit = getManagedForm().getToolkit();
-       // Label lbl = toolkit.createLabel(parent, label);
-       // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
-       // lbl.setFont(EclipseUiUtils.getBoldFont(parent));
-       // Text text = toolkit.createText(parent, value, SWT.NONE);
-       // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-       // text.setEditable(false);
-       // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
-       // return text;
-       // }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java
deleted file mode 100644 (file)
index 3bf4891..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.e4.users.providers.CommonNameLP;
-import org.argeo.cms.e4.users.providers.DomainNameLP;
-import org.argeo.cms.e4.users.providers.RoleIconLP;
-import org.argeo.cms.e4.users.providers.UserDragListener;
-import org.argeo.cms.swt.CmsException;
-//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin;
-//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener;
-//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper;
-//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP;
-//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP;
-//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP;
-//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener;
-//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-//import org.eclipse.ui.part.ViewPart;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** List all groups with filter */
-public class GroupsView {
-       private final static CmsLog log = CmsLog.getLog(GroupsView.class);
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView";
-
-       @Inject
-       private EPartService partService;
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-
-       // UI Objects
-       private LdifUsersTable groupTableViewerCmp;
-       private TableViewer userViewer;
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-
-       private UserAdminListener listener;
-
-       @PostConstruct
-       public void createPartControl(Composite parent, ESelectionService selectionService) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
-
-               // Define the displayed columns
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
-               // Only show technical DN to admin
-               // if (isAdmin)
-               // columnDefs.add(new ColumnDefinition(new UserNameLP(),
-               // "Distinguished Name", 300));
-
-               // Create and configure the table
-               groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-
-               groupTableViewerCmp.setColumnDefinitions(columnDefs);
-               // if (isAdmin)
-               // groupTableViewerCmp.populateWithStaticFilters(false, false);
-               // else
-               groupTableViewerCmp.populate(true, false);
-
-               groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               // Links
-               userViewer = groupTableViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
-               // getViewSite().setSelectionProvider(userViewer);
-               userViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-
-                       @Override
-                       public void selectionChanged(SelectionChangedEvent event) {
-                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
-                               selectionService.setSelection(selection.toList());
-                       }
-               });
-
-               // Really?
-               groupTableViewerCmp.refresh();
-
-               // Drag and drop
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer));
-
-               // // Register a useradmin listener
-               // listener = new UserAdminListener() {
-               // @Override
-               // public void roleChanged(UserAdminEvent event) {
-               // if (userViewer != null && !userViewer.getTable().isDisposed())
-               // refresh();
-               // }
-               // };
-               // userAdminWrapper.addListener(listener);
-               // }
-
-               // Register a useradmin listener
-               listener = new MyUiUAListener(parent.getDisplay());
-               userAdminWrapper.addListener(listener);
-       }
-
-       private class MyUiUAListener extends UiUserAdminListener {
-               public MyUiUAListener(Display display) {
-                       super(display);
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       if (userViewer != null && !userViewer.getTable().isDisposed())
-                               refresh();
-               }
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private boolean showSystemRoles = true;
-
-               private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN };
-
-               public MyUserTableViewer(Composite parent, int style) {
-                       super(parent, style);
-                       showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
-               }
-
-               protected void populateStaticFilters(Composite staticFilterCmp) {
-                       staticFilterCmp.setLayout(new GridLayout());
-                       final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
-                       showSystemRoleBtn.setText("Show system roles");
-                       showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
-                       showSystemRoleBtn.setSelection(showSystemRoles);
-
-                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = -7033424592697691676L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       showSystemRoles = showSystemRoleBtn.getSelection();
-                                       refresh();
-                               }
-
-                       });
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Role[] roles;
-                       try {
-                               StringBuilder builder = new StringBuilder();
-                               StringBuilder tmpBuilder = new StringBuilder();
-                               if (EclipseUiUtils.notEmpty(filter))
-                                       for (String prop : knownProps) {
-                                               tmpBuilder.append("(");
-                                               tmpBuilder.append(prop);
-                                               tmpBuilder.append("=*");
-                                               tmpBuilder.append(filter);
-                                               tmpBuilder.append("*)");
-                                       }
-                               if (tmpBuilder.length() > 1) {
-                                       builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
-                                                       .append(LdapObjs.groupOfNames.name()).append(")");
-                                       // hide tokens
-                                       builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN)
-                                                       .append("))");
-
-                                       if (!showSystemRoles)
-                                               builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.ROLES_BASEDN)
-                                                               .append("))");
-                                       builder.append("(|");
-                                       builder.append(tmpBuilder.toString());
-                                       builder.append("))");
-                               } else {
-                                       if (!showSystemRoles)
-                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
-                                                               .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*")
-                                                               .append(CmsConstants.ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*")
-                                                               .append(CmsConstants.TOKENS_BASEDN).append(")))");
-                                       else
-                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
-                                                               .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*")
-                                                               .append(CmsConstants.TOKENS_BASEDN).append(")))");
-
-                               }
-                               roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
-                       } catch (InvalidSyntaxException e) {
-                               throw new CmsException("Unable to get roles with filter: " + filter, e);
-                       }
-                       List<User> users = new ArrayList<User>();
-                       for (Role role : roles)
-                               if (!users.contains(role))
-                                       users.add((User) role);
-                               else
-                                       log.warn("Duplicated role: " + role);
-
-                       return users;
-               }
-       }
-
-       public void refresh() {
-               groupTableViewerCmp.refresh();
-       }
-
-       @PreDestroy
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-       }
-
-       @Focus
-       public void setFocus() {
-               groupTableViewerCmp.setFocus();
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java
deleted file mode 100644 (file)
index 7bbe3c7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import org.argeo.cms.ui.theme.CmsImages;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.graphics.Image;
-
-/** Shared icons that must be declared programmatically . */
-public class SecurityAdminImages extends CmsImages {
-       private final static String PREFIX = "icons/";
-
-       public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png");
-       public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png");
-
-       public final static Image ICON_USER = ICON_USER_DESC.createImage();
-       public final static Image ICON_GROUP = createImg(PREFIX + "group.png");
-       public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png");
-       public final static Image ICON_ROLE = createImg(PREFIX + "role.gif");
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java
deleted file mode 100644 (file)
index fb48a47..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import org.argeo.util.transaction.WorkTransaction;
-
-/** First effort to centralize back end methods used by the user admin UI */
-public class UiAdminUtils {
-       /*
-        * INTERNAL METHODS: Below methods are meant to stay here and are not part
-        * of a potential generic backend to manage the useradmin
-        */
-       /** Easily notify the ActiveWindow that the transaction had a state change */
-       public final static void notifyTransactionStateChange(
-                       WorkTransaction userTransaction) {
-//             try {
-//                     IWorkbenchWindow aww = PlatformUI.getWorkbench()
-//                                     .getActiveWorkbenchWindow();
-//                     ISourceProviderService sourceProviderService = (ISourceProviderService) aww
-//                                     .getService(ISourceProviderService.class);
-//                     UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService
-//                                     .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE);
-//                     esp.fireTransactionStateChange();
-//             } catch (Exception e) {
-//                     throw new CmsException("Unable to begin transaction", e);
-//             }
-       }
-
-       /**
-        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
-        * Thanks to <a href=
-        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
-        * >this tip</a>.
-        */
-       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java
deleted file mode 100644 (file)
index eb64aba..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** Convenience class to insure the call to refresh is done in the UI thread */
-public abstract class UiUserAdminListener implements UserAdminListener {
-
-       private final Display display;
-
-       public UiUserAdminListener(Display display) {
-               this.display = display;
-       }
-
-       @Override
-       public void roleChanged(final UserAdminEvent event) {
-               display.asyncExec(new Runnable() {
-                       @Override
-                       public void run() {
-                               roleChangedToUiThread(event);
-                       }
-               });
-       }
-
-       public abstract void roleChangedToUiThread(UserAdminEvent event);
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
deleted file mode 100644 (file)
index d120ae9..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.osgi.useradmin.UserDirectory;
-import org.argeo.util.directory.DirectoryConf;
-import org.argeo.util.transaction.WorkTransaction;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** Centralise interaction with the UserAdmin in this bundle */
-public class UserAdminWrapper {
-
-       private UserAdmin userAdmin;
-       // private ServiceReference<UserAdmin> userAdminServiceReference;
-//     private Set<String> uris;
-       private Map<UserDirectory, Hashtable<String, String>> userDirectories = Collections
-                       .synchronizedMap(new LinkedHashMap<>());
-       private WorkTransaction userTransaction;
-
-       // First effort to simplify UX while managing users and groups
-       public final static boolean COMMIT_ON_SAVE = true;
-
-       // Registered listeners
-       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
-
-       /**
-        * Starts a transaction if necessary. Should always been called together with
-        * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the
-        * security model changes have been performed.
-        */
-       public WorkTransaction beginTransactionIfNeeded() {
-               try {
-                       // UserTransaction userTransaction = getUserTransaction();
-                       if (userTransaction.isNoTransactionStatus()) {
-                               userTransaction.begin();
-                               // UiAdminUtils.notifyTransactionStateChange(userTransaction);
-                       }
-                       return userTransaction;
-               } catch (Exception e) {
-                       throw new CmsException("Unable to begin transaction", e);
-               }
-       }
-
-       /**
-        * Depending on the current application configuration, it will either commit the
-        * current transaction or throw a notification that the transaction state has
-        * changed (In the later case, it must be called from the UI thread).
-        */
-       public void commitOrNotifyTransactionStateChange() {
-               try {
-                       // UserTransaction userTransaction = getUserTransaction();
-                       if (userTransaction.isNoTransactionStatus())
-                               return;
-
-                       if (UserAdminWrapper.COMMIT_ON_SAVE)
-                               userTransaction.commit();
-                       else
-                               UiAdminUtils.notifyTransactionStateChange(userTransaction);
-               } catch (Exception e) {
-                       throw new CmsException("Unable to clean transaction", e);
-               }
-       }
-
-       // TODO implement safer mechanism
-       public void addListener(UserAdminListener userAdminListener) {
-               if (!listeners.contains(userAdminListener))
-                       listeners.add(userAdminListener);
-       }
-
-       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 (UserDirectory userDirectory : userDirectories.keySet()) {
-                       Boolean readOnly = userDirectory.isReadOnly();
-                       String baseDn = userDirectory.getContext();
-
-                       if (onlyWritable && readOnly)
-                               continue;
-                       if (baseDn.equalsIgnoreCase(CmsConstants.ROLES_BASEDN))
-                               continue;
-                       if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN))
-                               continue;
-                       dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString());
-
-               }
-//             for (String uri : uris) {
-//                     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(NodeConstants.ROLES_BASEDN))
-//                             continue;
-//                     if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN))
-//                             continue;
-//                     dns.put(baseDn, uri);
-//             }
-               return dns;
-       }
-
-       public UserAdmin getUserAdmin() {
-               return userAdmin;
-       }
-
-       public WorkTransaction getUserTransaction() {
-               return userTransaction;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdmin(UserAdmin userAdmin, Map<String, String> properties) {
-               this.userAdmin = userAdmin;
-//             this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet()));
-       }
-
-       public void setUserTransaction(WorkTransaction userTransaction) {
-               this.userTransaction = userTransaction;
-       }
-
-       public void addUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
-               userDirectories.put(userDirectory, new Hashtable<>(properties));
-       }
-
-       public void removeUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
-               userDirectories.remove(userDirectory);
-       }
-
-       // public void setUserAdminServiceReference(
-       // ServiceReference<UserAdmin> userAdminServiceReference) {
-       // this.userAdminServiceReference = userAdminServiceReference;
-       // }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java
deleted file mode 100644 (file)
index 4fc59d3..0000000
+++ /dev/null
@@ -1,606 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.providers.CommonNameLP;
-import org.argeo.cms.e4.users.providers.DomainNameLP;
-import org.argeo.cms.e4.users.providers.MailLP;
-import org.argeo.cms.e4.users.providers.UserNameLP;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.argeo.util.transaction.WorkTransaction;
-import org.eclipse.jface.dialogs.IPageChangeProvider;
-import org.eclipse.jface.dialogs.IPageChangedListener;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.PageChangedEvent;
-import org.eclipse.jface.wizard.IWizardContainer;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Wizard to update users */
-public class UserBatchUpdateWizard extends Wizard {
-
-       private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class);
-       private UserAdminWrapper userAdminWrapper;
-
-       // pages
-       private ChooseCommandWizardPage chooseCommandPage;
-       private ChooseUsersWizardPage userListPage;
-       private ValidateAndLaunchWizardPage validatePage;
-
-       // Various implemented commands keys
-       private final static String CMD_UPDATE_PASSWORD = "resetPassword";
-       private final static String CMD_UPDATE_EMAIL = "resetEmail";
-       private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
-
-       private final Map<String, String> commands = new HashMap<String, String>() {
-               private static final long serialVersionUID = 1L;
-               {
-                       put("Reset password(s)", CMD_UPDATE_PASSWORD);
-                       put("Reset email(s)", CMD_UPDATE_EMAIL);
-                       // TODO implement role / group management
-                       // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
-               }
-       };
-
-       public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-
-       @Override
-       public void addPages() {
-               chooseCommandPage = new ChooseCommandWizardPage();
-               addPage(chooseCommandPage);
-               userListPage = new ChooseUsersWizardPage();
-               addPage(userListPage);
-               validatePage = new ValidateAndLaunchWizardPage();
-               addPage(validatePage);
-       }
-
-       @Override
-       public boolean performFinish() {
-               if (!canFinish())
-                       return false;
-               WorkTransaction ut = userAdminWrapper.getUserTransaction();
-               if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction",
-                               "A user transaction is already existing, " + "are you sure you want to proceed ?"))
-                       return false;
-
-               // We cannot use jobs, user modifications are still meant to be done in
-               // the UIThread
-               // UpdateJob job = null;
-               // if (job != null)
-               // job.schedule();
-
-               if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
-                       char[] newValue = chooseCommandPage.getPwdValue();
-                       if (newValue == null)
-                               throw new CmsException("Password cannot be null or an empty string");
-                       ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
-                       job.doUpdate();
-               } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) {
-                       String newValue = chooseCommandPage.getEmailValue();
-                       if (newValue == null)
-                               throw new CmsException("Password cannot be null or an empty string");
-                       ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
-                       job.doUpdate();
-               }
-               return true;
-       }
-
-       public boolean canFinish() {
-               if (this.getContainer().getCurrentPage() == validatePage)
-                       return true;
-               return false;
-       }
-
-       private class ResetPassword {
-               private char[] newPwd;
-               private UserAdminWrapper userAdminWrapper;
-               private List<User> usersToUpdate;
-
-               public ResetPassword(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, char[] newPwd) {
-                       this.newPwd = newPwd;
-                       this.usersToUpdate = usersToUpdate;
-                       this.userAdminWrapper = userAdminWrapper;
-               }
-
-               @SuppressWarnings("unchecked")
-               protected void doUpdate() {
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       try {
-                               for (User user : usersToUpdate) {
-                                       // the char array is emptied after being used.
-                                       user.getCredentials().put(null, newPwd.clone());
-                               }
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot perform batch update on users", e);
-                       } finally {
-                               WorkTransaction ut = userAdminWrapper.getUserTransaction();
-                               if (!ut.isNoTransactionStatus())
-                                       ut.rollback();
-                       }
-               }
-       }
-
-       private class ResetEmail {
-               private String newEmail;
-               private UserAdminWrapper userAdminWrapper;
-               private List<User> usersToUpdate;
-
-               public ResetEmail(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, String newEmail) {
-                       this.newEmail = newEmail;
-                       this.usersToUpdate = usersToUpdate;
-                       this.userAdminWrapper = userAdminWrapper;
-               }
-
-               @SuppressWarnings("unchecked")
-               protected void doUpdate() {
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       try {
-                               for (User user : usersToUpdate) {
-                                       // the char array is emptied after being used.
-                                       user.getProperties().put(LdapAttrs.mail.name(), newEmail);
-                               }
-
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               if (!usersToUpdate.isEmpty())
-                                       userAdminWrapper.notifyListeners(
-                                                       new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0)));
-                       } catch (Exception e) {
-                               throw new CmsException("Cannot perform batch update on users", e);
-                       } finally {
-                               WorkTransaction ut = userAdminWrapper.getUserTransaction();
-                               if (!ut.isNoTransactionStatus())
-                                       ut.rollback();
-                       }
-               }
-       }
-
-       // @SuppressWarnings("unused")
-       // private class AddToGroup extends UpdateJob {
-       // private String groupID;
-       // private Session session;
-       //
-       // public AddToGroup(Session session, List<Node> nodesToUpdate,
-       // String groupID) {
-       // super(session, nodesToUpdate);
-       // this.session = session;
-       // this.groupID = groupID;
-       // }
-       //
-       // protected void doUpdate(Node node) {
-       // log.info("Add/Remove to group actions are not yet implemented");
-       // // TODO implement this
-       // // try {
-       // // throw new CmsException("Not yet implemented");
-       // // } catch (RepositoryException re) {
-       // // throw new CmsException(
-       // // "Unable to update boolean value for node " + node, re);
-       // // }
-       // }
-       // }
-
-       // /**
-       // * Base privileged job that will be run asynchronously to perform the
-       // batch
-       // * update
-       // */
-       // private abstract class UpdateJob extends PrivilegedJob {
-       //
-       // private final UserAdminWrapper userAdminWrapper;
-       // private final List<User> usersToUpdate;
-       //
-       // protected abstract void doUpdate(User user);
-       //
-       // public UpdateJob(UserAdminWrapper userAdminWrapper,
-       // List<User> usersToUpdate) {
-       // super("Perform update");
-       // this.usersToUpdate = usersToUpdate;
-       // this.userAdminWrapper = userAdminWrapper;
-       // }
-       //
-       // @Override
-       // protected IStatus doRun(IProgressMonitor progressMonitor) {
-       // try {
-       // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
-       // int total = usersToUpdate.size();
-       // monitor.beginTask("Performing change", total);
-       // userAdminWrapper.beginTransactionIfNeeded();
-       // for (User user : usersToUpdate) {
-       // doUpdate(user);
-       // monitor.worked(1);
-       // }
-       // userAdminWrapper.getUserTransaction().commit();
-       // } catch (Exception e) {
-       // throw new CmsException(
-       // "Cannot perform batch update on users", e);
-       // } finally {
-       // UserTransaction ut = userAdminWrapper.getUserTransaction();
-       // try {
-       // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
-       // ut.rollback();
-       // } catch (IllegalStateException | SecurityException
-       // | SystemException e) {
-       // log.error("Unable to rollback session in 'finally', "
-       // + "the system might be in a dirty state");
-       // e.printStackTrace();
-       // }
-       // }
-       // return Status.OK_STATUS;
-       // }
-       // }
-
-       // PAGES
-       /**
-        * Displays a combo box that enables user to choose which action to perform
-        */
-       private class ChooseCommandWizardPage extends WizardPage {
-               private static final long serialVersionUID = -8069434295293996633L;
-               private Combo chooseCommandCmb;
-               private Button trueChk;
-               private Text valueTxt;
-               private Text pwdTxt;
-               private Text pwd2Txt;
-
-               public ChooseCommandWizardPage() {
-                       super("Choose a command to run.");
-                       setTitle("Choose a command to run.");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       GridLayout gl = new GridLayout();
-                       Composite container = new Composite(parent, SWT.NO_FOCUS);
-                       container.setLayout(gl);
-
-                       chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
-                       chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
-                       String[] values = commands.keySet().toArray(new String[0]);
-                       chooseCommandCmb.setItems(values);
-
-                       final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
-                       bottomPart.setLayoutData(EclipseUiUtils.fillAll());
-                       bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = 1L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       if (getCommand().equals(CMD_UPDATE_PASSWORD))
-                                               populatePasswordCmp(bottomPart);
-                                       else if (getCommand().equals(CMD_UPDATE_EMAIL))
-                                               populateEmailCmp(bottomPart);
-                                       else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
-                                               populateGroupCmp(bottomPart);
-                                       else
-                                               populateBooleanFlagCmp(bottomPart);
-                                       checkPageComplete();
-                                       bottomPart.layout(true, true);
-                               }
-                       });
-                       setControl(container);
-               }
-
-               private void populateBooleanFlagCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       trueChk = new Button(parent, SWT.CHECK);
-                       trueChk.setText("Do it. (It will to the contrary if unchecked)");
-                       trueChk.setSelection(true);
-                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
-               }
-
-               private void populatePasswordCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       Composite body = new Composite(parent, SWT.NO_FOCUS);
-
-                       ModifyListener ml = new ModifyListener() {
-                               private static final long serialVersionUID = -1558726363536729634L;
-
-                               @Override
-                               public void modifyText(ModifyEvent event) {
-                                       checkPageComplete();
-                               }
-                       };
-
-                       body.setLayout(new GridLayout(2, false));
-                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
-                       pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
-               }
-
-               private void populateEmailCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       Composite body = new Composite(parent, SWT.NO_FOCUS);
-
-                       ModifyListener ml = new ModifyListener() {
-                               private static final long serialVersionUID = 2147704227294268317L;
-
-                               @Override
-                               public void modifyText(ModifyEvent event) {
-                                       checkPageComplete();
-                               }
-                       };
-
-                       body.setLayout(new GridLayout(2, false));
-                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml);
-               }
-
-               private void checkPageComplete() {
-                       String errorMsg = null;
-                       if (chooseCommandCmb.getSelectionIndex() < 0)
-                               errorMsg = "Please select an action";
-                       else if (CMD_UPDATE_EMAIL.equals(getCommand())) {
-                               if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN))
-                                       errorMsg = "Not a valid e-mail address";
-                       } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
-                               if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4)
-                                       errorMsg = "Please enter a password that is at least 4 character long";
-                               else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
-                                       errorMsg = "Passwords are different";
-                       }
-                       if (EclipseUiUtils.notEmpty(errorMsg)) {
-                               setMessage(errorMsg, WizardPage.ERROR);
-                               setPageComplete(false);
-                       } else {
-                               setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION);
-                               setPageComplete(true);
-                       }
-
-                       getContainer().updateButtons();
-               }
-
-               private void populateGroupCmp(Composite parent) {
-                       EclipseUiUtils.clear(parent);
-                       trueChk = new Button(parent, SWT.CHECK);
-                       trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)");
-                       trueChk.setSelection(true);
-                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
-               }
-
-               protected String getCommand() {
-                       return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()));
-               }
-
-               protected String getCommandLbl() {
-                       return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex());
-               }
-
-               @SuppressWarnings("unused")
-               protected boolean getBoleanValue() {
-                       // FIXME this is not consistent and will lead to errors.
-                       if ("argeo:enabled".equals(getCommand()))
-                               return trueChk.getSelection();
-                       else
-                               return !trueChk.getSelection();
-               }
-
-               @SuppressWarnings("unused")
-               protected String getStringValue() {
-                       String value = null;
-                       if (valueTxt != null) {
-                               value = valueTxt.getText();
-                               if ("".equals(value.trim()))
-                                       value = null;
-                       }
-                       return value;
-               }
-
-               protected char[] getPwdValue() {
-                       // We do not directly reset the password text fields: There is no
-                       // need to over secure this process: setting a pwd to multi users
-                       // at the same time is anyhow a bad practice and should be used only
-                       // in test environment or for temporary access
-                       if (pwdTxt == null || pwdTxt.isDisposed())
-                               return null;
-                       else
-                               return pwdTxt.getText().toCharArray();
-               }
-
-               protected String getEmailValue() {
-                       // We do not directly reset the password text fields: There is no
-                       // need to over secure this process: setting a pwd to multi users
-                       // at the same time is anyhow a bad practice and should be used only
-                       // in test environment or for temporary access
-                       if (valueTxt == null || valueTxt.isDisposed())
-                               return null;
-                       else
-                               return valueTxt.getText();
-               }
-       }
-
-       /**
-        * Displays a list of users with a check box to be able to choose some of them
-        */
-       private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener {
-               private static final long serialVersionUID = 7651807402211214274L;
-               private ChooseUserTableViewer userTableCmp;
-
-               public ChooseUsersWizardPage() {
-                       super("Choose Users");
-                       setTitle("Select users who will be impacted");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       Composite pageCmp = new Composite(parent, SWT.NONE);
-                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       // Define the displayed columns
-                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-                       columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
-                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
-
-                       // Only show technical DN to admin
-                       if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
-                               columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
-
-                       userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
-                       userTableCmp.setColumnDefinitions(columnDefs);
-                       userTableCmp.populate(true, true);
-                       userTableCmp.refresh();
-
-                       setControl(pageCmp);
-
-                       // Add listener to update message when shown
-                       final IWizardContainer wContainer = this.getContainer();
-                       if (wContainer instanceof IPageChangeProvider) {
-                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
-                       }
-
-               }
-
-               @Override
-               public void pageChanged(PageChangedEvent event) {
-                       if (event.getSelectedPage() == this) {
-                               String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl();
-                               ((WizardPage) event.getSelectedPage()).setMessage(msg);
-                       }
-               }
-
-               protected List<User> getSelectedUsers() {
-                       return userTableCmp.getSelectedUsers();
-               }
-
-               private class ChooseUserTableViewer extends LdifUsersTable {
-                       private static final long serialVersionUID = 5080437561015853124L;
-                       private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(),
-                                       LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() };
-
-                       public ChooseUserTableViewer(Composite parent, int style) {
-                               super(parent, style);
-                       }
-
-                       @Override
-                       protected List<User> listFilteredElements(String filter) {
-                               Role[] roles;
-
-                               try {
-                                       StringBuilder builder = new StringBuilder();
-
-                                       StringBuilder tmpBuilder = new StringBuilder();
-                                       if (EclipseUiUtils.notEmpty(filter))
-                                               for (String prop : knownProps) {
-                                                       tmpBuilder.append("(");
-                                                       tmpBuilder.append(prop);
-                                                       tmpBuilder.append("=*");
-                                                       tmpBuilder.append(filter);
-                                                       tmpBuilder.append("*)");
-                                               }
-                                       if (tmpBuilder.length() > 1) {
-                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
-                                                               .append(LdapObjs.inetOrgPerson.name()).append(")(|");
-                                               builder.append(tmpBuilder.toString());
-                                               builder.append("))");
-                                       } else
-                                               builder.append("(").append(LdapAttrs.objectClass.name()).append("=")
-                                                               .append(LdapObjs.inetOrgPerson.name()).append(")");
-                                       roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
-                               } catch (InvalidSyntaxException e) {
-                                       throw new CmsException("Unable to get roles with filter: " + filter, e);
-                               }
-                               List<User> users = new ArrayList<User>();
-                               for (Role role : roles)
-                                       // Prevent current logged in user to perform batch on
-                                       // himself
-                                       if (!UserAdminUtils.isCurrentUser((User) role))
-                                               users.add((User) role);
-                               return users;
-                       }
-               }
-       }
-
-       /** Summary of input data before launching the process */
-       private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener {
-               private static final long serialVersionUID = 7098918351451743853L;
-               private ChosenUsersTableViewer userTableCmp;
-
-               public ValidateAndLaunchWizardPage() {
-                       super("Validate and launch");
-                       setTitle("Validate and launch");
-               }
-
-               @Override
-               public void createControl(Composite parent) {
-                       Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
-                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-                       columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
-                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
-                       // Only show technical DN to admin
-                       if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
-                               columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
-                       userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
-                       userTableCmp.setColumnDefinitions(columnDefs);
-                       userTableCmp.populate(false, false);
-                       userTableCmp.refresh();
-                       setControl(pageCmp);
-                       // Add listener to update message when shown
-                       final IWizardContainer wContainer = this.getContainer();
-                       if (wContainer instanceof IPageChangeProvider) {
-                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
-                       }
-               }
-
-               @Override
-               public void pageChanged(PageChangedEvent event) {
-                       if (event.getSelectedPage() == this) {
-                               @SuppressWarnings({ "unchecked", "rawtypes" })
-                               Object[] values = ((ArrayList) userListPage.getSelectedUsers())
-                                               .toArray(new Object[userListPage.getSelectedUsers().size()]);
-                               userTableCmp.getTableViewer().setInput(values);
-                               String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl()
-                                               + "] will be perfomed on the users listed below.\n";
-                               // + "Are you sure you want to proceed?";
-                               setMessage(msg);
-                       }
-               }
-
-               private class ChosenUsersTableViewer extends LdifUsersTable {
-                       private static final long serialVersionUID = 7814764735794270541L;
-
-                       public ChosenUsersTableViewer(Composite parent, int style) {
-                               super(parent, style);
-                       }
-
-                       @Override
-                       protected List<User> listFilteredElements(String filter) {
-                               return userListPage.getSelectedUsers();
-                       }
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java
deleted file mode 100644 (file)
index 66f4420..0000000
+++ /dev/null
@@ -1,535 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import static org.argeo.cms.auth.UserAdminUtils.getProperty;
-import static org.argeo.util.naming.LdapAttrs.cn;
-import static org.argeo.util.naming.LdapAttrs.givenName;
-import static org.argeo.util.naming.LdapAttrs.mail;
-import static org.argeo.util.naming.LdapAttrs.sn;
-import static org.argeo.util.naming.LdapAttrs.uid;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.providers.CommonNameLP;
-import org.argeo.cms.e4.users.providers.DomainNameLP;
-import org.argeo.cms.e4.users.providers.RoleIconLP;
-import org.argeo.cms.e4.users.providers.UserFilter;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
-//import org.argeo.cms.ui.eclipse.forms.FormToolkit;
-import org.argeo.cms.ui.eclipse.forms.IManagedForm;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.ToolBarManager;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.TrayDialog;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerDropAdapter;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.dnd.TransferData;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-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.Display;
-import org.eclipse.swt.widgets.Link;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.ToolBar;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Display/edit the properties of a given user */
-public class UserEditor extends AbstractRoleEditor {
-       // final static String ID = "UserEditor.mainPage";
-
-       @Inject
-       private EPartService partService;
-
-       // private final UserEditor editor;
-       // private UserAdminWrapper userAdminWrapper;
-
-       // Local configuration
-       // private final int PRE_TITLE_INDENT = 10;
-
-       // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
-       // super(editor, ID, "Main");
-       // this.editor = (UserEditor) editor;
-       // this.userAdminWrapper = userAdminWrapper;
-       // }
-
-       // protected void createFormContent(final IManagedForm mf) {
-       // ScrolledForm form = mf.getForm();
-       // Composite body = form.getBody();
-       // GridLayout mainLayout = new GridLayout();
-       // // mainLayout.marginRight = 10;
-       // body.setLayout(mainLayout);
-       // User user = editor.getDisplayedUser();
-       // appendOverviewPart(body, user);
-       // // Remove to ability to force the password for his own user. The user
-       // // must then use the change pwd feature
-       // appendMemberOfPart(body, user);
-       // }
-
-       @Override
-       protected void createUi(Composite body) {
-               // Composite body = new Composite(parent, SWT.BORDER);
-               GridLayout mainLayout = new GridLayout();
-               // mainLayout.marginRight = 10;
-               body.setLayout(mainLayout);
-               // body.getParent().setLayout(new GridLayout());
-               // body.setLayoutData(CmsUiUtils.fillAll());
-               User user = getDisplayedUser();
-               appendOverviewPart(body, user);
-               // Remove to ability to force the password for his own user. The user
-               // must then use the change pwd feature
-               appendMemberOfPart(body, user);
-       }
-
-       /** Creates the general section */
-       private void appendOverviewPart(final Composite parent, final User user) {
-               // FormToolkit tk = getManagedForm().getToolkit();
-
-               // Section section = tk.createSection(parent, SWT.NO_FOCUS);
-               // GridData gd = EclipseUiUtils.fillWidth();
-               // // gd.verticalAlignment = PRE_TITLE_INDENT;
-               // section.setLayoutData(gd);
-               Composite body = new Composite(parent, SWT.NONE);
-               body.setLayoutData(EclipseUiUtils.fillWidth());
-               // section.setClient(body);
-               // body.setLayout(new GridLayout(6, false));
-               body.setLayout(new GridLayout(2, false));
-
-               Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn));
-               Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid));
-               Text firstName = createLT(body, "First name", getProperty(user, givenName));
-               Text lastName = createLT(body, "Last name", getProperty(user, sn));
-               Text email = createLT(body, "Email", getProperty(user, mail));
-
-               Link resetPwdLk = new Link(body, SWT.NONE);
-               if (!UserAdminUtils.isCurrentUser(user)) {
-                       resetPwdLk.setText("<a>Reset password</a>");
-               }
-               resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
-
-               // create form part (controller)
-               AbstractFormPart part = new AbstractFormPart() {
-                       private MainInfoListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = new MainInfoListener(parent.getDisplay(), this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       @SuppressWarnings("unchecked")
-                       public void commit(boolean onSave) {
-                               // TODO Sanity checks (mail validity...)
-                               user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText());
-                               user.getProperties().put(LdapAttrs.sn.name(), lastName.getText());
-                               user.getProperties().put(LdapAttrs.cn.name(), commonName.getText());
-                               user.getProperties().put(LdapAttrs.mail.name(), email.getText());
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void refresh() {
-                               distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name()));
-                               commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name()));
-                               firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name()));
-                               lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name()));
-                               email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name()));
-                               refreshFormTitle(user);
-                               super.refresh();
-                       }
-               };
-
-               // Improve this: automatically generate CN when first or last name
-               // changes
-               ModifyListener cnML = new ModifyListener() {
-                       private static final long serialVersionUID = 4298649222869835486L;
-
-                       @Override
-                       public void modifyText(ModifyEvent event) {
-                               String first = firstName.getText();
-                               String last = lastName.getText();
-                               String cn = first.trim() + " " + last.trim() + " ";
-                               cn = cn.trim();
-                               commonName.setText(cn);
-                               // getManagedForm().getForm().setText(cn);
-                               updateEditorTitle(cn);
-                       }
-               };
-               firstName.addModifyListener(cnML);
-               lastName.addModifyListener(cnML);
-
-               ModifyListener defaultListener = new FormPartML(part);
-               firstName.addModifyListener(defaultListener);
-               lastName.addModifyListener(defaultListener);
-               email.addModifyListener(defaultListener);
-
-               if (!UserAdminUtils.isCurrentUser(user))
-                       resetPwdLk.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = 5881800534589073787L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       new ChangePasswordDialog(user, "Reset password").open();
-                               }
-                       });
-
-               getManagedForm().addPart(part);
-       }
-
-       private class ChangePasswordDialog extends TrayDialog {
-               private static final long serialVersionUID = 2843538207460082349L;
-
-               private User user;
-               private Text password1;
-               private Text password2;
-               private String title;
-               // private FormToolkit tk;
-
-               public ChangePasswordDialog(User user, String title) {
-                       super(Display.getDefault().getActiveShell());
-                       // this.tk = tk;
-                       this.user = user;
-                       this.title = title;
-               }
-
-               protected Control createDialogArea(Composite parent) {
-                       Composite dialogarea = (Composite) super.createDialogArea(parent);
-                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       Composite body = new Composite(dialogarea, SWT.NO_FOCUS);
-                       body.setLayoutData(EclipseUiUtils.fillAll());
-                       GridLayout layout = new GridLayout(2, false);
-                       body.setLayout(layout);
-
-                       password1 = createLP(body, "New password", "");
-                       password2 = createLP(body, "Repeat password", "");
-                       parent.pack();
-                       return body;
-               }
-
-               @SuppressWarnings("unchecked")
-               @Override
-               protected void okPressed() {
-                       String msg = null;
-
-                       if (password1.getText().equals(""))
-                               msg = "Password cannot be empty";
-                       else if (password1.getText().equals(password2.getText())) {
-                               char[] newPassword = password1.getText().toCharArray();
-                               // userAdminWrapper.beginTransactionIfNeeded();
-                               userAdminWrapper.beginTransactionIfNeeded();
-                               user.getCredentials().put(null, newPassword);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               super.okPressed();
-                       } else {
-                               msg = "Passwords are not equals";
-                       }
-
-                       if (EclipseUiUtils.notEmpty(msg))
-                               MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg);
-               }
-
-               protected void configureShell(Shell shell) {
-                       super.configureShell(shell);
-                       shell.setText(title);
-               }
-       }
-
-       private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
-               // Section section = addSection(tk, parent, "Roles");
-               // Composite body = (Composite) section.getClient();
-               // Composite body= parent;
-               Composite body = new Composite(parent, SWT.BORDER);
-               body.setLayout(new GridLayout());
-               body.setLayoutData(CmsSwtUtils.fillAll());
-
-               // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
-
-               // Displayed columns
-               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
-               // Only show technical DN to administrators
-               // if (isAdmin)
-               // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
-               // 300));
-
-               // Create and configure the table
-               final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
-
-               userViewerCmp.setColumnDefinitions(columnDefs);
-               // if (isAdmin)
-               // userViewerCmp.populateWithStaticFilters(false, false);
-               // else
-               userViewerCmp.populate(true, false);
-               GridData gd = EclipseUiUtils.fillAll();
-               gd.heightHint = 500;
-               userViewerCmp.setLayoutData(gd);
-
-               // Controllers
-               TableViewer userViewer = userViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user);
-               userViewer.addDropSupport(operations, tt, dropL);
-
-               AbstractFormPart part = new AbstractFormPart() {
-
-                       private GroupChangeListener listener;
-
-                       @Override
-                       public void initialize(IManagedForm form) {
-                               super.initialize(form);
-                               listener = new GroupChangeListener(parent.getDisplay(), this);
-                               userAdminWrapper.addListener(listener);
-                       }
-
-                       public void commit(boolean onSave) {
-                               super.commit(onSave);
-                       }
-
-                       @Override
-                       public void dispose() {
-                               userAdminWrapper.removeListener(listener);
-                               super.dispose();
-                       }
-
-                       @Override
-                       public void refresh() {
-                               userViewerCmp.refresh();
-                               super.refresh();
-                       }
-               };
-               getManagedForm().addPart(part);
-               // addRemoveAbitily(body, userViewer, user);
-               // userViewerCmp.refresh();
-               String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups";
-               Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC);
-               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-               ToolBar toolBar = toolBarManager.createControl(body);
-               toolBar.setLayoutData(CmsSwtUtils.fillWidth());
-               toolBarManager.add(action);
-               toolBarManager.update(true);
-               return userViewerCmp;
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 2653790051461237329L;
-
-               private Button showSystemRoleBtn;
-
-               private final User user;
-               private final UserFilter userFilter;
-
-               public MyUserTableViewer(Composite parent, int style, User user) {
-                       super(parent, style, true);
-                       this.user = user;
-                       userFilter = new UserFilter();
-               }
-
-               protected void populateStaticFilters(Composite staticFilterCmp) {
-                       staticFilterCmp.setLayout(new GridLayout());
-                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
-                       showSystemRoleBtn.setText("Show system roles");
-                       boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
-                       showSystemRoleBtn.setSelection(showSysRole);
-                       userFilter.setShowSystemRole(showSysRole);
-                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
-                               private static final long serialVersionUID = -7033424592697691676L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       userFilter.setShowSystemRole(showSystemRoleBtn.getSelection());
-                                       refresh();
-                               }
-                       });
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       List<User> users = (List<User>) getFlatGroups(null);
-                       List<User> filteredUsers = new ArrayList<User>();
-                       if (users.contains(user))
-                               users.remove(user);
-                       userFilter.setSearchText(filter);
-                       for (User user : users)
-                               if (userFilter.select(null, null, user))
-                                       filteredUsers.add(user);
-                       return filteredUsers;
-               }
-       }
-
-       // private void addRemoveAbility(Composite parent, TableViewer userViewer, User
-       // user) {
-       // // Section section = sectionPart.getSection();
-       // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-       // ToolBar toolbar = toolBarManager.createControl(parent);
-       // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
-       // toolbar.setCursor(handCursor);
-       // toolbar.addDisposeListener(new DisposeListener() {
-       // private static final long serialVersionUID = 3882131405820522925L;
-       //
-       // public void widgetDisposed(DisposeEvent e) {
-       // if ((handCursor != null) && (handCursor.isDisposed() == false)) {
-       // handCursor.dispose();
-       // }
-       // }
-       // });
-       //
-       // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) +
-       // " from the below selected groups";
-       // Action action = new RemoveMembershipAction(userViewer, user, tooltip,
-       // SecurityAdminImages.ICON_REMOVE_DESC);
-       // toolBarManager.add(action);
-       // toolBarManager.update(true);
-       // // section.setTextClient(toolbar);
-       // }
-
-       private class RemoveMembershipAction extends Action {
-               private static final long serialVersionUID = -1337713097184522588L;
-
-               private final TableViewer userViewer;
-               private final User user;
-
-               RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) {
-                       super(name, img);
-                       this.userViewer = userViewer;
-                       this.user = user;
-               }
-
-               @Override
-               public void run() {
-                       ISelection selection = userViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-
-                       @SuppressWarnings("unchecked")
-                       Iterator<Group> it = ((IStructuredSelection) selection).iterator();
-                       List<Group> groups = new ArrayList<Group>();
-                       while (it.hasNext()) {
-                               Group currGroup = it.next();
-                               groups.add(currGroup);
-                       }
-
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       for (Group group : groups) {
-                               group.removeMember(user);
-                       }
-                       userAdminWrapper.commitOrNotifyTransactionStateChange();
-                       for (Group group : groups) {
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
-                       }
-               }
-       }
-
-       /**
-        * Defines the table as being a potential target to add group memberships
-        * (roles) to this user
-        */
-       private class GroupDropListener extends ViewerDropAdapter {
-               private static final long serialVersionUID = 2893468717831451621L;
-
-               private final UserAdminWrapper myUserAdminWrapper;
-               private final User myUser;
-
-               public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) {
-                       super(userViewer);
-                       this.myUserAdminWrapper = userAdminWrapper;
-                       this.myUser = user;
-               }
-
-               @Override
-               public boolean validateDrop(Object target, int operation, TransferData transferType) {
-                       // Target is always OK in a list only view
-                       // TODO check if not a string
-                       boolean validDrop = true;
-                       return validDrop;
-               }
-
-               @Override
-               public void drop(DropTargetEvent event) {
-                       String name = (String) event.data;
-                       UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
-                       Role role = myUserAdmin.getRole(name);
-                       // TODO this check should be done before.
-                       if (role.getType() == Role.GROUP) {
-                               // TODO check if the user is already member of this group
-
-                               myUserAdminWrapper.beginTransactionIfNeeded();
-                               Group group = (Group) role;
-                               group.addMember(myUser);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
-                       }
-                       super.drop(event);
-               }
-
-               @Override
-               public boolean performDrop(Object data) {
-                       // userTableViewerCmp.refresh();
-                       return true;
-               }
-       }
-
-       // LOCAL HELPERS
-       private void refreshFormTitle(User group) {
-               // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group,
-               // LdapAttrs.cn.name()));
-       }
-
-       /** Appends a section with a title */
-       // private Section addSection(FormToolkit tk, Composite parent, String title) {
-       // Section section = tk.createSection(parent, Section.TITLE_BAR);
-       // GridData gd = EclipseUiUtils.fillWidth();
-       // gd.verticalAlignment = PRE_TITLE_INDENT;
-       // section.setLayoutData(gd);
-       // section.setText(title);
-       // // section.getMenu().setVisible(true);
-       //
-       // Composite body = tk.createComposite(section, SWT.WRAP);
-       // body.setLayoutData(EclipseUiUtils.fillAll());
-       // section.setClient(body);
-       //
-       // return section;
-       // }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java
deleted file mode 100644 (file)
index c6d024e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import org.argeo.cms.e4.CmsE4Utils;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.User;
-
-/**
- * Default double click listener for the various user tables, will open the
- * clicked item in the editor
- */
-public class UserTableDefaultDClickListener implements IDoubleClickListener {
-       private final EPartService partService;
-
-       public UserTableDefaultDClickListener(EPartService partService) {
-               this.partService = partService;
-       }
-
-       public void doubleClick(DoubleClickEvent evt) {
-               if (evt.getSelection().isEmpty())
-                       return;
-               Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement();
-               User user = (User) obj;
-
-               String editorId = getEditorId(user);
-               CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName());
-       }
-
-       protected String getEditorId(User user) {
-               if (user instanceof Group)
-                       return "org.argeo.cms.e4.partdescriptor.groupEditor";
-               else
-                       return "org.argeo.cms.e4.partdescriptor.userEditor";
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java
deleted file mode 100644 (file)
index 720945c..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-package org.argeo.cms.e4.users;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.inject.Inject;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.e4.users.providers.CommonNameLP;
-import org.argeo.cms.e4.users.providers.DomainNameLP;
-import org.argeo.cms.e4.users.providers.MailLP;
-import org.argeo.cms.e4.users.providers.UserDragListener;
-import org.argeo.cms.e4.users.providers.UserNameLP;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-import org.osgi.service.useradmin.UserAdminListener;
-
-/** List all users with filter - based on Ldif userAdmin */
-public class UsersView {
-       // private final static Log log = LogFactory.getLog(UsersView.class);
-
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView";
-
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-       @Inject
-       private EPartService partService;
-
-       // UI Objects
-       private LdifUsersTable userTableViewerCmp;
-       private TableViewer userViewer;
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-
-       private UserAdminListener listener;
-
-       @PostConstruct
-       public void createPartControl(Composite parent, ESelectionService selectionService) {
-
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               // Define the displayed columns
-               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
-               columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
-               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
-               // Only show technical DN to admin
-               if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
-                       columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
-
-               // Create and configure the table
-               userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-               userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
-               userTableViewerCmp.setColumnDefinitions(columnDefs);
-               userTableViewerCmp.populate(true, false);
-
-               // Links
-               userViewer = userTableViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
-               userViewer.addSelectionChangedListener(new ISelectionChangedListener() {
-
-                       @Override
-                       public void selectionChanged(SelectionChangedEvent event) {
-                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
-                               selectionService.setSelection(selection.toList());
-                       }
-               });
-               // getViewSite().setSelectionProvider(userViewer);
-
-               // Really?
-               userTableViewerCmp.refresh();
-
-               // Drag and drop
-               int operations = DND.DROP_COPY | DND.DROP_MOVE;
-               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
-               userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer));
-
-               // Register a useradmin listener
-               listener = new MyUiUAListener(parent.getDisplay());
-               userAdminWrapper.addListener(listener);
-       }
-
-       private class MyUiUAListener extends UiUserAdminListener {
-               public MyUiUAListener(Display display) {
-                       super(display);
-               }
-
-               @Override
-               public void roleChangedToUiThread(UserAdminEvent event) {
-                       if (userViewer != null && !userViewer.getTable().isDisposed())
-                               refresh();
-               }
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(),
-                               LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() };
-
-               public MyUserTableViewer(Composite parent, int style) {
-                       super(parent, style);
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Role[] roles;
-
-                       try {
-                               StringBuilder builder = new StringBuilder();
-
-                               StringBuilder tmpBuilder = new StringBuilder();
-                               if (EclipseUiUtils.notEmpty(filter))
-                                       for (String prop : knownProps) {
-                                               tmpBuilder.append("(");
-                                               tmpBuilder.append(prop);
-                                               tmpBuilder.append("=*");
-                                               tmpBuilder.append(filter);
-                                               tmpBuilder.append("*)");
-                                       }
-                               if (tmpBuilder.length() > 1) {
-                                       builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
-                                                       .append(LdapObjs.inetOrgPerson.name()).append(")(|");
-                                       builder.append(tmpBuilder.toString());
-                                       builder.append("))");
-                               } else
-                                       builder.append("(").append(LdapAttrs.objectClass.name()).append("=")
-                                                       .append(LdapObjs.inetOrgPerson.name()).append(")");
-                               roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
-                       } catch (InvalidSyntaxException e) {
-                               throw new CmsException("Unable to get roles with filter: " + filter, e);
-                       }
-                       List<User> users = new ArrayList<User>();
-                       for (Role role : roles)
-                               // if (role.getType() == Role.USER && role.getType() !=
-                               // Role.GROUP)
-                               users.add((User) role);
-                       return users;
-               }
-       }
-
-       public void refresh() {
-               userTableViewerCmp.refresh();
-       }
-
-       // Override generic view methods
-       @PreDestroy
-       public void dispose() {
-               userAdminWrapper.removeListener(listener);
-       }
-
-       @Focus
-       public void setFocus() {
-               userTableViewerCmp.setFocus();
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java
deleted file mode 100644 (file)
index 742bc3f..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.argeo.cms.e4.users.handlers;
-
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.GroupsView;
-import org.argeo.cms.e4.users.UserAdminWrapper;
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Delete the selected groups */
-public class DeleteGroups {
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID +
-       // ".deleteGroups";
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-
-       @Inject
-       ESelectionService selectionService;
-
-       @SuppressWarnings("unchecked")
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               // ISelection selection = null;// HandlerUtil.getCurrentSelection(event);
-               // if (selection.isEmpty())
-               // return null;
-               //
-               // List<Group> groups = new ArrayList<Group>();
-               // Iterator<Group> it = ((IStructuredSelection) selection).iterator();
-
-               List<Group> selection = (List<Group>) selectionService.getSelection();
-               if (selection == null)
-                       return;
-
-               StringBuilder builder = new StringBuilder();
-               for (Group group : selection) {
-                       Group currGroup = group;
-                       String groupName = UserAdminUtils.getUserLocalId(currGroup.getName());
-                       // TODO add checks
-                       builder.append(groupName).append("; ");
-                       // groups.add(currGroup);
-               }
-
-               if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you "
-                               + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2)))
-                       return;
-
-               userAdminWrapper.beginTransactionIfNeeded();
-               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
-               // IWorkbenchPage iwp =
-               // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
-               for (Group group : selection) {
-                       String groupName = group.getName();
-                       // TODO find a way to close the editor cleanly if opened. Cannot be
-                       // done through the UserAdminListeners, it causes a
-                       // java.util.ConcurrentModificationException because disposing the
-                       // editor unregisters and disposes the listener
-                       // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName));
-                       // if (part != null)
-                       // iwp.closeEditor(part, false);
-                       userAdmin.removeRole(groupName);
-               }
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-
-               // Update the view
-               for (Group group : selection) {
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group));
-               }
-
-               // return null;
-       }
-
-       @CanExecute
-       public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               return part.getObject() instanceof GroupsView && selectionService.getSelection() != null;
-       }
-
-       /* DEPENDENCY INJECTION */
-       // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-       // this.userAdminWrapper = userAdminWrapper;
-       // }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java
deleted file mode 100644 (file)
index d1afd22..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.argeo.cms.e4.users.handlers;
-
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.UserAdminWrapper;
-import org.argeo.cms.e4.users.UsersView;
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.e4.ui.services.IServiceConstants;
-import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Delete the selected users */
-public class DeleteUsers {
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers";
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-
-       @SuppressWarnings("unchecked")
-       @Execute
-       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               // ISelection selection = null;// HandlerUtil.getCurrentSelection(event);
-               // if (selection.isEmpty())
-               // return null;
-               List<User> selection = (List<User>) selectionService.getSelection();
-               if (selection == null)
-                       return;
-
-//             Iterator<User> it = ((IStructuredSelection) selection).iterator();
-//             List<User> users = new ArrayList<User>();
-               StringBuilder builder = new StringBuilder();
-
-               for(User user:selection) {
-                       User currUser = user;
-//                     User currUser = it.next();
-                       String userName = UserAdminUtils.getUserLocalId(currUser.getName());
-                       if (UserAdminUtils.isCurrentUser(currUser)) {
-                               MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden",
-                                               "You cannot delete your own user this way.");
-                               return;
-                       }
-                       builder.append(userName).append("; ");
-//                     users.add(currUser);
-               }
-
-               if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users",
-                               "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2)))
-                       return;
-
-               userAdminWrapper.beginTransactionIfNeeded();
-               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
-               // IWorkbenchPage iwp =
-               // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
-
-               for (User user : selection) {
-                       String userName = user.getName();
-                       // TODO find a way to close the editor cleanly if opened. Cannot be
-                       // done through the UserAdminListeners, it causes a
-                       // java.util.ConcurrentModificationException because disposing the
-                       // editor unregisters and disposes the listener
-                       // IEditorPart part = iwp.findEditor(new UserEditorInput(userName));
-                       // if (part != null)
-                       // iwp.closeEditor(part, false);
-                       userAdmin.removeRole(userName);
-               }
-               userAdminWrapper.commitOrNotifyTransactionStateChange();
-
-               for (User user : selection) {
-                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user));
-               }
-       }
-
-       @CanExecute
-       public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
-               return part.getObject() instanceof UsersView && selectionService.getSelection() != null;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java
deleted file mode 100644 (file)
index 41e14e0..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-package org.argeo.cms.e4.users.handlers;
-
-import java.util.Dictionary;
-import java.util.Map;
-
-import javax.inject.Inject;
-
-import org.argeo.cms.e4.users.UserAdminWrapper;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.util.directory.DirectoryConf;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Create a new group */
-public class NewGroup {
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup";
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-
-       @Execute
-       public Object execute() {
-               NewGroupWizard newGroupWizard = new NewGroupWizard();
-               newGroupWizard.setWindowTitle("Group creation");
-               WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard);
-               dialog.open();
-               return null;
-       }
-
-       private class NewGroupWizard extends Wizard {
-
-               // Pages
-               private MainGroupInfoWizardPage mainGroupInfo;
-
-               // UI fields
-               private Text dNameTxt, commonNameTxt, descriptionTxt;
-               private Combo baseDnCmb;
-
-               public NewGroupWizard() {
-               }
-
-               @Override
-               public void addPages() {
-                       mainGroupInfo = new MainGroupInfoWizardPage();
-                       addPage(mainGroupInfo);
-               }
-
-               @SuppressWarnings({ "rawtypes", "unchecked" })
-               @Override
-               public boolean performFinish() {
-                       if (!canFinish())
-                               return false;
-                       String commonName = commonNameTxt.getText();
-                       try {
-                               userAdminWrapper.beginTransactionIfNeeded();
-                               String dn = getDn(commonName);
-                               Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP);
-                               Dictionary props = group.getProperties();
-                               String descStr = descriptionTxt.getText();
-                               if (EclipseUiUtils.notEmpty(descStr))
-                                       props.put(LdapAttrs.description.name(), descStr);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group));
-                               return true;
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot create new group " + commonName, e);
-                               return false;
-                       }
-               }
-
-               private class MainGroupInfoWizardPage extends WizardPage implements FocusListener {
-                       private static final long serialVersionUID = -3150193365151601807L;
-
-                       public MainGroupInfoWizardPage() {
-                               super("Main");
-                               setTitle("General information");
-                               setMessage("Please choose a domain, provide a common name " + "and a free description");
-                       }
-
-                       @Override
-                       public void createControl(Composite parent) {
-                               Composite bodyCmp = new Composite(parent, SWT.NONE);
-                               setControl(bodyCmp);
-                               bodyCmp.setLayout(new GridLayout(2, false));
-
-                               dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name");
-                               dNameTxt.setEnabled(false);
-
-                               baseDnCmb = createGridLC(bodyCmp, "Base DN");
-                               // Initialise before adding the listener to avoid NPE
-                               initialiseDnCmb(baseDnCmb);
-                               baseDnCmb.addFocusListener(this);
-
-                               commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name");
-                               commonNameTxt.addFocusListener(this);
-
-                               Label descLbl = new Label(bodyCmp, SWT.LEAD);
-                               descLbl.setText("Description");
-                               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
-                               descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER);
-                               descriptionTxt.setLayoutData(EclipseUiUtils.fillAll());
-                               descriptionTxt.addFocusListener(this);
-
-                               // Initialize buttons
-                               setPageComplete(false);
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void focusLost(FocusEvent event) {
-                               String name = commonNameTxt.getText();
-                               if (EclipseUiUtils.isEmpty(name))
-                                       dNameTxt.setText("");
-                               else
-                                       dNameTxt.setText(getDn(name));
-
-                               String message = checkComplete();
-                               if (message != null) {
-                                       setMessage(message, WizardPage.ERROR);
-                                       setPageComplete(false);
-                               } else {
-                                       setMessage("Complete", WizardPage.INFORMATION);
-                                       setPageComplete(true);
-                               }
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void focusGained(FocusEvent event) {
-                       }
-
-                       /** @return the error message or null if complete */
-                       protected String checkComplete() {
-                               String name = commonNameTxt.getText();
-
-                               if (name.trim().equals(""))
-                                       return "Common name must not be empty";
-                               Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name));
-                               if (role != null)
-                                       return "Group " + name + " already exists";
-                               return null;
-                       }
-
-                       @Override
-                       public void setVisible(boolean visible) {
-                               super.setVisible(visible);
-                               if (visible)
-                                       if (baseDnCmb.getSelectionIndex() == -1)
-                                               baseDnCmb.setFocus();
-                                       else
-                                               commonNameTxt.setFocus();
-                       }
-               }
-
-               private Map<String, String> getDns() {
-                       return userAdminWrapper.getKnownBaseDns(true);
-               }
-
-               private String getDn(String cn) {
-                       Map<String, String> dns = getDns();
-                       String bdn = baseDnCmb.getText();
-                       if (EclipseUiUtils.notEmpty(bdn)) {
-                               Dictionary<String, ?> props = DirectoryConf.uriAsProperties(dns.get(bdn));
-                               String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn;
-                               return dn;
-                       }
-                       return null;
-               }
-
-               private void initialiseDnCmb(Combo combo) {
-                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
-                       if (dns.isEmpty())
-                               throw new CmsException("No writable base dn found. Cannot create group");
-                       combo.setItems(dns.keySet().toArray(new String[0]));
-                       if (dns.size() == 1)
-                               combo.select(0);
-               }
-       }
-
-       private Combo createGridLC(Composite parent, String label) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return combo;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java
deleted file mode 100644 (file)
index 40a4460..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-package org.argeo.cms.e4.users.handlers;
-
-import java.util.Dictionary;
-import java.util.List;
-import java.util.Map;
-
-import javax.inject.Inject;
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-import javax.naming.ldap.Rdn;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.UiAdminUtils;
-import org.argeo.cms.e4.users.UserAdminWrapper;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.util.directory.DirectoryConf;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.jface.wizard.WizardDialog;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdminEvent;
-
-/** Open a wizard that enables creation of a new user. */
-public class NewUser {
-       // private final static Log log = LogFactory.getLog(NewUser.class);
-       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser";
-
-       /* DEPENDENCY INJECTION */
-       @Inject
-       private UserAdminWrapper userAdminWrapper;
-
-       @Execute
-       public Object execute() {
-               NewUserWizard newUserWizard = new NewUserWizard();
-               newUserWizard.setWindowTitle("User creation");
-               WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard);
-               dialog.open();
-               return null;
-       }
-
-       private class NewUserWizard extends Wizard {
-
-               // pages
-               private MainUserInfoWizardPage mainUserInfo;
-
-               // End user fields
-               private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt;
-               private Combo baseDnCmb;
-
-               public NewUserWizard() {
-
-               }
-
-               @Override
-               public void addPages() {
-                       mainUserInfo = new MainUserInfoWizardPage();
-                       addPage(mainUserInfo);
-                       String message = "Default wizard that also eases user creation tests:\n "
-                                       + "Mail and last name are automatically "
-                                       + "generated form the uid. Password are defauted to 'demo'.";
-                       mainUserInfo.setMessage(message, WizardPage.WARNING);
-               }
-
-               @SuppressWarnings({ "rawtypes", "unchecked" })
-               @Override
-               public boolean performFinish() {
-                       if (!canFinish())
-                               return false;
-                       String username = mainUserInfo.getUsername();
-                       userAdminWrapper.beginTransactionIfNeeded();
-                       try {
-                               User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER);
-
-                               Dictionary props = user.getProperties();
-
-                               String lastNameStr = lastNameTxt.getText();
-                               if (EclipseUiUtils.notEmpty(lastNameStr))
-                                       props.put(LdapAttrs.sn.name(), lastNameStr);
-
-                               String firstNameStr = firstNameTxt.getText();
-                               if (EclipseUiUtils.notEmpty(firstNameStr))
-                                       props.put(LdapAttrs.givenName.name(), firstNameStr);
-
-                               String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr);
-                               if (EclipseUiUtils.notEmpty(cn))
-                                       props.put(LdapAttrs.cn.name(), cn);
-
-                               String mailStr = primaryMailTxt.getText();
-                               if (EclipseUiUtils.notEmpty(mailStr))
-                                       props.put(LdapAttrs.mail.name(), mailStr);
-
-                               char[] password = mainUserInfo.getPassword();
-                               user.getCredentials().put(null, password);
-                               userAdminWrapper.commitOrNotifyTransactionStateChange();
-                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user));
-                               return true;
-                       } catch (Exception e) {
-                               ErrorFeedback.show("Cannot create new user " + username, e);
-                               return false;
-                       }
-               }
-
-               private class MainUserInfoWizardPage extends WizardPage implements ModifyListener {
-                       private static final long serialVersionUID = -3150193365151601807L;
-
-                       public MainUserInfoWizardPage() {
-                               super("Main");
-                               setTitle("Required Information");
-                       }
-
-                       @Override
-                       public void createControl(Composite parent) {
-                               Composite composite = new Composite(parent, SWT.NONE);
-                               composite.setLayout(new GridLayout(2, false));
-                               dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this);
-                               dNameTxt.setEnabled(false);
-
-                               baseDnCmb = createGridLC(composite, "Base DN");
-                               initialiseDnCmb(baseDnCmb);
-                               baseDnCmb.addModifyListener(this);
-                               baseDnCmb.addModifyListener(new ModifyListener() {
-                                       private static final long serialVersionUID = -1435351236582736843L;
-
-                                       @Override
-                                       public void modifyText(ModifyEvent event) {
-                                               String name = usernameTxt.getText();
-                                               dNameTxt.setText(getDn(name));
-                                       }
-                               });
-
-                               usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this);
-                               usernameTxt.addModifyListener(new ModifyListener() {
-                                       private static final long serialVersionUID = -1435351236582736843L;
-
-                                       @Override
-                                       public void modifyText(ModifyEvent event) {
-                                               String name = usernameTxt.getText();
-                                               if (name.trim().equals("")) {
-                                                       dNameTxt.setText("");
-                                                       lastNameTxt.setText("");
-                                                       primaryMailTxt.setText("");
-                                                       pwd1Txt.setText("");
-                                                       pwd2Txt.setText("");
-                                               } else {
-                                                       dNameTxt.setText(getDn(name));
-                                                       lastNameTxt.setText(name.toUpperCase());
-                                                       primaryMailTxt.setText(getMail(name));
-                                                       pwd1Txt.setText("demo");
-                                                       pwd2Txt.setText("demo");
-                                               }
-                                       }
-                               });
-
-                               primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this);
-                               firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this);
-                               lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this);
-                               pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this);
-                               pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this);
-                               setControl(composite);
-
-                               // Initialize buttons
-                               setPageComplete(false);
-                               getContainer().updateButtons();
-                       }
-
-                       @Override
-                       public void modifyText(ModifyEvent event) {
-                               String message = checkComplete();
-                               if (message != null) {
-                                       setMessage(message, WizardPage.ERROR);
-                                       setPageComplete(false);
-                               } else {
-                                       setMessage("Complete", WizardPage.INFORMATION);
-                                       setPageComplete(true);
-                               }
-                               getContainer().updateButtons();
-                       }
-
-                       /** @return error message or null if complete */
-                       protected String checkComplete() {
-                               String name = usernameTxt.getText();
-
-                               if (name.trim().equals(""))
-                                       return "User name must not be empty";
-                               Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name));
-                               if (role != null)
-                                       return "User " + name + " already exists";
-                               if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN))
-                                       return "Not a valid email address";
-                               if (lastNameTxt.getText().trim().equals(""))
-                                       return "Specify a last name";
-                               if (pwd1Txt.getText().trim().equals(""))
-                                       return "Specify a password";
-                               if (pwd2Txt.getText().trim().equals(""))
-                                       return "Repeat the password";
-                               if (!pwd2Txt.getText().equals(pwd1Txt.getText()))
-                                       return "Passwords are different";
-                               return null;
-                       }
-
-                       @Override
-                       public void setVisible(boolean visible) {
-                               super.setVisible(visible);
-                               if (visible)
-                                       if (baseDnCmb.getSelectionIndex() == -1)
-                                               baseDnCmb.setFocus();
-                                       else
-                                               usernameTxt.setFocus();
-                       }
-
-                       public String getUsername() {
-                               return usernameTxt.getText();
-                       }
-
-                       public char[] getPassword() {
-                               return pwd1Txt.getTextChars();
-                       }
-
-               }
-
-               private Map<String, String> getDns() {
-                       return userAdminWrapper.getKnownBaseDns(true);
-               }
-
-               private String getDn(String uid) {
-                       Map<String, String> dns = getDns();
-                       String bdn = baseDnCmb.getText();
-                       if (EclipseUiUtils.notEmpty(bdn)) {
-                               Dictionary<String, ?> props = DirectoryConf.uriAsProperties(dns.get(bdn));
-                               String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn;
-                               return dn;
-                       }
-                       return null;
-               }
-
-               private void initialiseDnCmb(Combo combo) {
-                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
-                       if (dns.isEmpty())
-                               throw new CmsException("No writable base dn found. Cannot create user");
-                       combo.setItems(dns.keySet().toArray(new String[0]));
-                       if (dns.size() == 1)
-                               combo.select(0);
-               }
-
-               private String getMail(String username) {
-                       if (baseDnCmb.getSelectionIndex() == -1)
-                               return null;
-                       String baseDn = baseDnCmb.getText();
-                       try {
-                               LdapName name = new LdapName(baseDn);
-                               List<Rdn> rdns = name.getRdns();
-                               return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue();
-                       } catch (InvalidNameException e) {
-                               throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e);
-                       }
-               }
-       }
-
-       private Combo createGridLC(Composite parent, String label) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               return combo;
-       }
-
-       /* DEPENDENCY INJECTION */
-       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
-               this.userAdminWrapper = userAdminWrapper;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java
deleted file mode 100644 (file)
index cf3db1d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Users management handlers. */
-package org.argeo.cms.e4.users.handlers;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java
deleted file mode 100644 (file)
index c6f14b0..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Users management perspective. */
-package org.argeo.cms.e4.users;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java
deleted file mode 100644 (file)
index 2d8db67..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the common name of a user */
-public class CommonNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 5256703081044911941L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getProperty(user, LdapAttrs.cn.name());
-       }
-
-       @Override
-       public String getToolTipText(Object element) {
-               return UserAdminUtils.getProperty((User) element, LdapAttrs.DN);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java
deleted file mode 100644 (file)
index e23729d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.osgi.service.useradmin.User;
-
-/** The human friendly domain name for the corresponding user. */
-public class DomainNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 5256703081044911941L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getDomainName(user);
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java
deleted file mode 100644 (file)
index 52d3b85..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the Primary Mail of a user */
-public class MailLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 8329764452141982707L;
-
-       @Override
-       public String getText(User user) {
-               return UserAdminUtils.getProperty(user, LdapAttrs.mail.name());
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java
deleted file mode 100644 (file)
index 8c94093..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.e4.users.SecurityAdminImages;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.swt.graphics.Image;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Provide a bundle specific image depending on the current user type */
-public class RoleIconLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 6550449442061090388L;
-
-       @Override
-       public String getText(User user) {
-               return "";
-       }
-
-       @Override
-       public Image getImage(Object element) {
-               User user = (User) element;
-               String dn = user.getName();
-               if (dn.endsWith(CmsConstants.ROLES_BASEDN))
-                       return SecurityAdminImages.ICON_ROLE;
-               else if (user.getType() == Role.GROUP) {
-                       String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory);
-                       if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP))
-                               return SecurityAdminImages.ICON_WORKGROUP;
-                       return SecurityAdminImages.ICON_GROUP;
-               } else
-                       return SecurityAdminImages.ICON_USER;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java
deleted file mode 100644 (file)
index 29873db..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import javax.naming.InvalidNameException;
-import javax.naming.ldap.LdapName;
-
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.jface.resource.JFaceResources;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.User;
-
-/**
- * Utility class that add font modifications to a column label provider
- * depending on the given user properties
- */
-public abstract class UserAdminAbstractLP extends ColumnLabelProvider {
-       private static final long serialVersionUID = 137336765024922368L;
-
-       // private Font italic;
-       private Font bold;
-
-       @Override
-       public Font getFont(Object element) {
-               // Self as bold
-               try {
-                       LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName();
-                       String userName = ((User) element).getName();
-                       LdapName userLdapName = new LdapName(userName);
-                       if (userLdapName.equals(selfUserName)) {
-                               if (bold == null)
-                                       bold = JFaceResources.getFontRegistry()
-                                                       .defaultFontDescriptor().setStyle(SWT.BOLD)
-                                                       .createFont(Display.getCurrent());
-                               return bold;
-                       }
-               } catch (InvalidNameException e) {
-                       throw new CmsException("cannot parse dn for " + element, e);
-               }
-
-               // Disabled as Italic
-               // Node userProfile = (Node) elem;
-               // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
-               // return italic;
-
-               return null;
-               // return super.getFont(element);
-       }
-
-       @Override
-       public String getText(Object element) {
-               User user = (User) element;
-               return getText(user);
-       }
-
-       public void setDisplay(Display display) {
-               // italic = JFaceResources.getFontRegistry().defaultFontDescriptor()
-               // .setStyle(SWT.ITALIC).createFont(display);
-               bold = JFaceResources.getFontRegistry().defaultFontDescriptor()
-                               .setStyle(SWT.BOLD).createFont(Display.getCurrent());
-       }
-
-       public abstract String getText(User user);
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java
deleted file mode 100644 (file)
index 56a2624..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.dnd.DragSourceEvent;
-import org.eclipse.swt.dnd.DragSourceListener;
-import org.osgi.service.useradmin.User;
-
-/** Default drag listener to modify group and users via the UI */
-public class UserDragListener implements DragSourceListener {
-       private static final long serialVersionUID = -2074337775033781454L;
-       private final Viewer viewer;
-
-       public UserDragListener(Viewer viewer) {
-               this.viewer = viewer;
-       }
-
-       public void dragStart(DragSourceEvent event) {
-               // TODO implement finer checks
-               IStructuredSelection selection = (IStructuredSelection) viewer
-                               .getSelection();
-               if (selection.isEmpty() || selection.size() > 1)
-                       event.doit = false;
-               else
-                       event.doit = true;
-       }
-
-       public void dragSetData(DragSourceEvent event) {
-               // TODO Support multiple selection
-               Object obj = ((IStructuredSelection) viewer.getSelection())
-                               .getFirstElement();
-               if (obj != null) {
-                       User user = (User) obj;
-                       event.data = user.getName();
-               }
-       }
-
-       public void dragFinished(DragSourceEvent event) {
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java
deleted file mode 100644 (file)
index 154b047..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.util.naming.LdapAttrs;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerFilter;
-import org.osgi.service.useradmin.User;
-
-/**
- * Filter user list using JFace mechanism on the client (yet on the server) side
- * rather than having the UserAdmin to process the search
- */
-public class UserFilter extends ViewerFilter {
-       private static final long serialVersionUID = 5082509381672880568L;
-
-       private String searchString;
-       private boolean showSystemRole = true;
-
-       private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(),
-                       LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() };
-
-       public void setSearchText(String s) {
-               // ensure that the value can be used for matching
-               if (notEmpty(s))
-                       searchString = ".*" + s.toLowerCase() + ".*";
-               else
-                       searchString = ".*";
-       }
-
-       public void setShowSystemRole(boolean showSystemRole) {
-               this.showSystemRole = showSystemRole;
-       }
-
-       @Override
-       public boolean select(Viewer viewer, Object parentElement, Object element) {
-               User user = (User) element;
-               if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.ROLES_BASEDN + ")"))
-                       // UserAdminUtils.getProperty(user, LdifName.dn.name())
-                       // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN))
-                       return false;
-
-               if (searchString == null || searchString.length() == 0)
-                       return true;
-
-               if (user.getName().matches(searchString))
-                       return true;
-
-               for (String key : knownProps) {
-                       String currVal = UserAdminUtils.getProperty(user, key);
-                       if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString))
-                               return true;
-               }
-               return false;
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java
deleted file mode 100644 (file)
index 3cd00eb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.argeo.cms.e4.users.providers;
-
-import org.osgi.service.useradmin.User;
-
-/** Simply declare a label provider that returns the username of a user */
-public class UserNameLP extends UserAdminAbstractLP {
-       private static final long serialVersionUID = 6550449442061090388L;
-
-       @Override
-       public String getText(User user) {
-               return user.getName();
-       }
-}
diff --git a/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/eclipse/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java
deleted file mode 100644 (file)
index 33bef8d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Users management content providers. */
-package org.argeo.cms.e4.users.providers;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/.classpath b/eclipse/org.argeo.cms.swt/.classpath
deleted file mode 100644 (file)
index e03d341..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/eclipse/org.argeo.cms.swt/.project b/eclipse/org.argeo.cms.swt/.project
deleted file mode 100644 (file)
index 8ac021b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.swt</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/eclipse/org.argeo.cms.swt/META-INF/.gitignore b/eclipse/org.argeo.cms.swt/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml b/eclipse/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml
deleted file mode 100644 (file)
index 4f2a405..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS User App">
-   <implementation class="org.argeo.cms.swt.app.CmsUserApp"/>
-   <property name="argeo.cms.app.contextName" type="String" value="cms/user"/>
-   <service>
-      <provide interface="org.argeo.api.cms.CmsApp"/>
-   </service>
-   <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
-   <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
-</scr:component>
diff --git a/eclipse/org.argeo.cms.swt/bnd.bnd b/eclipse/org.argeo.cms.swt/bnd.bnd
deleted file mode 100644 (file)
index 2dda08b..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-Import-Package: org.eclipse.swt,\
-org.eclipse.jface.window,\
-org.eclipse.core.commands.common,\
-javax.servlet.*;version="[3,5)",\
-*
-
-Bundle-ActivationPolicy: lazy
-                       
-Service-Component: \
-OSGI-INF/cmsUserApp.xml
-                       
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/build.properties b/eclipse/org.argeo.cms.swt/build.properties
deleted file mode 100644 (file)
index 5f0f21a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               OSGI-INF/cmsUserApp.xml
-source.. = src/
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/add.png b/eclipse/org.argeo.cms.swt/icons/actions/add.png
deleted file mode 100644 (file)
index 5c06bf0..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/add.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/close-all.png b/eclipse/org.argeo.cms.swt/icons/actions/close-all.png
deleted file mode 100644 (file)
index 81bfc95..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/close-all.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/delete.png b/eclipse/org.argeo.cms.swt/icons/actions/delete.png
deleted file mode 100644 (file)
index 9712723..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/delete.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/edit.png b/eclipse/org.argeo.cms.swt/icons/actions/edit.png
deleted file mode 100644 (file)
index ad3db9f..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/edit.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/save-all.png b/eclipse/org.argeo.cms.swt/icons/actions/save-all.png
deleted file mode 100644 (file)
index f48ed32..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/save-all.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/actions/save.png b/eclipse/org.argeo.cms.swt/icons/actions/save.png
deleted file mode 100644 (file)
index 1c58ada..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/actions/save.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/active.gif b/eclipse/org.argeo.cms.swt/icons/active.gif
deleted file mode 100644 (file)
index 7d24707..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/active.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/add.gif b/eclipse/org.argeo.cms.swt/icons/add.gif
deleted file mode 100644 (file)
index 252d7eb..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/add.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/add.png b/eclipse/org.argeo.cms.swt/icons/add.png
deleted file mode 100644 (file)
index c7edfec..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/add.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/addFolder.gif b/eclipse/org.argeo.cms.swt/icons/addFolder.gif
deleted file mode 100644 (file)
index d3f43d9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/addFolder.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif b/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif
deleted file mode 100644 (file)
index a6b251f..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/addPrivileges.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/addRepo.gif b/eclipse/org.argeo.cms.swt/icons/addRepo.gif
deleted file mode 100644 (file)
index 26d81c0..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/addRepo.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/addWorkspace.png b/eclipse/org.argeo.cms.swt/icons/addWorkspace.png
deleted file mode 100644 (file)
index bbee775..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/addWorkspace.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/adminLog.gif b/eclipse/org.argeo.cms.swt/icons/adminLog.gif
deleted file mode 100644 (file)
index 6ef3bca..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/adminLog.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/batch.gif b/eclipse/org.argeo.cms.swt/icons/batch.gif
deleted file mode 100644 (file)
index b8ca14a..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/batch.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/begin.gif b/eclipse/org.argeo.cms.swt/icons/begin.gif
deleted file mode 100755 (executable)
index feb8e94..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/begin.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/binary.png b/eclipse/org.argeo.cms.swt/icons/binary.png
deleted file mode 100644 (file)
index fdf4f82..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/binary.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/browser.gif b/eclipse/org.argeo.cms.swt/icons/browser.gif
deleted file mode 100644 (file)
index 6c7320c..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/browser.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/bundles.gif b/eclipse/org.argeo.cms.swt/icons/bundles.gif
deleted file mode 100644 (file)
index e9a6bd9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/bundles.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/changePassword.gif b/eclipse/org.argeo.cms.swt/icons/changePassword.gif
deleted file mode 100644 (file)
index 274a850..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/changePassword.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/clear.gif b/eclipse/org.argeo.cms.swt/icons/clear.gif
deleted file mode 100644 (file)
index 6bc10f9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/clear.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/close-all.png b/eclipse/org.argeo.cms.swt/icons/close-all.png
deleted file mode 100644 (file)
index 85d4d42..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/close-all.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/commit.gif b/eclipse/org.argeo.cms.swt/icons/commit.gif
deleted file mode 100755 (executable)
index 876f3eb..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/commit.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/delete.png b/eclipse/org.argeo.cms.swt/icons/delete.png
deleted file mode 100644 (file)
index 676a39d..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/delete.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/dumpNode.gif b/eclipse/org.argeo.cms.swt/icons/dumpNode.gif
deleted file mode 100644 (file)
index 14eb1be..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/dumpNode.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/file.gif b/eclipse/org.argeo.cms.swt/icons/file.gif
deleted file mode 100644 (file)
index ef30288..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/file.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/folder.gif b/eclipse/org.argeo.cms.swt/icons/folder.gif
deleted file mode 100644 (file)
index 42e027c..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/folder.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/getSize.gif b/eclipse/org.argeo.cms.swt/icons/getSize.gif
deleted file mode 100644 (file)
index b05bf3e..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/getSize.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/group.png b/eclipse/org.argeo.cms.swt/icons/group.png
deleted file mode 100644 (file)
index cc6683a..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/group.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/home.gif b/eclipse/org.argeo.cms.swt/icons/home.gif
deleted file mode 100644 (file)
index fd0c669..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/home.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/home.png b/eclipse/org.argeo.cms.swt/icons/home.png
deleted file mode 100644 (file)
index 5eb0967..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/home.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/import_fs.png b/eclipse/org.argeo.cms.swt/icons/import_fs.png
deleted file mode 100644 (file)
index d7c890c..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/import_fs.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/installed.gif b/eclipse/org.argeo.cms.swt/icons/installed.gif
deleted file mode 100644 (file)
index 2988716..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/installed.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/log.gif b/eclipse/org.argeo.cms.swt/icons/log.gif
deleted file mode 100644 (file)
index e3ecc55..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/log.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/logout.png b/eclipse/org.argeo.cms.swt/icons/logout.png
deleted file mode 100644 (file)
index f2952fa..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/logout.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/maintenance.gif b/eclipse/org.argeo.cms.swt/icons/maintenance.gif
deleted file mode 100644 (file)
index e5690ec..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/maintenance.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/node.gif b/eclipse/org.argeo.cms.swt/icons/node.gif
deleted file mode 100644 (file)
index 364c0e7..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/node.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/nodes.gif b/eclipse/org.argeo.cms.swt/icons/nodes.gif
deleted file mode 100644 (file)
index bba3dbc..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/nodes.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif b/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif
deleted file mode 100644 (file)
index e9a6bd9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/osgi_explorer.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/password.gif b/eclipse/org.argeo.cms.swt/icons/password.gif
deleted file mode 100644 (file)
index a6b251f..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/password.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/person-logged-in.png b/eclipse/org.argeo.cms.swt/icons/person-logged-in.png
deleted file mode 100644 (file)
index 87acc14..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/person-logged-in.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/person.png b/eclipse/org.argeo.cms.swt/icons/person.png
deleted file mode 100644 (file)
index 7d979a5..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/person.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/query.png b/eclipse/org.argeo.cms.swt/icons/query.png
deleted file mode 100644 (file)
index 54c089d..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/query.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/refresh.png b/eclipse/org.argeo.cms.swt/icons/refresh.png
deleted file mode 100644 (file)
index 71b3481..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/refresh.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/remote_connected.gif b/eclipse/org.argeo.cms.swt/icons/remote_connected.gif
deleted file mode 100644 (file)
index 1492b4e..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/remote_connected.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif b/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif
deleted file mode 100644 (file)
index 6c54da9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/remote_disconnected.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/remove.gif b/eclipse/org.argeo.cms.swt/icons/remove.gif
deleted file mode 100644 (file)
index 0ae6dec..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/remove.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif b/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif
deleted file mode 100644 (file)
index aa78fd2..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/removePrivileges.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/rename.gif b/eclipse/org.argeo.cms.swt/icons/rename.gif
deleted file mode 100644 (file)
index 8048405..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/rename.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/repositories.gif b/eclipse/org.argeo.cms.swt/icons/repositories.gif
deleted file mode 100644 (file)
index c13bea1..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/repositories.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/repository_connected.gif b/eclipse/org.argeo.cms.swt/icons/repository_connected.gif
deleted file mode 100644 (file)
index a15fa55..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/repository_connected.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif b/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif
deleted file mode 100644 (file)
index 4576dc5..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/repository_disconnected.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/resolved.gif b/eclipse/org.argeo.cms.swt/icons/resolved.gif
deleted file mode 100644 (file)
index f4a1ea1..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/resolved.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/role.gif b/eclipse/org.argeo.cms.swt/icons/role.gif
deleted file mode 100644 (file)
index 274a850..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/role.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/rollback.gif b/eclipse/org.argeo.cms.swt/icons/rollback.gif
deleted file mode 100755 (executable)
index c753995..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/rollback.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/save-all.png b/eclipse/org.argeo.cms.swt/icons/save-all.png
deleted file mode 100644 (file)
index b68a29b..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/save-all.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/save.gif b/eclipse/org.argeo.cms.swt/icons/save.gif
deleted file mode 100644 (file)
index 654ad7b..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/save.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/save.png b/eclipse/org.argeo.cms.swt/icons/save.png
deleted file mode 100644 (file)
index f27ef2d..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/save.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/save_security.png b/eclipse/org.argeo.cms.swt/icons/save_security.png
deleted file mode 100644 (file)
index ca41dc9..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/save_security.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png b/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png
deleted file mode 100644 (file)
index fb7d08d..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/save_security_disabled.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/security.gif b/eclipse/org.argeo.cms.swt/icons/security.gif
deleted file mode 100644 (file)
index 57fb95e..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/security.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/service_published.gif b/eclipse/org.argeo.cms.swt/icons/service_published.gif
deleted file mode 100644 (file)
index 17f771a..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/service_published.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/service_referenced.gif b/eclipse/org.argeo.cms.swt/icons/service_referenced.gif
deleted file mode 100644 (file)
index c24a95f..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/service_referenced.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/sort.gif b/eclipse/org.argeo.cms.swt/icons/sort.gif
deleted file mode 100644 (file)
index 23c5d0b..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/sort.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/starting.gif b/eclipse/org.argeo.cms.swt/icons/starting.gif
deleted file mode 100644 (file)
index 563743d..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/starting.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/sync.gif b/eclipse/org.argeo.cms.swt/icons/sync.gif
deleted file mode 100644 (file)
index b4fa052..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/sync.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/user.gif b/eclipse/org.argeo.cms.swt/icons/user.gif
deleted file mode 100644 (file)
index 90a0014..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/user.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/users.gif b/eclipse/org.argeo.cms.swt/icons/users.gif
deleted file mode 100644 (file)
index 2de7edd..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/users.gif and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/workgroup.png b/eclipse/org.argeo.cms.swt/icons/workgroup.png
deleted file mode 100644 (file)
index 7fef996..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/workgroup.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/workgroup.xcf b/eclipse/org.argeo.cms.swt/icons/workgroup.xcf
deleted file mode 100644 (file)
index f517c82..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/workgroup.xcf and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/workspace_connected.png b/eclipse/org.argeo.cms.swt/icons/workspace_connected.png
deleted file mode 100644 (file)
index 0430baa..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/workspace_connected.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png b/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png
deleted file mode 100644 (file)
index fddcb8c..0000000
Binary files a/eclipse/org.argeo.cms.swt/icons/workspace_disconnected.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java
deleted file mode 100644 (file)
index 33841a1..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-package org.argeo.cms.jface.dialog;
-
-import java.lang.reflect.InvocationTargetException;
-
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.swt.dialogs.LightweightDialog;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.operation.IRunnableWithProgress;
-import org.eclipse.jface.wizard.IWizard;
-import org.eclipse.jface.wizard.IWizardContainer2;
-import org.eclipse.jface.wizard.IWizardPage;
-import org.eclipse.swt.SWT;
-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.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;
-
-/** A wizard dialog based on {@link LightweightDialog}. */
-public class CmsWizardDialog extends LightweightDialog implements IWizardContainer2 {
-       private static final long serialVersionUID = -2123153353654812154L;
-
-       private IWizard wizard;
-       private IWizardPage currentPage;
-       private int currentPageIndex;
-
-       private Label titleBar;
-       private Label message;
-       private Composite[] pageBodies;
-       private Composite buttons;
-       private Button back;
-       private Button next;
-       private Button finish;
-
-       public CmsWizardDialog(Shell parentShell, IWizard wizard) {
-               super(parentShell);
-               this.wizard = wizard;
-               wizard.setContainer(this);
-               // create the pages
-               wizard.addPages();
-               currentPage = wizard.getStartingPage();
-               if (currentPage == null)
-                       throw new IllegalArgumentException("At least one wizard page is required");
-       }
-
-       @Override
-       protected Control createDialogArea(Composite parent) {
-               updateWindowTitle();
-
-               Composite messageArea = new Composite(parent, SWT.NONE);
-               messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               {
-                       messageArea.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
-                       titleBar = new Label(messageArea, SWT.WRAP);
-                       titleBar.setFont(EclipseUiUtils.getBoldFont(parent));
-                       titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
-                       updateTitleBar();
-                       Button cancelButton = new Button(messageArea, SWT.FLAT);
-                       cancelButton.setText(CmsMsg.cancel.lead());
-                       cancelButton.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false, 1, 3));
-                       cancelButton.addSelectionListener((Selected) (e) -> closeShell(CANCEL));
-                       message = new Label(messageArea, SWT.WRAP);
-                       message.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2));
-                       updateMessage();
-               }
-
-               Composite body = new Composite(parent, SWT.BORDER);
-               body.setLayout(new FormLayout());
-               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               pageBodies = new Composite[wizard.getPageCount()];
-               IWizardPage[] pages = wizard.getPages();
-               for (int i = 0; i < pages.length; i++) {
-                       pageBodies[i] = new Composite(body, SWT.NONE);
-                       pageBodies[i].setLayout(CmsSwtUtils.noSpaceGridLayout());
-                       setSwitchingFormData(pageBodies[i]);
-                       pages[i].createControl(pageBodies[i]);
-               }
-               showPage(currentPage);
-
-               buttons = new Composite(parent, SWT.NONE);
-               buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
-               {
-                       boolean singlePage = wizard.getPageCount() == 1;
-                       // singlePage = false;// dev
-                       GridLayout layout = new GridLayout(singlePage ? 1 : 3, true);
-                       layout.marginWidth = 0;
-                       layout.marginHeight = 0;
-                       buttons.setLayout(layout);
-                       // TODO revert order for right-to-left languages
-
-                       if (!singlePage) {
-                               back = new Button(buttons, SWT.PUSH);
-                               back.setText(CmsMsg.wizardBack.lead());
-                               back.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                               back.addSelectionListener((Selected) (e) -> backPressed());
-
-                               next = new Button(buttons, SWT.PUSH);
-                               next.setText(CmsMsg.wizardNext.lead());
-                               next.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                               next.addSelectionListener((Selected) (e) -> nextPressed());
-                       }
-                       finish = new Button(buttons, SWT.PUSH);
-                       finish.setText(CmsMsg.wizardFinish.lead());
-                       finish.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                       finish.addSelectionListener((Selected) (e) -> finishPressed());
-
-                       updateButtons();
-               }
-               return body;
-       }
-
-       @Override
-       public IWizardPage getCurrentPage() {
-               return currentPage;
-       }
-
-       @Override
-       public Shell getShell() {
-               return getForegoundShell();
-       }
-
-       @Override
-       public void showPage(IWizardPage page) {
-               IWizardPage[] pages = wizard.getPages();
-               int index = -1;
-               for (int i = 0; i < pages.length; i++) {
-                       if (page == pages[i]) {
-                               index = i;
-                               break;
-                       }
-               }
-               if (index < 0)
-                       throw new IllegalArgumentException("Cannot find index of wizard page " + page);
-               pageBodies[index].moveAbove(pageBodies[currentPageIndex]);
-
-               // // clear
-               // for (Control c : body.getChildren())
-               // c.dispose();
-               // page.createControl(body);
-               // body.layout(true, true);
-               currentPageIndex = index;
-               currentPage = page;
-       }
-
-       @Override
-       public void updateButtons() {
-               if (back != null)
-                       back.setEnabled(wizard.getPreviousPage(currentPage) != null);
-               if (next != null)
-                       next.setEnabled(wizard.getNextPage(currentPage) != null && currentPage.canFlipToNextPage());
-               if (finish != null) {
-                       finish.setEnabled(wizard.canFinish());
-               }
-       }
-
-       @Override
-       public void updateMessage() {
-               if (currentPage.getMessage() != null)
-                       message.setText(currentPage.getMessage());
-       }
-
-       @Override
-       public void updateTitleBar() {
-               if (currentPage.getTitle() != null)
-                       titleBar.setText(currentPage.getTitle());
-       }
-
-       @Override
-       public void updateWindowTitle() {
-               setTitle(wizard.getWindowTitle());
-       }
-
-       @Override
-       public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
-                       throws InvocationTargetException, InterruptedException {
-               // FIXME it creates a dependency to Eclipse Core Runtime
-               // runnable.run(null);
-       }
-
-       @Override
-       public void updateSize() {
-               // TODO pack?
-       }
-
-       protected boolean onCancel() {
-               return wizard.performCancel();
-       }
-
-       protected void nextPressed() {
-               IWizardPage page = wizard.getNextPage(currentPage);
-               showPage(page);
-               updateButtons();
-       }
-
-       protected void backPressed() {
-               IWizardPage page = wizard.getPreviousPage(currentPage);
-               showPage(page);
-               updateButtons();
-       }
-
-       protected void finishPressed() {
-               if (wizard.performFinish())
-                       closeShell(OK);
-       }
-
-       private static void setSwitchingFormData(Composite composite) {
-               FormData fdLabel = new FormData();
-               fdLabel.top = new FormAttachment(0, 0);
-               fdLabel.left = new FormAttachment(0, 0);
-               fdLabel.right = new FormAttachment(100, 0);
-               fdLabel.bottom = new FormAttachment(100, 0);
-               composite.setLayoutData(fdLabel);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java
deleted file mode 100644 (file)
index 874ea96..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.argeo.cms.swt;
-
-/** @deprecated Use standard Java {@link RuntimeException} instead. */
-@Deprecated
-public class CmsException extends RuntimeException {
-       private static final long serialVersionUID = -5341764743356771313L;
-
-       public CmsException(String message) {
-               super(message);
-       }
-
-       public CmsException(String message, Throwable e) {
-               super(message, e);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java
deleted file mode 100644 (file)
index 9eba6f6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.cms.swt;
-
-/** Styles references in the CSS. */
-@Deprecated
-public interface CmsStyles {
-       // General
-       public final static String CMS_SHELL = "cms_shell";
-       public final static String CMS_MENU_LINK = "cms_menu_link";
-
-       // Header
-       public final static String CMS_HEADER = "cms_header";
-       public final static String CMS_HEADER_LEAD = "cms_header-lead";
-       public final static String CMS_HEADER_CENTER = "cms_header-center";
-       public final static String CMS_HEADER_END = "cms_header-end";
-
-       public final static String CMS_LEAD = "cms_lead";
-       public final static String CMS_END = "cms_end";
-       public final static String CMS_FOOTER = "cms_footer";
-
-       public final static String CMS_USER_MENU = "cms_user_menu";
-       public final static String CMS_USER_MENU_LINK = "cms_user_menu-link";
-       public final static String CMS_USER_MENU_ITEM = "cms_user_menu-item";
-       public final static String CMS_LOGIN_DIALOG = "cms_login_dialog";
-       public final static String CMS_LOGIN_DIALOG_USERNAME = "cms_login_dialog-username";
-       public final static String CMS_LOGIN_DIALOG_PASSWORD = "cms_login_dialog-password";
-
-       // Body
-       public final static String CMS_SCROLLED_AREA = "cms_scrolled_area";
-       public final static String CMS_BODY = "cms_body";
-       public final static String CMS_STATIC_TEXT = "cms_static-text";
-       public final static String CMS_LINK = "cms_link";
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java
deleted file mode 100644 (file)
index 7669b15..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.eclipse.swt.graphics.Image;
-
-/** SWT specific {@link CmsTheme}. */
-public interface CmsSwtTheme extends CmsTheme {
-//     /** The image registered at this path, or <code>null</code> if not found. */
-//     Image getImage(String path);
-
-       /**
-        * And icon with this file name (without the extension), with a best effort to
-        * find the appropriate size, or <code>null</code> if not found.
-        * 
-        * @param name          An icon file name without path and extension.
-        * @param preferredSize the preferred size, if <code>null</code>,
-        *                      {@link #getDefaultIconSize()} will be tried.
-        */
-       Image getIcon(String name, Integer preferredSize);
-
-       Image getSmallIcon(CmsIcon icon);
-       
-       Image getBigIcon(CmsIcon icon);
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java
deleted file mode 100644 (file)
index 2fb79f4..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.argeo.api.cms.ux.CmsUi;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** A basic {@link CmsUi}, based on an SWT {@link Composite}. */
-public class CmsSwtUi extends Composite implements CmsUi {
-
-       private static final long serialVersionUID = -107939076610406448L;
-
-       public CmsSwtUi(Composite parent, int style) {
-               super(parent, style);
-               setLayout(new GridLayout());
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java
deleted file mode 100644 (file)
index 5d96409..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-package org.argeo.cms.swt;
-
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.api.cms.ux.CmsStyle;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Image;
-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.RowData;
-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.Layout;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.widgets.Widget;
-
-/** SWT utilities. */
-public class CmsSwtUtils {
-       /*
-        * THEME AND VIEW
-        */
-
-       public static CmsSwtTheme getCmsTheme(Composite parent) {
-               CmsSwtTheme theme = (CmsSwtTheme) parent.getData(CmsTheme.class.getName());
-               if (theme == null) {
-                       // find parent shell
-                       Shell topShell = parent.getShell();
-                       while (topShell.getParent() != null)
-                               topShell = (Shell) topShell.getParent();
-                       theme = (CmsSwtTheme) topShell.getData(CmsTheme.class.getName());
-                       parent.setData(CmsTheme.class.getName(), theme);
-               }
-               return theme;
-       }
-
-       public static void registerCmsTheme(Shell shell, CmsTheme theme) {
-               // find parent shell
-               Shell topShell = shell;
-               while (topShell.getParent() != null)
-                       topShell = (Shell) topShell.getParent();
-               // check if already set
-               if (topShell.getData(CmsTheme.class.getName()) != null) {
-                       CmsTheme registeredTheme = (CmsTheme) topShell.getData(CmsTheme.class.getName());
-                       throw new IllegalArgumentException(
-                                       "Theme " + registeredTheme.getThemeId() + " already registered in this shell");
-               }
-               topShell.setData(CmsTheme.class.getName(), theme);
-       }
-
-       public static CmsView getCmsView(Control parent) {
-               // find parent shell
-               Shell topShell = parent.getShell();
-               while (topShell.getParent() != null)
-                       topShell = (Shell) topShell.getParent();
-               return (CmsView) topShell.getData(CmsView.class.getName());
-       }
-
-       public static void registerCmsView(Shell shell, CmsView view) {
-               // find parent shell
-               Shell topShell = shell;
-               while (topShell.getParent() != null)
-                       topShell = (Shell) topShell.getParent();
-               // check if already set
-               if (topShell.getData(CmsView.class.getName()) != null) {
-                       CmsView registeredView = (CmsView) topShell.getData(CmsView.class.getName());
-                       throw new IllegalArgumentException("Cms view " + registeredView + " already registered in this shell");
-               }
-               shell.setData(CmsView.class.getName(), view);
-       }
-
-       /*
-        * EVENTS
-        */
-
-       /** Sends an event via {@link CmsView#sendEvent(String, Map)}. */
-       public static void sendEventOnSelect(Control control, String topic, Map<String, Object> properties) {
-               SelectionListener listener = (Selected) (e) -> {
-                       getCmsView(control.getParent()).sendEvent(topic, properties);
-               };
-               if (control instanceof Button) {
-                       ((Button) control).addSelectionListener(listener);
-               } else
-                       throw new UnsupportedOperationException("Control type " + control.getClass() + " is not supported.");
-       }
-
-       /**
-        * Convenience method to sends an event via
-        * {@link CmsView#sendEvent(String, Map)}.
-        */
-       public static void sendEventOnSelect(Control control, String topic, String key, Object value) {
-               Map<String, Object> properties = new HashMap<>();
-               properties.put(key, value);
-               sendEventOnSelect(control, topic, properties);
-       }
-
-       /*
-        * ICONS
-        */
-       /** Get a small icon from this theme. */
-       public static Image getSmallIcon(CmsTheme theme, CmsIcon icon) {
-               return ((CmsSwtTheme) theme).getSmallIcon(icon);
-       }
-
-       /** Get a big icon from this theme. */
-       public static Image getBigIcon(CmsTheme theme, CmsIcon icon) {
-               return ((CmsSwtTheme) theme).getBigIcon(icon);
-       }
-
-       /*
-        * LAYOUT INDEPENDENT
-        */
-       /** Takes the most space possible, depending on parent layout. */
-       public static void fill(Control control) {
-               Layout parentLayout = control.getParent().getLayout();
-               if (parentLayout == null)
-                       throw new IllegalStateException("Parent layout is not set");
-               if (parentLayout instanceof GridLayout) {
-                       control.setLayoutData(fillAll());
-               } else if (parentLayout instanceof FormLayout) {
-                       control.setLayoutData(coverAll());
-               } else {
-                       throw new IllegalArgumentException("Unsupported parent layout  " + parentLayout.getClass().getName());
-               }
-       }
-
-       /*
-        * GRID LAYOUT
-        */
-       /** A {@link GridLayout} without any spacing and one column. */
-       public static GridLayout noSpaceGridLayout() {
-               return noSpaceGridLayout(new GridLayout());
-       }
-
-       /**
-        * A {@link GridLayout} without any spacing and multiple columns of unequal
-        * width.
-        */
-       public static GridLayout noSpaceGridLayout(int columns) {
-               return noSpaceGridLayout(new GridLayout(columns, false));
-       }
-
-       /** @return the same layout, with spaces removed. */
-       public static GridLayout noSpaceGridLayout(GridLayout layout) {
-               layout.horizontalSpacing = 0;
-               layout.verticalSpacing = 0;
-               layout.marginWidth = 0;
-               layout.marginHeight = 0;
-               return layout;
-       }
-
-       public static GridData fillAll() {
-               return new GridData(SWT.FILL, SWT.FILL, true, true);
-       }
-
-       public static GridData fillWidth() {
-               return grabWidth(SWT.FILL, SWT.FILL);
-       }
-
-       public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
-               return new GridData(horizontalAlignment, horizontalAlignment, true, false);
-       }
-
-       public static GridData fillHeight() {
-               return grabHeight(SWT.FILL, SWT.FILL);
-       }
-
-       public static GridData grabHeight(int horizontalAlignment, int verticalAlignment) {
-               return new GridData(horizontalAlignment, horizontalAlignment, false, true);
-       }
-
-       /*
-        * ROW LAYOUT
-        */
-       /** @return the same layout, with margins removed. */
-       public static RowLayout noMarginsRowLayout(RowLayout rowLayout) {
-               rowLayout.marginTop = 0;
-               rowLayout.marginBottom = 0;
-               rowLayout.marginLeft = 0;
-               rowLayout.marginRight = 0;
-               return rowLayout;
-       }
-
-       public static RowLayout noMarginsRowLayout(int type) {
-               return noMarginsRowLayout(new RowLayout(type));
-       }
-
-       public static RowData rowData16px() {
-               return new RowData(16, 16);
-       }
-
-       /*
-        * FORM LAYOUT
-        */
-       public static FormData coverAll() {
-               FormData fdLabel = new FormData();
-               fdLabel.top = new FormAttachment(0, 0);
-               fdLabel.left = new FormAttachment(0, 0);
-               fdLabel.right = new FormAttachment(100, 0);
-               fdLabel.bottom = new FormAttachment(100, 0);
-               return fdLabel;
-       }
-
-       /*
-        * STYLING
-        */
-
-       /** Style widget */
-       public static <T extends Widget> T style(T widget, String style) {
-               if (style == null)
-                       return widget;// does nothing
-               EclipseUiSpecificUtils.setStyleData(widget, style);
-               if (widget instanceof Control) {
-                       CmsView cmsView = getCmsView((Control) widget);
-                       if (cmsView != null)
-                               cmsView.applyStyles(widget);
-               }
-               return widget;
-       }
-
-       /** Style widget */
-       public static <T extends Widget> T style(T widget, CmsStyle style) {
-               return style(widget, style.style());
-       }
-
-       /** Enable markups on widget */
-       public static <T extends Widget> T markup(T widget) {
-               EclipseUiSpecificUtils.setMarkupData(widget);
-               return widget;
-       }
-
-       /** Disable markup validation. */
-       public static <T extends Widget> T disableMarkupValidation(T widget) {
-               EclipseUiSpecificUtils.setMarkupValidationDisabledData(widget);
-               return widget;
-       }
-
-       /**
-        * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}.
-        * 
-        * @param widget the widget to style and to use in order to display text
-        * @param txt    the object to display via its <code>toString()</code> method.
-        *               This argument should not be null, but if it is null and
-        *               assertions are disabled "<null>" is displayed instead; if
-        *               assertions are enabled the call will fail.
-        * 
-        * @see markup
-        */
-       public static <T extends Widget> T text(T widget, Object txt) {
-               assert txt != null;
-               String str = txt != null ? txt.toString() : "<null>";
-               markup(widget);
-               if (widget instanceof Label)
-                       ((Label) widget).setText(str);
-               else if (widget instanceof Button)
-                       ((Button) widget).setText(str);
-               else if (widget instanceof Text)
-                       ((Text) widget).setText(str);
-               else
-                       throw new IllegalArgumentException("Unsupported widget type " + widget.getClass());
-               return widget;
-       }
-
-       /** A {@link Label} with markup activated. */
-       public static Label lbl(Composite parent, Object txt) {
-               return text(new Label(parent, SWT.NONE), txt);
-       }
-
-       /** A read-only {@link Text} whose content can be copy/pasted. */
-       public static Text txt(Composite parent, Object txt) {
-               return text(new Text(parent, SWT.NONE), txt);
-       }
-
-       /** Dispose all children of a Composite */
-       public static void clear(Composite composite) {
-               if (composite.isDisposed())
-                       return;
-               for (Control child : composite.getChildren())
-                       child.dispose();
-       }
-
-       /** Clean reserved URL characters for use in HTTP links. */
-       public static String cleanPathForUrl(String path) {
-               StringTokenizer st = new StringTokenizer(path, "/");
-               StringBuilder sb = new StringBuilder();
-               while (st.hasMoreElements()) {
-                       sb.append('/');
-                       String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
-                       encoded = encoded.replace("+", "%20");
-                       sb.append(encoded);
-
-               }
-               return sb.toString();
-       }
-
-       /** Singleton. */
-       private CmsSwtUtils() {
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java
deleted file mode 100644 (file)
index b818b06..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-
-/**
- * {@link MouseListener#mouseDoubleClick(MouseEvent)} as a functional interface
- * in order to use as a short lambda expression in UI code.
- * {@link MouseListener#mouseDownouseEvent)} and
- * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default.
- */
-@FunctionalInterface
-public interface MouseDoubleClick extends MouseListener {
-       @Override
-       void mouseDoubleClick(MouseEvent e);
-
-       @Override
-       default void mouseDown(MouseEvent e) {
-               // does nothing
-       }
-
-       @Override
-       default void mouseUp(MouseEvent e) {
-               // does nothing
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java
deleted file mode 100644 (file)
index baecb00..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-
-/**
- * {@link MouseListener#mouseDown(MouseEvent)} as a functional interface in
- * order to use as a short lambda expression in UI code.
- * {@link MouseListener#mouseDoubleClick(MouseEvent)} and
- * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default.
- */
-@FunctionalInterface
-public interface MouseDown extends MouseListener {
-       @Override
-       void mouseDown(MouseEvent e);
-
-       @Override
-       default void mouseDoubleClick(MouseEvent e) {
-               // does nothing
-       }
-
-       @Override
-       default void mouseUp(MouseEvent e) {
-               // does nothing
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java
deleted file mode 100644 (file)
index 03fbad0..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-
-/**
- * {@link SelectionListener} as a functional interface in order to use as a
- * short lambda expression in UI code.
- * {@link SelectionListener#widgetDefaultSelected(SelectionEvent)} does nothing
- * by default.
- */
-@FunctionalInterface
-public interface Selected extends SelectionListener {
-       @Override
-       public void widgetSelected(SelectionEvent e);
-
-       default public void widgetDefaultSelected(SelectionEvent e) {
-               // does nothing
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java
deleted file mode 100644 (file)
index e468c6d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.argeo.api.cms.ux.UxContext;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.widgets.Display;
-
-public class SimpleSwtUxContext implements UxContext {
-       private Point size;
-       private Point small = new Point(400, 400);
-
-       public SimpleSwtUxContext() {
-               this(Display.getCurrent().getBounds());
-       }
-
-       public SimpleSwtUxContext(Rectangle rect) {
-               this.size = new Point(rect.width, rect.height);
-       }
-
-       public SimpleSwtUxContext(Point size) {
-               this.size = size;
-       }
-
-       @Override
-       public boolean isPortrait() {
-               return size.x >= size.y;
-       }
-
-       @Override
-       public boolean isLandscape() {
-               return size.x < size.y;
-       }
-
-       @Override
-       public boolean isSquare() {
-               return size.x == size.y;
-       }
-
-       @Override
-       public boolean isSmall() {
-               return size.x <= small.x || size.y <= small.y;
-       }
-
-       @Override
-       public boolean isMasterData() {
-               // TODO make it configurable
-               return true;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java
deleted file mode 100644 (file)
index f2cceef..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.argeo.cms.swt;
-
-import org.argeo.cms.ux.widgets.EditablePart;
-import org.eclipse.swt.widgets.Control;
-
-/** Manages whether an editable or non editable control is shown. */
-public interface SwtEditablePart extends EditablePart {
-       public Control getControl();
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java
deleted file mode 100644 (file)
index 951889e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.cms.swt.acr;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.eclipse.swt.widgets.Composite;
-
-/** A composite which can (optionally) manage a content. */
-public class ContentComposite extends Composite {
-       private static final long serialVersionUID = -1447009015451153367L;
-
-       public ContentComposite(Composite parent, int style, Content item) {
-               super(parent, style);
-               setData(item);
-       }
-
-       public Content getContent() {
-               return (Content) getData();
-       }
-
-       @Deprecated
-       public Content getNode() {
-               return getContent();
-       }
-
-       protected ProvidedContent getProvidedContent() {
-               return (ProvidedContent) getContent();
-       }
-
-       public String getSessionLocalId() {
-               return getProvidedContent().getSessionLocalId();
-       }
-
-       protected void itemUpdated() {
-               layout();
-       }
-
-       public void setContent(Content content) {
-               setData(content);
-               itemUpdated();
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java
deleted file mode 100644 (file)
index 89d0038..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-package org.argeo.cms.swt.acr;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.argeo.api.acr.Content;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ux.widgets.EditablePart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** A structured UI related to a JCR context. */
-public class SwtSection extends ContentComposite {
-       private static final long serialVersionUID = -5933796173755739207L;
-
-       private final SwtSection parentSection;
-       private Composite sectionHeader;
-       private final Integer relativeDepth;
-
-       public SwtSection(Composite parent, int style, Content node) {
-               this(parent, findSection(parent), style, node);
-       }
-
-       public SwtSection(SwtSection section, int style, Content node) {
-               this(section, section, style, node);
-       }
-
-       protected SwtSection(Composite parent, SwtSection parentSection, int style, Content node) {
-               super(parent, style, node);
-               this.parentSection = parentSection;
-               if (parentSection != null) {
-                       relativeDepth = getProvidedContent().getDepth() - parentSection.getProvidedContent().getDepth();
-               } else {
-                       relativeDepth = 0;
-               }
-               setLayout(CmsSwtUtils.noSpaceGridLayout());
-       }
-
-       public Map<String, SwtSection> getSubSections() {
-               LinkedHashMap<String, SwtSection> result = new LinkedHashMap<String, SwtSection>();
-               for (Control child : getChildren()) {
-                       if (child instanceof Composite) {
-                               collectDirectSubSections((Composite) child, result);
-                       }
-               }
-               return Collections.unmodifiableMap(result);
-       }
-
-       private void collectDirectSubSections(Composite composite, LinkedHashMap<String, SwtSection> subSections) {
-               if (composite == sectionHeader || composite instanceof EditablePart)
-                       return;
-               if (composite instanceof SwtSection) {
-                       SwtSection section = (SwtSection) composite;
-                       subSections.put(section.getProvidedContent().getSessionLocalId(), section);
-                       return;
-               }
-
-               for (Control child : composite.getChildren())
-                       if (child instanceof Composite)
-                               collectDirectSubSections((Composite) child, subSections);
-       }
-
-       public Composite createHeader() {
-               return createHeader(this);
-       }
-
-       public Composite createHeader(Composite parent) {
-               if (sectionHeader != null)
-                       sectionHeader.dispose();
-
-               sectionHeader = new Composite(parent, SWT.NONE);
-               sectionHeader.setLayoutData(CmsSwtUtils.fillWidth());
-               sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               // sectionHeader.moveAbove(null);
-               // layout();
-               return sectionHeader;
-       }
-
-       public Composite getHeader() {
-               if (sectionHeader != null && sectionHeader.isDisposed())
-                       sectionHeader = null;
-               return sectionHeader;
-       }
-
-       // SECTION PARTS
-       public SwtSectionPart getSectionPart(String partId) {
-               for (Control child : getChildren()) {
-                       if (child instanceof SwtSectionPart) {
-                               SwtSectionPart sectionPart = (SwtSectionPart) child;
-                               if (sectionPart.getPartId().equals(partId))
-                                       return sectionPart;
-                       }
-               }
-               return null;
-       }
-
-       public SwtSectionPart nextSectionPart(SwtSectionPart sectionPart) {
-               Control[] children = getChildren();
-               for (int i = 0; i < children.length; i++) {
-                       if (sectionPart == children[i]) {
-                               for (int j = i + 1; j < children.length; j++) {
-                                       if (children[i + 1] instanceof SwtSectionPart) {
-                                               return (SwtSectionPart) children[i + 1];
-                                       }
-                               }
-
-//                             if (i + 1 < children.length) {
-//                                     Composite next = (Composite) children[i + 1];
-//                                     return (SectionPart) next;
-//                             } else {
-//                                     // next section
-//                             }
-                       }
-               }
-               return null;
-       }
-
-       public SwtSectionPart previousSectionPart(SwtSectionPart 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 (SwtSectionPart) previous;
-                               } else {
-                                       // previous section
-                               }
-               }
-               return null;
-       }
-
-       @Override
-       public String toString() {
-               if (parentSection == null)
-                       return "Main section " + getContent();
-               return "Section " + getContent();
-       }
-
-       public SwtSection getParentSection() {
-               return parentSection;
-       }
-
-       public Integer getRelativeDepth() {
-               return relativeDepth;
-       }
-
-       /** Recursively finds the related section in the parents (can be itself) */
-       public static SwtSection findSection(Control control) {
-               if (control == null)
-                       return null;
-               if (control instanceof SwtSection)
-                       return (SwtSection) control;
-               else
-                       return findSection(control.getParent());
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java
deleted file mode 100644 (file)
index 7fbf4bb..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.argeo.cms.swt.acr;
-
-import org.argeo.cms.ux.acr.ContentPart;
-import org.argeo.cms.ux.widgets.EditablePart;
-
-/** An editable part dynamically related to a Section */
-public interface SwtSectionPart extends EditablePart, ContentPart {
-       public String getPartId();
-
-       public SwtSection getSection();
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java
deleted file mode 100644 (file)
index cd4e37d..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-package org.argeo.cms.swt.acr;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.spi.ProvidedContent;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StackLayout;
-import org.eclipse.swt.graphics.Image;
-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.ToolBar;
-import org.eclipse.swt.widgets.ToolItem;
-
-/** Manages {@link SwtSection} in a tab-like structure. */
-public class SwtTabbedArea extends Composite {
-       private static final long serialVersionUID = 8659669229482033444L;
-
-       private Composite headers;
-       private Composite body;
-
-       private List<SwtSection> sections = new ArrayList<>();
-
-       private ProvidedContent previousNode;
-       private SwtUiProvider previousUiProvider;
-       private SwtUiProvider currentUiProvider;
-
-       private String tabStyle;
-       private String tabSelectedStyle;
-       private String bodyStyle;
-       private Image closeIcon;
-
-       private StackLayout stackLayout;
-
-       private boolean singleTab = false;
-
-       public SwtTabbedArea(Composite parent, int style) {
-               super(parent, SWT.NONE);
-               CmsSwtUtils.style(parent, bodyStyle);
-
-               setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               // TODO manage tabs at bottom or sides
-               headers = new Composite(this, SWT.NONE);
-               headers.setLayoutData(CmsSwtUtils.fillWidth());
-               body = new Composite(this, SWT.NONE);
-               body.setLayoutData(CmsSwtUtils.fillAll());
-               // body.setLayout(new FormLayout());
-               stackLayout = new StackLayout();
-               body.setLayout(stackLayout);
-               emptyState();
-       }
-
-       protected void refreshTabHeaders() {
-               int tabCount = sections.size() > 0 ? sections.size() : 1;
-               for (Control tab : headers.getChildren())
-                       tab.dispose();
-
-               headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true)));
-
-               if (sections.size() == 0) {
-                       Composite emptyHeader = new Composite(headers, SWT.NONE);
-                       emptyHeader.setLayoutData(CmsSwtUtils.fillAll());
-                       emptyHeader.setLayout(new GridLayout());
-                       Label lbl = new Label(emptyHeader, SWT.NONE);
-                       lbl.setText("");
-                       lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
-
-               }
-
-               SwtSection currentSection = getCurrentSection();
-               for (SwtSection section : sections) {
-                       boolean selected = section == currentSection;
-                       Composite sectionHeader = section.createHeader(headers);
-                       CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle);
-                       int headerColumns = singleTab ? 1 : 2;
-                       sectionHeader.setLayout(new GridLayout(headerColumns, false));
-                       sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns));
-                       Button title = new Button(sectionHeader, SWT.FLAT);
-                       CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle);
-                       title.setLayoutData(CmsSwtUtils.fillWidth());
-                       title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode())));
-                       Content node = section.getContent();
-
-                       // FIXME find a standard way to display titles
-                       String titleStr = node.getName().getLocalPart();
-                       
-                       // TODO internationalize
-                       title.setText(titleStr);
-                       if (!singleTab) {
-                               ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE);
-                               ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT);
-                               if (closeIcon != null)
-                                       closeItem.setImage(closeIcon);
-                               else
-                                       closeItem.setText("X");
-                               CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle);
-                               closeItem.addSelectionListener((Selected) (e) -> closeTab(section));
-                       }
-               }
-
-       }
-
-       public void view(SwtUiProvider uiProvider, Content context) {
-               if (body.isDisposed())
-                       return;
-               int index = tabIndex(context);
-               if (index >= 0) {
-                       showTab(index);
-                       previousNode = (ProvidedContent) context;
-                       previousUiProvider = uiProvider;
-                       return;
-               }
-               SwtSection section = (SwtSection) body.getChildren()[0];
-               previousNode = (ProvidedContent) section.getNode();
-               if (previousNode == null) {// empty state
-                       previousNode = (ProvidedContent) context;
-                       previousUiProvider = uiProvider;
-               } else {
-                       previousUiProvider = currentUiProvider;
-               }
-               currentUiProvider = uiProvider;
-               section.setContent(context);
-               // section.setLayoutData(CmsUiUtils.coverAll());
-               build(section, uiProvider, context);
-               if (sections.size() == 0)
-                       sections.add(section);
-               refreshTabHeaders();
-               index = tabIndex(context);
-               showTab(index);
-               layout(true, true);
-       }
-
-       public void open(SwtUiProvider uiProvider, Content context) {
-               if (singleTab)
-                       throw new UnsupportedOperationException("Open is not supported in single tab mode.");
-
-               if (previousNode != null
-                               && previousNode.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) {
-                       // does nothing
-                       return;
-               }
-               if (sections.size() == 0)
-                       CmsSwtUtils.clear(body);
-               SwtSection currentSection = getCurrentSection();
-               int currentIndex = sections.indexOf(currentSection);
-               SwtSection previousSection = new SwtSection(body, SWT.NONE, context);
-               build(previousSection, previousUiProvider, previousNode);
-               // previousSection.setLayoutData(CmsUiUtils.coverAll());
-               int newIndex = currentIndex + 1;
-               sections.add(currentIndex, previousSection);
-//             sections.add(newIndex, previousSection);
-               showTab(newIndex);
-               refreshTabHeaders();
-               layout(true, true);
-       }
-
-       public void showTab(int index) {
-               SwtSection sectionToShow = sections.get(index);
-               // sectionToShow.moveAbove(null);
-               stackLayout.topControl = sectionToShow;
-               refreshTabHeaders();
-               layout(true, true);
-       }
-
-       protected void build(SwtSection section, SwtUiProvider uiProvider, Content context) {
-               for (Control child : section.getChildren())
-                       child.dispose();
-               CmsSwtUtils.style(section, bodyStyle);
-               section.setContent(context);
-               uiProvider.createUiPart(section, context);
-
-       }
-
-       private int tabIndex(Content context) {
-               for (int i = 0; i < sections.size(); i++) {
-                       SwtSection section = sections.get(i);
-                       if (section.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId()))
-                               return i;
-               }
-               return -1;
-       }
-
-       public void closeTab(SwtSection section) {
-               int currentIndex = sections.indexOf(section);
-               int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1;
-               sections.remove(section);
-               section.dispose();
-               if (sections.size() == 0) {
-                       emptyState();
-                       refreshTabHeaders();
-                       layout(true, true);
-                       return;
-               }
-               refreshTabHeaders();
-               showTab(nextIndex);
-       }
-
-       public void closeAllTabs() {
-               for (SwtSection section : sections) {
-                       section.dispose();
-               }
-               sections.clear();
-               emptyState();
-               refreshTabHeaders();
-               layout(true, true);
-       }
-
-       protected void emptyState() {
-               new SwtSection(body, SWT.NONE, null);
-               refreshTabHeaders();
-       }
-
-       public Composite getCurrent() {
-               return getCurrentSection();
-       }
-
-       protected SwtSection getCurrentSection() {
-               return (SwtSection) stackLayout.topControl;
-       }
-
-       public Content getCurrentContext() {
-               SwtSection section = getCurrentSection();
-               if (section != null) {
-                       return section.getNode();
-               } else {
-                       return null;
-               }
-       }
-
-       public void setTabStyle(String tabStyle) {
-               this.tabStyle = tabStyle;
-       }
-
-       public void setTabSelectedStyle(String tabSelectedStyle) {
-               this.tabSelectedStyle = tabSelectedStyle;
-       }
-
-       public void setBodyStyle(String bodyStyle) {
-               this.bodyStyle = bodyStyle;
-       }
-
-       public void setCloseIcon(Image closeIcon) {
-               this.closeIcon = closeIcon;
-       }
-
-       public void setSingleTab(boolean singleTab) {
-               this.singleTab = singleTab;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java
deleted file mode 100644 (file)
index 4988fc6..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.argeo.cms.swt.acr;
-
-import org.argeo.api.acr.Content;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-@FunctionalInterface
-public interface SwtUiProvider {
-       Control createUiPart(Composite parent, Content context);
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java
deleted file mode 100644 (file)
index fb1a79d..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-package org.argeo.cms.swt.app;
-
-import static org.argeo.api.acr.NamespaceUtils.toPrefixedName;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.widgets.SwtHierarchicalPart;
-import org.argeo.cms.swt.widgets.SwtTabularPart;
-import org.argeo.cms.ux.acr.ContentHierarchicalPart;
-import org.argeo.cms.ux.widgets.Column;
-import org.argeo.cms.ux.widgets.DefaultTabularPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.widgets.Composite;
-
-public class AcrContentTreeView extends Composite {
-       private static final long serialVersionUID = -3707881216246077323L;
-
-       private Content rootContent;
-
-//     private Content selected;
-
-       public AcrContentTreeView(Composite parent, int style, Content content) {
-               super(parent, style);
-               this.rootContent = content;
-               // this.selected = rootContent;
-               setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               SashForm split = new SashForm(this, SWT.HORIZONTAL);
-               split.setLayoutData(CmsSwtUtils.fillAll());
-
-               ContentHierarchicalPart contentPart = new ContentHierarchicalPart();
-               contentPart.setInput(rootContent);
-
-               SwtHierarchicalPart<Content> hPart = new SwtHierarchicalPart<>(split, getStyle(), contentPart);
-
-               Composite area = new Composite(split, SWT.BORDER);
-               area.setLayout(CmsSwtUtils.noSpaceGridLayout(2));
-               split.setWeights(new int[] { 30, 70 });
-
-               // attributes
-               DefaultTabularPart<Content, QName> attributesPart = new DefaultTabularPart<>() {
-
-                       @Override
-                       protected List<QName> asList(Content input) {
-                               return new ArrayList<>(input.keySet());
-                       }
-               };
-
-               attributesPart.addColumn(new Column<QName>() {
-
-                       @Override
-                       public String getText(QName model) {
-                               try {
-                                       return NamespaceUtils.toPrefixedName(model);
-                               } catch (IllegalStateException e) {
-                                       return model.toString();
-                               }
-                       }
-               });
-               attributesPart.addColumn(new Column<QName>() {
-
-                       @Override
-                       public String getText(QName model) {
-                               return attributesPart.getInput().get(model).toString();
-                       }
-
-                       @Override
-                       public int getWidth() {
-                               return 400;
-                       }
-
-               });
-               // attributesPart.setInput(selected);
-
-               SwtTabularPart<Content, QName> attributeTable = new SwtTabularPart<>(area, style, attributesPart);
-               attributeTable.setLayoutData(CmsSwtUtils.fillAll());
-
-               // types
-               DefaultTabularPart<Content, QName> typesPart = new DefaultTabularPart<>() {
-
-                       @Override
-                       protected List<QName> asList(Content input) {
-                               return input.getContentClasses();
-                       }
-               };
-               typesPart.addColumn(new Column<QName>() {
-
-                       @Override
-                       public String getText(QName model) {
-                               return toPrefixedName(model);
-                       }
-
-               });
-
-               // typesPart.setInput(selected);
-
-               SwtTabularPart<Content, QName> typesTable = new SwtTabularPart<>(area, style, typesPart);
-               typesTable.setLayoutData(CmsSwtUtils.fillAll());
-
-               // controller
-               contentPart.setInput(rootContent);
-               contentPart.onSelected((o) -> {
-                       Content c = (Content) o;
-//                     selected = c;
-                       attributesPart.setInput(c);
-                       typesPart.setInput(c);
-               });
-
-               attributesPart.refresh();
-               typesPart.refresh();
-       }
-
-//     protected void refreshTable() {
-//             for (TableItem item : table.getItems()) {
-//                     item.dispose();
-//             }
-//             for (QName key : selected.keySet()) {
-//                     TableItem item = new TableItem(table, 0);
-//                     item.setText(0, key.toString());
-//                     Object value = selected.get(key);
-//                     item.setText(1, value.toString());
-//             }
-//             table.getColumn(0).pack();
-//             table.getColumn(1).pack();
-//     }
-
-//     public static void main(String[] args) {
-//             Path basePath;
-//             if (args.length > 0) {
-//                     basePath = Paths.get(args[0]);
-//             } else {
-//                     basePath = Paths.get(System.getProperty("user.home"));
-//             }
-//
-//             final Display display = new Display();
-//             final Shell shell = new Shell(display);
-//             shell.setText(basePath.toString());
-//             shell.setLayout(new FillLayout());
-//
-//             FsContentProvider contentSession = new FsContentProvider("/", basePath);
-////           GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/"));
-//
-//             shell.setSize(shell.computeSize(800, 600));
-//             shell.open();
-//             while (!shell.isDisposed()) {
-//                     if (!display.readAndDispatch())
-//                             display.sleep();
-//             }
-//             display.dispose();
-//     }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java
deleted file mode 100644 (file)
index add6e9e..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.argeo.cms.swt.app;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.ContentRepository;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.AbstractCmsApp;
-import org.argeo.cms.swt.CmsSwtUi;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.auth.CmsLogin;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-public class CmsUserApp extends AbstractCmsApp {
-       private ContentRepository contentRepository;
-
-       @Override
-       public Set<String> getUiNames() {
-               Set<String> uiNames = new HashSet<>();
-               uiNames.add("login");
-               uiNames.add("data");
-               return uiNames;
-       }
-
-       @Override
-       public CmsUi initUi(Object uiParent) {
-               Composite parent = (Composite) uiParent;
-               String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null;
-               CmsSwtUi cmsUi = new CmsSwtUi(parent, SWT.NONE);
-               if ("login".equals(uiName)) {
-                       CmsView cmsView = CmsSwtUtils.getCmsView(cmsUi);
-                       CmsLogin cmsLogin = new CmsLogin(cmsView, getCmsContext());
-                       cmsLogin.createUi(cmsUi);
-
-               } else if ("data".equals(uiName)) {
-                       Content rootContent = contentRepository.get().get("/");
-                       AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent);
-                       view.setLayoutData(CmsSwtUtils.fillAll());
-
-               }
-               return cmsUi;
-       }
-
-       @Override
-       public void refreshUi(CmsUi cmsUi, String state) {
-       }
-
-       @Override
-       public void setState(CmsUi cmsUi, String state) {
-               // TODO Auto-generated method stub
-
-       }
-
-       public void setContentRepository(ContentRepository contentRepository) {
-               this.contentRepository = contentRepository;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java
deleted file mode 100644 (file)
index 6cc410c..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-package org.argeo.cms.swt.auth;
-
-import static org.argeo.cms.CmsMsg.password;
-import static org.argeo.cms.CmsMsg.username;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.LanguageCallback;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.auth.RemoteAuthCallback;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.argeo.cms.swt.CmsStyles;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.events.TraverseEvent;
-import org.eclipse.swt.events.TraverseListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-public class CmsLogin implements CmsStyles, CallbackHandler {
-       private final static CmsLog log = CmsLog.getLog(CmsLogin.class);
-
-       private Composite parent;
-       private Text usernameT, passwordT;
-       private Composite credentialsBlock;
-       private final SelectionListener loginSelectionListener;
-
-       private final Locale defaultLocale;
-       private LocaleChoice localeChoice = null;
-
-       private final CmsView cmsView;
-
-       // optional subject to be set explicitly
-       private Subject subject = null;
-
-       private CmsContext cmsContext;
-
-       public CmsLogin(CmsView cmsView, CmsContext cmsContext) {
-               this.cmsView = cmsView;
-               this.cmsContext = cmsContext;
-               if (this.cmsContext != null) {
-                       defaultLocale = this.cmsContext.getDefaultLocale();
-                       List<Locale> locales = this.cmsContext.getLocales();
-                       if (locales != null)
-                               localeChoice = new LocaleChoice(locales, defaultLocale);
-               } else {
-                       defaultLocale = Locale.getDefault();
-               }
-               loginSelectionListener = new SelectionListener() {
-                       private static final long serialVersionUID = -8832133363830973578L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                               login();
-                       }
-
-                       @Override
-                       public void widgetDefaultSelected(SelectionEvent e) {
-                       }
-               };
-       }
-
-       protected boolean isAnonymous() {
-               return cmsView.isAnonymous();
-       }
-
-       public final void createUi(Composite parent) {
-               this.parent = parent;
-               createContents(parent);
-       }
-
-       protected void createContents(Composite parent) {
-               defaultCreateContents(parent);
-       }
-
-       public final void defaultCreateContents(Composite parent) {
-               parent.setLayout(CmsSwtUtils.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);
-               }
-       }
-
-       public Composite getCredentialsBlock() {
-               return credentialsBlock;
-       }
-
-       protected Composite userUi(Composite parent) {
-               Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
-               credentialsBlock = new Composite(parent, SWT.NONE);
-               credentialsBlock.setLayout(new GridLayout());
-               // credentialsBlock.setLayoutData(CmsUiUtils.fillAll());
-
-               specificUserUi(credentialsBlock);
-
-               Label l = new Label(credentialsBlock, SWT.NONE);
-               CmsSwtUtils.style(l, CMS_USER_MENU_ITEM);
-               l.setText(CmsMsg.logout.lead(locale));
-               GridData lData = CmsSwtUtils.fillWidth();
-               lData.widthHint = 120;
-               l.setLayoutData(lData);
-
-               l.addMouseListener(new MouseAdapter() {
-                       private static final long serialVersionUID = 6444395812777413116L;
-
-                       public void mouseDown(MouseEvent e) {
-                               logout();
-                       }
-               });
-               return credentialsBlock;
-       }
-
-       /** To be overridden */
-       protected void specificUserUi(Composite parent) {
-
-       }
-
-       protected Composite anonymousUi(Composite parent) {
-               Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
-               // We need a composite for the traversal
-               credentialsBlock = new Composite(parent, SWT.NONE);
-               credentialsBlock.setLayout(new GridLayout());
-               // credentialsBlock.setLayoutData(CmsUiUtils.fillAll());
-               CmsSwtUtils.style(credentialsBlock, CMS_LOGIN_DIALOG);
-
-               Integer textWidth = 120;
-               if (parent instanceof Shell)
-                       CmsSwtUtils.style(parent, CMS_USER_MENU);
-               // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
-               usernameT = new Text(credentialsBlock, SWT.BORDER);
-               usernameT.setMessage(username.lead(locale));
-               CmsSwtUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME);
-               GridData gd = CmsSwtUtils.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));
-               CmsSwtUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD);
-               gd = CmsSwtUtils.fillWidth();
-               gd.widthHint = textWidth;
-               passwordT.setLayoutData(gd);
-
-               TraverseListener tl = new TraverseListener() {
-                       private static final long serialVersionUID = -1158892811534971856L;
-
-                       public void keyTraversed(TraverseEvent e) {
-                               if (e.detail == SWT.TRAVERSE_RETURN)
-                                       login();
-                       }
-               };
-               credentialsBlock.addTraverseListener(tl);
-               usernameT.addTraverseListener(tl);
-               passwordT.addTraverseListener(tl);
-               parent.setTabList(new Control[] { credentialsBlock });
-               credentialsBlock.setTabList(new Control[] { usernameT, passwordT });
-
-               // Button
-               Button loginButton = new Button(credentialsBlock, SWT.PUSH);
-               loginButton.setText(CmsMsg.login.lead(locale));
-               loginButton.setLayoutData(CmsSwtUtils.fillWidth());
-               loginButton.addSelectionListener(loginSelectionListener);
-
-               extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener);
-               if (localeChoice != null)
-                       createLocalesBlock(credentialsBlock);
-               return credentialsBlock;
-       }
-
-       /**
-        * To be overridden in order to provide custom login button and other links.
-        */
-       protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale,
-                       SelectionListener loginSelectionListener) {
-
-       }
-
-       protected void updateLocale(Locale selectedLocale) {
-               // save already entered values
-               String usernameStr = usernameT.getText();
-               char[] pwd = passwordT.getTextChars();
-
-               for (Control child : parent.getChildren())
-                       child.dispose();
-               createContents(parent);
-               if (parent.getParent() != null)
-                       parent.getParent().layout(true, true);
-               else
-                       parent.layout();
-               usernameT.setText(usernameStr);
-               passwordT.setTextChars(pwd);
-       }
-
-       protected Composite createLocalesBlock(final Composite parent) {
-               Composite c = new Composite(parent, SWT.NONE);
-               CmsSwtUtils.style(c, CMS_USER_MENU_ITEM);
-               c.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               c.setLayoutData(CmsSwtUtils.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);
-                       CmsSwtUtils.style(button, CMS_USER_MENU_ITEM);
-                       button.setData(i);
-                       button.setText(LocaleUtils.toLead(locale.getDisplayName(locale), locale) + " (" + locale + ")");
-                       // button.addListener(SWT.Selection, listener);
-                       button.addSelectionListener(selectionListener);
-                       if (i == localeChoice.getSelectedIndex())
-                               button.setSelection(true);
-               }
-               return c;
-       }
-
-       protected boolean login() {
-               // TODO use CmsVie in order to retrieve subject?
-               // Subject subject = cmsView.getLoginContext().getSubject();
-               // LoginContext loginContext = cmsView.getLoginContext();
-               try {
-                       //
-                       // LOGIN
-                       //
-                       // loginContext.logout();
-                       LoginContext loginContext;
-                       if (subject == null)
-                               loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, this);
-                       else
-                               loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, this);
-                       loginContext.login();
-                       cmsView.authChange(loginContext);
-                       return true;
-               } catch (LoginException e) {
-                       if (log.isTraceEnabled())
-                               log.warn("Login failed: " + e.getMessage(), e);
-                       else
-                               log.warn("Login failed: " + e.getMessage());
-
-                       try {
-                               Thread.sleep(3000);
-                       } catch (InterruptedException e2) {
-                               // silent
-                       }
-                       // ErrorFeedback.show("Login failed", e);
-                       return false;
-               }
-               // catch (LoginException e) {
-               // log.error("Cannot login", e);
-               // return false;
-               // }
-       }
-
-
-       protected void logout() {
-               cmsView.logout();
-               cmsView.navigateTo("~");
-       }
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-               for (Callback callback : callbacks) {
-                       if (callback instanceof NameCallback && usernameT != null)
-                               ((NameCallback) callback).setName(usernameT.getText());
-                       else if (callback instanceof PasswordCallback && passwordT != null)
-                               ((PasswordCallback) callback).setPassword(passwordT.getTextChars());
-                       else if (callback instanceof RemoteAuthCallback) {
-                               ((RemoteAuthCallback) callback).setRequest(new ServletHttpRequest(UiContext.getHttpRequest()));
-                               ((RemoteAuthCallback) callback).setResponse(new ServletHttpResponse(UiContext.getHttpResponse()));
-                       } else if (callback instanceof LanguageCallback) {
-                               Locale toUse = null;
-                               if (localeChoice != null)
-                                       toUse = localeChoice.getSelectedLocale();
-                               else if (defaultLocale != null)
-                                       toUse = defaultLocale;
-
-                               if (toUse != null) {
-                                       ((LanguageCallback) callback).setLocale(toUse);
-                                       UiContext.setLocale(toUse);
-                               }
-
-                       }
-               }
-       }
-
-       public void setSubject(Subject subject) {
-               this.subject = subject;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java
deleted file mode 100644 (file)
index 39cf82a..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.cms.swt.auth;
-
-import org.argeo.api.cms.CmsContext;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-/** The site-related user menu */
-public class CmsLoginShell extends CmsLogin {
-       private final Shell shell;
-
-       public CmsLoginShell(CmsView cmsView, CmsContext cmsContext) {
-               super(cmsView, cmsContext);
-               shell = createShell();
-//             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() {
-               CmsSwtUtils.style(shell, CMS_USER_MENU);
-               shell.open();
-       }
-
-       @Override
-       protected boolean login() {
-               boolean success = false;
-               try {
-                       success = super.login();
-                       return success;
-               } finally {
-                       if (success)
-                               closeShell();
-                       else {
-                               for (Control child : shell.getChildren())
-                                       child.dispose();
-                               createUi(shell);
-                               shell.layout();
-                               // TODO error message
-                       }
-               }
-       }
-
-       @Override
-       protected void logout() {
-               closeShell();
-               super.logout();
-       }
-
-       protected void closeShell() {
-               if (!shell.isDisposed()) {
-                       shell.close();
-                       shell.dispose();
-               }
-       }
-
-       public Shell getShell() {
-               return shell;
-       }
-
-       public void createUi() {
-               createUi(shell);
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java
deleted file mode 100644 (file)
index 495007c..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-package org.argeo.cms.swt.auth;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * A composite that can populate itself based on {@link Callback}s. It can be
- * used directly as a {@link CallbackHandler} or be used by one by calling the
- * {@link #createCallbackHandlers(Callback[])}. Supported standard
- * {@link Callback}s are:<br>
- * <ul>
- * <li>{@link PasswordCallback}</li>
- * <li>{@link NameCallback}</li>
- * <li>{@link TextOutputCallback}</li>
- * </ul>
- * Supported Argeo {@link Callback}s are:<br>
- * <ul>
- * <li>{@link LocaleChoice}</li>
- * </ul>
- */
-public class CompositeCallbackHandler extends Composite implements CallbackHandler {
-       private static final long serialVersionUID = -928223893722723777L;
-
-       private boolean wasUsedAlready = false;
-       private boolean isSubmitted = false;
-       private boolean isCanceled = false;
-
-       public CompositeCallbackHandler(Composite parent, int style) {
-               super(parent, style);
-       }
-
-       @Override
-       public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-               // reset
-               if (wasUsedAlready && !isSubmitted() && !isCanceled()) {
-                       cancel();
-                       for (Control control : getChildren())
-                               control.dispose();
-                       isSubmitted = false;
-                       isCanceled = false;
-               }
-
-               for (Callback callback : callbacks)
-                       checkCallbackSupported(callback);
-               // create controls synchronously in the UI thread
-               getDisplay().syncExec(new Runnable() {
-
-                       @Override
-                       public void run() {
-                               createCallbackHandlers(callbacks);
-                       }
-               });
-
-               if (!wasUsedAlready)
-                       wasUsedAlready = true;
-
-               // while (!isSubmitted() && !isCanceled()) {
-               // try {
-               // wait(1000l);
-               // } catch (InterruptedException e) {
-               // // silent
-               // }
-               // }
-
-               // cleanCallbacksAfterCancel(callbacks);
-       }
-
-       public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException {
-               if (callback instanceof TextOutputCallback || callback instanceof NameCallback
-                               || callback instanceof PasswordCallback || callback instanceof LocaleChoice) {
-                       return;
-               } else {
-                       throw new UnsupportedCallbackException(callback);
-               }
-       }
-
-       /**
-        * Set writable callbacks to null if the handle is canceled (check is done
-        * by the method)
-        */
-       public void cleanCallbacksAfterCancel(Callback[] callbacks) {
-               if (isCanceled()) {
-                       for (Callback callback : callbacks) {
-                               if (callback instanceof NameCallback) {
-                                       ((NameCallback) callback).setName(null);
-                               } else if (callback instanceof PasswordCallback) {
-                                       PasswordCallback pCallback = (PasswordCallback) callback;
-                                       char[] arr = pCallback.getPassword();
-                                       if (arr != null) {
-                                               Arrays.fill(arr, '*');
-                                               pCallback.setPassword(null);
-                                       }
-                               }
-                       }
-               }
-       }
-
-       public void createCallbackHandlers(Callback[] callbacks) {
-               Composite composite = this;
-               for (int i = 0; i < callbacks.length; i++) {
-                       Callback callback = callbacks[i];
-                       if (callback instanceof TextOutputCallback) {
-                               createLabelTextoutputHandler(composite, (TextOutputCallback) callback);
-                       } else if (callback instanceof NameCallback) {
-                               createNameHandler(composite, (NameCallback) callback);
-                       } else if (callback instanceof PasswordCallback) {
-                               createPasswordHandler(composite, (PasswordCallback) callback);
-                       } else if (callback instanceof LocaleChoice) {
-                               createLocaleHandler(composite, (LocaleChoice) callback);
-                       }
-               }
-       }
-
-       protected Text createNameHandler(Composite composite, final NameCallback callback) {
-               Label label = new Label(composite, SWT.NONE);
-               label.setText(callback.getPrompt());
-               final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
-               if (callback.getDefaultName() != null) {
-                       // set default value, if provided
-                       text.setText(callback.getDefaultName());
-                       callback.setName(callback.getDefaultName());
-               }
-               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               text.addModifyListener(new ModifyListener() {
-                       private static final long serialVersionUID = 7300032545287292973L;
-
-                       public void modifyText(ModifyEvent event) {
-                               callback.setName(text.getText());
-                       }
-               });
-               text.addSelectionListener(new SelectionListener() {
-                       private static final long serialVersionUID = 1820530045857665111L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                       }
-
-                       @Override
-                       public void widgetDefaultSelected(SelectionEvent e) {
-                               submit();
-                       }
-               });
-
-               text.addKeyListener(new KeyListener() {
-                       private static final long serialVersionUID = -8698107785092095713L;
-
-                       @Override
-                       public void keyReleased(KeyEvent e) {
-                       }
-
-                       @Override
-                       public void keyPressed(KeyEvent e) {
-                       }
-               });
-               return text;
-       }
-
-       protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) {
-               Label label = new Label(composite, SWT.NONE);
-               label.setText(callback.getPrompt());
-               final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER);
-               passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               passwordText.addModifyListener(new ModifyListener() {
-                       private static final long serialVersionUID = -7099363995047686732L;
-
-                       public void modifyText(ModifyEvent event) {
-                               callback.setPassword(passwordText.getTextChars());
-                       }
-               });
-               passwordText.addSelectionListener(new SelectionListener() {
-                       private static final long serialVersionUID = 1820530045857665111L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                       }
-
-                       @Override
-                       public void widgetDefaultSelected(SelectionEvent e) {
-                               submit();
-                       }
-               });
-               return passwordText;
-       }
-
-       protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) {
-               String[] labels = callback.getSupportedLocalesLabels();
-               if (labels.length == 0)
-                       return null;
-               Label label = new Label(composite, SWT.NONE);
-               label.setText("Language");
-
-               final Combo combo = new Combo(composite, SWT.READ_ONLY);
-               combo.setItems(labels);
-               combo.select(callback.getDefaultIndex());
-               combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               combo.addSelectionListener(new SelectionListener() {
-                       private static final long serialVersionUID = 38678989091946277L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                               callback.setSelectedIndex(combo.getSelectionIndex());
-                       }
-
-                       @Override
-                       public void widgetDefaultSelected(SelectionEvent e) {
-                       }
-               });
-               return combo;
-       }
-
-       protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) {
-               Label label = new Label(composite, SWT.NONE);
-               label.setText(callback.getMessage());
-               GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
-               data.horizontalSpan = 2;
-               label.setLayoutData(data);
-               return label;
-               // TODO: find a way to pass this information
-               // int messageType = callback.getMessageType();
-               // int dialogMessageType = IMessageProvider.NONE;
-               // switch (messageType) {
-               // case TextOutputCallback.INFORMATION:
-               // dialogMessageType = IMessageProvider.INFORMATION;
-               // break;
-               // case TextOutputCallback.WARNING:
-               // dialogMessageType = IMessageProvider.WARNING;
-               // break;
-               // case TextOutputCallback.ERROR:
-               // dialogMessageType = IMessageProvider.ERROR;
-               // break;
-               // }
-               // setMessage(callback.getMessage(), dialogMessageType);
-       }
-
-       synchronized boolean isSubmitted() {
-               return isSubmitted;
-       }
-
-       synchronized boolean isCanceled() {
-               return isCanceled;
-       }
-
-       protected synchronized void submit() {
-               isSubmitted = true;
-               notifyAll();
-       }
-
-       protected synchronized void cancel() {
-               isCanceled = true;
-               notifyAll();
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java
deleted file mode 100644 (file)
index b0c36c6..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.cms.swt.auth;
-
-import java.io.IOException;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.argeo.eclipse.ui.dialogs.LightweightDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-public class DynamicCallbackHandler implements CallbackHandler {
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-               Shell activeShell = Display.getCurrent().getActiveShell();
-               LightweightDialog dialog = new LightweightDialog(activeShell) {
-
-                       @Override
-                       protected Control createDialogArea(Composite parent) {
-                               CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE);
-                               cch.createCallbackHandlers(callbacks);
-                               return cch;
-                       }
-               };
-               dialog.setBlockOnOpen(true);
-               dialog.open();
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java
deleted file mode 100644 (file)
index 3ce5ae5..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.argeo.cms.swt.auth;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import javax.security.auth.callback.LanguageCallback;
-
-import org.argeo.cms.swt.CmsException;
-
-/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
-@Deprecated
-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/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java
deleted file mode 100644 (file)
index b431423..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Argeo CMS authentication widgets, based on SWT. */
-package org.argeo.cms.swt.auth;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java
deleted file mode 100644 (file)
index 06e4d0f..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.argeo.cms.swt.dialogs;
-
-import java.security.PrivilegedAction;
-import java.util.Arrays;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.CmsUserManager;
-import org.argeo.cms.swt.CmsSwtUtils;
-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.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/** Dialog to change a password. */
-public class ChangePasswordDialog extends CmsMessageDialog {
-       private final static CmsLog log = CmsLog.getLog(ChangePasswordDialog.class);
-
-       private CmsUserManager cmsUserManager;
-       private CmsView cmsView;
-
-       private PrivilegedAction<Integer> doIt;
-
-       public ChangePasswordDialog(Shell parentShell, String message, int kind, CmsUserManager cmsUserManager) {
-               super(parentShell, message, kind);
-               this.cmsUserManager = cmsUserManager;
-               cmsView = CmsSwtUtils.getCmsView(parentShell);
-       }
-
-       @Override
-       protected Control createInputArea(Composite userSection) {
-               addFormLabel(userSection, CmsMsg.currentPassword.lead());
-               Text previousPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
-               previousPassword.setLayoutData(CmsSwtUtils.fillWidth());
-               addFormLabel(userSection, CmsMsg.newPassword.lead());
-               Text newPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
-               newPassword.setLayoutData(CmsSwtUtils.fillWidth());
-               addFormLabel(userSection, CmsMsg.repeatNewPassword.lead());
-               Text confirmPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
-               confirmPassword.setLayoutData(CmsSwtUtils.fillWidth());
-
-               doIt = () -> {
-                       if (Arrays.equals(newPassword.getTextChars(), confirmPassword.getTextChars())) {
-                               try {
-                                       cmsUserManager.changeOwnPassword(previousPassword.getTextChars(), newPassword.getTextChars());
-                                       return OK;
-                               } catch (Exception e1) {
-                                       log.error("Could not change password", e1);
-                                       cancel();
-                                       CmsMessageDialog.openError(CmsMsg.invalidPassword.lead());
-                                       return CANCEL;
-                               }
-                       } else {
-                               cancel();
-                               CmsMessageDialog.openError(CmsMsg.repeatNewPassword.lead());
-                               return CANCEL;
-                       }
-               };
-
-               pack();
-               return previousPassword;
-       }
-
-       @Override
-       protected void okPressed() {
-               Integer returnCode = cmsView.doAs(doIt);
-               if (returnCode.equals(OK)) {
-                       super.okPressed();
-                       CmsMessageDialog.openInformation(CmsMsg.passwordChanged.lead());
-               }
-       }
-
-       private static Label addFormLabel(Composite parent, String label) {
-               Label lbl = new Label(parent, SWT.WRAP);
-               lbl.setText(label);
-//             CmsUiUtils.style(lbl, SuiteStyle.simpleLabel);
-               return lbl;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java
deleted file mode 100644 (file)
index a01c919..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.argeo.cms.swt.dialogs;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.swt.Selected;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/** A dialog feedback based on a {@link LightweightDialog}. */
-public class CmsFeedback extends LightweightDialog {
-       private final static CmsLog log = CmsLog.getLog(CmsFeedback.class);
-
-       private String message;
-       private Throwable exception;
-
-       public CmsFeedback(Shell parentShell, String message, Throwable e) {
-               super(parentShell);
-               this.message = message;
-               this.exception = e;
-               log.error(message, e);
-       }
-
-       public static CmsFeedback show(String message, Throwable e) {
-               // rethrow ThreaDeath in order to make sure that RAP will properly clean
-               // up the UI thread
-               if (e instanceof ThreadDeath)
-                       throw (ThreadDeath) e;
-
-               try {
-                       CmsFeedback cmsFeedback = new CmsFeedback(null, message, e);
-                       cmsFeedback.setBlockOnOpen(false);
-                       cmsFeedback.open();
-                       return cmsFeedback;
-               } catch (Throwable e1) {
-                       log.error("Cannot open error feedback (" + e.getMessage() + "), original error below", e);
-                       return null;
-               }
-       }
-
-       public static CmsFeedback show(String message) {
-               CmsFeedback cmsFeedback = new CmsFeedback(null, message, null);
-               cmsFeedback.open();
-               return cmsFeedback;
-       }
-
-       /** Tries to find a display */
-       // private static Display getDisplay() {
-       // try {
-       // Display display = Display.getCurrent();
-       // if (display != null)
-       // return display;
-       // else
-       // return Display.getDefault();
-       // } catch (Exception e) {
-       // return Display.getCurrent();
-       // }
-       // }
-
-       protected Control createDialogArea(Composite parent) {
-               parent.setLayout(new GridLayout(2, false));
-
-               Label messageLbl = new Label(parent, SWT.WRAP);
-               if (message != null)
-                       messageLbl.setText(message);
-               else if (exception != null)
-                       messageLbl.setText(exception.getLocalizedMessage());
-
-               Button close = new Button(parent, SWT.FLAT);
-               close.setText(CmsMsg.close.lead());
-               close.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
-               close.addSelectionListener((Selected) (e) -> closeShell(OK));
-
-               // Composite composite = new Composite(dialogarea, SWT.NONE);
-               // composite.setLayout(new GridLayout(2, false));
-               // composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               if (exception != null) {
-                       Text stack = new Text(parent, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
-                       stack.setEditable(false);
-                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
-                       StringWriter sw = new StringWriter();
-                       exception.printStackTrace(new PrintWriter(sw));
-                       stack.setText(sw.toString());
-               }
-
-               // parent.pack();
-               return messageLbl;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java
deleted file mode 100644 (file)
index 66e6405..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-package org.argeo.cms.swt.dialogs;
-
-import org.argeo.cms.CmsMsg;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.TraverseEvent;
-import org.eclipse.swt.events.TraverseListener;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-
-/** Base class for dialogs displaying messages or small forms. */
-public class CmsMessageDialog extends LightweightDialog {
-       public final static int NONE = 0;
-       public final static int ERROR = 1;
-       public final static int INFORMATION = 2;
-       public final static int QUESTION = 3;
-       public final static int WARNING = 4;
-       public final static int CONFIRM = 5;
-       public final static int QUESTION_WITH_CANCEL = 6;
-
-       private int kind;
-       private String message;
-
-       public CmsMessageDialog(Shell parentShell, String message, int kind) {
-               super(parentShell);
-               this.kind = kind;
-               this.message = message;
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               parent.setLayout(new GridLayout());
-
-               TraverseListener traverseListener = new TraverseListener() {
-                       private static final long serialVersionUID = -1158892811534971856L;
-
-                       public void keyTraversed(TraverseEvent e) {
-                               if (e.detail == SWT.TRAVERSE_RETURN)
-                                       okPressed();
-                               else if (e.detail == SWT.TRAVERSE_ESCAPE)
-                                       cancelPressed();
-                       }
-               };
-
-               // message
-               Composite body = new Composite(parent, SWT.NONE);
-               body.addTraverseListener(traverseListener);
-               GridLayout bodyGridLayout = new GridLayout();
-               bodyGridLayout.marginHeight = 20;
-               bodyGridLayout.marginWidth = 20;
-               body.setLayout(bodyGridLayout);
-               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               if (message != null) {
-                       Label messageLbl = new Label(body, SWT.WRAP);
-                       CmsSwtUtils.markup(messageLbl);
-                       messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-                       messageLbl.setFont(EclipseUiUtils.getBoldFont(parent));
-                       messageLbl.setText(message);
-               }
-
-               // buttons
-               Composite buttons = new Composite(parent, SWT.NONE);
-               buttons.addTraverseListener(traverseListener);
-               buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
-               if (kind == INFORMATION || kind == WARNING || kind == ERROR || kind == ERROR) {
-                       GridLayout layout = new GridLayout(1, true);
-                       layout.marginWidth = 0;
-                       layout.marginHeight = 0;
-                       buttons.setLayout(layout);
-
-                       Button close = new Button(buttons, SWT.FLAT);
-                       close.setText(CmsMsg.close.lead());
-                       close.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                       close.addSelectionListener((Selected) (e) -> closeShell(OK));
-                       close.setFocus();
-                       close.addTraverseListener(traverseListener);
-
-                       buttons.setTabList(new Control[] { close });
-               } else if (kind == CONFIRM || kind == QUESTION || kind == QUESTION_WITH_CANCEL) {
-                       Control input = createInputArea(body);
-                       if (input != null) {
-                               input.addTraverseListener(traverseListener);
-                               body.setTabList(new Control[] { input });
-                       }
-                       GridLayout layout = new GridLayout(2, true);
-                       layout.marginWidth = 0;
-                       layout.marginHeight = 0;
-                       buttons.setLayout(layout);
-
-                       Button cancel = new Button(buttons, SWT.FLAT);
-                       cancel.setText(CmsMsg.cancel.lead());
-                       cancel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                       cancel.addSelectionListener((Selected) (e) -> cancelPressed());
-                       cancel.addTraverseListener(traverseListener);
-
-                       Button ok = new Button(buttons, SWT.FLAT);
-                       ok.setText(CmsMsg.ok.lead());
-                       ok.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-                       ok.addSelectionListener((Selected) (e) -> okPressed());
-                       ok.addTraverseListener(traverseListener);
-                       if (input == null)
-                               ok.setFocus();
-                       else
-                               input.setFocus();
-
-                       buttons.setTabList(new Control[] { ok, cancel });
-               }
-               // pack();
-               parent.setTabList(new Control[] { body, buttons });
-               return body;
-       }
-
-       protected Control createInputArea(Composite parent) {
-               return null;
-       }
-
-       protected void okPressed() {
-               closeShell(OK);
-       }
-
-       protected void cancelPressed() {
-               closeShell(CANCEL);
-       }
-
-       protected void cancel() {
-               closeShell(CANCEL);
-       }
-
-       protected Point getInitialSize() {
-               return new Point(400, 200);
-       }
-
-       public static boolean open(int kind, Shell parent, String message) {
-               CmsMessageDialog dialog = new CmsMessageDialog(parent, message, kind);
-               return dialog.open() == 0;
-       }
-
-       public static boolean openConfirm(String message) {
-               return open(CONFIRM, Display.getCurrent().getActiveShell(), message);
-       }
-
-       public static void openInformation(String message) {
-               open(INFORMATION, Display.getCurrent().getActiveShell(), message);
-       }
-
-       public static boolean openQuestion(String message) {
-               return open(QUESTION, Display.getCurrent().getActiveShell(), message);
-       }
-
-       public static void openWarning(String message) {
-               open(WARNING, Display.getCurrent().getActiveShell(), message);
-       }
-
-       public static void openError(String message) {
-               open(ERROR, Display.getCurrent().getActiveShell(), message);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java
deleted file mode 100644 (file)
index bf6417b..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-package org.argeo.cms.swt.dialogs;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-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;
-
-/** Generic lightweight dialog, not based on JFace. */
-public class LightweightDialog {
-       private final static CmsLog log = CmsLog.getLog(LightweightDialog.class);
-
-       // must be the same value as org.eclipse.jface.window.Window#OK
-       public final static int OK = 0;
-       // must be the same value as org.eclipse.jface.window.Window#CANCEL
-       public final static int CANCEL = 1;
-
-       private Shell parentShell;
-       private Shell backgroundShell;
-       private Shell foregoundShell;
-
-       private Integer returnCode = null;
-       private boolean block = true;
-
-       private String title;
-
-       /** Tries to find a display */
-       private static Display getDisplay() {
-               try {
-                       Display display = Display.getCurrent();
-                       if (display != null)
-                               return display;
-                       else
-                               return Display.getDefault();
-               } catch (Exception e) {
-                       return Display.getCurrent();
-               }
-       }
-
-       public LightweightDialog(Shell parentShell) {
-               this.parentShell = parentShell;
-       }
-
-       public int open() {
-               if (foregoundShell != null)
-                       throw new EclipseUiException("There is already a shell");
-               backgroundShell = new Shell(parentShell, SWT.ON_TOP);
-               backgroundShell.setFullScreen(true);
-               // if (parentShell != null) {
-               // backgroundShell.setBounds(parentShell.getBounds());
-               // } else
-               // backgroundShell.setMaximized(true);
-               backgroundShell.setAlpha(128);
-               backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
-               foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
-               if (title != null)
-                       setTitle(title);
-               foregoundShell.setLayout(new GridLayout());
-               foregoundShell.setSize(getInitialSize());
-               createDialogArea(foregoundShell);
-               // shell.pack();
-               // shell.layout();
-
-               Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP
-               Point dialogSize = foregoundShell.getSize();
-               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
-               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
-               foregoundShell.setLocation(x, y);
-
-               foregoundShell.addShellListener(new ShellAdapter() {
-                       private static final long serialVersionUID = -2701270481953688763L;
-
-                       @Override
-                       public void shellDeactivated(ShellEvent e) {
-                               if (hasChildShells())
-                                       return;
-                               if (returnCode == null)// not yet closed
-                                       closeShell(CANCEL);
-                       }
-
-                       @Override
-                       public void shellClosed(ShellEvent e) {
-                               notifyClose();
-                       }
-
-               });
-
-               backgroundShell.open();
-               foregoundShell.open();
-               // after the foreground shell has been opened
-               backgroundShell.addFocusListener(new FocusListener() {
-                       private static final long serialVersionUID = 3137408447474661070L;
-
-                       @Override
-                       public void focusLost(FocusEvent event) {
-                       }
-
-                       @Override
-                       public void focusGained(FocusEvent event) {
-                               if (hasChildShells())
-                                       return;
-                               if (returnCode == null)// not yet closed
-                                       closeShell(CANCEL);
-                       }
-               });
-
-               if (block) {
-                       block();
-               }
-               if (returnCode == null)
-                       returnCode = OK;
-               return returnCode;
-       }
-
-       public void block() {
-               try {
-                       runEventLoop(foregoundShell);
-               } catch (ThreadDeath t) {
-                       returnCode = CANCEL;
-                       if (log.isTraceEnabled())
-                               log.error("Thread death, canceling dialog", t);
-               } catch (Throwable t) {
-                       returnCode = CANCEL;
-                       log.error("Cannot open blocking lightweight dialog", t);
-               }
-       }
-
-       private boolean hasChildShells() {
-               if (foregoundShell == null)
-                       return false;
-               return foregoundShell.getShells().length != 0;
-       }
-
-       // public synchronized int openAndWait() {
-       // open();
-       // while (returnCode == null)
-       // try {
-       // wait(100);
-       // } catch (InterruptedException e) {
-       // // silent
-       // }
-       // return returnCode;
-       // }
-
-       private synchronized void notifyClose() {
-               if (returnCode == null)
-                       returnCode = CANCEL;
-               notifyAll();
-       }
-
-       protected void closeShell(int returnCode) {
-               this.returnCode = returnCode;
-               if (CANCEL == returnCode)
-                       onCancel();
-               if (foregoundShell != null && !foregoundShell.isDisposed()) {
-                       foregoundShell.close();
-                       foregoundShell.dispose();
-                       foregoundShell = null;
-               }
-
-               if (backgroundShell != null && !backgroundShell.isDisposed()) {
-                       backgroundShell.close();
-                       backgroundShell.dispose();
-               }
-       }
-
-       protected Point getInitialSize() {
-               // if (exception != null)
-               // return new Point(800, 600);
-               // else
-               return new Point(600, 400);
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogarea = new Composite(parent, SWT.NONE);
-               dialogarea.setLayout(new GridLayout());
-               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               return dialogarea;
-       }
-
-       protected Shell getBackgroundShell() {
-               return backgroundShell;
-       }
-
-       protected Shell getForegoundShell() {
-               return foregoundShell;
-       }
-
-       public void setBlockOnOpen(boolean shouldBlock) {
-               block = shouldBlock;
-       }
-
-       public void pack() {
-               foregoundShell.pack();
-       }
-
-       private void runEventLoop(Shell loopShell) {
-               Display display;
-               if (foregoundShell == null) {
-                       display = Display.getCurrent();
-               } else {
-                       display = loopShell.getDisplay();
-               }
-
-               while (loopShell != null && !loopShell.isDisposed()) {
-                       try {
-                               if (!display.readAndDispatch()) {
-                                       display.sleep();
-                               }
-                       } catch (UnsupportedOperationException e) {
-                               throw e;
-                       } catch (Throwable e) {
-                               handleException(e);
-                       }
-               }
-               if (!display.isDisposed())
-                       display.update();
-       }
-
-       protected void handleException(Throwable t) {
-               if (t instanceof ThreadDeath) {
-                       // Don't catch ThreadDeath as this is a normal occurrence when
-                       // the thread dies
-                       throw (ThreadDeath) t;
-               }
-               // Try to keep running.
-               t.printStackTrace();
-       }
-
-       /** @return false, if the dialog should not be closed. */
-       protected boolean onCancel() {
-               return true;
-       }
-
-       public void setTitle(String title) {
-               this.title = title;
-               if (title != null && getForegoundShell() != null)
-                       getForegoundShell().setText(title);
-       }
-
-       public Integer getReturnCode() {
-               return returnCode;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java
deleted file mode 100644 (file)
index 9404b81..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.swt.dialogs;
-
-import org.eclipse.jface.window.Window;
-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.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/** A dialog asking a for a single value. */
-public class SingleValueDialog extends CmsMessageDialog {
-       private Text valueT;
-       private String value;
-       private String defaultValue;
-
-       public SingleValueDialog(Shell parentShell, String message) {
-               super(parentShell, message, QUESTION);
-       }
-
-       public SingleValueDialog(Shell parentShell, String message, String defaultValue) {
-               super(parentShell, message, QUESTION);
-               this.defaultValue = defaultValue;
-       }
-
-       @Override
-       protected Control createInputArea(Composite parent) {
-               valueT = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE);
-               valueT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
-               if (defaultValue != null)
-                       valueT.setText(defaultValue);
-               return valueT;
-       }
-
-       @Override
-       protected void okPressed() {
-               value = valueT.getText();
-               super.okPressed();
-       }
-
-       public String getString() {
-               return value;
-       }
-
-       public Long getLong() {
-               return Long.valueOf(getString());
-       }
-
-       public Double getDouble() {
-               return Double.valueOf(getString());
-       }
-
-       public static String ask(String message) {
-               return ask(message, null);
-       }
-
-       public static String ask(String message, String defaultValue) {
-               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message, defaultValue);
-               if (svd.open() == Window.OK)
-                       return svd.getString();
-               else
-                       return null;
-       }
-
-       public static Long askLong(String message) {
-               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message);
-               if (svd.open() == Window.OK)
-                       return svd.getLong();
-               else
-                       return null;
-       }
-
-       public static Double askDouble(String message) {
-               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message);
-               if (svd.open() == Window.OK)
-                       return svd.getDouble();
-               else
-                       return null;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java
deleted file mode 100644 (file)
index ac76dba..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** SWT/JFace dialogs. */
-package org.argeo.cms.swt.dialogs;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java
deleted file mode 100644 (file)
index b3fec78..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-package org.argeo.cms.swt.osgi;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.cms.osgi.BundleCmsTheme;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.widgets.Display;
-
-/** Centralises some generic {@link CmsSwtTheme} patterns. */
-public class BundleCmsSwtTheme extends BundleCmsTheme implements CmsSwtTheme {
-       private Map<String, ImageData> imageCache = new HashMap<>();
-
-       private Map<String, Map<Integer, String>> iconPaths = new HashMap<>();
-
-       protected Image getImage(String path) {
-               if (!imageCache.containsKey(path)) {
-                       try (InputStream in = getResourceAsStream(path)) {
-                               if (in == null)
-                                       return null;
-                               ImageData imageData = new ImageData(in);
-                               imageCache.put(path, imageData);
-                       } catch (IOException e) {
-                               throw new IllegalStateException(e);
-                       }
-               }
-               ImageData imageData = imageCache.get(path);
-               Image image = new Image(Display.getCurrent(), imageData);
-               return image;
-       }
-
-       /**
-        * And icon with this file name (without the extension), with a best effort to
-        * find the appropriate size, or <code>null</code> if not found.
-        * 
-        * @param name          An icon file name without path and extension.
-        * @param preferredSize the preferred size, if <code>null</code>,
-        *                      {@link #getSmallIconSize()} will be tried.
-        */
-       public Image getIcon(String name, Integer preferredSize) {
-               if (preferredSize == null)
-                       preferredSize = getSmallIconSize();
-               Map<Integer, String> subCache;
-               if (!iconPaths.containsKey(name))
-                       subCache = new HashMap<>();
-               else
-                       subCache = iconPaths.get(name);
-               Image image = null;
-               if (!subCache.containsKey(preferredSize)) {
-                       Image bestMatchSoFar = null;
-                       paths: for (String p : getImagesPaths()) {
-                               int lastSlash = p.lastIndexOf('/');
-                               String fileName = p;
-                               String ext = "";
-                               if (lastSlash >= 0)
-                                       fileName = p.substring(lastSlash + 1);
-                               int lastDot = fileName.lastIndexOf('.');
-                               if (lastDot >= 0) {
-                                       ext = fileName.substring(lastDot + 1);
-                                       fileName = fileName.substring(0, lastDot);
-                               }
-
-                               if ("svg".equals(ext))
-                                       continue paths;
-
-                               if (fileName.equals(name)) {// matched
-                                       Image img = getImage(p);
-                                       int width = img.getBounds().width;
-                                       if (width == preferredSize) {// perfect match
-                                               subCache.put(preferredSize, p);
-                                               image = img;
-                                               break paths;
-                                       }
-                                       if (bestMatchSoFar == null) {
-                                               bestMatchSoFar = img;
-                                       } else {
-                                               if (Math.abs(width - preferredSize) < Math
-                                                               .abs(bestMatchSoFar.getBounds().width - preferredSize))
-                                                       bestMatchSoFar = img;
-                                       }
-                               }
-                       }
-
-                       if (image == null)
-                               image = bestMatchSoFar;
-               } else {
-                       image = getImage(subCache.get(preferredSize));
-               }
-
-               if (image != null && !iconPaths.containsKey(name))
-                       iconPaths.put(name, subCache);
-
-               return image;
-       }
-
-       @Override
-       public Image getSmallIcon(CmsIcon icon) {
-               return getIcon(icon.name(), getSmallIconSize());
-       }
-
-       @Override
-       public Image getBigIcon(CmsIcon icon) {
-               return getIcon(icon.name(), getBigIconSize());
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java
deleted file mode 100644 (file)
index e65f226..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-package org.argeo.cms.swt.osgi;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.batik.transcoder.TranscoderException;
-import org.apache.batik.transcoder.TranscoderInput;
-import org.apache.batik.transcoder.TranscoderOutput;
-import org.apache.batik.transcoder.image.ImageTranscoder;
-import org.apache.batik.transcoder.image.PNGTranscoder;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.framework.BundleContext;
-
-/** Theme which can dynamically create icons from SVG data. */
-public class BundleSvgTheme extends BundleCmsSwtTheme {
-       private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName());
-
-       private Map<String, Map<Integer, Image>> imageCache = Collections.synchronizedMap(new HashMap<>());
-
-       private Map<Integer, ImageTranscoder> transcoders = Collections.synchronizedMap(new HashMap<>());
-
-       @Override
-       public Image getIcon(String name, Integer preferredSize) {
-               String path = "icons/types/svg/" + name + ".svg";
-               return createImageFromSvg(path, preferredSize);
-       }
-
-       protected Image createImageFromSvg(String path, Integer preferredSize) {
-               Image image = null;
-               if (imageCache.containsKey(path)) {
-                       image = imageCache.get(path).get(preferredSize);
-               }
-               if (image != null)
-                       return image;
-               ImageData imageData = loadFromSvg(path, preferredSize);
-               image = new Image(Display.getDefault(), imageData);
-               if (!imageCache.containsKey(path))
-                       imageCache.put(path, Collections.synchronizedMap(new HashMap<>()));
-               imageCache.get(path).put(preferredSize, image);
-               return image;
-       }
-
-       protected ImageData loadFromSvg(String path, int size) {
-               ImageTranscoder transcoder = null;
-               synchronized (this) {
-                       transcoder = transcoders.get(size);
-                       if (transcoder == null) {
-                               transcoder = new PNGTranscoder();
-                               transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size);
-                               transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size);
-                               transcoders.put(size, transcoder);
-                       }
-               }
-               ImageData imageData;
-               try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
-                       if (in == null)
-                               throw new IllegalArgumentException(path + " not found");
-                       TranscoderInput input = new TranscoderInput(in);
-                       TranscoderOutput output = new TranscoderOutput(out);
-                       transcoder.transcode(input, output);
-                       try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) {
-                               imageData = new ImageData(imageIn);
-                       }
-                       logger.log(Level.DEBUG, () -> "Generated " + size + "x" + size + " PNG icon from " + path);
-               } catch (IOException | TranscoderException e) {
-                       throw new RuntimeException("Cannot transcode SVG " + path, e);
-               }
-
-               return imageData;
-       }
-
-       @Override
-       public void init(BundleContext bundleContext, Map<String, String> properties) {
-               super.init(bundleContext, properties);
-
-               // preload all icons
-//             paths: for (String p : getImagesPaths()) {
-//                     if (!p.endsWith(".svg"))
-//                             continue paths;
-//                     createImageFromSvg(p, getDefaultIconSize());
-//             }
-       }
-
-       @Override
-       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-               Display display = Display.getDefault();
-               if (display != null)
-                       for (String path : imageCache.keySet()) {
-                               for (Image image : imageCache.get(path).values()) {
-                                       display.syncExec(() -> image.dispose());
-                               }
-                       }
-               super.destroy(bundleContext, properties);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java
deleted file mode 100644 (file)
index ed1bfd8..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-package org.argeo.cms.swt.useradmin;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.util.naming.LdapAttrs;
-import org.argeo.util.naming.LdapObjs;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.dialogs.TrayDialog;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.TableViewer;
-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.FillLayout;
-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.Shell;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.useradmin.Group;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-import org.osgi.service.useradmin.UserAdmin;
-
-/** Dialog with a user (or group) list to pick up one */
-public class PickUpUserDialog extends TrayDialog {
-       private static final long serialVersionUID = -1420106871173920369L;
-
-       // Business objects
-       private final UserAdmin userAdmin;
-       private User selectedUser;
-
-       // this page widgets and UI objects
-       private String title;
-       private LdifUsersTable userTableViewerCmp;
-       private TableViewer userViewer;
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-
-       /**
-        * A dialog to pick up a group or a user, showing a table with default
-        * columns
-        */
-       public PickUpUserDialog(Shell parentShell, String title, UserAdmin userAdmin) {
-               super(parentShell);
-               this.title = title;
-               this.userAdmin = userAdmin;
-
-               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_ICON), "",
-                               24, 24));
-               columnDefs.add(new ColumnDefinition(
-                               new UserLP(UserLP.COL_DISPLAY_NAME), "Common Name", 150, 100));
-               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DOMAIN),
-                               "Domain", 100, 120));
-               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DN),
-                               "Distinguished Name", 300, 100));
-       }
-
-       /** A dialog to pick up a group or a user */
-       public PickUpUserDialog(Shell parentShell, String title,
-                       UserAdmin userAdmin, List<ColumnDefinition> columnDefs) {
-               super(parentShell);
-               this.title = title;
-               this.userAdmin = userAdmin;
-               this.columnDefs = columnDefs;
-       }
-
-       @Override
-       protected void okPressed() {
-               if (getSelected() == null)
-                       MessageDialog.openError(getShell(), "No user chosen",
-                                       "Please, choose a user or press Cancel.");
-               else
-                       super.okPressed();
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogArea = (Composite) super.createDialogArea(parent);
-               dialogArea.setLayout(new FillLayout());
-
-               Composite bodyCmp = new Composite(dialogArea, SWT.NO_FOCUS);
-               bodyCmp.setLayout(new GridLayout());
-
-               // Create and configure the table
-               userTableViewerCmp = new MyUserTableViewer(bodyCmp, SWT.MULTI
-                               | SWT.H_SCROLL | SWT.V_SCROLL);
-
-               userTableViewerCmp.setColumnDefinitions(columnDefs);
-               userTableViewerCmp.populateWithStaticFilters(false, false);
-               GridData gd = EclipseUiUtils.fillAll();
-               gd.minimumHeight = 300;
-               userTableViewerCmp.setLayoutData(gd);
-               userTableViewerCmp.refresh();
-
-               // Controllers
-               userViewer = userTableViewerCmp.getTableViewer();
-               userViewer.addDoubleClickListener(new MyDoubleClickListener());
-               userViewer
-                               .addSelectionChangedListener(new MySelectionChangedListener());
-
-               parent.pack();
-               return dialogArea;
-       }
-
-       public User getSelected() {
-               if (selectedUser == null)
-                       return null;
-               else
-                       return selectedUser;
-       }
-
-       protected void configureShell(Shell shell) {
-               super.configureShell(shell);
-               shell.setText(title);
-       }
-
-       class MyDoubleClickListener implements IDoubleClickListener {
-               public void doubleClick(DoubleClickEvent evt) {
-                       if (evt.getSelection().isEmpty())
-                               return;
-
-                       Object obj = ((IStructuredSelection) evt.getSelection())
-                                       .getFirstElement();
-                       if (obj instanceof User) {
-                               selectedUser = (User) obj;
-                               okPressed();
-                       }
-               }
-       }
-
-       class MySelectionChangedListener implements ISelectionChangedListener {
-               @Override
-               public void selectionChanged(SelectionChangedEvent event) {
-                       if (event.getSelection().isEmpty()) {
-                               selectedUser = null;
-                               return;
-                       }
-                       Object obj = ((IStructuredSelection) event.getSelection())
-                                       .getFirstElement();
-                       if (obj instanceof Group) {
-                               selectedUser = (Group) obj;
-                       }
-               }
-       }
-
-       private class MyUserTableViewer extends LdifUsersTable {
-               private static final long serialVersionUID = 8467999509931900367L;
-
-               private final String[] knownProps = { LdapAttrs.uid.name(),
-                               LdapAttrs.cn.name(), LdapAttrs.DN };
-
-               private Button showSystemRoleBtn;
-               private Button showUserBtn;
-
-               public MyUserTableViewer(Composite parent, int style) {
-                       super(parent, style);
-               }
-
-               protected void populateStaticFilters(Composite staticFilterCmp) {
-                       staticFilterCmp.setLayout(new GridLayout());
-                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
-                       showSystemRoleBtn.setText("Show system roles  ");
-
-                       showUserBtn = new Button(staticFilterCmp, SWT.CHECK);
-                       showUserBtn.setText("Show users  ");
-
-                       SelectionListener sl = new SelectionAdapter() {
-                               private static final long serialVersionUID = -7033424592697691676L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       refresh();
-                               }
-                       };
-
-                       showSystemRoleBtn.addSelectionListener(sl);
-                       showUserBtn.addSelectionListener(sl);
-               }
-
-               @Override
-               protected List<User> listFilteredElements(String filter) {
-                       Role[] roles;
-                       try {
-                               StringBuilder builder = new StringBuilder();
-
-                               StringBuilder filterBuilder = new StringBuilder();
-                               if (notNull(filter))
-                                       for (String prop : knownProps) {
-                                               filterBuilder.append("(");
-                                               filterBuilder.append(prop);
-                                               filterBuilder.append("=*");
-                                               filterBuilder.append(filter);
-                                               filterBuilder.append("*)");
-                                       }
-
-                               String typeStr = "(" + LdapAttrs.objectClass.name() + "="
-                                               + LdapObjs.groupOfNames.name() + ")";
-                               if ((showUserBtn.getSelection()))
-                                       typeStr = "(|(" + LdapAttrs.objectClass.name() + "="
-                                                       + LdapObjs.inetOrgPerson.name() + ")" + typeStr
-                                                       + ")";
-
-                               if (!showSystemRoleBtn.getSelection())
-                                       typeStr = "(& " + typeStr + "(!(" + LdapAttrs.DN + "=*"
-                                                       + CmsConstants.ROLES_BASEDN + ")))";
-
-                               if (filterBuilder.length() > 1) {
-                                       builder.append("(&" + typeStr);
-                                       builder.append("(|");
-                                       builder.append(filterBuilder.toString());
-                                       builder.append("))");
-                               } else {
-                                       builder.append(typeStr);
-                               }
-                               roles = userAdmin.getRoles(builder.toString());
-                       } catch (InvalidSyntaxException e) {
-                               throw new EclipseUiException(
-                                               "Unable to get roles with filter: " + filter, e);
-                       }
-                       List<User> users = new ArrayList<User>();
-                       for (Role role : roles)
-                               if (!users.contains(role))
-                                       users.add((User) role);
-                       return users;
-               }
-       }
-
-       private boolean notNull(String string) {
-               if (string == null)
-                       return false;
-               else
-                       return !"".equals(string.trim());
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java
deleted file mode 100644 (file)
index d1c90a4..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.argeo.cms.swt.useradmin;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.auth.UserAdminUtils;
-import org.eclipse.jface.resource.JFaceResources;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.useradmin.Role;
-import org.osgi.service.useradmin.User;
-
-/** Centralize label providers for the group table */
-class UserLP extends ColumnLabelProvider {
-       private static final long serialVersionUID = -4645930210988368571L;
-
-       final static String COL_ICON = "colID.icon";
-       final static String COL_DN = "colID.dn";
-       final static String COL_DISPLAY_NAME = "colID.displayName";
-       final static String COL_DOMAIN = "colID.domain";
-
-       final String currType;
-
-       // private Font italic;
-       private Font bold;
-
-       UserLP(String colId) {
-               this.currType = colId;
-       }
-
-       @Override
-       public Font getFont(Object element) {
-               // Current user as bold
-               if (UserAdminUtils.isCurrentUser(((User) element))) {
-                       if (bold == null)
-                               bold = JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD)
-                                               .createFont(Display.getCurrent());
-                       return bold;
-               }
-               return null;
-       }
-
-       @Override
-       public Image getImage(Object element) {
-               if (COL_ICON.equals(currType)) {
-                       User user = (User) element;
-                       String dn = user.getName();
-                       if (dn.endsWith(CmsConstants.ROLES_BASEDN))
-                               return UsersImages.ICON_ROLE;
-                       else if (user.getType() == Role.GROUP)
-                               return UsersImages.ICON_GROUP;
-                       else
-                               return UsersImages.ICON_USER;
-               } else
-                       return null;
-       }
-
-       @Override
-       public String getText(Object element) {
-               User user = (User) element;
-               return getText(user);
-
-       }
-
-       public String getText(User user) {
-               if (COL_DN.equals(currType))
-                       return user.getName();
-               else if (COL_DISPLAY_NAME.equals(currType))
-                       return UserAdminUtils.getCommonName(user);
-               else if (COL_DOMAIN.equals(currType))
-                       return UserAdminUtils.getDomainName(user);
-               else
-                       return "";
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java
deleted file mode 100644 (file)
index 21fc5af..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.cms.swt.useradmin;
-
-import org.argeo.cms.ui.theme.CmsImages;
-import org.eclipse.swt.graphics.Image;
-
-/** Specific users icons. */
-public class UsersImages {
-       private final static String PREFIX = "icons/";
-
-       public final static Image ICON_USER = CmsImages.createImg(PREFIX + "person.png");
-       public final static Image ICON_GROUP = CmsImages.createImg(PREFIX + "group.png");
-       public final static Image ICON_ROLE = CmsImages.createImg(PREFIX + "role.gif");
-       public final static Image ICON_CHANGE_PASSWORD = CmsImages.createImg(PREFIX + "security.gif");
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java
deleted file mode 100644 (file)
index 3597bfc..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** SWT/JFace users management components. */
-package org.argeo.cms.swt.useradmin;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java
deleted file mode 100644 (file)
index c3d11a1..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ux.widgets.DataPart;
-import org.argeo.cms.ux.widgets.DataView;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Composite;
-
-public abstract class AbstractSwtPart<INPUT, TYPE> extends Composite implements DataView<INPUT, TYPE> {
-       private static final long serialVersionUID = -1999179054267812170L;
-
-       protected DataPart<INPUT, TYPE> dataPart;
-
-       protected final SelectionListener selectionListener;
-
-       public AbstractSwtPart(Composite parent, int style, DataPart<INPUT, TYPE> dataPart) {
-               super(parent, style);
-               setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               this.dataPart = dataPart;
-
-               selectionListener = new SelectionListener() {
-
-                       private static final long serialVersionUID = 4334785560035009330L;
-
-                       @Override
-                       public void widgetSelected(SelectionEvent e) {
-                               if (dataPart.getOnSelected() != null)
-                                       dataPart.getOnSelected().accept((TYPE) e.item.getData());
-                       }
-
-                       @Override
-                       public void widgetDefaultSelected(SelectionEvent e) {
-                               if (dataPart.getOnAction() != null)
-                                       dataPart.getOnAction().accept((TYPE) e.item.getData());
-                       }
-               };
-
-               dataPart.addView(this);
-               addDisposeListener((e) -> dataPart.removeView(this));
-       }
-
-       public abstract void refresh();
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java
deleted file mode 100644 (file)
index f7b6443..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Shell;
-
-/**
- * Manages a lightweight shell which is related to a {@link Control}, typically
- * in order to reproduce a dropdown semantic, but with more flexibility.
- */
-public class ContextOverlay extends ScrolledPage {
-       private static final long serialVersionUID = 6702077429573324009L;
-
-//     private Shell shell;
-       private Control control;
-
-       private int maxHeight = 400;
-
-       public ContextOverlay(Control control, int style) {
-               super(createShell(control, style), SWT.NONE);
-               Shell shell = getShell();
-               setLayoutData(CmsSwtUtils.fillAll());
-               // TODO make autohide configurable?
-               //shell.addShellListener(new AutoHideShellListener());
-               this.control = control;
-               control.addDisposeListener((e) -> {
-                       dispose();
-                       shell.dispose();
-               });
-       }
-
-       private static Composite createShell(Control control, int style) {
-               if (control == null)
-                       throw new IllegalArgumentException("Control cannot be null");
-               if (control.isDisposed())
-                       throw new IllegalArgumentException("Control is disposed");
-               Shell shell = new Shell(control.getShell(), SWT.NO_TRIM);
-               shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               Composite placeholder = new Composite(shell, SWT.BORDER);
-               placeholder.setLayoutData(CmsSwtUtils.fillAll());
-               placeholder.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               return placeholder;
-       }
-
-       public void show() {
-               Point relativeControlLocation = control.getLocation();
-               Point controlLocation = control.toDisplay(relativeControlLocation.x, relativeControlLocation.y);
-
-               int controlWidth = control.getBounds().width;
-
-               Shell shell = getShell();
-
-               layout(true, true);
-               shell.pack();
-               shell.layout(true, true);
-               int targetShellWidth = shell.getSize().x < controlWidth ? controlWidth : shell.getSize().x;
-               if (shell.getSize().y > maxHeight) {
-                       shell.setSize(targetShellWidth, maxHeight);
-               } else {
-                       shell.setSize(targetShellWidth, shell.getSize().y);
-               }
-
-               int shellHeight = shell.getSize().y;
-               int controlHeight = control.getBounds().height;
-               Point shellLocation = new Point(controlLocation.x, controlLocation.y + controlHeight);
-               int displayHeight = shell.getDisplay().getBounds().height;
-               if (shellLocation.y + shellHeight > displayHeight) {// bottom of page
-                       shellLocation = new Point(controlLocation.x, controlLocation.y - shellHeight);
-               }
-               shell.setLocation(shellLocation);
-
-               if (getChildren().length != 0)
-                       shell.open();
-               if (!control.isDisposed())
-                       control.setFocus();
-       }
-
-       public void hide() {
-               getShell().setVisible(false);
-               onHide();
-       }
-
-       public boolean isShellVisible() {
-               if (isDisposed())
-                       return false;
-               return getShell().isVisible();
-       }
-
-       /** to be overridden */
-       protected void onHide() {
-               // does nothing by default.
-       }
-
-       private class AutoHideShellListener extends ShellAdapter {
-               private static final long serialVersionUID = 7743287433907938099L;
-
-               @Override
-               public void shellDeactivated(ShellEvent e) {
-                       try {
-                               Thread.sleep(1000);
-                       } catch (InterruptedException e1) {
-                               // silent
-                       }
-                       if (!control.isDisposed() && !control.isFocusControl())
-                               hide();
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java
deleted file mode 100644 (file)
index 0612e8f..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Editable text part displaying styled text. */
-public class EditableText extends StyledControl {
-       private static final long serialVersionUID = -6372283442330912755L;
-
-       private boolean editable = true;
-       private boolean multiLine = true;
-
-       private Color highlightColor;
-       private Composite highlight;
-
-       private boolean useTextAsLabel = false;
-
-       public EditableText(Composite parent, int style) {
-               super(parent, style);
-               editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
-               multiLine = !(SWT.SINGLE == (style & SWT.SINGLE));
-               highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY);
-               useTextAsLabel = SWT.FLAT == (style & SWT.FLAT);
-       }
-
-       @Override
-       protected Control createControl(Composite box, String style) {
-               if (isEditing() && getEditable()) {
-                       return createText(box, style, true);
-               } else {
-                       if (useTextAsLabel) {
-                               return createTextLabel(box, style);
-                       } else {
-                               return createLabel(box, style);
-                       }
-               }
-       }
-
-       protected Label createLabel(Composite box, String style) {
-               Label lbl = new Label(box, getStyle() | SWT.WRAP);
-               lbl.setLayoutData(CmsSwtUtils.fillWidth());
-               if (style != null)
-                       CmsSwtUtils.style(lbl, style);
-               CmsSwtUtils.markup(lbl);
-               if (mouseListener != null)
-                       lbl.addMouseListener(mouseListener);
-               return lbl;
-       }
-
-       protected Text createTextLabel(Composite box, String style) {
-               Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE));
-               lbl.setEditable(false);
-               lbl.setLayoutData(CmsSwtUtils.fillWidth());
-               if (style != null)
-                       CmsSwtUtils.style(lbl, style);
-               CmsSwtUtils.markup(lbl);
-               if (mouseListener != null)
-                       lbl.addMouseListener(mouseListener);
-               return lbl;
-       }
-
-       protected Text createText(Composite box, String style, boolean editable) {
-               highlight = new Composite(box, SWT.NONE);
-               highlight.setBackground(highlightColor);
-               GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
-               highlightGd.widthHint = 5;
-               highlightGd.heightHint = 3;
-               highlight.setLayoutData(highlightGd);
-
-               final Text text = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE) | SWT.WRAP);
-               text.setEditable(editable);
-               GridData textLayoutData = CmsSwtUtils.fillWidth();
-               // textLayoutData.heightHint = preferredHeight;
-               text.setLayoutData(textLayoutData);
-               if (style != null)
-                       CmsSwtUtils.style(text, style);
-               text.setFocus();
-               return text;
-       }
-
-       @Override
-       protected void clear(boolean deep) {
-               if (highlight != null)
-                       highlight.dispose();
-               super.clear(deep);
-       }
-
-       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();
-       }
-
-       public String getText() {
-               Control child = getControl();
-
-               if (child instanceof Label)
-                       return ((Label) child).getText();
-               else if (child instanceof Text)
-                       return ((Text) child).getText();
-               else
-                       throw new IllegalStateException("Unsupported control " + child.getClass());
-       }
-
-       /** @deprecated Use {@link #isEditable()} instead. */
-       @Deprecated
-       public boolean getEditable() {
-               return isEditable();
-       }
-
-       public boolean isEditable() {
-               return editable;
-       }
-
-       public void setUseTextAsLabel(boolean useTextAsLabel) {
-               this.useTextAsLabel = useTextAsLabel;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java
deleted file mode 100644 (file)
index 135f4c1..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.argeo.cms.swt.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;
-import org.eclipse.swt.widgets.Control;
-
-/**
- * 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) {
-               this(parent, style, false);
-       }
-
-       public ScrolledPage(Composite parent, int style, boolean alwaysShowScroll) {
-               super(createScrolledComposite(parent, alwaysShowScroll), style);
-               scrolledComposite = (ScrolledComposite) getParent();
-               scrolledComposite.setContent(this);
-
-               scrolledComposite.setExpandVertical(true);
-               scrolledComposite.setExpandHorizontal(true);
-               scrolledComposite.addControlListener(new ScrollControlListener());
-       }
-
-       private static ScrolledComposite createScrolledComposite(Composite parent, boolean alwaysShowScroll) {
-               ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL);
-               scrolledComposite.setAlwaysShowScrollBars(alwaysShowScroll);
-               return scrolledComposite;
-       }
-
-       @Override
-       public void layout(boolean changed, boolean all) {
-               updateScroll();
-               super.layout(changed, all);
-       }
-
-       public void showControl(Control control) {
-               scrolledComposite.showControl(control);
-       }
-
-       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/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java
deleted file mode 100644 (file)
index 82c04a2..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import org.argeo.api.cms.ux.CmsStyle;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SwtEditablePart;
-import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
-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 Composite implements SwtEditablePart {
-       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;
-
-       private Composite ancestorToLayout;
-
-       public StyledControl(Composite parent, int swtStyle) {
-               super(parent, swtStyle);
-               setLayout(CmsSwtUtils.noSpaceGridLayout());
-       }
-
-       protected abstract Control createControl(Composite box, String style);
-
-       protected Composite createBox() {
-               Composite box = new Composite(container, SWT.INHERIT_DEFAULT);
-               setContainerLayoutData(box);
-               box.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
-               return box;
-       }
-
-       protected Composite createContainer() {
-               Composite container = new Composite(this, SWT.INHERIT_DEFAULT);
-               setContainerLayoutData(container);
-               container.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               return container;
-       }
-
-       @Override
-       public Control getControl() {
-               return control;
-       }
-
-       protected synchronized Boolean isEditing() {
-               return editing;
-       }
-
-       @Override
-       public synchronized void startEditing() {
-               assert !isEditing();
-               editing = true;
-               // int height = control.getSize().y;
-               String style = (String) EclipseUiSpecificUtils.getStyleData(control);
-               clear(false);
-               refreshControl(style);
-
-               // add the focus listener to the newly created edition control
-               if (focusListener != null)
-                       control.addFocusListener(focusListener);
-       }
-
-       @Override
-       public synchronized void stopEditing() {
-               assert isEditing();
-               editing = false;
-               String style = (String) EclipseUiSpecificUtils.getStyleData(control);
-               clear(false);
-               refreshControl(style);
-       }
-
-       protected void refreshControl(String style) {
-               control = createControl(box, style);
-               setControlLayoutData(control);
-               if (ancestorToLayout != null)
-                       ancestorToLayout.layout(true, true);
-               else
-                       getParent().layout(true, true);
-       }
-
-       public void setStyle(CmsStyle style) {
-               setStyle(style.style());
-       }
-
-       public void setStyle(String style) {
-               Object currentStyle = null;
-               if (control != null)
-                       currentStyle = EclipseUiSpecificUtils.getStyleData(control);
-               if (currentStyle != null && currentStyle.equals(style))
-                       return;
-
-               clear(true);
-               refreshControl(style);
-
-               if (style != null) {
-                       CmsSwtUtils.style(box, style + "_box");
-                       CmsSwtUtils.style(container, style + "_container");
-               }
-       }
-
-       /** To be overridden */
-       protected void setControlLayoutData(Control control) {
-               control.setLayoutData(CmsSwtUtils.fillWidth());
-       }
-
-       /** To be overridden */
-       protected void setContainerLayoutData(Composite composite) {
-               composite.setLayoutData(CmsSwtUtils.fillWidth());
-       }
-
-       protected void clear(boolean deep) {
-               if (deep) {
-                       for (Control control : getChildren())
-                               control.dispose();
-                       container = createContainer();
-                       box = createBox();
-               } 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);
-       }
-
-       public void setAncestorToLayout(Composite ancestorToLayout) {
-               this.ancestorToLayout = ancestorToLayout;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java
deleted file mode 100644 (file)
index 07c9bac..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import java.util.List;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ux.widgets.HierarchicalPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeItem;
-
-/** {@link HierarchicalPart} implementation based on a {@link Tree}. */
-public class SwtHierarchicalPart<T> extends AbstractSwtPart<T, T> {
-       private static final long serialVersionUID = -6247710601465713047L;
-
-       private final Tree tree;
-
-       private HierarchicalPart<T> hierarchicalPart;
-
-       public SwtHierarchicalPart(Composite parent, int style, HierarchicalPart<T> hierarchicalPart) {
-               super(parent, style, hierarchicalPart);
-               tree = new Tree(this, SWT.BORDER);
-               tree.setLayoutData(CmsSwtUtils.fillAll());
-               this.hierarchicalPart = hierarchicalPart;
-
-               tree.addSelectionListener(selectionListener);
-       }
-
-       @Override
-       public void refresh() {
-               // TODO optimise
-               // tree.clearAll(true);
-
-               for (TreeItem rootItem : tree.getItems()) {
-                       rootItem.dispose();
-               }
-
-               List<T> rootItems = hierarchicalPart.getChildren(hierarchicalPart.getInput());
-               for (T child : rootItems) {
-                       TreeItem childItem = addTreeItem(null, child);
-//                     List<T> grandChildren = hierarchicalPart.getChildren(child);
-//                     for (T grandChild : grandChildren) {
-//                             addTreeItem(childItem, grandChild);
-//                     }
-               }
-//             tree.addListener(SWT.SetData, event -> {
-//                     TreeItem item = (TreeItem) event.item;
-//                     TreeItem parentItem = item.getParentItem();
-//                     if (parentItem == null) {
-//                             refreshRootItem(item);
-//                     } else {
-//                             refreshItem(parentItem, item);
-//                     }
-//             });
-//             tree.setItemCount(getRootItemCount());
-
-               tree.addListener(SWT.Expand, event -> {
-                       final TreeItem root = (TreeItem) event.item;
-                       TreeItem[] items = root.getItems();
-                       for (TreeItem item : items) {
-                               if (item.getData() != null) {
-//                                     List<T> grandChildren = hierarchicalPart.getChildren((T) item.getData());
-//                                     for (T grandChild : grandChildren) {
-//                                             addTreeItem(item, grandChild);
-//                                     }
-                                       return;
-                               }
-                               item.dispose();
-                       }
-
-                       List<T> children = hierarchicalPart.getChildren((T) root.getData());
-                       for (T child : children) {
-                               TreeItem childItem = addTreeItem(root, child);
-//                             List<T> grandChildren = hierarchicalPart.getChildren(child);
-//                             for (T grandChild : grandChildren) {
-//                                     addTreeItem(childItem, grandChild);
-//                             }
-                       }
-               });
-
-               CmsSwtUtils.fill(tree);
-
-       }
-
-       protected TreeItem addTreeItem(TreeItem parent, T data) {
-               TreeItem item = parent == null ? new TreeItem(tree, SWT.NONE) : new TreeItem(parent, SWT.NONE);
-               item.setData(data);
-               String txt = hierarchicalPart.getText(data);
-               if (txt != null)
-                       item.setText(hierarchicalPart.getText(data));
-               CmsIcon icon = hierarchicalPart.getIcon(data);
-               // TODO optimize
-               List<T> grandChildren = hierarchicalPart.getChildren(data);
-               if (grandChildren.size() != 0)
-                       new TreeItem(item, SWT.NONE);
-               return item;
-//if(icon!=null)
-//     item.setImage(null);
-       }
-
-       protected Tree getTree() {
-               return tree;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java
deleted file mode 100644 (file)
index 2f10cac..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.cms.swt.widgets;
-
-import org.argeo.api.cms.ux.CmsIcon;
-import org.argeo.cms.swt.CmsSwtTheme;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.ux.widgets.Column;
-import org.argeo.cms.ux.widgets.TabularPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TableItem;
-
-/** {@link TabularPart} implementation based on a {@link Table}. */
-public class SwtTabularPart<INPUT, T> extends AbstractSwtPart<INPUT, T> {
-       private static final long serialVersionUID = -1114155772446357750L;
-       private final Table table;
-       private TabularPart<INPUT, T> tabularPart;
-
-       private CmsSwtTheme theme;
-
-       public SwtTabularPart(Composite parent, int style, TabularPart<INPUT, T> tabularPart) {
-               super(parent, style, tabularPart);
-               theme = CmsSwtUtils.getCmsTheme(parent);
-
-               table = new Table(this, SWT.VIRTUAL | SWT.BORDER);
-               table.setLinesVisible(true);
-               table.setLayoutData(CmsSwtUtils.fillAll());
-
-               this.tabularPart = tabularPart;
-       }
-
-       @Override
-       public void refresh() {
-               // TODO optimise
-               table.clearAll();
-               table.addListener(SWT.SetData, event -> {
-                       TableItem item = (TableItem) event.item;
-                       refreshItem(item);
-               });
-               table.setItemCount(tabularPart.getItemCount());
-               for (int i = 0; i < tabularPart.getColumnCount(); i++) {
-                       TableColumn swtColumn = new TableColumn(table, SWT.NONE);
-                       swtColumn.setWidth(tabularPart.getColumn(i).getWidth());
-               }
-               CmsSwtUtils.fill(table);
-
-               table.addSelectionListener(selectionListener);
-
-       }
-
-       protected Object getDataFromEvent(SelectionEvent e) {
-               Object data = e.item.getData();
-               if (data == null)
-                       data = tabularPart.getData(getTable().indexOf((TableItem) e.item));
-               return data;
-       }
-
-       protected void refreshItem(TableItem item) {
-               int row = getTable().indexOf(item);
-               for (int i = 0; i < tabularPart.getColumnCount(); i++) {
-                       Column<T> column = tabularPart.getColumn(i);
-                       T data = tabularPart.getData(row);
-                       item.setData(data);
-                       String text = data != null ? column.getText(data) : "";
-                       if (text != null)
-                               item.setText(i, text);
-                       CmsIcon icon = column.getIcon(data);
-                       if (icon != null) {
-                               Image image = theme.getSmallIcon(icon);
-                               item.setImage(i, image);
-                       }
-               }
-       }
-
-       protected Table getTable() {
-               return table;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java
deleted file mode 100644 (file)
index 1c4d79e..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.argeo.cms.ui.theme;
-
-import java.net.URL;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-public class CmsImages {
-       private static BundleContext themeBc = FrameworkUtil.getBundle(CmsImages.class).getBundleContext();
-
-       final public static String ICONS_BASE = "icons/";
-       final public static String TYPES_BASE = ICONS_BASE + "types/";
-       final public static String ACTIONS_BASE = ICONS_BASE + "actions/";
-
-       public static Image createIcon(String name) {
-               return createImg(CmsImages.ICONS_BASE + name);
-       }
-
-       public static Image createAction(String name) {
-               return createImg(CmsImages.ACTIONS_BASE + name);
-       }
-
-       public static Image createType(String name) {
-               return createImg(CmsImages.TYPES_BASE + name);
-       }
-
-       public static Image createImg(String name) {
-               return CmsImages.createDesc(name).createImage(Display.getDefault());
-       }
-
-       public static ImageDescriptor createDesc(String name) {
-               return createDesc(themeBc, name);
-       }
-
-       public static ImageDescriptor createDesc(BundleContext bc, String name) {
-               URL url = bc.getBundle().getResource(name);
-               if (url == null)
-                       return ImageDescriptor.getMissingImageDescriptor();
-               return ImageDescriptor.createFromURL(url);
-       }
-
-       public static Image createImg(BundleContext bc, String name) {
-               return createDesc(bc, name).createImage(Display.getDefault());
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java
deleted file mode 100644 (file)
index 7d3a260..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Argeo CMS core theme images. */
-package org.argeo.cms.ui.theme;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java
deleted file mode 100644 (file)
index 64ea2db..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import org.argeo.cms.ux.widgets.TreeParent;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.Viewer;
-
-/**
- * Tree content provider dealing with tree objects and providing reasonable
- * defaults.
- */
-public abstract class AbstractTreeContentProvider implements
-               ITreeContentProvider {
-       private static final long serialVersionUID = 8246126401957763868L;
-
-       /** Does nothing */
-       public void dispose() {
-       }
-
-       /** Does nothing */
-       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-       }
-
-       public Object[] getChildren(Object element) {
-               if (element instanceof TreeParent) {
-                       return ((TreeParent) element).getChildren();
-               }
-               return new Object[0];
-       }
-
-       public Object getParent(Object element) {
-               if (element instanceof TreeParent) {
-                       return ((TreeParent) element).getParent();
-               }
-               return null;
-       }
-
-       public boolean hasChildren(Object element) {
-               if (element instanceof TreeParent) {
-                       return ((TreeParent) element).hasChildren();
-               }
-               return false;
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java
deleted file mode 100644 (file)
index a38552c..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-
-/**
- * Wraps the definition of a column to be used in the various JFace viewers
- * (typically tree and table). It enables definition of generic viewers which
- * column can be then defined externally. Also used to generate export.
- */
-public class ColumnDefinition {
-       private ColumnLabelProvider labelProvider;
-       private String label;
-       private int weight = 0;
-       private int minWidth = 120;
-
-       public ColumnDefinition(ColumnLabelProvider labelProvider, String label) {
-               this.labelProvider = labelProvider;
-               this.label = label;
-       }
-
-       public ColumnDefinition(ColumnLabelProvider labelProvider, String label,
-                       int weight) {
-               this.labelProvider = labelProvider;
-               this.label = label;
-               this.weight = weight;
-               this.minWidth = weight;
-       }
-
-       public ColumnDefinition(ColumnLabelProvider labelProvider, String label,
-                       int weight, int minimumWidth) {
-               this.labelProvider = labelProvider;
-               this.label = label;
-               this.weight = weight;
-               this.minWidth = minimumWidth;
-       }
-
-       public ColumnLabelProvider getLabelProvider() {
-               return labelProvider;
-       }
-
-       public void setLabelProvider(ColumnLabelProvider labelProvider) {
-               this.labelProvider = labelProvider;
-       }
-
-       public String getLabel() {
-               return label;
-       }
-
-       public void setLabel(String label) {
-               this.label = label;
-       }
-
-       public int getWeight() {
-               return weight;
-       }
-
-       public void setWeight(int weight) {
-               this.weight = weight;
-       }
-
-       public int getMinWidth() {
-               return minWidth;
-       }
-
-       public void setMinWidth(int minWidth) {
-               this.minWidth = minWidth;
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java
deleted file mode 100644 (file)
index 9430a20..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerComparator;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-
-/** Generic column viewer sorter */
-public class ColumnViewerComparator extends ViewerComparator {
-       private static final long serialVersionUID = -2266218906355859909L;
-
-       public static final int ASC = 1;
-
-       public static final int NONE = 0;
-
-       public static final int DESC = -1;
-
-       private int direction = 0;
-
-       private TableViewerColumn column;
-
-       private ColumnViewer viewer;
-
-       public ColumnViewerComparator(TableViewerColumn column) {
-               super(null);
-               this.column = column;
-               this.viewer = column.getViewer();
-               this.column.getColumn().addSelectionListener(new SelectionAdapter() {
-                       private static final long serialVersionUID = 7586796298965472189L;
-
-                       public void widgetSelected(SelectionEvent e) {
-                               if (ColumnViewerComparator.this.viewer.getComparator() != null) {
-                                       if (ColumnViewerComparator.this.viewer.getComparator() == ColumnViewerComparator.this) {
-                                               int tdirection = ColumnViewerComparator.this.direction;
-
-                                               if (tdirection == ASC) {
-                                                       setSortDirection(DESC);
-                                               } else if (tdirection == DESC) {
-                                                       setSortDirection(NONE);
-                                               }
-                                       } else {
-                                               setSortDirection(ASC);
-                                       }
-                               } else {
-                                       setSortDirection(ASC);
-                               }
-                       }
-               });
-       }
-
-       private void setSortDirection(int direction) {
-               if (direction == NONE) {
-                       column.getColumn().getParent().setSortColumn(null);
-                       column.getColumn().getParent().setSortDirection(SWT.NONE);
-                       viewer.setComparator(null);
-               } else {
-                       column.getColumn().getParent().setSortColumn(column.getColumn());
-                       this.direction = direction;
-
-                       if (direction == ASC) {
-                               column.getColumn().getParent().setSortDirection(SWT.DOWN);
-                       } else {
-                               column.getColumn().getParent().setSortDirection(SWT.UP);
-                       }
-
-                       if (viewer.getComparator() == this) {
-                               viewer.refresh();
-                       } else {
-                               viewer.setComparator(this);
-                       }
-
-               }
-       }
-
-       public int compare(Viewer viewer, Object e1, Object e2) {
-               return direction * super.compare(viewer, e1, e2);
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java
deleted file mode 100644 (file)
index 37a36e8..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.eclipse.ui;
-
-/** CMS specific exceptions. */
-public class EclipseUiException extends RuntimeException {
-       private static final long serialVersionUID = -5341764743356771313L;
-
-       public EclipseUiException(String message) {
-               super(message);
-       }
-
-       public EclipseUiException(String message, Throwable e) {
-               super(message, e);
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java
deleted file mode 100644 (file)
index 95b45fe..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import org.eclipse.jface.resource.JFaceResources;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.layout.FormAttachment;
-import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-/** Utilities to simplify UI development. */
-public class EclipseUiUtils {
-
-       /** Dispose all children of a Composite */
-       public static void clear(Composite composite) {
-               for (Control child : composite.getChildren())
-                       child.dispose();
-       }
-
-       /**
-        * Enables efficient call to the layout method of a composite, refreshing only
-        * some of the children controls.
-        */
-       public static void layout(Composite parent, Control... toUpdateControls) {
-               parent.layout(toUpdateControls);
-       }
-
-       //
-       // FONTS
-       //
-       /** Shortcut to retrieve default italic font from display */
-       public static Font getItalicFont(Composite parent) {
-               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.ITALIC)
-                               .createFont(parent.getDisplay());
-       }
-
-       /** Shortcut to retrieve default bold font from display */
-       public static Font getBoldFont(Composite parent) {
-               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD)
-                               .createFont(parent.getDisplay());
-       }
-
-       /** Shortcut to retrieve default bold italic font from display */
-       public static Font getBoldItalicFont(Composite parent) {
-               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD | SWT.ITALIC)
-                               .createFont(parent.getDisplay());
-       }
-
-       //
-       // Simplify grid layouts management
-       //
-       public static GridLayout noSpaceGridLayout() {
-               return noSpaceGridLayout(new GridLayout());
-       }
-
-       public static GridLayout noSpaceGridLayout(int columns) {
-               return noSpaceGridLayout(new GridLayout(columns, false));
-       }
-
-       public static GridLayout noSpaceGridLayout(GridLayout layout) {
-               layout.horizontalSpacing = 0;
-               layout.verticalSpacing = 0;
-               layout.marginWidth = 0;
-               layout.marginHeight = 0;
-               return layout;
-       }
-
-       public static GridData fillWidth() {
-               return grabWidth(SWT.FILL, SWT.FILL);
-       }
-
-       public static GridData fillWidth(int colSpan) {
-               GridData gd = grabWidth(SWT.FILL, SWT.FILL);
-               gd.horizontalSpan = colSpan;
-               return gd;
-       }
-
-       public static GridData fillAll() {
-               return new GridData(SWT.FILL, SWT.FILL, true, true);
-       }
-
-       public static GridData fillAll(int colSpan, int rowSpan) {
-               return new GridData(SWT.FILL, SWT.FILL, true, true, colSpan, rowSpan);
-       }
-
-       public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
-               return new GridData(horizontalAlignment, horizontalAlignment, true, false);
-       }
-
-       //
-       // Simplify Form layout management
-       //
-
-       /**
-        * Creates a basic form data that is attached to the 4 corners of the parent
-        * composite
-        */
-       public static FormData fillFormData() {
-               FormData formData = new FormData();
-               formData.top = new FormAttachment(0, 0);
-               formData.left = new FormAttachment(0, 0);
-               formData.right = new FormAttachment(100, 0);
-               formData.bottom = new FormAttachment(100, 0);
-               return formData;
-       }
-
-       /**
-        * Create a label and a text field for a grid layout, the text field grabbing
-        * excess horizontal
-        * 
-        * @param parent
-        *            the parent composite
-        * @param label
-        *            the label to display
-        * @param modifyListener
-        *            a {@link ModifyListener} to listen on events on the text, can be
-        *            null
-        * @return the created text
-        * 
-        */
-       // FIXME why was this deprecated.
-       // * @ deprecated use { @ link #createGridLT(Composite, String)} instead
-       // @ Deprecated
-       public static Text createGridLT(Composite parent, String label, ModifyListener modifyListener) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER);
-               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               if (modifyListener != null)
-                       txt.addModifyListener(modifyListener);
-               return txt;
-       }
-
-       /**
-        * Create a label and a text field for a grid layout, the text field grabbing
-        * excess horizontal
-        */
-       public static Text createGridLT(Composite parent, String label) {
-               return createGridLT(parent, label, null);
-       }
-
-       /**
-        * Creates one label and a text field not editable with background colour of the
-        * parent (like a label but with selectable text)
-        */
-       public static Text createGridLL(Composite parent, String label, String text) {
-               Text txt = createGridLT(parent, label);
-               txt.setText(text);
-               txt.setEditable(false);
-               txt.setBackground(parent.getBackground());
-               return txt;
-       }
-
-       /**
-        * Create a label and a text field with password display for a grid layout, the
-        * text field grabbing excess horizontal
-        */
-       public static Text createGridLP(Composite parent, String label) {
-               return createGridLP(parent, label, null);
-       }
-
-       /**
-        * Create a label and a text field with password display for a grid layout, the
-        * text field grabbing excess horizontal. The given modify listener will be
-        * added to the newly created text field if not null.
-        */
-       public static Text createGridLP(Composite parent, String label, ModifyListener modifyListener) {
-               Label lbl = new Label(parent, SWT.LEAD);
-               lbl.setText(label);
-               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
-               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
-               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
-               if (modifyListener != null)
-                       txt.addModifyListener(modifyListener);
-               return txt;
-       }
-
-       // MISCELLANEOUS
-
-       /** Simply checks if a string is not null nor empty */
-       public static boolean notEmpty(String stringToTest) {
-               return !(stringToTest == null || "".equals(stringToTest.trim()));
-       }
-
-       /** Simply checks if a string is null or empty */
-       public static boolean isEmpty(String stringToTest) {
-               return stringToTest == null || "".equals(stringToTest.trim());
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java
deleted file mode 100644 (file)
index e82505d..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import java.io.InputStream;
-
-/**
- * Used for file download : subclasses must implement model specific methods to
- * get a byte array representing a file given is ID.
- */
-@Deprecated
-public interface FileProvider {
-
-       public byte[] getByteArrayFileFromId(String fileId);
-
-       public InputStream getInputStreamFromFileId(String fileId);
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java
deleted file mode 100644 (file)
index e1d8b05..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerComparator;
-
-public abstract class GenericTableComparator extends ViewerComparator {
-       private static final long serialVersionUID = -1175894935075325810L;
-       protected int propertyIndex;
-       public static final int ASCENDING = 0, DESCENDING = 1;
-       protected int direction = DESCENDING;
-
-       /**
-        * Creates an instance of a sorter for TableViewer.
-        * 
-        * @param defaultColumnIndex
-        *            the default sorter column
-        */
-
-       public GenericTableComparator(int defaultColumnIndex, int direction) {
-               propertyIndex = defaultColumnIndex;
-               this.direction = direction;
-       }
-
-       public void setColumn(int column) {
-               if (column == this.propertyIndex) {
-                       // Same column as last sort; toggle the direction
-                       direction = 1 - direction;
-               } else {
-                       // New column; do a descending sort
-                       this.propertyIndex = column;
-                       direction = DESCENDING;
-               }
-       }
-
-       /**
-        * Must be Overriden in each view.
-        */
-       public abstract int compare(Viewer viewer, Object e1, Object e2);
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java
deleted file mode 100644 (file)
index ac7b2d8..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.eclipse.ui;
-
-import java.util.List;
-
-/**
- * Views and editors can implement this interface so that one of the list that
- * is displayed in the part (For instance in a Table or a Tree Viewer) can be
- * rebuilt externally. Typically to generate csv or calc extract.
- */
-public interface IListProvider {
-       /**
-        * Returns an array of current and relevant elements
-        */
-       public Object[] getElements(String extractId);
-
-       /**
-        * Returns the column definition for passed ID
-        */
-       public List<ColumnDefinition> getColumnDefinition(String extractId);
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java
deleted file mode 100644 (file)
index a388e74..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.argeo.eclipse.ui.dialogs;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.argeo.api.cms.CmsLog;
-import org.eclipse.jface.dialogs.IMessageProvider;
-import org.eclipse.jface.dialogs.TitleAreaDialog;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Generic error dialog to be used in try/catch blocks.
- * 
- * @deprecated Use CMS dialogs instead.
- */
-@Deprecated
-public class ErrorFeedback extends TitleAreaDialog {
-       private static final long serialVersionUID = -8918084784628179044L;
-
-       private final static CmsLog log = CmsLog.getLog(ErrorFeedback.class);
-
-       private final String message;
-       private final Throwable exception;
-
-       public static void show(String message, Throwable e) {
-               // rethrow ThreaDeath in order to make sure that RAP will properly clean
-               // up the UI thread
-               if (e instanceof ThreadDeath)
-                       throw (ThreadDeath) e;
-
-               new ErrorFeedback(newShell(), message, e).open();
-       }
-
-       public static void show(String message) {
-               new ErrorFeedback(newShell(), message, null).open();
-       }
-
-       private static Shell newShell() {
-               return new Shell(getDisplay(), SWT.NO_TRIM);
-       }
-
-       /** Tries to find a display */
-       private static Display getDisplay() {
-               try {
-                       Display display = Display.getCurrent();
-                       if (display != null)
-                               return display;
-                       else
-                               return Display.getDefault();
-               } catch (Exception e) {
-                       return Display.getCurrent();
-               }
-       }
-
-       public ErrorFeedback(Shell parentShell, String message, Throwable e) {
-               super(parentShell);
-               setShellStyle(SWT.NO_TRIM);
-               this.message = message;
-               this.exception = e;
-               log.error(message, e);
-       }
-
-       protected Point getInitialSize() {
-               if (exception != null)
-                       return new Point(800, 600);
-               else
-                       return new Point(400, 300);
-       }
-
-       @Override
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogarea = (Composite) super.createDialogArea(parent);
-               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               Composite composite = new Composite(dialogarea, SWT.NONE);
-               composite.setLayout(new GridLayout(2, false));
-               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               setMessage(message != null ? message + (exception != null ? ": " + exception.getMessage() : "")
-                               : exception != null ? exception.getMessage() : "Unkown Error", IMessageProvider.ERROR);
-
-               if (exception != null) {
-                       Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
-                       stack.setEditable(false);
-                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       StringWriter sw = new StringWriter();
-                       exception.printStackTrace(new PrintWriter(sw));
-                       stack.setText(sw.toString());
-               }
-
-               parent.pack();
-               return composite;
-       }
-
-       protected void configureShell(Shell shell) {
-               super.configureShell(shell);
-               shell.setText("Error");
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java
deleted file mode 100644 (file)
index f2715bc..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.argeo.eclipse.ui.dialogs;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-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.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Generic lightweight dialog, not based on JFace.
- * 
- * @deprecated Use CMS dialogs instead.
- */
-@Deprecated
-public class FeedbackDialog extends LightweightDialog {
-       private final static CmsLog log = CmsLog.getLog(FeedbackDialog.class);
-
-       private String message;
-       private Throwable exception;
-
-//     private Shell parentShell;
-       private Shell shell;
-
-       public static void show(String message, Throwable e) {
-               // rethrow ThreaDeath in order to make sure that RAP will properly clean
-               // up the UI thread
-               if (e instanceof ThreadDeath)
-                       throw (ThreadDeath) e;
-
-               new FeedbackDialog(getDisplay().getActiveShell(), message, e).open();
-       }
-
-       public static void show(String message) {
-               new FeedbackDialog(getDisplay().getActiveShell(), message, null).open();
-       }
-
-       /** Tries to find a display */
-       private static Display getDisplay() {
-               try {
-                       Display display = Display.getCurrent();
-                       if (display != null)
-                               return display;
-                       else
-                               return Display.getDefault();
-               } catch (Exception e) {
-                       return Display.getCurrent();
-               }
-       }
-
-       public FeedbackDialog(Shell parentShell, String message, Throwable e) {
-               super(parentShell);
-               this.message = message;
-               this.exception = e;
-               log.error(message, e);
-       }
-
-       public int open() {
-               if (shell != null)
-                       throw new EclipseUiException("There is already a shell");
-               shell = new Shell(getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
-               shell.setLayout(new GridLayout());
-               // shell.setText("Error");
-               shell.setSize(getInitialSize());
-               createDialogArea(shell);
-               // shell.pack();
-               // shell.layout();
-
-               Rectangle shellBounds = Display.getCurrent().getBounds();// RAP
-               Point dialogSize = shell.getSize();
-               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
-               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
-               shell.setLocation(x, y);
-
-               shell.addShellListener(new ShellAdapter() {
-                       private static final long serialVersionUID = -2701270481953688763L;
-
-                       @Override
-                       public void shellDeactivated(ShellEvent e) {
-                               closeShell();
-                       }
-               });
-
-               shell.open();
-               return OK;
-       }
-
-       protected void closeShell() {
-               shell.close();
-               shell.dispose();
-               shell = null;
-       }
-
-       protected Point getInitialSize() {
-               // if (exception != null)
-               // return new Point(800, 600);
-               // else
-               return new Point(400, 300);
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogarea = new Composite(parent, SWT.NONE);
-               dialogarea.setLayout(new GridLayout());
-               // Composite dialogarea = (Composite) super.createDialogArea(parent);
-               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               Label messageLbl = new Label(dialogarea, SWT.NONE);
-               if (message != null)
-                       messageLbl.setText(message);
-               else if (exception != null)
-                       messageLbl.setText(exception.getLocalizedMessage());
-
-               Composite composite = new Composite(dialogarea, SWT.NONE);
-               composite.setLayout(new GridLayout(2, false));
-               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-
-               if (exception != null) {
-                       Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
-                       stack.setEditable(false);
-                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-                       StringWriter sw = new StringWriter();
-                       exception.printStackTrace(new PrintWriter(sw));
-                       stack.setText(sw.toString());
-               }
-
-               // parent.pack();
-               return composite;
-       }
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java
deleted file mode 100644 (file)
index 615e141..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-package org.argeo.eclipse.ui.dialogs;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
-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;
-
-/** Generic lightweight dialog, not based on JFace. */
-@Deprecated
-public class LightweightDialog {
-       private final static CmsLog log = CmsLog.getLog(LightweightDialog.class);
-
-       // must be the same value as org.eclipse.jface.window.Window#OK
-       public final static int OK = 0;
-       // must be the same value as org.eclipse.jface.window.Window#CANCEL
-       public final static int CANCEL = 1;
-
-       private Shell parentShell;
-       private Shell backgroundShell;
-       private Shell foregoundShell;
-
-       private Integer returnCode = null;
-       private boolean block = true;
-
-       private String title;
-
-       /** Tries to find a display */
-       private static Display getDisplay() {
-               try {
-                       Display display = Display.getCurrent();
-                       if (display != null)
-                               return display;
-                       else
-                               return Display.getDefault();
-               } catch (Exception e) {
-                       return Display.getCurrent();
-               }
-       }
-
-       public LightweightDialog(Shell parentShell) {
-               this.parentShell = parentShell;
-       }
-
-       public int open() {
-               if (foregoundShell != null)
-                       throw new EclipseUiException("There is already a shell");
-               backgroundShell = new Shell(parentShell, SWT.ON_TOP);
-               backgroundShell.setFullScreen(true);
-               // if (parentShell != null) {
-               // backgroundShell.setBounds(parentShell.getBounds());
-               // } else
-               // backgroundShell.setMaximized(true);
-               backgroundShell.setAlpha(128);
-               backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
-               foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
-               if (title != null)
-                       setTitle(title);
-               foregoundShell.setLayout(new GridLayout());
-               foregoundShell.setSize(getInitialSize());
-               createDialogArea(foregoundShell);
-               // shell.pack();
-               // shell.layout();
-
-               Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP
-               Point dialogSize = foregoundShell.getSize();
-               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
-               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
-               foregoundShell.setLocation(x, y);
-
-               foregoundShell.addShellListener(new ShellAdapter() {
-                       private static final long serialVersionUID = -2701270481953688763L;
-
-                       @Override
-                       public void shellDeactivated(ShellEvent e) {
-                               if (hasChildShells())
-                                       return;
-                               if (returnCode == null)// not yet closed
-                                       closeShell(CANCEL);
-                       }
-
-                       @Override
-                       public void shellClosed(ShellEvent e) {
-                               notifyClose();
-                       }
-
-               });
-
-               backgroundShell.open();
-               foregoundShell.open();
-               // after the foreground shell has been opened
-               backgroundShell.addFocusListener(new FocusListener() {
-                       private static final long serialVersionUID = 3137408447474661070L;
-
-                       @Override
-                       public void focusLost(FocusEvent event) {
-                       }
-
-                       @Override
-                       public void focusGained(FocusEvent event) {
-                               if (hasChildShells())
-                                       return;
-                               if (returnCode == null)// not yet closed
-                                       closeShell(CANCEL);
-                       }
-               });
-
-               if (block) {
-                       block();
-               }
-               if (returnCode == null)
-                       returnCode = OK;
-               return returnCode;
-       }
-
-       public void block() {
-               try {
-                       runEventLoop(foregoundShell);
-               } catch (ThreadDeath t) {
-                       returnCode = CANCEL;
-                       if (log.isTraceEnabled())
-                               log.error("Thread death, canceling dialog", t);
-               } catch (Throwable t) {
-                       returnCode = CANCEL;
-                       log.error("Cannot open blocking lightweight dialog", t);
-               }
-       }
-
-       private boolean hasChildShells() {
-               if (foregoundShell == null)
-                       return false;
-               return foregoundShell.getShells().length != 0;
-       }
-
-       // public synchronized int openAndWait() {
-       // open();
-       // while (returnCode == null)
-       // try {
-       // wait(100);
-       // } catch (InterruptedException e) {
-       // // silent
-       // }
-       // return returnCode;
-       // }
-
-       private synchronized void notifyClose() {
-               if (returnCode == null)
-                       returnCode = CANCEL;
-               notifyAll();
-       }
-
-       protected void closeShell(int returnCode) {
-               this.returnCode = returnCode;
-               if (CANCEL == returnCode)
-                       onCancel();
-               if (foregoundShell != null && !foregoundShell.isDisposed()) {
-                       foregoundShell.close();
-                       foregoundShell.dispose();
-                       foregoundShell = null;
-               }
-
-               if (backgroundShell != null && !backgroundShell.isDisposed()) {
-                       backgroundShell.close();
-                       backgroundShell.dispose();
-               }
-       }
-
-       protected Point getInitialSize() {
-               // if (exception != null)
-               // return new Point(800, 600);
-               // else
-               return new Point(600, 400);
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogarea = new Composite(parent, SWT.NONE);
-               dialogarea.setLayout(new GridLayout());
-               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               return dialogarea;
-       }
-
-       protected Shell getBackgroundShell() {
-               return backgroundShell;
-       }
-
-       protected Shell getForegoundShell() {
-               return foregoundShell;
-       }
-
-       public void setBlockOnOpen(boolean shouldBlock) {
-               block = shouldBlock;
-       }
-
-       public void pack() {
-               foregoundShell.pack();
-       }
-
-       private void runEventLoop(Shell loopShell) {
-               Display display;
-               if (foregoundShell == null) {
-                       display = Display.getCurrent();
-               } else {
-                       display = loopShell.getDisplay();
-               }
-
-               while (loopShell != null && !loopShell.isDisposed()) {
-                       try {
-                               if (!display.readAndDispatch()) {
-                                       display.sleep();
-                               }
-                       } catch (UnsupportedOperationException e) {
-                               throw e;
-                       } catch (Throwable e) {
-                               handleException(e);
-                       }
-               }
-               if (!display.isDisposed())
-                       display.update();
-       }
-
-       protected void handleException(Throwable t) {
-               if (t instanceof ThreadDeath) {
-                       // Don't catch ThreadDeath as this is a normal occurrence when
-                       // the thread dies
-                       throw (ThreadDeath) t;
-               }
-               // Try to keep running.
-               t.printStackTrace();
-       }
-
-       /** @return false, if the dialog should not be closed. */
-       protected boolean onCancel() {
-               return true;
-       }
-
-       public void setTitle(String title) {
-               this.title = title;
-               if (title != null && getForegoundShell() != null)
-                       getForegoundShell().setText(title);
-       }
-
-       public Integer getReturnCode() {
-               return returnCode;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java
deleted file mode 100644 (file)
index 8ce9b44..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-package org.argeo.eclipse.ui.dialogs;
-
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.dialogs.IMessageProvider;
-import org.eclipse.jface.dialogs.TitleAreaDialog;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.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;
-import org.eclipse.swt.widgets.Text;
-
-/**
- * Dialog to retrieve a single value.
- * 
- * @deprecated Use CMS dialogs instead.
- */
-@Deprecated
-public class SingleValue extends TitleAreaDialog {
-       private static final long serialVersionUID = 2843538207460082349L;
-
-       private Text valueT;
-       private String value;
-       private final String title, message, label;
-       private final Boolean multiline;
-
-       public static String ask(String label, String message) {
-               SingleValue svd = new SingleValue(label, message);
-               if (svd.open() == Window.OK)
-                       return svd.getString();
-               else
-                       return null;
-       }
-
-       public static Long askLong(String label, String message) {
-               SingleValue svd = new SingleValue(label, message);
-               if (svd.open() == Window.OK)
-                       return svd.getLong();
-               else
-                       return null;
-       }
-
-       public static Double askDouble(String label, String message) {
-               SingleValue svd = new SingleValue(label, message);
-               if (svd.open() == Window.OK)
-                       return svd.getDouble();
-               else
-                       return null;
-       }
-
-       public SingleValue(String label, String message) {
-               this(Display.getDefault().getActiveShell(), label, message, label, false);
-       }
-
-       public SingleValue(Shell parentShell, String title, String message, String label, Boolean multiline) {
-               super(parentShell);
-               this.title = title;
-               this.message = message;
-               this.label = label;
-               this.multiline = multiline;
-       }
-
-       protected Point getInitialSize() {
-               if (multiline)
-                       return new Point(450, 350);
-
-               else
-                       return new Point(400, 270);
-       }
-
-       protected Control createDialogArea(Composite parent) {
-               Composite dialogarea = (Composite) super.createDialogArea(parent);
-               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               Composite composite = new Composite(dialogarea, SWT.NONE);
-               composite.setLayoutData(EclipseUiUtils.fillAll());
-               GridLayout layout = new GridLayout(2, false);
-               layout.marginWidth = layout.marginHeight = 20;
-               composite.setLayout(layout);
-
-               valueT = createLT(composite, label);
-
-               setMessage(message, IMessageProvider.NONE);
-
-               parent.pack();
-               valueT.setFocus();
-               return composite;
-       }
-
-       @Override
-       protected void okPressed() {
-               value = valueT.getText();
-               super.okPressed();
-       }
-
-       /** Creates label and text. */
-       protected Text createLT(Composite parent, String label) {
-               new Label(parent, SWT.NONE).setText(label);
-               Text text;
-               if (multiline) {
-                       text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.MULTI);
-                       text.setLayoutData(EclipseUiUtils.fillAll());
-               } else {
-                       text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE);
-                       text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
-               }
-               return text;
-       }
-
-       protected void configureShell(Shell shell) {
-               super.configureShell(shell);
-               shell.setText(title);
-       }
-
-       public String getString() {
-               return value;
-       }
-
-       public Long getLong() {
-               return Long.valueOf(getString());
-       }
-
-       public Double getDouble() {
-               return Double.valueOf(getString());
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java
deleted file mode 100644 (file)
index d6ab148..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic SWT/JFace dialogs. */
-package org.argeo.eclipse.ui.dialogs;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java
deleted file mode 100644 (file)
index c01b2d7..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.LinkedHashMap;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-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.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-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.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.Text;
-
-/** Simple UI provider that populates a composite parent given a NIO path */
-public class AdvancedFsBrowser {
-       private final static CmsLog log = CmsLog.getLog(AdvancedFsBrowser.class);
-
-       // Some local constants to experiment. should be cleaned
-       // private final static int THUMBNAIL_WIDTH = 400;
-       // private Point imageWidth = new Point(250, 0);
-       private final static int COLUMN_WIDTH = 160;
-
-       private Path initialPath;
-       private Path currEdited;
-       // Filter
-       private Composite displayBoxCmp;
-       private Text parentPathTxt;
-       private Text filterTxt;
-       // Browser columns
-       private ScrolledComposite scrolledCmp;
-       // Keep a cache of the opened directories
-       private LinkedHashMap<Path, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<>();
-       private Composite scrolledCmpBody;
-
-       public Control createUi(Composite parent, Path basePath) {
-               if (basePath == null)
-                       throw new IllegalArgumentException("Context cannot be null");
-               parent.setLayout(new GridLayout());
-
-               // top filter
-               Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
-               filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
-               addFilterPanel(filterCmp);
-
-               // Bottom part a sash with browser on the left
-               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
-               // form.setLayout(new FillLayout());
-               form.setLayoutData(EclipseUiUtils.fillAll());
-               Composite leftCmp = new Composite(form, SWT.NO_FOCUS);
-               displayBoxCmp = new Composite(form, SWT.NONE);
-               form.setWeights(new int[] { 3, 1 });
-
-               createBrowserPart(leftCmp, basePath);
-               // leftCmp.addControlListener(new ControlAdapter() {
-               // @Override
-               // public void controlResized(ControlEvent e) {
-               // Rectangle r = leftCmp.getClientArea();
-               // log.warn("Browser resized: " + r.toString());
-               // scrolledCmp.setMinSize(browserCols.size() * (COLUMN_WIDTH + 2),
-               // SWT.DEFAULT);
-               // // scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT,
-               // // r.height));
-               // }
-               // });
-
-               populateCurrEditedDisplay(displayBoxCmp, basePath);
-
-               // INIT
-               setEdited(basePath);
-               initialPath = basePath;
-               // form.layout(true, true);
-               return parent;
-       }
-
-       private void createBrowserPart(Composite parent, Path context) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               // scrolled composite
-               scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS);
-               scrolledCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               scrolledCmp.setExpandVertical(true);
-               scrolledCmp.setExpandHorizontal(true);
-               scrolledCmp.setShowFocusedControl(true);
-
-               scrolledCmpBody = new Composite(scrolledCmp, SWT.NO_FOCUS);
-               scrolledCmp.setContent(scrolledCmpBody);
-               scrolledCmpBody.addControlListener(new ControlAdapter() {
-                       private static final long serialVersionUID = 183238447102854553L;
-
-                       @Override
-                       public void controlResized(ControlEvent e) {
-                               Rectangle r = scrolledCmp.getClientArea();
-                               scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, r.height));
-                       }
-               });
-               initExplorer(scrolledCmpBody, context);
-               scrolledCmpBody.layout(true, true);
-               scrolledCmp.layout();
-
-       }
-
-       private Control initExplorer(Composite parent, Path context) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               return createBrowserColumn(parent, context);
-       }
-
-       private Control createBrowserColumn(Composite parent, Path context) {
-               // 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, table);
-               parent.layout(true, true);
-               return table;
-       }
-
-       public void addFilterPanel(Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)));
-
-               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(EclipseUiUtils.fillWidth());
-               filterTxt.addModifyListener(new ModifyListener() {
-                       private static final long serialVersionUID = 1L;
-
-                       public void modifyText(ModifyEvent event) {
-                               modifyFilter(false);
-                       }
-               });
-               filterTxt.addKeyListener(new KeyListener() {
-                       private static final long serialVersionUID = 2533535233583035527L;
-
-                       @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(currEdited);
-                                       if (table != null && !table.isDisposed())
-                                               currTable = table;
-                               }
-
-                               if (e.keyCode == SWT.ARROW_DOWN)
-                                       currTable.setFocus();
-                               else if (e.keyCode == SWT.BS) {
-                                       if (filterTxt.getText().equals("")
-                                                       && !(currEdited.getNameCount() == 1 || currEdited.equals(initialPath))) {
-                                               Path oldEdited = currEdited;
-                                               Path parentPath = currEdited.getParent();
-                                               setEdited(parentPath);
-                                               if (browserCols.containsKey(parentPath))
-                                                       browserCols.get(parentPath).setSelected(oldEdited);
-                                               filterTxt.setFocus();
-                                               e.doit = false;
-                                       }
-                               } else if (e.keyCode == SWT.TAB && !shiftPressed) {
-                                       Path uniqueChild = getOnlyChild(currEdited, filterTxt.getText());
-                                       if (uniqueChild != null) {
-                                               // Highlight the unique chosen child
-                                               currTable.setSelected(uniqueChild);
-                                               setEdited(uniqueChild);
-                                       }
-                                       filterTxt.setFocus();
-                                       e.doit = false;
-                               }
-                       }
-               });
-       }
-
-       private Path getOnlyChild(Path parent, String filter) {
-               try (DirectoryStream<Path> stream = Files.newDirectoryStream(currEdited, filter + "*")) {
-                       Path uniqueChild = null;
-                       boolean moreThanOne = false;
-                       loop: for (Path entry : stream) {
-                               if (uniqueChild == null) {
-                                       uniqueChild = entry;
-                               } else {
-                                       moreThanOne = true;
-                                       break loop;
-                               }
-                       }
-                       if (!moreThanOne)
-                               return uniqueChild;
-                       return null;
-               } catch (IOException ioe) {
-                       throw new FsUiException(
-                                       "Unable to determine unique child existence and get it under " + parent + " with filter " + filter,
-                                       ioe);
-               }
-       }
-
-       private void setEdited(Path path) {
-               currEdited = path;
-               EclipseUiUtils.clear(displayBoxCmp);
-               populateCurrEditedDisplay(displayBoxCmp, currEdited);
-               refreshFilters(path);
-               refreshBrowser(path);
-       }
-
-       private void refreshFilters(Path path) {
-               parentPathTxt.setText(path.toUri().toString());
-               filterTxt.setText("");
-               filterTxt.getParent().layout();
-       }
-
-       private void refreshBrowser(Path currPath) {
-               Path currParPath = currPath.getParent();
-               Object[][] colMatrix = new Object[browserCols.size()][2];
-
-               int i = 0, currPathIndex = -1, lastLeftOpenedIndex = -1;
-               for (Path path : browserCols.keySet()) {
-                       colMatrix[i][0] = path;
-                       colMatrix[i][1] = browserCols.get(path);
-                       if (currPathIndex >= 0 && lastLeftOpenedIndex < 0 && currParPath != null) {
-                               boolean leaveOpened = path.startsWith(currPath);
-                               if (!leaveOpened)
-                                       lastLeftOpenedIndex = i;
-                       }
-                       if (currParPath.equals(path))
-                               currPathIndex = i;
-                       i++;
-               }
-
-               if (currPathIndex >= 0 && lastLeftOpenedIndex >= 0) {
-                       // dispose and remove useless cols
-                       for (int l = i - 1; l >= lastLeftOpenedIndex; l--) {
-                               ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose();
-                               browserCols.remove(colMatrix[l][0]);
-                       }
-               }
-
-               if (browserCols.containsKey(currPath)) {
-                       FilterEntitiesVirtualTable currCol = browserCols.get(currPath);
-                       if (currCol.isDisposed()) {
-                               // Does it still happen ?
-                               log.warn(currPath + " browser column was disposed and still listed");
-                               browserCols.remove(currPath);
-                       }
-               }
-
-               if (!browserCols.containsKey(currPath) && Files.isDirectory(currPath))
-                       createBrowserColumn(scrolledCmpBody, currPath);
-
-               scrolledCmpBody.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false)));
-               scrolledCmpBody.layout(true, true);
-               // also resize the scrolled composite
-               scrolledCmp.layout();
-       }
-
-       private void modifyFilter(boolean fromOutside) {
-               if (!fromOutside)
-                       if (currEdited != null) {
-                               String filter = filterTxt.getText() + "*";
-                               FilterEntitiesVirtualTable table = browserCols.get(currEdited);
-                               if (table != null && !table.isDisposed())
-                                       table.filterList(filter);
-                       }
-       }
-
-       /**
-        * Recreates the content of the box that displays information about the current
-        * selected node.
-        */
-       private void populateCurrEditedDisplay(Composite parent, Path context) {
-               parent.setLayout(new GridLayout());
-
-               // if (isImg(context)) {
-               // EditableImage image = new Img(parent, RIGHT, context, imageWidth);
-               // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false,
-               // 2, 1));
-               // }
-
-               try {
-                       Label contextL = new Label(parent, SWT.NONE);
-                       contextL.setText(context.getFileName().toString());
-                       contextL.setFont(EclipseUiUtils.getBoldFont(parent));
-                       addProperty(parent, "Last modified", Files.getLastModifiedTime(context).toString());
-                       addProperty(parent, "Owner", Files.getOwner(context).getName());
-                       if (Files.isDirectory(context)) {
-                               addProperty(parent, "Type", "Folder");
-                       } else {
-                               String mimeType = Files.probeContentType(context);
-                               if (EclipseUiUtils.isEmpty(mimeType))
-                                       mimeType = "<i>Unknown</i>";
-                               addProperty(parent, "Type", mimeType);
-                               addProperty(parent, "Size", FsUiUtils.humanReadableByteCount(Files.size(context), false));
-                       }
-                       parent.layout(true, true);
-               } catch (IOException e) {
-                       throw new FsUiException("Cannot display details for " + context, e);
-               }
-       }
-
-       private void addProperty(Composite parent, String propName, String value) {
-               Label contextL = new Label(parent, SWT.NONE);
-               contextL.setText(propName + ": " + value);
-       }
-
-       /**
-        * Almost canonical implementation of a table that displays the content of a
-        * directory
-        */
-       private class FilterEntitiesVirtualTable extends Composite {
-               private static final long serialVersionUID = 2223410043691844875L;
-
-               // Context
-               private Path context;
-               private Path currSelected = null;
-
-               // UI Objects
-               private FsTableViewer viewer;
-
-               @Override
-               public boolean setFocus() {
-                       if (viewer.getTable().isDisposed())
-                               return false;
-                       if (currSelected != null)
-                               viewer.setSelection(new StructuredSelection(currSelected), true);
-                       else if (viewer.getSelection().isEmpty()) {
-                               Object first = viewer.getElementAt(0);
-                               if (first != null)
-                                       viewer.setSelection(new StructuredSelection(first), true);
-                       }
-                       return viewer.getTable().setFocus();
-               }
-
-               /**
-                * Enable highlighting the correct element in the table when externally browsing
-                * (typically via the command-line-like Text field)
-                */
-               void setSelected(Path selected) {
-                       // to prevent change selection event to be thrown
-                       currSelected = selected;
-                       viewer.setSelection(new StructuredSelection(currSelected), true);
-               }
-
-               void filterList(String filter) {
-                       viewer.setInput(context, filter);
-               }
-
-               public FilterEntitiesVirtualTable(Composite parent, int style, Path context) {
-                       super(parent, SWT.NO_FOCUS);
-                       this.context = context;
-                       createTableViewer(this);
-               }
-
-               private void createTableViewer(final Composite parent) {
-                       parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-                       // 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(EclipseUiUtils.noSpaceGridLayout());
-                       // viewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.MULTI |
-                       // SWT.V_SCROLL);
-                       // Table table = viewer.getTable();
-                       // table.setLayoutData(EclipseUiUtils.fillAll());
-
-                       viewer = new FsTableViewer(parent, SWT.MULTI);
-                       Table table = viewer.configureDefaultSingleColumnTable(COLUMN_WIDTH);
-
-                       viewer.addSelectionChangedListener(new ISelectionChangedListener() {
-
-                               @Override
-                               public void selectionChanged(SelectionChangedEvent event) {
-                                       IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
-                                       if (selection.isEmpty())
-                                               return;
-                                       Object obj = selection.getFirstElement();
-                                       Path newSelected;
-                                       if (obj instanceof Path)
-                                               newSelected = (Path) obj;
-                                       else if (obj instanceof ParentDir)
-                                               newSelected = ((ParentDir) obj).getPath();
-                                       else
-                                               return;
-                                       if (newSelected.equals(currSelected))
-                                               return;
-                                       currSelected = newSelected;
-                                       setEdited(newSelected);
-
-                               }
-                       });
-
-                       table.addKeyListener(new KeyListener() {
-                               private static final long serialVersionUID = -8083424284436715709L;
-
-                               @Override
-                               public void keyReleased(KeyEvent e) {
-                               }
-
-                               @Override
-                               public void keyPressed(KeyEvent e) {
-                                       IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
-                                       Path selected = null;
-                                       if (!selection.isEmpty())
-                                               selected = ((Path) selection.getFirstElement());
-                                       if (e.keyCode == SWT.ARROW_RIGHT) {
-                                               if (!Files.isDirectory(selected))
-                                                       return;
-                                               if (selected != null) {
-                                                       setEdited(selected);
-                                                       browserCols.get(selected).setFocus();
-                                               }
-                                       } else if (e.keyCode == SWT.ARROW_LEFT) {
-                                               if (context.equals(initialPath))
-                                                       return;
-                                               Path parent = context.getParent();
-                                               if (parent == null)
-                                                       return;
-
-                                               setEdited(parent);
-                                               browserCols.get(parent).setFocus();
-                                       }
-                               }
-                       });
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java
deleted file mode 100644 (file)
index d3fc1c9..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.swt.graphics.Image;
-
-/** Basic label provider with icon for NIO file viewers */
-public class FileIconNameLabelProvider extends ColumnLabelProvider {
-       private static final long serialVersionUID = 8187902187946523148L;
-
-       private Image folderIcon;
-       private Image fileIcon;
-
-       public FileIconNameLabelProvider() {
-               // if (!PlatformUI.isWorkbenchRunning()) {
-               folderIcon = ImageDescriptor.createFromFile(getClass(), "folder.png").createImage();
-               fileIcon = ImageDescriptor.createFromFile(getClass(), "file.png").createImage();
-               // }
-       }
-
-       @Override
-       public void dispose() {
-               if (folderIcon != null)
-                       folderIcon.dispose();
-               if (fileIcon != null)
-                       fileIcon.dispose();
-               super.dispose();
-       }
-
-       @Override
-       public String getText(Object element) {
-               if (element instanceof Path) {
-                       Path curr = ((Path) element);
-                       Path name = curr.getFileName();
-                       if (name == null)
-                               return "[No name]";
-                       else
-                               return name.toString();
-               } else if (element instanceof ParentDir) {
-                       return "..";
-               }
-               return null;
-       }
-
-       @Override
-       public Image getImage(Object element) {
-               if (element instanceof Path) {
-                       Path curr = ((Path) element);
-                       if (Files.isDirectory(curr))
-                               // if (folderIcon != null)
-                               return folderIcon;
-                       // else
-                       // return
-                       // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
-                       // else if (fileIcon != null)
-                       return fileIcon;
-                       // else
-                       // return
-                       // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);
-               } else if (element instanceof ParentDir) {
-                       return folderIcon;
-               }
-               return null;
-       }
-
-       @Override
-       public String getToolTipText(Object element) {
-               if (element instanceof Path) {
-                       Path curr = ((Path) element);
-                       Path name = curr.getFileName();
-                       if (name == null)
-                               return "[No name]";
-                       else
-                               return name.toAbsolutePath().toString();
-               } else if (element instanceof ParentDir) {
-                       return ((ParentDir) element).getPath().toAbsolutePath().toString();
-               }
-               return null;
-       }
-
-}
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java
deleted file mode 100644 (file)
index 3b126e9..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.nio.file.Path;
-import java.util.List;
-
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.eclipse.jface.viewers.CellLabelProvider;
-import org.eclipse.jface.viewers.ILazyContentProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-
-/**
- * Canonical implementation of a JFace table viewer to display the content of a
- * file folder
- */
-public class FsTableViewer extends TableViewer {
-       private static final long serialVersionUID = -5632407542678477234L;
-
-       private boolean showHiddenItems = false;
-       private boolean folderFirst = true;
-       private boolean reverseOrder = false;
-       private String orderProperty = FsUiConstants.PROPERTY_NAME;
-
-       private Path initialPath = null;
-
-       public FsTableViewer(Composite parent, int style) {
-               super(parent, style | SWT.VIRTUAL);
-       }
-
-       public Table configureDefaultSingleColumnTable(int tableWidthHint) {
-
-               return configureDefaultSingleColumnTable(tableWidthHint, new FileIconNameLabelProvider());
-       }
-
-       public Table configureDefaultSingleColumnTable(int tableWidthHint, CellLabelProvider labelProvider) {
-               Table table = this.getTable();
-               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-               table.setLinesVisible(false);
-               table.setHeaderVisible(false);
-               // CmsUtils.markup(table);
-               // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
-
-               TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
-               TableColumn tcol = column.getColumn();
-               tcol.setWidth(tableWidthHint);
-               column.setLabelProvider(labelProvider);
-               this.setContentProvider(new MyLazyCP());
-               return table;
-       }
-
-       public Table configureDefaultTable(List<ColumnDefinition> columns) {
-               this.setContentProvider(new MyLazyCP());
-               Table table = this.getTable();
-               table.setLinesVisible(true);
-               table.setHeaderVisible(true);
-               // CmsUtils.markup(table);
-               // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
-               for (ColumnDefinition colDef : columns) {
-                       TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
-                       column.setLabelProvider(colDef.getLabelProvider());
-                       TableColumn tcol = column.getColumn();
-                       tcol.setResizable(true);
-                       tcol.setText(colDef.getLabel());
-                       tcol.setWidth(colDef.getMinWidth());
-               }
-               return table;
-       }
-
-       public void setInput(Path dir, String filter) {
-               Path[] rows = FsUiUtils.getChildren(dir, filter, showHiddenItems, folderFirst, orderProperty, reverseOrder);
-               if (rows == null) {
-                       this.setInput(null);
-                       this.setItemCount(0);
-                       return;
-               }
-               boolean isRoot;
-               try {
-                       isRoot = dir.getRoot().equals(dir);
-               } catch (Exception e) {
-                       // FIXME Workaround for JCR root node access
-                       isRoot = dir.toString().equals("/");
-               }
-               final Object[] res;
-               if (isRoot)
-                       res = rows;
-               else if (initialPath != null && initialPath.equals(dir))
-                       res = rows;
-               else {
-                       res = new Object[rows.length + 1];
-                       res[0] = new ParentDir(dir.getParent());
-                       for (int i = 1; i < res.length; i++) {
-                               res[i] = rows[i - 1];
-                       }
-               }
-               this.setInput(res);
-               int length = res.length;
-               this.setItemCount(length);
-               this.refresh();
-       }
-
-       /** Directly displays bookmarks **/
-       public void setPathsInput(Path... paths) {
-               this.setInput((Object[]) paths);
-               this.setItemCount(paths.length);
-               this.refresh();
-       }
-
-       /**
-        * A path which is to be considered as root (and thus provide no link to a
-        * parent directory)
-        */
-       public void setInitialPath(Path initialPath) {
-               this.initialPath = initialPath;
-       }
-
-       private class MyLazyCP implements ILazyContentProvider {
-               private static final long serialVersionUID = 9096550041395433128L;
-               private Object[] elements;
-
-               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) {
-                       if (index < elements.length)
-                               FsTableViewer.this.replace(elements[index], index);
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java
deleted file mode 100644 (file)
index f55ead7..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.TreeViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
-
-/**
- * Canonical implementation of a JFace TreeViewer to display the content of a
- * repository
- */
-public class FsTreeViewer extends TreeViewer {
-       private static final long serialVersionUID = -5632407542678477234L;
-
-       private boolean showHiddenItems = false;
-       private boolean showDirectoryFirst = true;
-       private String orderingProperty = FsUiConstants.PROPERTY_NAME;
-
-       public FsTreeViewer(Composite parent, int style) {
-               super(parent, style | SWT.VIRTUAL);
-       }
-
-       public Tree configureDefaultSingleColumnTable(int tableWidthHint) {
-               Tree tree = this.getTree();
-               tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
-               tree.setLinesVisible(true);
-               tree.setHeaderVisible(false);
-//             CmsUtils.markup(tree);
-
-               TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
-               TreeColumn tcol = column.getColumn();
-               tcol.setWidth(tableWidthHint);
-               column.setLabelProvider(new FileIconNameLabelProvider());
-
-               this.setContentProvider(new MyCP());
-               return tree;
-       }
-
-       public Tree configureDefaultTable(List<ColumnDefinition> columns) {
-               this.setContentProvider(new MyCP());
-               Tree tree = this.getTree();
-               tree.setLinesVisible(true);
-               tree.setHeaderVisible(true);
-//             CmsUtils.markup(tree);
-//             CmsUtils.style(tree, MaintenanceStyles.BROWSER_COLUMN);
-               for (ColumnDefinition colDef : columns) {
-                       TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
-                       column.setLabelProvider(colDef.getLabelProvider());
-                       TreeColumn tcol = column.getColumn();
-                       tcol.setResizable(true);
-                       tcol.setText(colDef.getLabel());
-                       tcol.setWidth(colDef.getMinWidth());
-               }
-               return tree;
-       }
-
-       public void setInput(Path dir, String filter) {
-               try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
-                       // TODO make this lazy
-                       List<Path> paths = new ArrayList<>();
-                       for (Path entry : stream) {
-                               paths.add(entry);
-                       }
-                       Object[] rows = paths.toArray(new Object[0]);
-                       this.setInput(rows);
-                       // this.setItemCount(rows.length);
-                       this.refresh();
-               } catch (IOException | DirectoryIteratorException e) {
-                       throw new FsUiException("Unable to filter " + dir + " children with filter " + filter, e);
-               }
-       }
-
-       /** Directly displays bookmarks **/
-       public void setPathsInput(Path... paths) {
-               this.setInput((Object[]) paths);
-               // this.setItemCount(paths.length);
-               this.refresh();
-       }
-
-       private class MyCP implements ITreeContentProvider {
-               private static final long serialVersionUID = 9096550041395433128L;
-               private Object[] elements;
-
-               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;
-               }
-
-               @Override
-               public Object[] getElements(Object inputElement) {
-                       return elements;
-               }
-
-               @Override
-               public Object[] getChildren(Object parentElement) {
-                       Path path = (Path) parentElement;
-                       if (!Files.isDirectory(path))
-                               return null;
-                       else
-                               return FsUiUtils.getChildren(path, "*", showHiddenItems, showDirectoryFirst, orderingProperty, false);
-               }
-
-               @Override
-               public Object getParent(Object element) {
-                       Path path = (Path) element;
-                       return path.getParent();
-               }
-
-               @Override
-               public boolean hasChildren(Object element) {
-                       Path path = (Path) element;
-                       try {
-                               if (!Files.isDirectory(path))
-                                       return false;
-                               else
-                                       try (DirectoryStream<Path> children = Files.newDirectoryStream(path, "*")) {
-                                               return children.iterator().hasNext();
-                                       }
-                       } catch (IOException e) {
-                               throw new FsUiException("Unable to check child existence on " + path, e);
-                       }
-               }
-
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java
deleted file mode 100644 (file)
index 2b51e71..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-/** Centralize constants used by the Nio FS UI parts */
-public interface FsUiConstants {
-
-       // TODO use standard properties
-       String PROPERTY_NAME = "name";
-       String PROPERTY_SIZE = "size";
-       String PROPERTY_LAST_MODIFIED = "last-modified";
-       String PROPERTY_TYPE = "type";
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java
deleted file mode 100644 (file)
index 422b0e1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-/** Files specific exception */
-public class FsUiException extends RuntimeException {
-       private static final long serialVersionUID = 1L;
-
-       public FsUiException(String message) {
-               super(message);
-       }
-
-       public FsUiException(String message, Throwable e) {
-               super(message, e);
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java
deleted file mode 100644 (file)
index 956d96b..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/** Centralise additional utilitary methods to manage Java7 NIO files */
-public class FsUiUtils {
-
-       /**
-        * thanks to
-        * http://programming.guide/java/formatting-byte-size-to-human-readable-format.html
-        */
-       public static String humanReadableByteCount(long bytes, boolean si) {
-               int unit = si ? 1000 : 1024;
-               if (bytes < unit)
-                       return bytes + " B";
-               int exp = (int) (Math.log(bytes) / Math.log(unit));
-               String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
-               return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
-       }
-
-       public static Path[] getChildren(Path parent, String filter, boolean showHiddenItems, boolean folderFirst,
-                       String orderProperty, boolean reverseOrder) {
-               if (!Files.isDirectory(parent))
-                       return null;
-               List<Pair> pairs = new ArrayList<>();
-               try (DirectoryStream<Path> stream = Files.newDirectoryStream(parent, filter)) {
-                       loop: for (Path entry : stream) {
-                               if (!showHiddenItems)
-                                       if (Files.isHidden(entry))
-                                               continue loop;
-                               switch (orderProperty) {
-                               case FsUiConstants.PROPERTY_SIZE:
-                                       if (folderFirst)
-                                               pairs.add(new LPair(entry, Files.size(entry), Files.isDirectory(entry)));
-                                       else
-                                               pairs.add(new LPair(entry, Files.size(entry)));
-                                       break;
-                               case FsUiConstants.PROPERTY_LAST_MODIFIED:
-                                       if (folderFirst)
-                                               pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis(),
-                                                               Files.isDirectory(entry)));
-                                       else
-                                               pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis()));
-                                       break;
-                               case FsUiConstants.PROPERTY_NAME:
-                                       if (folderFirst)
-                                               pairs.add(new SPair(entry, entry.getFileName().toString(), Files.isDirectory(entry)));
-                                       else
-                                               pairs.add(new SPair(entry, entry.getFileName().toString()));
-                                       break;
-                               default:
-                                       throw new FsUiException("Unable to prepare sort for property " + orderProperty);
-                               }
-                       }
-                       Pair[] rows = pairs.toArray(new Pair[0]);
-                       Arrays.sort(rows);
-                       Path[] results = new Path[rows.length];
-                       if (reverseOrder) {
-                               int j = rows.length - 1;
-                               for (int i = 0; i < rows.length; i++)
-                                       results[i] = rows[j - i].p;
-                       } else
-                               for (int i = 0; i < rows.length; i++)
-                                       results[i] = rows[i].p;
-                       return results;
-               } catch (IOException | DirectoryIteratorException e) {
-                       throw new FsUiException("Unable to filter " + parent + " children with filter " + filter, e);
-               }
-       }
-
-       static abstract class Pair implements Comparable<Object> {
-               Path p;
-               Boolean i;
-       };
-
-       static class LPair extends Pair {
-               long v;
-
-               public LPair(Path path, long propValue) {
-                       p = path;
-                       v = propValue;
-               }
-
-               public LPair(Path path, long propValue, boolean isDir) {
-                       p = path;
-                       v = propValue;
-                       i = isDir;
-               }
-
-               public int compareTo(Object o) {
-                       if (i != null) {
-                               Boolean j = ((LPair) o).i;
-                               if (i.booleanValue() != j.booleanValue())
-                                       return i.booleanValue() ? -1 : 1;
-                       }
-                       long u = ((LPair) o).v;
-                       return v < u ? -1 : v == u ? 0 : 1;
-               }
-       };
-
-       static class SPair extends Pair {
-               String v;
-
-               public SPair(Path path, String propValue) {
-                       p = path;
-                       v = propValue;
-               }
-
-               public SPair(Path path, String propValue, boolean isDir) {
-                       p = path;
-                       v = propValue;
-                       i = isDir;
-               }
-
-               public int compareTo(Object o) {
-                       if (i != null) {
-                               Boolean j = ((SPair) o).i;
-                               if (i.booleanValue() != j.booleanValue())
-                                       return i.booleanValue() ? -1 : 1;
-                       }
-                       String u = ((SPair) o).v;
-                       return v.compareTo(u);
-               }
-       };
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java
deleted file mode 100644 (file)
index 2bb65ee..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-
-/** Expect a {@link Path} as input element */
-public class NioFileLabelProvider extends ColumnLabelProvider {
-       private final static FileTime EPOCH = FileTime.fromMillis(0);
-       private static final long serialVersionUID = 2160026425187796930L;
-       private final String propName;
-       private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
-
-       // TODO use new formatting
-       // DateTimeFormatter formatter =
-       // DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
-       // .withLocale( Locale.UK )
-       // .withZone( ZoneId.systemDefault() );
-       public NioFileLabelProvider(String propName) {
-               this.propName = propName;
-       }
-
-       @Override
-       public String getText(Object element) {
-               try {
-                       Path path;
-                       if (element instanceof ParentDir) {
-//                             switch (propName) {
-//                             case FsUiConstants.PROPERTY_SIZE:
-//                                     return "-";
-//                             case FsUiConstants.PROPERTY_LAST_MODIFIED:
-//                                     return "-";
-//                             // return Files.getLastModifiedTime(((ParentDir) element).getPath()).toString();
-//                             case FsUiConstants.PROPERTY_TYPE:
-//                                     return "Folder";
-//                             }
-                               path = ((ParentDir) element).getPath();
-                       } else
-                               path = (Path) element;
-                       switch (propName) {
-                       case FsUiConstants.PROPERTY_SIZE:
-                               if (Files.isDirectory(path))
-                                       return "-";
-                               else
-                                       return FsUiUtils.humanReadableByteCount(Files.size(path), false);
-                       case FsUiConstants.PROPERTY_LAST_MODIFIED:
-                               if (Files.isDirectory(path))
-                                       return "-";
-                               FileTime time = Files.getLastModifiedTime(path);
-                               if (time.equals(EPOCH))
-                                       return "-";
-                               else
-                                       return dateFormat.format(new Date(time.toMillis()));
-                       case FsUiConstants.PROPERTY_TYPE:
-                               if (Files.isDirectory(path))
-                                       return "Folder";
-                               else {
-                                       String mimeType = Files.probeContentType(path);
-                                       if (EclipseUiUtils.isEmpty(mimeType))
-                                               return "Unknown";
-                                       else
-                                               return mimeType;
-                               }
-                       default:
-                               throw new IllegalArgumentException("Unsupported property " + propName);
-                       }
-               } catch (IOException ioe) {
-                       throw new FsUiException("Cannot get property " + propName + " on " + element);
-               }
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java
deleted file mode 100644 (file)
index 6f09c29..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.nio.file.Path;
-
-/** A parent directory (..) reference. */
-public class ParentDir {
-       Path path;
-
-       public ParentDir(Path path) {
-               super();
-               this.path = path;
-       }
-
-       public Path getPath() {
-               return path;
-       }
-
-       @Override
-       public int hashCode() {
-               return path.hashCode();
-       }
-
-       @Override
-       public String toString() {
-               return "Parent dir " + path;
-       }
-
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java
deleted file mode 100644 (file)
index 2e3d6b4..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-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.eclipse.swt.widgets.Table;
-
-/**
- * Experimental UI upon Java 7 nio files api: SashForm layout with bookmarks on
- * the left hand side and a simple table on the right hand side.
- */
-public class SimpleFsBrowser extends Composite {
-       private final static CmsLog log = CmsLog.getLog(SimpleFsBrowser.class);
-       private static final long serialVersionUID = -40347919096946585L;
-
-       private Path currSelected;
-       private FsTableViewer bookmarksViewer;
-       private FsTableViewer directoryDisplayViewer;
-
-       public SimpleFsBrowser(Composite parent, int style) {
-               super(parent, style);
-               createContent(this);
-               // parent.layout(true, true);
-       }
-
-       public Viewer getViewer() {
-               return directoryDisplayViewer;
-       }
-
-       private void createContent(Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-
-               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
-               Composite leftCmp = new Composite(form, SWT.NONE);
-               populateBookmarks(leftCmp);
-
-               Composite rightCmp = new Composite(form, SWT.BORDER);
-               populateDisplay(rightCmp);
-               form.setLayoutData(EclipseUiUtils.fillAll());
-               form.setWeights(new int[] { 1, 3 });
-       }
-
-       public void setInput(Path... paths) {
-               bookmarksViewer.setPathsInput(paths);
-               bookmarksViewer.getTable().getParent().layout(true, true);
-       }
-
-       private void populateBookmarks(final Composite parent) {
-               // GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
-               // layout.verticalSpacing = 5;
-               parent.setLayout(new GridLayout());
-
-               ISelectionChangedListener selList = new MySelectionChangedListener();
-
-               appendTitle(parent, "My bookmarks");
-               bookmarksViewer = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL);
-               Table table = bookmarksViewer.configureDefaultSingleColumnTable(500);
-               GridData gd = EclipseUiUtils.fillWidth();
-               gd.horizontalIndent = 10;
-               table.setLayoutData(gd);
-               bookmarksViewer.addSelectionChangedListener(selList);
-
-               appendTitle(parent, "Jcr + File");
-
-               FsTableViewer jcrFilesViewers = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL);
-               table = jcrFilesViewers.configureDefaultSingleColumnTable(500);
-               gd = EclipseUiUtils.fillWidth();
-               gd.horizontalIndent = 10;
-               table.setLayoutData(gd);
-               jcrFilesViewers.addSelectionChangedListener(selList);
-
-               // FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider();
-               // try {
-               // Path testPath = fsProvider.getPath(new URI("jcr+memory:/"));
-               // jcrFilesViewers.setPathsInput(testPath);
-               // } catch (URISyntaxException e) {
-               // // TODO Auto-generated catch block
-               // e.printStackTrace();
-               // }
-       }
-
-       private Label appendTitle(Composite parent, String value) {
-               Label titleLbl = new Label(parent, SWT.NONE);
-               titleLbl.setText(value);
-               titleLbl.setFont(EclipseUiUtils.getBoldFont(parent));
-               GridData gd = EclipseUiUtils.fillWidth();
-               gd.horizontalIndent = 5;
-               gd.verticalIndent = 5;
-               titleLbl.setLayoutData(gd);
-               return titleLbl;
-       }
-
-       private class MySelectionChangedListener implements ISelectionChangedListener {
-               @Override
-               public void selectionChanged(SelectionChangedEvent event) {
-                       IStructuredSelection selection = (IStructuredSelection) bookmarksViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-                       else {
-                               Path newSelected = (Path) selection.getFirstElement();
-                               if (newSelected.equals(currSelected))
-                                       return;
-                               currSelected = newSelected;
-                               directoryDisplayViewer.setInput(currSelected, "*");
-                       }
-               }
-       }
-
-       private void populateDisplay(final Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
-               List<ColumnDefinition> colDefs = new ArrayList<>();
-               colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 250));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
-                               "Last modified", 200));
-               Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
-               table.setLayoutData(EclipseUiUtils.fillAll());
-
-               table.addKeyListener(new KeyListener() {
-                       private static final long serialVersionUID = -8083424284436715709L;
-
-                       @Override
-                       public void keyReleased(KeyEvent e) {
-                       }
-
-                       @Override
-                       public void keyPressed(KeyEvent e) {
-                               log.debug("Key event received: " + e.keyCode);
-                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
-                               Path selected = null;
-                               if (!selection.isEmpty())
-                                       selected = ((Path) selection.getFirstElement());
-                               if (e.keyCode == SWT.CR) {
-                                       if (!Files.isDirectory(selected))
-                                               return;
-                                       if (selected != null) {
-                                               currSelected = selected;
-                                               directoryDisplayViewer.setInput(currSelected, "*");
-                                       }
-                               } else if (e.keyCode == SWT.BS) {
-                                       currSelected = currSelected.getParent();
-                                       directoryDisplayViewer.setInput(currSelected, "*");
-                                       directoryDisplayViewer.getTable().setFocus();
-                               }
-                       }
-               });
-
-//             directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
-//                     @Override
-//                     public void doubleClick(DoubleClickEvent event) {
-//                             IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
-//                             Path selected = null;
-//                             if (!selection.isEmpty()) {
-//                                     Object obj = selection.getFirstElement();
-//                                     if (obj instanceof Path)
-//                                             selected = (Path) obj;
-//                                     else if (obj instanceof ParentDir)
-//                                             selected = ((ParentDir) obj).getPath();
-//                             }
-//                             if (selected != null) {
-//                                     if (!Files.isDirectory(selected))
-//                                             return;
-//                                     currSelected = selected;
-//                                     directoryDisplayViewer.setInput(currSelected, "*");
-//                             }
-//                     }
-//             });
-
-               directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
-                       @Override
-                       public void doubleClick(DoubleClickEvent event) {
-                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
-                               Path selected = null;
-                               if (!selection.isEmpty()) {
-                                       Object obj = selection.getFirstElement();
-                                       if (obj instanceof Path)
-                                               selected = (Path) obj;
-                                       else if (obj instanceof ParentDir)
-                                               selected = ((ParentDir) obj).getPath();
-                               }
-                               if (selected != null) {
-                                       if (!Files.isDirectory(selected))
-                                               return;
-                                       currSelected = selected;
-                                       directoryDisplayViewer.setInput(currSelected, "*");
-                               }
-                       }
-               });
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java
deleted file mode 100644 (file)
index 401e5cb..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.argeo.eclipse.ui.fs;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.Tree;
-
-/** A simple Java 7 nio files browser with a tree */
-public class SimpleFsTreeBrowser extends Composite {
-       private final static CmsLog log = CmsLog.getLog(SimpleFsTreeBrowser.class);
-       private static final long serialVersionUID = -40347919096946585L;
-
-       private Path currSelected;
-       private FsTreeViewer treeViewer;
-       private FsTableViewer directoryDisplayViewer;
-
-       public SimpleFsTreeBrowser(Composite parent, int style) {
-               super(parent, style);
-               createContent(this);
-               // parent.layout(true, true);
-       }
-
-       private void createContent(Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
-               Composite child1 = new Composite(form, SWT.NONE);
-               populateTree(child1);
-               Composite child2 = new Composite(form, SWT.BORDER);
-               populateDisplay(child2);
-               form.setLayoutData(EclipseUiUtils.fillAll());
-               form.setWeights(new int[] { 1, 3 });
-       }
-
-       public void setInput(Path... paths) {
-               treeViewer.setPathsInput(paths);
-               treeViewer.getControl().getParent().layout(true, true);
-       }
-
-       private void populateTree(final Composite parent) {
-               // GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
-               // layout.verticalSpacing = 5;
-               parent.setLayout(new GridLayout());
-
-               ISelectionChangedListener selList = new MySelectionChangedListener();
-
-               treeViewer = new FsTreeViewer(parent, SWT.MULTI);
-               Tree tree = treeViewer.configureDefaultSingleColumnTable(500);
-               GridData gd = EclipseUiUtils.fillAll();
-               // gd.horizontalIndent = 10;
-               tree.setLayoutData(gd);
-               treeViewer.addSelectionChangedListener(selList);
-       }
-
-       private class MySelectionChangedListener implements ISelectionChangedListener {
-               @Override
-               public void selectionChanged(SelectionChangedEvent event) {
-                       IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
-                       if (selection.isEmpty())
-                               return;
-                       else {
-                               Path newSelected = (Path) selection.getFirstElement();
-                               if (newSelected.equals(currSelected))
-                                       return;
-                               currSelected = newSelected;
-                               if (Files.isDirectory(currSelected))
-                                       directoryDisplayViewer.setInput(currSelected, "*");
-                       }
-               }
-       }
-
-       private void populateDisplay(final Composite parent) {
-               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
-               List<ColumnDefinition> colDefs = new ArrayList<>();
-               colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200, 200));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100, 100));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 300, 300));
-               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
-                               "Last modified", 100, 100));
-               Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
-               table.setLayoutData(EclipseUiUtils.fillAll());
-
-               table.addKeyListener(new KeyListener() {
-                       private static final long serialVersionUID = -8083424284436715709L;
-
-                       @Override
-                       public void keyReleased(KeyEvent e) {
-                       }
-
-                       @Override
-                       public void keyPressed(KeyEvent e) {
-                               log.debug("Key event received: " + e.keyCode);
-                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
-                               Path selected = null;
-                               if (!selection.isEmpty())
-                                       selected = ((Path) selection.getFirstElement());
-                               if (e.keyCode == SWT.CR) {
-                                       if (!Files.isDirectory(selected))
-                                               return;
-                                       if (selected != null) {
-                                               currSelected = selected;
-                                               directoryDisplayViewer.setInput(currSelected, "*");
-                                       }
-                               } else if (e.keyCode == SWT.BS) {
-                                       currSelected = currSelected.getParent();
-                                       directoryDisplayViewer.setInput(currSelected, "*");
-                                       directoryDisplayViewer.getTable().setFocus();
-                               }
-                       }
-               });
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png
deleted file mode 100644 (file)
index ce2f2a8..0000000
Binary files a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png
deleted file mode 100644 (file)
index c31f37e..0000000
Binary files a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png and /dev/null differ
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java
deleted file mode 100644 (file)
index d7f83c3..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic SWT/JFace file system utilities. */
-package org.argeo.eclipse.ui.fs;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java
deleted file mode 100644 (file)
index 0d245db..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic SWT/JFace utilities. */
-package org.argeo.eclipse.ui;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java
deleted file mode 100644 (file)
index 5713905..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-package org.argeo.eclipse.ui.parts;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.eclipse.ui.ColumnDefinition;
-import org.argeo.eclipse.ui.EclipseUiException;
-import org.argeo.eclipse.ui.EclipseUiUtils;
-import org.argeo.eclipse.ui.util.ViewerUtils;
-import org.eclipse.jface.layout.TableColumnLayout;
-import org.eclipse.jface.viewers.CheckboxTableViewer;
-import org.eclipse.jface.viewers.ColumnLabelProvider;
-import org.eclipse.jface.viewers.ColumnWeightData;
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Link;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.Text;
-import org.osgi.service.useradmin.User;
-
-/**
- * Generic composite that display a filter and a table viewer to display users
- * (can also be groups)
- * 
- * Warning: this class does not extends <code>TableViewer</code>. Use the
- * getTableViewer method to access it.
- * 
- */
-public abstract class LdifUsersTable extends Composite {
-       private static final long serialVersionUID = -7385959046279360420L;
-
-       // Context
-       // private UserAdmin userAdmin;
-
-       // Configuration
-       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
-       private boolean hasFilter;
-       private boolean preventTableLayout = false;
-       private boolean hasSelectionColumn;
-       private int tableStyle;
-
-       // Local UI Objects
-       private TableViewer usersViewer;
-       private Text filterTxt;
-
-       /* EXPOSED METHODS */
-
-       /**
-        * @param parent
-        * @param style
-        */
-       public LdifUsersTable(Composite parent, int style) {
-               super(parent, SWT.NO_FOCUS);
-               this.tableStyle = style;
-       }
-
-       // TODO workaround the bug of the table layout in the Form
-       public LdifUsersTable(Composite parent, int style, boolean preventTableLayout) {
-               super(parent, SWT.NO_FOCUS);
-               this.tableStyle = style;
-               this.preventTableLayout = preventTableLayout;
-       }
-
-       /** This must be called before the call to populate method */
-       public void setColumnDefinitions(List<ColumnDefinition> columnDefinitions) {
-               this.columnDefs = columnDefinitions;
-       }
-
-       /**
-        * 
-        * @param addFilter
-        *            choose to add a field to filter results or not
-        * @param addSelection
-        *            choose to add a column to select some of the displayed results or
-        *            not
-        */
-       public void populate(boolean addFilter, boolean addSelection) {
-               // initialization
-               Composite parent = this;
-               hasFilter = addFilter;
-               hasSelectionColumn = addSelection;
-
-               // Main Layout
-               GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
-               layout.verticalSpacing = 5;
-               this.setLayout(layout);
-               if (hasFilter)
-                       createFilterPart(parent);
-
-               Composite tableComp = new Composite(parent, SWT.NO_FOCUS);
-               tableComp.setLayoutData(EclipseUiUtils.fillAll());
-               usersViewer = createTableViewer(tableComp);
-               usersViewer.setContentProvider(new UsersContentProvider());
-       }
-
-       /**
-        * 
-        * @param showMore
-        *            display static filters on creation
-        * @param addSelection
-        *            choose to add a column to select some of the displayed results or
-        *            not
-        */
-       public void populateWithStaticFilters(boolean showMore, boolean addSelection) {
-               // initialization
-               Composite parent = this;
-               hasFilter = true;
-               hasSelectionColumn = addSelection;
-
-               // Main Layout
-               GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
-               layout.verticalSpacing = 5;
-               this.setLayout(layout);
-               createStaticFilterPart(parent, showMore);
-
-               Composite tableComp = new Composite(parent, SWT.NO_FOCUS);
-               tableComp.setLayoutData(EclipseUiUtils.fillAll());
-               usersViewer = createTableViewer(tableComp);
-               usersViewer.setContentProvider(new UsersContentProvider());
-       }
-
-       /** Enable access to the selected users or groups */
-       public List<User> getSelectedUsers() {
-               if (hasSelectionColumn) {
-                       Object[] elements = ((CheckboxTableViewer) usersViewer).getCheckedElements();
-
-                       List<User> result = new ArrayList<User>();
-                       for (Object obj : elements) {
-                               result.add((User) obj);
-                       }
-                       return result;
-               } else
-                       throw new EclipseUiException(
-                                       "Unvalid request: no selection column " + "has been created for the current table");
-       }
-
-       /** Returns the User table viewer, typically to add doubleclick listener */
-       public TableViewer getTableViewer() {
-               return usersViewer;
-       }
-
-       /**
-        * Force the refresh of the underlying table using the current filter string if
-        * relevant
-        */
-       public void refresh() {
-               String filter = hasFilter ? filterTxt.getText().trim() : null;
-               if ("".equals(filter))
-                       filter = null;
-               refreshFilteredList(filter);
-       }
-
-       /** Effective repository request: caller must implement this method */
-       abstract protected List<User> listFilteredElements(String filter);
-
-       // protected List<User> listFilteredElements(String filter) {
-       // List<User> users = new ArrayList<User>();
-       // try {
-       // Role[] roles = userAdmin.getRoles(filter);
-       // // Display all users and groups
-       // for (Role role : roles)
-       // users.add((User) role);
-       // } catch (InvalidSyntaxException e) {
-       // throw new EclipseUiException("Unable to get roles with filter: "
-       // + filter, e);
-       // }
-       // return users;
-       // }
-
-       /* GENERIC COMPOSITE METHODS */
-       @Override
-       public boolean setFocus() {
-               if (hasFilter)
-                       return filterTxt.setFocus();
-               else
-                       return usersViewer.getTable().setFocus();
-       }
-
-       @Override
-       public void dispose() {
-               super.dispose();
-       }
-
-       /* LOCAL CLASSES AND METHODS */
-       // Will be usefull to rather use a virtual table viewer
-       private void refreshFilteredList(String filter) {
-               List<User> users = listFilteredElements(filter);
-               usersViewer.setInput(users.toArray());
-       }
-
-       private class UsersContentProvider implements IStructuredContentProvider {
-               private static final long serialVersionUID = 1L;
-
-               public Object[] getElements(Object inputElement) {
-                       return (Object[]) inputElement;
-               }
-
-               public void dispose() {
-               }
-
-               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-               }
-       }
-
-       /* MANAGE FILTER */
-       private void createFilterPart(Composite parent) {
-               // Text Area for the filter
-               filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
-               filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
-               filterTxt.addModifyListener(new ModifyListener() {
-                       private static final long serialVersionUID = 1L;
-
-                       public void modifyText(ModifyEvent event) {
-                               refreshFilteredList(filterTxt.getText());
-                       }
-               });
-       }
-
-       private void createStaticFilterPart(Composite parent, boolean showMore) {
-               Composite filterComp = new Composite(parent, SWT.NO_FOCUS);
-               filterComp.setLayout(new GridLayout(2, false));
-               filterComp.setLayoutData(EclipseUiUtils.fillWidth());
-               // generic search
-               filterTxt = new Text(filterComp, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
-               filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
-               // filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL |
-               // GridData.HORIZONTAL_ALIGN_FILL));
-               filterTxt.addModifyListener(new ModifyListener() {
-                       private static final long serialVersionUID = 1L;
-
-                       public void modifyText(ModifyEvent event) {
-                               refreshFilteredList(filterTxt.getText());
-                       }
-               });
-
-               // add static filter abilities
-               Link moreLk = new Link(filterComp, SWT.NONE);
-               Composite staticFilterCmp = new Composite(filterComp, SWT.NO_FOCUS);
-               staticFilterCmp.setLayoutData(EclipseUiUtils.fillWidth(2));
-               populateStaticFilters(staticFilterCmp);
-
-               MoreLinkListener listener = new MoreLinkListener(moreLk, staticFilterCmp, showMore);
-               // initialise the layout
-               listener.refresh();
-               moreLk.addSelectionListener(listener);
-       }
-
-       /** Overwrite to add static filters */
-       protected void populateStaticFilters(Composite staticFilterCmp) {
-       }
-
-       // private void addMoreSL(final Link more) {
-       // more.addSelectionListener( }
-
-       private class MoreLinkListener extends SelectionAdapter {
-               private static final long serialVersionUID = -524987616510893463L;
-               private boolean isShown;
-               private final Composite staticFilterCmp;
-               private final Link moreLk;
-
-               public MoreLinkListener(Link moreLk, Composite staticFilterCmp, boolean isShown) {
-                       this.moreLk = moreLk;
-                       this.staticFilterCmp = staticFilterCmp;
-                       this.isShown = isShown;
-               }
-
-               @Override
-               public void widgetSelected(SelectionEvent e) {
-                       isShown = !isShown;
-                       refresh();
-               }
-
-               public void refresh() {
-                       GridData gd = (GridData) staticFilterCmp.getLayoutData();
-                       if (isShown) {
-                               moreLk.setText("<a> Less... </a>");
-                               gd.heightHint = SWT.DEFAULT;
-                       } else {
-                               moreLk.setText("<a> More... </a>");
-                               gd.heightHint = 0;
-                       }
-                       forceLayout();
-               }
-       }
-
-       private void forceLayout() {
-               LdifUsersTable.this.getParent().layout(true, true);
-       }
-
-       private TableViewer createTableViewer(final Composite parent) {
-
-               int style = tableStyle | SWT.H_SCROLL | SWT.V_SCROLL;
-               if (hasSelectionColumn)
-                       style = style | SWT.CHECK;
-               Table table = new Table(parent, style);
-               TableColumnLayout layout = new TableColumnLayout();
-
-               // TODO the table layout does not works with the scrolled form
-
-               if (preventTableLayout) {
-                       parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
-                       table.setLayoutData(EclipseUiUtils.fillAll());
-               } else
-                       parent.setLayout(layout);
-
-               TableViewer viewer;
-               if (hasSelectionColumn)
-                       viewer = new CheckboxTableViewer(table);
-               else
-                       viewer = new TableViewer(table);
-               table.setLinesVisible(true);
-               table.setHeaderVisible(true);
-
-               TableViewerColumn column;
-               // int offset = 0;
-               if (hasSelectionColumn) {
-                       // offset = 1;
-                       column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 25);
-                       column.setLabelProvider(new ColumnLabelProvider() {
-                               private static final long serialVersionUID = 1L;
-
-                               @Override
-                               public String getText(Object element) {
-                                       return null;
-                               }
-                       });
-                       layout.setColumnData(column.getColumn(), new ColumnWeightData(25, 25, false));
-
-                       SelectionAdapter selectionAdapter = new SelectionAdapter() {
-                               private static final long serialVersionUID = 1L;
-
-                               boolean allSelected = false;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       allSelected = !allSelected;
-                                       ((CheckboxTableViewer) usersViewer).setAllChecked(allSelected);
-                               }
-                       };
-                       column.getColumn().addSelectionListener(selectionAdapter);
-               }
-
-               // NodeViewerComparator comparator = new NodeViewerComparator();
-               // TODO enable the sort by click on the header
-               // int i = offset;
-               for (ColumnDefinition colDef : columnDefs)
-                       createTableColumn(viewer, layout, colDef);
-
-               // column = ViewerUtils.createTableViewerColumn(viewer,
-               // colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize());
-               // column.setLabelProvider(new CLProvider(colDef.getPropertyName()));
-               // column.getColumn().addSelectionListener(
-               // JcrUiUtils.getNodeSelectionAdapter(i,
-               // colDef.getPropertyType(), colDef.getPropertyName(),
-               // comparator, viewer));
-               // i++;
-               // }
-
-               // IMPORTANT: initialize comparator before setting it
-               // JcrColumnDefinition firstCol = colDefs.get(0);
-               // comparator.setColumn(firstCol.getPropertyType(),
-               // firstCol.getPropertyName());
-               // viewer.setComparator(comparator);
-
-               return viewer;
-       }
-
-       /** Default creation of a column for a user table */
-       private TableViewerColumn createTableColumn(TableViewer tableViewer, TableColumnLayout layout,
-                       ColumnDefinition columnDef) {
-
-               boolean resizable = true;
-               TableViewerColumn tvc = new TableViewerColumn(tableViewer, SWT.NONE);
-               TableColumn column = tvc.getColumn();
-
-               column.setText(columnDef.getLabel());
-               column.setWidth(columnDef.getMinWidth());
-               column.setResizable(resizable);
-
-               ColumnLabelProvider lp = columnDef.getLabelProvider();
-               // add a reference to the display to enable font management
-               // if (lp instanceof UserAdminAbstractLP)
-               // ((UserAdminAbstractLP) lp).setDisplay(tableViewer.getTable()
-               // .getDisplay());
-               tvc.setLabelProvider(lp);
-
-               layout.setColumnData(column, new ColumnWeightData(columnDef.getWeight(), columnDef.getMinWidth(), resizable));
-
-               return tvc;
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java
deleted file mode 100644 (file)
index 9e93b11..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic SWT/JFace composites. */
-package org.argeo.eclipse.ui.parts;
\ No newline at end of file
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java
deleted file mode 100644 (file)
index 8f4df17..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.argeo.eclipse.ui.util;
-
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.jface.viewers.TableViewerColumn;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.jface.viewers.TreeViewerColumn;
-import org.eclipse.swt.widgets.Table;
-import org.eclipse.swt.widgets.TableColumn;
-import org.eclipse.swt.widgets.TreeColumn;
-
-/**
- * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers.
- */
-public class ViewerUtils {
-
-       /**
-        * Creates a basic column for the given table. For the time being, we do not
-        * support movable columns.
-        */
-       public static TableColumn createColumn(Table parent, String name, int style, int width) {
-               TableColumn result = new TableColumn(parent, style);
-               result.setText(name);
-               result.setWidth(width);
-               result.setResizable(true);
-               return result;
-       }
-
-       /**
-        * Creates a TableViewerColumn for the given viewer. For the time being, we do
-        * not support movable columns.
-        */
-       public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) {
-               TableViewerColumn tvc = new TableViewerColumn(parent, style);
-               TableColumn column = tvc.getColumn();
-               column.setText(name);
-               column.setWidth(width);
-               column.setResizable(true);
-               return tvc;
-       }
-
-       // public static TableViewerColumn createTableViewerColumn(TableViewer parent,
-       // Localized name, int style, int width) {
-       // return createTableViewerColumn(parent, name.lead(), style, width);
-       // }
-
-       /**
-        * Creates a TreeViewerColumn for the given viewer. For the time being, we do
-        * not support movable columns.
-        */
-       public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) {
-               TreeViewerColumn tvc = new TreeViewerColumn(parent, style);
-               TreeColumn column = tvc.getColumn();
-               column.setText(name);
-               column.setWidth(width);
-               column.setResizable(true);
-               return tvc;
-       }
-}
diff --git a/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java b/eclipse/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java
deleted file mode 100644 (file)
index 798d174..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Generic SWT/JFace JCR helpers. */
-package org.argeo.eclipse.ui.util;
\ No newline at end of file
diff --git a/org.argeo.api.cli/.classpath b/org.argeo.api.cli/.classpath
new file mode 100644 (file)
index 0000000..81fe078
--- /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-17"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.argeo.api.cli/.project b/org.argeo.api.cli/.project
new file mode 100644 (file)
index 0000000..8f5cd41
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.api.cli</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/org.argeo.api.cli/bnd.bnd b/org.argeo.api.cli/bnd.bnd
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/org.argeo.api.cli/build.properties b/org.argeo.api.cli/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandArgsException.java
new file mode 100644 (file)
index 0000000..b2a12a6
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.api.cli;
+
+public class CommandArgsException extends IllegalArgumentException {
+       private static final long serialVersionUID = -7271050747105253935L;
+       private String commandName;
+       private volatile CommandsCli commandsCli;
+
+       public CommandArgsException(Exception cause) {
+               super(cause.getMessage(), cause);
+       }
+
+       public CommandArgsException(String message) {
+               super(message);
+       }
+
+       public String getCommandName() {
+               return commandName;
+       }
+
+       public void setCommandName(String commandName) {
+               this.commandName = commandName;
+       }
+
+       public CommandsCli getCommandsCli() {
+               return commandsCli;
+       }
+
+       public void setCommandsCli(CommandsCli commandsCli) {
+               this.commandsCli = commandsCli;
+       }
+
+}
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandRuntimeException.java
new file mode 100644 (file)
index 0000000..52c0334
--- /dev/null
@@ -0,0 +1,35 @@
+package org.argeo.api.cli;
+
+import java.util.List;
+
+/** {@link RuntimeException} referring during a command run. */
+public class CommandRuntimeException extends RuntimeException {
+       private static final long serialVersionUID = 5595999301269377128L;
+
+       private final DescribedCommand<?> command;
+       private final List<String> arguments;
+
+       public CommandRuntimeException(Throwable e, DescribedCommand<?> command, List<String> arguments) {
+               this(null, e, command, arguments);
+       }
+
+       public CommandRuntimeException(String message, DescribedCommand<?> command, List<String> arguments) {
+               this(message, null, command, arguments);
+       }
+
+       public CommandRuntimeException(String message, Throwable e, DescribedCommand<?> command, List<String> arguments) {
+               super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")"
+                               : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e);
+               this.command = command;
+               this.arguments = arguments;
+       }
+
+       public DescribedCommand<?> getCommand() {
+               return command;
+       }
+
+       public List<String> getArguments() {
+               return arguments;
+       }
+
+}
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java b/org.argeo.api.cli/src/org/argeo/api/cli/CommandsCli.java
new file mode 100644 (file)
index 0000000..b82308a
--- /dev/null
@@ -0,0 +1,136 @@
+package org.argeo.api.cli;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Function;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/** Base class for a CLI managing sub commands. */
+public abstract class CommandsCli implements DescribedCommand<Object> {
+       public final static String HELP = "help";
+
+       private final String commandName;
+       private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
+
+       protected final Options options = new Options();
+
+       public CommandsCli(String commandName) {
+               this.commandName = commandName;
+       }
+
+       @Override
+       public Object apply(List<String> args) {
+               String cmd = null;
+               List<String> newArgs = new ArrayList<>();
+               try {
+                       CommandLineParser clParser = new DefaultParser();
+                       CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
+                       List<String> leftOvers = commonCl.getArgList();
+                       for (String arg : leftOvers) {
+                               if (!arg.startsWith("-") && cmd == null) {
+                                       cmd = arg;
+                               } else {
+                                       newArgs.add(arg);
+                               }
+                       }
+               } catch (ParseException e) {
+                       CommandArgsException cae = new CommandArgsException(e);
+                       throw cae;
+               }
+
+               Function<List<String>, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand();
+               if (function == null)
+                       throw new IllegalArgumentException("Uknown command " + cmd);
+               try {
+                       Object value = function.apply(newArgs);
+                       return value != null ? value.toString() : null;
+               } catch (CommandArgsException e) {
+                       if (e.getCommandName() == null) {
+                               e.setCommandName(cmd);
+                               e.setCommandsCli(this);
+                       }
+                       throw e;
+               } catch (IllegalArgumentException e) {
+                       CommandArgsException cae = new CommandArgsException(e);
+                       cae.setCommandName(cmd);
+                       throw cae;
+               }
+       }
+
+       @Override
+       public Options getOptions() {
+               return options;
+       }
+
+       protected void addCommand(String cmd, Function<List<String>, ?> function) {
+               commands.put(cmd, function);
+
+       }
+
+       @Override
+       public String getUsage() {
+               return "[command]";
+       }
+
+       protected void addCommandsCli(CommandsCli commandsCli) {
+               addCommand(commandsCli.getCommandName(), commandsCli);
+               commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli));
+       }
+
+       public String getCommandName() {
+               return commandName;
+       }
+
+       public Set<String> getSubCommands() {
+               return commands.keySet();
+       }
+
+       public Function<List<String>, ?> getCommand(String command) {
+               return commands.get(command);
+       }
+
+       public HelpCommand getHelpCommand() {
+               return (HelpCommand) getCommand(HELP);
+       }
+
+       public Function<List<String>, String> getDefaultCommand() {
+               return getHelpCommand();
+       }
+
+       /** In order to implement quickly a main method. */
+       public static void mainImpl(CommandsCli cli, String[] args) {
+               try {
+                       cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli));
+                       Object output = cli.apply(Arrays.asList(args));
+                       System.out.println(output);
+                       System.exit(0);
+               } catch (CommandArgsException e) {
+                       System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage());
+                       Throwable cause = e.getCause();
+                       if (!(cause instanceof MissingOptionException))
+                               e.printStackTrace();
+                       if (e.getCommandName() != null) {
+                               StringWriter out = new StringWriter();
+                               HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
+                               System.err.println(out.toString());
+                       } else {
+                               e.printStackTrace();
+                       }
+                       System.exit(1);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       System.exit(1);
+               }
+       }
+}
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java b/org.argeo.api.cli/src/org/argeo/api/cli/DescribedCommand.java
new file mode 100644 (file)
index 0000000..7a9d5d9
--- /dev/null
@@ -0,0 +1,55 @@
+package org.argeo.api.cli;
+
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/** A command that can be described. */
+public interface DescribedCommand<T> extends Function<List<String>, T> {
+       default Options getOptions() {
+               return new Options();
+       }
+
+       String getDescription();
+
+       default String getUsage() {
+               return null;
+       }
+
+       default String getExamples() {
+               return null;
+       }
+
+       default CommandLine toCommandLine(List<String> args) {
+               try {
+                       DefaultParser parser = new DefaultParser();
+                       return parser.parse(getOptions(), args.toArray(new String[args.size()]));
+               } catch (ParseException e) {
+                       throw new CommandArgsException(e);
+               }
+       }
+
+       /** In order to implement quickly a main method. */
+       public static void mainImpl(DescribedCommand<?> command, String[] args) {
+               try {
+                       Object output = command.apply(Arrays.asList(args));
+                       System.out.println(output);
+                       System.exit(0);
+               } catch (IllegalArgumentException e) {
+                       StringWriter out = new StringWriter();
+                       HelpCommand.printHelp(command, out);
+                       System.err.println(out.toString());
+                       System.exit(1);
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       System.exit(1);
+               }
+       }
+
+}
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java b/org.argeo.api.cli/src/org/argeo/api/cli/HelpCommand.java
new file mode 100644 (file)
index 0000000..d5285a6
--- /dev/null
@@ -0,0 +1,143 @@
+package org.argeo.api.cli;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.function.Function;
+
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+
+/** A special command that can describe {@link DescribedCommand}. */
+public class HelpCommand implements DescribedCommand<String> {
+       private CommandsCli commandsCli;
+       private CommandsCli parentCommandsCli;
+
+       // Help formatting
+       private static int helpWidth = 80;
+       private static int helpLeftPad = 4;
+       private static int helpDescPad = 20;
+
+       public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) {
+               super();
+               this.parentCommandsCli = parentCommandsCli;
+               this.commandsCli = commandsCli;
+       }
+
+       @Override
+       public String apply(List<String> args) {
+               StringWriter out = new StringWriter();
+
+               if (args.size() == 0) {// overview
+                       printHelp(commandsCli, out);
+               } else {
+                       String cmd = args.get(0);
+                       Function<List<String>, ?> function = commandsCli.getCommand(cmd);
+                       if (function == null)
+                               return "Command " + cmd + " not found.";
+                       Options options;
+                       String examples;
+                       DescribedCommand<?> command = null;
+                       if (function instanceof DescribedCommand) {
+                               command = (DescribedCommand<?>) function;
+                               options = command.getOptions();
+                               examples = command.getExamples();
+                       } else {
+                               options = new Options();
+                               examples = null;
+                       }
+                       String description = getShortDescription(function);
+                       String commandCall = getCommandUsage(cmd, command);
+                       HelpFormatter formatter = new HelpFormatter();
+                       formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad,
+                                       helpDescPad, examples, false);
+               }
+               return out.toString();
+       }
+
+       private static String getShortDescription(Function<List<String>, ?> function) {
+               if (function instanceof DescribedCommand) {
+                       return ((DescribedCommand<?>) function).getDescription();
+               } else {
+                       return function.toString();
+               }
+       }
+
+       public String getCommandUsage(String cmd, DescribedCommand<?> command) {
+               String commandCall = getCommandCall(commandsCli) + " " + cmd;
+               assert command != null;
+               if (command != null && command.getUsage() != null) {
+                       commandCall = commandCall + " " + command.getUsage();
+               }
+               return commandCall;
+       }
+
+       @Override
+       public String getDescription() {
+               return "Shows this help or describes a command";
+       }
+
+       @Override
+       public String getUsage() {
+               return "[command]";
+       }
+
+       public CommandsCli getParentCommandsCli() {
+               return parentCommandsCli;
+       }
+
+       protected String getCommandCall(CommandsCli commandsCli) {
+               HelpCommand hc = commandsCli.getHelpCommand();
+               if (hc.getParentCommandsCli() != null) {
+                       return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName();
+               } else {
+                       return commandsCli.getCommandName();
+               }
+       }
+
+       public static void printHelp(DescribedCommand<?> command, StringWriter out) {
+               String usage = "java " + command.getClass().getName()
+                               + (command.getUsage() != null ? " " + command.getUsage() : "");
+               HelpFormatter formatter = new HelpFormatter();
+               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
+                               helpLeftPad, helpDescPad, command.getExamples(), false);
+
+       }
+
+       public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) {
+               DescribedCommand<?> command = (DescribedCommand<?>) commandsCli.getCommand(commandName);
+               String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command);
+               HelpFormatter formatter = new HelpFormatter();
+               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
+                               helpLeftPad, helpDescPad, command.getExamples(), false);
+
+       }
+
+       public static void printHelp(CommandsCli commandsCli, StringWriter out) {
+               out.append(commandsCli.getDescription()).append('\n');
+               String leftPad = spaces(helpLeftPad);
+               for (String cmd : commandsCli.getSubCommands()) {
+                       Function<List<String>, ?> function = commandsCli.getCommand(cmd);
+                       assert function != null;
+                       out.append(leftPad);
+                       out.append(cmd);
+                       // TODO deal with long commands
+                       out.append(spaces(helpDescPad - cmd.length()));
+                       out.append(getShortDescription(function));
+                       out.append('\n');
+               }
+       }
+
+       private static String spaces(int count) {
+               // Java 11
+               // return " ".repeat(count);
+               if (count <= 0)
+                       return "";
+               else {
+                       StringBuilder sb = new StringBuilder(count);
+                       for (int i = 0; i < count; i++)
+                               sb.append(' ');
+                       return sb.toString();
+               }
+       }
+}
diff --git a/org.argeo.api.cli/src/org/argeo/api/cli/package-info.java b/org.argeo.api.cli/src/org/argeo/api/cli/package-info.java
new file mode 100644 (file)
index 0000000..114fd02
--- /dev/null
@@ -0,0 +1,2 @@
+/** Command line API. */
+package org.argeo.api.cli;
\ No newline at end of file
diff --git a/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshCli.java
new file mode 100644 (file)
index 0000000..12b4d5e
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.cms.ssh.cli;
+
+import org.argeo.api.cli.CommandsCli;
+
+public class SshCli extends CommandsCli {
+       public SshCli(String commandName) {
+               super(commandName);
+               addCommand("shell", new SshShell());
+       }
+
+       @Override
+       public String getDescription() {
+               return "SSH utilities.";
+       }
+
+        
+}
diff --git a/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java b/org.argeo.cms.ssh/src/org/argeo/cms/ssh/cli/SshShell.java
new file mode 100644 (file)
index 0000000..78903a7
--- /dev/null
@@ -0,0 +1,115 @@
+package org.argeo.cms.ssh.cli;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.URI;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.agent.local.LocalAgentFactory;
+import org.apache.sshd.agent.unix.UnixAgentFactory;
+import org.apache.sshd.client.config.keys.ClientIdentityLoader;
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.argeo.api.cli.DescribedCommand;
+import org.argeo.cms.ssh.AbstractSsh;
+import org.argeo.cms.ssh.Ssh;
+
+public class SshShell implements DescribedCommand<String> {
+               private Option portOption;
+
+               @Override
+               public Options getOptions() {
+                       Options options = new Options();
+                       portOption = Option.builder().option("p").longOpt("port").hasArg().desc("port to connect to").build();
+                       options.addOption(portOption);
+                       return options;
+               }
+
+               @Override
+               public String apply(List<String> args) {
+                       CommandLine cl = toCommandLine(args);
+                       String portStr = cl.getOptionValue(portOption);
+                       if (portStr == null)
+                               portStr = "22";
+
+                       String host = cl.getArgList().get(0);
+
+                       String uriStr = "ssh://" + host + ":" + portStr + "/";
+                       // System.out.println(uriStr);
+                       URI uri = URI.create(uriStr);
+
+                       Ssh ssh = null;
+                       try {
+                               ssh = new Ssh(uri);
+                               boolean osAgent;
+                               SshAgent sshAgent;
+                               try {
+                                       String sshAuthSockentEnv = System.getenv(SshAgent.SSH_AUTHSOCKET_ENV_NAME);
+                                       if (sshAuthSockentEnv != null) {
+                                               ssh.getSshClient().getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, sshAuthSockentEnv);
+                                               SshAgentFactory agentFactory = new UnixAgentFactory();
+                                               ssh.getSshClient().setAgentFactory(agentFactory);
+                                               sshAgent = agentFactory.createClient(null, ssh.getSshClient());
+                                               osAgent = true;
+                                       } else {
+                                               osAgent = false;
+                                       }
+                               } catch (Exception e) {
+                                       e.printStackTrace();
+                                       osAgent = false;
+                               }
+
+                               if (!osAgent) {
+                                       SshAgentFactory agentFactory = new LocalAgentFactory();
+                                       ssh.getSshClient().setAgentFactory(agentFactory);
+                                       sshAgent = agentFactory.createClient(null, ssh.getSshClient());
+                                       String keyPath = System.getProperty("user.home") + "/.ssh/id_rsa";
+
+                                       char[] keyPassword = AbstractSsh.readPassword();
+                                       NamedResource namedResource = new NamedResource() {
+
+                                               @Override
+                                               public String getName() {
+                                                       return keyPath;
+                                               }
+                                       };
+                                       KeyPair keyPair = ClientIdentityLoader.DEFAULT
+                                                       .loadClientIdentities(null, namedResource, FilePasswordProvider.of(new String(keyPassword)))
+                                                       .iterator().next();
+                                       sshAgent.addIdentity(keyPair, "NO COMMENT");
+                               }
+
+//                             char[] keyPassword = AbstractSsh.readPassword();
+//                             SshKeyPair keyPair = SshKeyPair.loadDefault(keyPassword);
+//                             Arrays.fill(keyPassword, '*');
+//                             ssh.setSshKeyPair(keyPair);
+//                             ssh.authenticate();
+                               ssh.verifyAuth();
+
+                               long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+                               System.out.println("Ssh available in " + jvmUptime + " ms.");
+
+                               AbstractSsh.openShell(ssh);
+                       } catch (IOException | GeneralSecurityException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       } finally {
+                               if (ssh != null)
+                                       ssh.closeSession();
+                       }
+                       return null;
+               }
+
+               @Override
+               public String getDescription() {
+                       return "Launch a static CMS.";
+               }
+
+       }
\ No newline at end of file
index 230a831a9b0ca1faf1984b63ddd3dbde170f728a..397caea34e7628aea6dc21e7dc3530654a488b88 100644 (file)
@@ -9,8 +9,8 @@ import java.util.List;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
-import org.argeo.cms.cli.CommandArgsException;
-import org.argeo.cms.cli.DescribedCommand;
+import org.argeo.api.cli.CommandArgsException;
+import org.argeo.api.cli.DescribedCommand;
 
 public class FileSync implements DescribedCommand<SyncResult<Path>> {
        final static Option deleteOption = Option.builder().longOpt("delete").desc("delete from target").build();
index 2aa240633e0892d08842e0c6159011916ae7da93..088c1c35227abf9de5e857ff80e1f3089ec3fd41 100644 (file)
@@ -1,6 +1,6 @@
 package org.argeo.cms.acr.fs;
 
-import org.argeo.cms.cli.CommandsCli;
+import org.argeo.api.cli.CommandsCli;
 
 /** File utilities. */
 public class FsCommands extends CommandsCli {
index de3a3027012b973b0ffbfff3b57d3e187b30035b..82873ad019db954f57059f60b8048d8c30a1d9ae 100644 (file)
@@ -9,8 +9,7 @@ import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
 
 import org.argeo.api.cms.CmsLog;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
+import org.argeo.cms.internal.runtime.CmsContextImpl;
 import org.osgi.service.useradmin.Authorization;
 import org.osgi.service.useradmin.UserAdmin;
 
@@ -22,7 +21,7 @@ public class AnonymousLoginModule implements LoginModule {
        private Map<String, Object> sharedState = null;
 
        // private state
-       private BundleContext bc;
+//     private BundleContext bc;
 
        @SuppressWarnings("unchecked")
        @Override
@@ -30,12 +29,12 @@ public class AnonymousLoginModule implements LoginModule {
                        Map<String, ?> options) {
                this.subject = subject;
                this.sharedState = (Map<String, Object>) sharedState;
-               try {
-                       bc = FrameworkUtil.getBundle(AnonymousLoginModule.class).getBundleContext();
-                       assert bc != null;
-               } catch (Exception e) {
-                       throw new IllegalStateException("Cannot initialize login module", e);
-               }
+//             try {
+//                     bc = FrameworkUtil.getBundle(AnonymousLoginModule.class).getBundleContext();
+//                     assert bc != null;
+//             } catch (Exception e) {
+//                     throw new IllegalStateException("Cannot initialize login module", e);
+//             }
        }
 
        @Override
@@ -45,7 +44,7 @@ public class AnonymousLoginModule implements LoginModule {
 
        @Override
        public boolean commit() throws LoginException {
-               UserAdmin userAdmin = bc.getService(bc.getServiceReference(UserAdmin.class));
+               UserAdmin userAdmin = CmsContextImpl.getCmsContext().getUserAdmin();
                Authorization authorization = userAdmin.getAuthorization(null);
                RemoteAuthRequest request = (RemoteAuthRequest) sharedState.get(CmsAuthUtils.SHARED_STATE_HTTP_REQUEST);
                Locale locale = Locale.getDefault();
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandArgsException.java
deleted file mode 100644 (file)
index 1f6d56b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.cms.cli;
-
-public class CommandArgsException extends IllegalArgumentException {
-       private static final long serialVersionUID = -7271050747105253935L;
-       private String commandName;
-       private volatile CommandsCli commandsCli;
-
-       public CommandArgsException(Exception cause) {
-               super(cause.getMessage(), cause);
-       }
-
-       public CommandArgsException(String message) {
-               super(message);
-       }
-
-       public String getCommandName() {
-               return commandName;
-       }
-
-       public void setCommandName(String commandName) {
-               this.commandName = commandName;
-       }
-
-       public CommandsCli getCommandsCli() {
-               return commandsCli;
-       }
-
-       public void setCommandsCli(CommandsCli commandsCli) {
-               this.commandsCli = commandsCli;
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandRuntimeException.java
deleted file mode 100644 (file)
index ef27c1f..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.argeo.cms.cli;
-
-import java.util.List;
-
-/** {@link RuntimeException} referring during a command run. */
-public class CommandRuntimeException extends RuntimeException {
-       private static final long serialVersionUID = 5595999301269377128L;
-
-       private final DescribedCommand<?> command;
-       private final List<String> arguments;
-
-       public CommandRuntimeException(Throwable e, DescribedCommand<?> command, List<String> arguments) {
-               this(null, e, command, arguments);
-       }
-
-       public CommandRuntimeException(String message, DescribedCommand<?> command, List<String> arguments) {
-               this(message, null, command, arguments);
-       }
-
-       public CommandRuntimeException(String message, Throwable e, DescribedCommand<?> command, List<String> arguments) {
-               super(message == null ? "(" + command.getClass().getName() + " " + arguments.toString() + ")"
-                               : message + " (" + command.getClass().getName() + " " + arguments.toString() + ")", e);
-               this.command = command;
-               this.arguments = arguments;
-       }
-
-       public DescribedCommand<?> getCommand() {
-               return command;
-       }
-
-       public List<String> getArguments() {
-               return arguments;
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java b/org.argeo.cms/src/org/argeo/cms/cli/CommandsCli.java
deleted file mode 100644 (file)
index b9d262d..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-package org.argeo.cms.cli;
-
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.function.Function;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.MissingOptionException;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-
-/** Base class for a CLI managing sub commands. */
-public abstract class CommandsCli implements DescribedCommand<Object> {
-       public final static String HELP = "help";
-
-       private final String commandName;
-       private Map<String, Function<List<String>, ?>> commands = new TreeMap<>();
-
-       protected final Options options = new Options();
-
-       public CommandsCli(String commandName) {
-               this.commandName = commandName;
-       }
-
-       @Override
-       public Object apply(List<String> args) {
-               String cmd = null;
-               List<String> newArgs = new ArrayList<>();
-               try {
-                       CommandLineParser clParser = new DefaultParser();
-                       CommandLine commonCl = clParser.parse(getOptions(), args.toArray(new String[args.size()]), true);
-                       List<String> leftOvers = commonCl.getArgList();
-                       for (String arg : leftOvers) {
-                               if (!arg.startsWith("-") && cmd == null) {
-                                       cmd = arg;
-                               } else {
-                                       newArgs.add(arg);
-                               }
-                       }
-               } catch (ParseException e) {
-                       CommandArgsException cae = new CommandArgsException(e);
-                       throw cae;
-               }
-
-               Function<List<String>, ?> function = cmd != null ? getCommand(cmd) : getDefaultCommand();
-               if (function == null)
-                       throw new IllegalArgumentException("Uknown command " + cmd);
-               try {
-                       Object value = function.apply(newArgs);
-                       return value != null ? value.toString() : null;
-               } catch (CommandArgsException e) {
-                       if (e.getCommandName() == null) {
-                               e.setCommandName(cmd);
-                               e.setCommandsCli(this);
-                       }
-                       throw e;
-               } catch (IllegalArgumentException e) {
-                       CommandArgsException cae = new CommandArgsException(e);
-                       cae.setCommandName(cmd);
-                       throw cae;
-               }
-       }
-
-       @Override
-       public Options getOptions() {
-               return options;
-       }
-
-       protected void addCommand(String cmd, Function<List<String>, ?> function) {
-               commands.put(cmd, function);
-
-       }
-
-       @Override
-       public String getUsage() {
-               return "[command]";
-       }
-
-       protected void addCommandsCli(CommandsCli commandsCli) {
-               addCommand(commandsCli.getCommandName(), commandsCli);
-               commandsCli.addCommand(HELP, new HelpCommand(this, commandsCli));
-       }
-
-       public String getCommandName() {
-               return commandName;
-       }
-
-       public Set<String> getSubCommands() {
-               return commands.keySet();
-       }
-
-       public Function<List<String>, ?> getCommand(String command) {
-               return commands.get(command);
-       }
-
-       public HelpCommand getHelpCommand() {
-               return (HelpCommand) getCommand(HELP);
-       }
-
-       public Function<List<String>, String> getDefaultCommand() {
-               return getHelpCommand();
-       }
-
-       /** In order to implement quickly a main method. */
-       public static void mainImpl(CommandsCli cli, String[] args) {
-               try {
-                       cli.addCommand(CommandsCli.HELP, new HelpCommand(null, cli));
-                       Object output = cli.apply(Arrays.asList(args));
-                       System.out.println(output);
-                       System.exit(0);
-               } catch (CommandArgsException e) {
-                       System.err.println("Wrong arguments " + Arrays.toString(args) + ": " + e.getMessage());
-                       Throwable cause = e.getCause();
-                       if (!(cause instanceof MissingOptionException))
-                               e.printStackTrace();
-                       if (e.getCommandName() != null) {
-                               StringWriter out = new StringWriter();
-                               HelpCommand.printHelp(e.getCommandsCli(), e.getCommandName(), out);
-                               System.err.println(out.toString());
-                       } else {
-                               e.printStackTrace();
-                       }
-                       System.exit(1);
-               } catch (Exception e) {
-                       e.printStackTrace();
-                       System.exit(1);
-               }
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java b/org.argeo.cms/src/org/argeo/cms/cli/DescribedCommand.java
deleted file mode 100644 (file)
index cdfe130..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.argeo.cms.cli;
-
-import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-
-/** A command that can be described. */
-public interface DescribedCommand<T> extends Function<List<String>, T> {
-       default Options getOptions() {
-               return new Options();
-       }
-
-       String getDescription();
-
-       default String getUsage() {
-               return null;
-       }
-
-       default String getExamples() {
-               return null;
-       }
-
-       default CommandLine toCommandLine(List<String> args) {
-               try {
-                       DefaultParser parser = new DefaultParser();
-                       return parser.parse(getOptions(), args.toArray(new String[args.size()]));
-               } catch (ParseException e) {
-                       throw new CommandArgsException(e);
-               }
-       }
-
-       /** In order to implement quickly a main method. */
-       public static void mainImpl(DescribedCommand<?> command, String[] args) {
-               try {
-                       Object output = command.apply(Arrays.asList(args));
-                       System.out.println(output);
-                       System.exit(0);
-               } catch (IllegalArgumentException e) {
-                       StringWriter out = new StringWriter();
-                       HelpCommand.printHelp(command, out);
-                       System.err.println(out.toString());
-                       System.exit(1);
-               } catch (Exception e) {
-                       e.printStackTrace();
-                       System.exit(1);
-               }
-       }
-
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java b/org.argeo.cms/src/org/argeo/cms/cli/HelpCommand.java
deleted file mode 100644 (file)
index ab899f4..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.argeo.cms.cli;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.function.Function;
-
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Options;
-
-/** A special command that can describe {@link DescribedCommand}. */
-public class HelpCommand implements DescribedCommand<String> {
-       private CommandsCli commandsCli;
-       private CommandsCli parentCommandsCli;
-
-       // Help formatting
-       private static int helpWidth = 80;
-       private static int helpLeftPad = 4;
-       private static int helpDescPad = 20;
-
-       public HelpCommand(CommandsCli parentCommandsCli, CommandsCli commandsCli) {
-               super();
-               this.parentCommandsCli = parentCommandsCli;
-               this.commandsCli = commandsCli;
-       }
-
-       @Override
-       public String apply(List<String> args) {
-               StringWriter out = new StringWriter();
-
-               if (args.size() == 0) {// overview
-                       printHelp(commandsCli, out);
-               } else {
-                       String cmd = args.get(0);
-                       Function<List<String>, ?> function = commandsCli.getCommand(cmd);
-                       if (function == null)
-                               return "Command " + cmd + " not found.";
-                       Options options;
-                       String examples;
-                       DescribedCommand<?> command = null;
-                       if (function instanceof DescribedCommand) {
-                               command = (DescribedCommand<?>) function;
-                               options = command.getOptions();
-                               examples = command.getExamples();
-                       } else {
-                               options = new Options();
-                               examples = null;
-                       }
-                       String description = getShortDescription(function);
-                       String commandCall = getCommandUsage(cmd, command);
-                       HelpFormatter formatter = new HelpFormatter();
-                       formatter.printHelp(new PrintWriter(out), helpWidth, commandCall, description, options, helpLeftPad,
-                                       helpDescPad, examples, false);
-               }
-               return out.toString();
-       }
-
-       private static String getShortDescription(Function<List<String>, ?> function) {
-               if (function instanceof DescribedCommand) {
-                       return ((DescribedCommand<?>) function).getDescription();
-               } else {
-                       return function.toString();
-               }
-       }
-
-       public String getCommandUsage(String cmd, DescribedCommand<?> command) {
-               String commandCall = getCommandCall(commandsCli) + " " + cmd;
-               assert command != null;
-               if (command != null && command.getUsage() != null) {
-                       commandCall = commandCall + " " + command.getUsage();
-               }
-               return commandCall;
-       }
-
-       @Override
-       public String getDescription() {
-               return "Shows this help or describes a command";
-       }
-
-       @Override
-       public String getUsage() {
-               return "[command]";
-       }
-
-       public CommandsCli getParentCommandsCli() {
-               return parentCommandsCli;
-       }
-
-       protected String getCommandCall(CommandsCli commandsCli) {
-               HelpCommand hc = commandsCli.getHelpCommand();
-               if (hc.getParentCommandsCli() != null) {
-                       return getCommandCall(hc.getParentCommandsCli()) + " " + commandsCli.getCommandName();
-               } else {
-                       return commandsCli.getCommandName();
-               }
-       }
-
-       public static void printHelp(DescribedCommand<?> command, StringWriter out) {
-               String usage = "java " + command.getClass().getName()
-                               + (command.getUsage() != null ? " " + command.getUsage() : "");
-               HelpFormatter formatter = new HelpFormatter();
-               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
-                               helpLeftPad, helpDescPad, command.getExamples(), false);
-
-       }
-
-       public static void printHelp(CommandsCli commandsCli, String commandName, StringWriter out) {
-               DescribedCommand<?> command = (DescribedCommand<?>) commandsCli.getCommand(commandName);
-               String usage = commandsCli.getHelpCommand().getCommandUsage(commandName, command);
-               HelpFormatter formatter = new HelpFormatter();
-               formatter.printHelp(new PrintWriter(out), helpWidth, usage, command.getDescription(), command.getOptions(),
-                               helpLeftPad, helpDescPad, command.getExamples(), false);
-
-       }
-
-       public static void printHelp(CommandsCli commandsCli, StringWriter out) {
-               out.append(commandsCli.getDescription()).append('\n');
-               String leftPad = spaces(helpLeftPad);
-               for (String cmd : commandsCli.getSubCommands()) {
-                       Function<List<String>, ?> function = commandsCli.getCommand(cmd);
-                       assert function != null;
-                       out.append(leftPad);
-                       out.append(cmd);
-                       // TODO deal with long commands
-                       out.append(spaces(helpDescPad - cmd.length()));
-                       out.append(getShortDescription(function));
-                       out.append('\n');
-               }
-       }
-
-       private static String spaces(int count) {
-               // Java 11
-               // return " ".repeat(count);
-               if (count <= 0)
-                       return "";
-               else {
-                       StringBuilder sb = new StringBuilder(count);
-                       for (int i = 0; i < count; i++)
-                               sb.append(' ');
-                       return sb.toString();
-               }
-       }
-}
diff --git a/org.argeo.cms/src/org/argeo/cms/cli/package-info.java b/org.argeo.cms/src/org/argeo/cms/cli/package-info.java
deleted file mode 100644 (file)
index 59feed1..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Command line API. */
-package org.argeo.cms.cli;
\ No newline at end of file
index f133d30497b2c869404c3d3cb0c630e44dee09ad..d1a19bd63a1af6d395d53e6312b8048270b91361 100644 (file)
@@ -19,6 +19,7 @@ import org.argeo.cms.internal.runtime.CmsStateImpl;
 import org.argeo.cms.internal.runtime.CmsUserAdmin;
 import org.argeo.cms.internal.runtime.DeployedContentRepository;
 import org.argeo.util.register.Component;
+import org.argeo.util.register.ComponentRegister;
 import org.argeo.util.register.SimpleRegister;
 import org.argeo.util.transaction.SimpleTransactionManager;
 import org.argeo.util.transaction.WorkControl;
@@ -26,7 +27,7 @@ import org.argeo.util.transaction.WorkTransaction;
 import org.osgi.service.useradmin.UserAdmin;
 
 /**
- * A CMS assembly which is programatically defined, as an alternative to OSGi
+ * A CMS assembly which is programmatically defined, as an alternative to OSGi
  * deployment. Useful for testing or AOT compilation.
  */
 public class StaticCms {
@@ -139,14 +140,18 @@ public class StaticCms {
                postActivation(register);
        }
 
-       protected void addComponents(SimpleRegister register) {
+       protected void addComponents(ComponentRegister register) {
 
        }
 
-       protected void postActivation(SimpleRegister register) {
+       protected void postActivation(ComponentRegister register) {
 
        }
 
+       public ComponentRegister getComponentRegister() {
+               return register;
+       }
+
        public void stop() {
                if (register.isActive()) {
                        register.deactivate();
index d5f9ea4211d67c774cddfa25a9d3b3a7639323fe..d78b6badbeb2dea14b6ea9103dc1760b71a0ea50 100644 (file)
@@ -10,6 +10,20 @@ public interface ComponentRegister {
 
        <T> SortedSet<Component<? extends T>> find(Class<T> clss, Predicate<Map<String, Object>> filter);
 
+       default <T> Component.PublishedType<T> getSingleton(Class<T> type) {
+               SortedSet<Component<? extends T>> found = find(type, null);
+               if (found.size() == 0)
+                       throw new IllegalStateException("No component found for " + type);
+               return found.first().getType(type);
+       }
+
+       default <T> T getObject(Class<T> clss) {
+               SortedSet<Component<? extends T>> found = find(clss, null);
+               if (found.size() == 0)
+                       return null;
+               return found.first().get();
+       }
+
        Component<?> get(Object instance);
 
 //     default <T> PublishedType<T> getType(Class<T> clss) {
diff --git a/rap/org.argeo.cms.e4.rap/.classpath b/rap/org.argeo.cms.e4.rap/.classpath
deleted file mode 100644 (file)
index e801ebf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/rap/org.argeo.cms.e4.rap/.project b/rap/org.argeo.cms.e4.rap/.project
deleted file mode 100644 (file)
index 40c9e01..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.e4.rap</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/rap/org.argeo.cms.e4.rap/META-INF/.gitignore b/rap/org.argeo.cms.e4.rap/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml b/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml
deleted file mode 100644 (file)
index 1f688ba..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="optional" name="CMS Admin RAP">
-   <implementation class="org.argeo.cms.e4.rap.CmsE4AdminApp"/>
-   <service>
-      <provide interface="org.eclipse.rap.rwt.application.ApplicationConfiguration"/>
-      <property name="contextName" type="String" value="cms"/>
-   </service>
-</scr:component>
diff --git a/rap/org.argeo.cms.e4.rap/bnd.bnd b/rap/org.argeo.cms.e4.rap/bnd.bnd
deleted file mode 100644 (file)
index 5bbe4bc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Bundle-ActivationPolicy: lazy
-Service-Component: OSGI-INF/cms-admin-rap.xml
-
-Import-Package: org.eclipse.swt,\
-org.eclipse.swt.graphics,\
-org.eclipse.e4.ui.workbench,\
-org.eclipse.rap.rwt.client,\
-org.eclipse.nebula.widgets.richtext;resolution:=optional,\
-*
diff --git a/rap/org.argeo.cms.e4.rap/build.properties b/rap/org.argeo.cms.e4.rap/build.properties
deleted file mode 100644 (file)
index c58ea21..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               OSGI-INF/
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java
deleted file mode 100644 (file)
index 5fe22ae..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-package org.argeo.cms.e4.rap;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.eclipse.rap.e4.E4ApplicationConfig;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.application.ExceptionHandler;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.osgi.framework.BundleContext;
-
-/** Base class for CMS RAP applications. */
-public abstract class AbstractRapE4App implements ApplicationConfiguration {
-       private String e4Xmi;
-       private String path;
-       private String lifeCycleUri = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle";
-
-       private Map<String, String> baseProperties = new HashMap<String, String>();
-
-       private BundleContext bundleContext;
-       public final static String CONTEXT_NAME_PROPERTY = "contextName";
-       private String contextName;
-
-       /**
-        * To be overridden in order to add multiple entry points, directly or using
-        * {@link #addE4EntryPoint(Application, String, String, Map)}.
-        */
-       protected void addEntryPoints(Application application) {
-       }
-
-       public void configure(Application application) {
-               application.setExceptionHandler(new ExceptionHandler() {
-
-                       @Override
-                       public void handleException(Throwable throwable) {
-                               CmsFeedback.show("Unexpected RWT exception", throwable);
-                       }
-               });
-
-               if (e4Xmi != null) {// backward compatibility
-                       addE4EntryPoint(application, path, e4Xmi, getBaseProperties());
-               } else {
-                       addEntryPoints(application);
-               }
-       }
-
-       protected Map<String, String> getBaseProperties() {
-               return baseProperties;
-       }
-
-//     protected void addEntryPoint(Application application, E4ApplicationConfig config, Map<String, String> properties) {
-//             CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config);
-//             application.addEntryPoint(path, entryPointFactory, properties);
-//             application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-//     }
-
-       protected void addE4EntryPoint(Application application, String path, String e4Xmi, Map<String, String> properties) {
-               E4ApplicationConfig config = createE4ApplicationConfig(e4Xmi);
-               CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config);
-               application.addEntryPoint(path, entryPointFactory, properties);
-               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-       }
-
-       /**
-        * To be overridden for further configuration.
-        * 
-        * @see E4ApplicationConfig
-        */
-       protected E4ApplicationConfig createE4ApplicationConfig(String e4Xmi) {
-               return new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true);
-       }
-
-       @Deprecated
-       public void setPageTitle(String pageTitle) {
-               if (pageTitle != null)
-                       baseProperties.put(WebClient.PAGE_TITLE, pageTitle);
-       }
-
-       /** Returns a new map used to customise and entry point. */
-       public Map<String, String> customise(String pageTitle) {
-               Map<String, String> custom = new HashMap<>(getBaseProperties());
-               if (pageTitle != null)
-                       custom.put(WebClient.PAGE_TITLE, pageTitle);
-               return custom;
-       }
-
-       @Deprecated
-       public void setE4Xmi(String e4Xmi) {
-               this.e4Xmi = e4Xmi;
-       }
-
-       @Deprecated
-       public void setPath(String path) {
-               this.path = path;
-       }
-
-       public void setLifeCycleUri(String lifeCycleUri) {
-               this.lifeCycleUri = lifeCycleUri;
-       }
-
-       protected BundleContext getBundleContext() {
-               return bundleContext;
-       }
-
-       protected void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       public String getContextName() {
-               return contextName;
-       }
-
-       public void setContextName(String contextName) {
-               this.contextName = contextName;
-       }
-
-       public void init(BundleContext bundleContext, Map<String, Object> properties) {
-               this.bundleContext = bundleContext;
-               for (String key : properties.keySet()) {
-                       Object value = properties.get(key);
-                       if (value != null)
-                               baseProperties.put(key, value.toString());
-               }
-
-               if (properties.containsKey(CONTEXT_NAME_PROPERTY)) {
-                       assert properties.get(CONTEXT_NAME_PROPERTY) != null;
-                       contextName = properties.get(CONTEXT_NAME_PROPERTY).toString();
-               } else {
-                       contextName = "<unknown context>";
-               }
-       }
-
-       public void destroy(Map<String, Object> properties) {
-
-       }
-}
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java
deleted file mode 100644 (file)
index 66be1e8..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.cms.e4.rap;
-
-import org.eclipse.rap.rwt.application.Application;
-
-/**
- * Access to canonical views of the core CMS concepts, useful for devleopers and
- * operators.
- */
-public class CmsE4AdminApp extends AbstractRapE4App {
-       @Override
-       protected void addEntryPoints(Application application) {
-               addE4EntryPoint(application, "/devops", "org.argeo.cms.e4/e4xmi/cms-devops.e4xmi",
-                               customise("Argeo CMS DevOps"));
-               addE4EntryPoint(application, "/ego", "org.argeo.cms.e4/e4xmi/cms-ego.e4xmi", customise("Argeo CMS Ego"));
-       }
-
-}
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java
deleted file mode 100644 (file)
index a5a3234..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.argeo.cms.e4.rap;
-
-import java.security.PrivilegedAction;
-
-import javax.security.auth.Subject;
-
-import org.eclipse.rap.e4.E4ApplicationConfig;
-import org.eclipse.rap.e4.E4EntryPointFactory;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-
-public class CmsE4EntryPointFactory extends E4EntryPointFactory {
-       public final static String DEFAULT_LIFECYCLE_URI = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle";
-
-       public CmsE4EntryPointFactory(E4ApplicationConfig config) {
-               super(config);
-       }
-
-       public CmsE4EntryPointFactory(String e4Xmi, String lifeCycleUri) {
-               super(defaultConfig(e4Xmi, lifeCycleUri));
-       }
-
-       public CmsE4EntryPointFactory(String e4Xmi) {
-               this(e4Xmi, DEFAULT_LIFECYCLE_URI);
-       }
-
-       public static E4ApplicationConfig defaultConfig(String e4Xmi, String lifeCycleUri) {
-               E4ApplicationConfig config = new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true);
-               return config;
-       }
-
-       @Override
-       public EntryPoint create() {
-               EntryPoint ep = createEntryPoint();
-               EntryPoint authEp = new EntryPoint() {
-
-                       @Override
-                       public int createUI() {
-                               Subject subject = new Subject();
-                               return Subject.doAs(subject, new PrivilegedAction<Integer>() {
-
-                                       @Override
-                                       public Integer run() {
-                                               // SPNEGO
-                                               // HttpServletRequest request = RWT.getRequest();
-                                               // String authorization = request.getHeader(HEADER_AUTHORIZATION);
-                                               // if (authorization == null || !authorization.startsWith("Negotiate")) {
-                                               // HttpServletResponse response = RWT.getResponse();
-                                               // response.setStatus(401);
-                                               // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
-                                               // response.setDateHeader("Date", System.currentTimeMillis());
-                                               // response.setDateHeader("Expires", System.currentTimeMillis() + (24 * 60 * 60
-                                               // * 1000));
-                                               // response.setHeader("Accept-Ranges", "bytes");
-                                               // response.setHeader("Connection", "Keep-Alive");
-                                               // response.setHeader("Keep-Alive", "timeout=5, max=97");
-                                               // // response.setContentType("text/html; charset=UTF-8");
-                                               // }
-
-                                               JavaScriptExecutor jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
-                                               Integer exitCode = ep.createUI();
-                                               jsExecutor.execute("location.reload()");
-                                               return exitCode;
-                                       }
-
-                               });
-                       }
-               };
-               return authEp;
-       }
-
-       protected EntryPoint createEntryPoint() {
-               return super.create();
-       }
-}
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java
deleted file mode 100644 (file)
index 471cdec..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.argeo.cms.e4.rap;
-
-import java.security.AccessController;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SimpleSwtUxContext;
-import org.argeo.cms.swt.auth.CmsLoginShell;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.ui.util.SimpleImageManager;
-import org.eclipse.e4.core.services.events.IEventBroker;
-import org.eclipse.e4.ui.workbench.UIEvents;
-import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
-import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.client.service.BrowserNavigation;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventHandler;
-
-@SuppressWarnings("restriction")
-public class CmsLoginLifecycle implements CmsView {
-       private final static CmsLog log = CmsLog.getLog(CmsLoginLifecycle.class);
-
-       private UxContext uxContext;
-       private CmsImageManager imageManager;
-
-       private LoginContext loginContext;
-       private BrowserNavigation browserNavigation;
-
-       private String state = null;
-       private String uid;
-
-       @PostContextCreate
-       boolean login(final IEventBroker eventBroker) {
-               uid = UUID.randomUUID().toString();
-               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
-               if (browserNavigation != null)
-                       browserNavigation.addBrowserNavigationListener(new BrowserNavigationListener() {
-                               private static final long serialVersionUID = -3668136623771902865L;
-
-                               @Override
-                               public void navigated(BrowserNavigationEvent event) {
-                                       state = event.getState();
-                                       if (uxContext != null)// is logged in
-                                               stateChanged();
-                               }
-                       });
-
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               Display display = Display.getCurrent();
-//             UiContext.setData(CmsView.KEY, this);
-               // FIXME get CMS context
-               CmsLoginShell loginShell = new CmsLoginShell(this, null);
-               CmsSwtUtils.registerCmsView(loginShell.getShell(), this);
-               loginShell.setSubject(subject);
-               try {
-                       // try pre-auth
-                       loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, loginShell);
-                       loginContext.login();
-               } catch (LoginException e) {
-                       loginShell.createUi();
-                       loginShell.open();
-
-                       while (!loginShell.getShell().isDisposed()) {
-                               if (!display.readAndDispatch())
-                                       display.sleep();
-                       }
-               }
-               if (CurrentUser.getUsername(getSubject()) == null)
-                       return false;
-               uxContext = new SimpleSwtUxContext();
-               imageManager = new SimpleImageManager();
-
-               eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new EventHandler() {
-                       @Override
-                       public void handleEvent(Event event) {
-                               startupComplete();
-                               eventBroker.unsubscribe(this);
-                       }
-               });
-
-               // lcs.changeApplicationLocale(Locale.FRENCH);
-               return true;
-       }
-
-       @PreSave
-       void destroy() {
-               // logout();
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-
-       @Override
-       public void navigateTo(String state) {
-               browserNavigation.pushState(state, state);
-       }
-
-       @Override
-       public void authChange(LoginContext loginContext) {
-               if (loginContext == null)
-                       throw new IllegalArgumentException("Login context cannot be null");
-               // logout previous login context
-               // if (this.loginContext != null)
-               // try {
-               // this.loginContext.logout();
-               // } catch (LoginException e1) {
-               // System.err.println("Could not log out: " + e1);
-               // }
-               this.loginContext = loginContext;
-       }
-
-       @Override
-       public void logout() {
-               if (loginContext == null)
-                       throw new IllegalStateException("Login context should not be null");
-               try {
-                       CurrentUser.logoutCmsSession(loginContext.getSubject());
-                       loginContext.logout();
-               } catch (LoginException e) {
-                       throw new IllegalStateException("Cannot log out", e);
-               }
-       }
-
-       @Override
-       public void exception(Throwable e) {
-               String msg = "Unexpected exception in Eclipse 4 RAP";
-               log.error(msg, e);
-               CmsFeedback.show(msg, e);
-       }
-
-       @Override
-       public CmsImageManager getImageManager() {
-               return imageManager;
-       }
-
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return CurrentUser.isAnonymous(getSubject());
-       }
-
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-       // CALLBACKS
-       protected void startupComplete() {
-       }
-
-       protected void stateChanged() {
-
-       }
-
-       // GETTERS
-       protected BrowserNavigation getBrowserNavigation() {
-               return browserNavigation;
-       }
-
-       protected String getState() {
-               return state;
-       }
-
-}
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java
deleted file mode 100644 (file)
index 1bca333..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.cms.e4.rap;
-
-import java.util.Enumeration;
-
-import org.apache.commons.io.FilenameUtils;
-import org.argeo.api.cms.CmsLog;
-import org.eclipse.rap.rwt.application.Application;
-import org.osgi.framework.Bundle;
-
-/** Simple RAP app which loads all e4xmi files. */
-public class SimpleRapE4App extends AbstractRapE4App {
-       private final static CmsLog log = CmsLog.getLog(SimpleRapE4App.class);
-
-       private String baseE4xmi = "/e4xmi";
-
-       @Override
-       protected void addEntryPoints(Application application) {
-               Bundle bundle = getBundleContext().getBundle();
-               Enumeration<String> paths = bundle.getEntryPaths(baseE4xmi);
-               while (paths.hasMoreElements()) {
-                       String p = paths.nextElement();
-                       if (p.endsWith(".e4xmi")) {
-                               String e4xmiPath = bundle.getSymbolicName() + '/' + p;
-                               String name = '/' + FilenameUtils.removeExtension(FilenameUtils.getName(p));
-                               addE4EntryPoint(application, name, e4xmiPath, getBaseProperties());
-                               if (log.isDebugEnabled())
-                                       log.debug("Registered " + e4xmiPath + " as " + getContextName() + name);
-                       }
-               }
-       }
-
-}
diff --git a/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java b/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java
deleted file mode 100644 (file)
index 1122f19..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Eclipse 4 RAP specific extensions. */
-package org.argeo.cms.e4.rap;
\ No newline at end of file
diff --git a/rap/org.argeo.cms.ui.rap/.classpath b/rap/org.argeo.cms.ui.rap/.classpath
deleted file mode 100644 (file)
index e801ebf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/rap/org.argeo.cms.ui.rap/.project b/rap/org.argeo.cms.ui.rap/.project
deleted file mode 100644 (file)
index 6301604..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.ui.rap</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/rap/org.argeo.cms.ui.rap/META-INF/.gitignore b/rap/org.argeo.cms.ui.rap/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml b/rap/org.argeo.cms.ui.rap/OSGI-INF/cmsWebAppFactory.xml
deleted file mode 100644 (file)
index aa7e0ad..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS Web App Factory">
-   <implementation class="org.argeo.cms.web.osgi.CmsWebAppFactory"/>
-   <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
-   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
-</scr:component>
diff --git a/rap/org.argeo.cms.ui.rap/bnd.bnd b/rap/org.argeo.cms.ui.rap/bnd.bnd
deleted file mode 100644 (file)
index 1d7c4b0..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-Import-Package:\
-org.argeo.api.acr,\
-org.eclipse.swt,\
-org.argeo.eclipse.ui,\
-javax.jcr.nodetype,\
-javax.jcr.security,\
-org.eclipse.swt.graphics,\
-org.eclipse.jetty.util.component;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.http;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.io;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.security;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.server.handler;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\
-javax.servlet.*;version="[3,5)",\
-*
-
-Service-Component: OSGI-INF/cmsWebAppFactory.xml
-
diff --git a/rap/org.argeo.cms.ui.rap/build.properties b/rap/org.argeo.cms.ui.rap/build.properties
deleted file mode 100644 (file)
index 2416e52..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               OSGI-INF/cmsWebAppFactory.xml
-source.. = src/
-additional.bundles = org.argeo.ext.slf4j,\
-                     org.slf4j.api
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/AppUi.java
deleted file mode 100644 (file)
index 01ebb23..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.script.Invocable;
-import javax.script.ScriptException;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.Selected;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.CmsPane;
-import org.argeo.cms.web.SimpleErgonomics;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.rap.rwt.application.EntryPointFactory;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-import org.osgi.framework.BundleContext;
-
-public class AppUi implements CmsUiProvider, Branding {
-       private final CmsScriptApp app;
-
-       private CmsUiProvider ui;
-       private String createUi;
-       private Object impl;
-       private String script;
-       // private Branding branding = new Branding();
-
-       private EntryPointFactory factory;
-
-       // Branding
-       private String themeId;
-       private String additionalHeaders;
-       private String bodyHtml;
-       private String pageTitle;
-       private String pageOverflow;
-       private String favicon;
-
-       public AppUi(CmsScriptApp app) {
-               this.app = app;
-       }
-
-       public AppUi(CmsScriptApp app, String scriptPath) {
-               this.app = app;
-               this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC),
-                               app.getScriptEngine(), scriptPath);
-       }
-
-       public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) {
-               this.app = app;
-               this.ui = uiProvider;
-       }
-
-       public AppUi(CmsScriptApp app, EntryPointFactory factory) {
-               this.app = app;
-               this.factory = factory;
-       }
-
-       public void apply(Repository repository, Application application, Branding appBranding, String path) {
-               Map<String, String> factoryProperties = new HashMap<>();
-               if (appBranding != null)
-                       appBranding.applyBranding(factoryProperties);
-               applyBranding(factoryProperties);
-               if (factory != null) {
-                       application.addEntryPoint("/" + path, factory, factoryProperties);
-               } else {
-                       EntryPointFactory entryPointFactory = new EntryPointFactory() {
-                               @Override
-                               public EntryPoint create() {
-                                       SimpleErgonomics ergonomics = new SimpleErgonomics(repository, CmsConstants.SYS_WORKSPACE,
-                                                       "/home/root/argeo:keyring", AppUi.this, factoryProperties);
-//                                     CmsUiProvider header = app.getHeader();
-//                                     if (header != null)
-//                                             ergonomics.setHeader(header);
-                                       app.applySides(ergonomics);
-                                       Integer headerHeight = app.getHeaderHeight();
-                                       if (headerHeight != null)
-                                               ergonomics.setHeaderHeight(headerHeight);
-                                       return ergonomics;
-                               }
-                       };
-                       application.addEntryPoint("/" + path, entryPointFactory, factoryProperties);
-               }
-       }
-
-       public void setUi(CmsUiProvider uiProvider) {
-               this.ui = uiProvider;
-       }
-
-       public void applyBranding(Map<String, String> properties) {
-               if (themeId != null)
-                       properties.put(WebClient.THEME_ID, themeId);
-               if (additionalHeaders != null)
-                       properties.put(WebClient.HEAD_HTML, additionalHeaders);
-               if (bodyHtml != null)
-                       properties.put(WebClient.BODY_HTML, bodyHtml);
-               if (pageTitle != null)
-                       properties.put(WebClient.PAGE_TITLE, pageTitle);
-               if (pageOverflow != null)
-                       properties.put(WebClient.PAGE_OVERFLOW, pageOverflow);
-               if (favicon != null)
-                       properties.put(WebClient.FAVICON, favicon);
-       }
-
-       // public Branding getBranding() {
-       // return branding;
-       // }
-
-       @Override
-       public Control createUi(Composite parent, Node context) throws RepositoryException {
-               CmsPane cmsPane = new CmsPane(parent, SWT.NONE);
-
-               if (false) {
-                       // QA
-                       CmsSwtUtils.style(cmsPane.getQaArea(), "qa");
-                       Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT);
-                       CmsSwtUtils.style(reload, "qa");
-                       reload.setText("Reload");
-                       reload.addSelectionListener(new Selected() {
-                               private static final long serialVersionUID = 1L;
-
-                               @Override
-                               public void widgetSelected(SelectionEvent e) {
-                                       new Thread() {
-                                               @Override
-                                               public void run() {
-                                                       app.reload();
-                                               }
-                                       }.start();
-                                       RWT.getClient().getService(JavaScriptExecutor.class)
-                                                       .execute("setTimeout('location.reload()',1000)");
-                               }
-                       });
-
-                       // Support
-                       CmsSwtUtils.style(cmsPane.getSupportArea(), "support");
-                       Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE);
-                       CmsSwtUtils.style(msg, "support");
-                       msg.setText("UNSUPPORTED DEVELOPMENT VERSION");
-               }
-
-               if (ui != null) {
-                       ui.createUi(cmsPane.getMainArea(), context);
-               }
-               if (createUi != null) {
-                       Invocable invocable = (Invocable) app.getScriptEngine();
-                       try {
-                               invocable.invokeFunction(createUi, cmsPane.getMainArea(), context);
-
-                       } catch (NoSuchMethodException e) {
-                               // TODO Auto-generated catch block
-                               e.printStackTrace();
-                       } catch (ScriptException e) {
-                               // TODO Auto-generated catch block
-                               e.printStackTrace();
-                       }
-               }
-               if (impl != null) {
-                       Invocable invocable = (Invocable) app.getScriptEngine();
-                       try {
-                               invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context);
-
-                       } catch (NoSuchMethodException e) {
-                               // TODO Auto-generated catch block
-                               e.printStackTrace();
-                       } catch (ScriptException e) {
-                               // TODO Auto-generated catch block
-                               e.printStackTrace();
-                       }
-               }
-
-               // Invocable invocable = (Invocable) app.getScriptEngine();
-               // try {
-               // invocable.invokeMethod(AppUi.this, "initUi", parent, context);
-               //
-               // } catch (NoSuchMethodException e) {
-               // // TODO Auto-generated catch block
-               // e.printStackTrace();
-               // } catch (ScriptException e) {
-               // // TODO Auto-generated catch block
-               // e.printStackTrace();
-               // }
-
-               return null;
-       }
-
-       public void setCreateUi(String createUi) {
-               this.createUi = createUi;
-       }
-
-       public void setImpl(Object impl) {
-               this.impl = impl;
-       }
-
-       public Object getImpl() {
-               return impl;
-       }
-
-       public String getScript() {
-               return script;
-       }
-
-       public void setScript(String script) {
-               this.script = script;
-       }
-
-       // Branding
-       public String getThemeId() {
-               return themeId;
-       }
-
-       public void setThemeId(String themeId) {
-               this.themeId = themeId;
-       }
-
-       public String getAdditionalHeaders() {
-               return additionalHeaders;
-       }
-
-       public void setAdditionalHeaders(String additionalHeaders) {
-               this.additionalHeaders = additionalHeaders;
-       }
-
-       public String getBodyHtml() {
-               return bodyHtml;
-       }
-
-       public void setBodyHtml(String bodyHtml) {
-               this.bodyHtml = bodyHtml;
-       }
-
-       public String getPageTitle() {
-               return pageTitle;
-       }
-
-       public void setPageTitle(String pageTitle) {
-               this.pageTitle = pageTitle;
-       }
-
-       public String getPageOverflow() {
-               return pageOverflow;
-       }
-
-       public void setPageOverflow(String pageOverflow) {
-               this.pageOverflow = pageOverflow;
-       }
-
-       public String getFavicon() {
-               return favicon;
-       }
-
-       public void setFavicon(String favicon) {
-               this.favicon = favicon;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/Branding.java
deleted file mode 100644 (file)
index f72338e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import java.util.Map;
-
-public interface Branding {
-       public void applyBranding(Map<String, String> properties);
-
-       public String getThemeId();
-
-       public String getAdditionalHeaders();
-
-       public String getBodyHtml();
-
-       public String getPageTitle();
-
-       public String getPageOverflow();
-
-       public String getFavicon();
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java
deleted file mode 100644 (file)
index 6b3a670..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.script.ScriptEngine;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.ui.CmsUiConstants;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.web.BundleResourceLoader;
-import org.argeo.cms.web.SimpleErgonomics;
-import org.argeo.cms.web.WebThemeUtils;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.application.ExceptionHandler;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
-import org.osgi.service.http.NamespaceException;
-
-public class CmsScriptApp implements Branding {
-       public final static String CONTEXT_NAME = "contextName";
-
-       ServiceRegistration<ApplicationConfiguration> appConfigReg;
-
-       private ScriptEngine scriptEngine;
-
-       private final static CmsLog log = CmsLog.getLog(CmsScriptApp.class);
-
-       private String webPath;
-       private String repo = "(cn=node)";
-
-       // private Branding branding = new Branding();
-       private CmsTheme theme;
-
-       private List<String> resources = new ArrayList<>();
-
-       private Map<String, AppUi> ui = new HashMap<>();
-
-       private CmsUiProvider header;
-       private Integer headerHeight = null;
-       private CmsUiProvider lead;
-       private CmsUiProvider end;
-       private CmsUiProvider footer;
-
-       // Branding
-       private String themeId;
-       private String additionalHeaders;
-       private String bodyHtml;
-       private String pageTitle;
-       private String pageOverflow;
-       private String favicon;
-
-       public CmsScriptApp(ScriptEngine scriptEngine) {
-               super();
-               this.scriptEngine = scriptEngine;
-       }
-
-       public void apply(BundleContext bundleContext, Repository repository, Application application) {
-               BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
-
-               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-               // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
-
-               application.setExceptionHandler(new CmsExceptionHandler());
-
-               // loading animated gif
-               application.addResource(CmsUiConstants.LOADING_IMAGE, createResourceLoader(CmsUiConstants.LOADING_IMAGE));
-               // empty image
-               application.addResource(CmsUiConstants.NO_IMAGE, createResourceLoader(CmsUiConstants.NO_IMAGE));
-
-               for (String resource : resources) {
-                       application.addResource(resource, bundleRL);
-                       if (log.isTraceEnabled())
-                               log.trace("Resource " + resource);
-               }
-
-               if (theme != null) {
-                       WebThemeUtils.apply(application, theme);
-                       String themeHeaders = theme.getHtmlHeaders();
-                       if (themeHeaders != null) {
-                               if (additionalHeaders == null)
-                                       additionalHeaders = themeHeaders;
-                               else
-                                       additionalHeaders = themeHeaders + "\n" + additionalHeaders;
-                       }
-                       themeId = theme.getThemeId();
-               }
-
-               // client JavaScript
-               Bundle appBundle = bundleRL.getBundle();
-               BundleContext bc = appBundle.getBundleContext();
-               HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class));
-               HttpContext httpContext = new BundleHttpContext(bc);
-               Enumeration<URL> themeResources = appBundle.findEntries("/js/", "*", true);
-               if (themeResources != null)
-                       bundleResources: while (themeResources.hasMoreElements()) {
-                               try {
-                                       String name = themeResources.nextElement().getPath();
-                                       if (name.endsWith("/"))
-                                               continue bundleResources;
-                                       String alias = "/" + getWebPath() + name;
-
-                                       httpService.registerResources(alias, name, httpContext);
-                                       if (log.isDebugEnabled())
-                                               log.debug("Mapped " + name + " to alias " + alias);
-
-                               } catch (NamespaceException e) {
-                                       // TODO Auto-generated catch block
-                                       e.printStackTrace();
-                               }
-                       }
-
-               // App UIs
-               for (String appUiName : ui.keySet()) {
-                       AppUi appUi = ui.get(appUiName);
-                       appUi.apply(repository, application, this, appUiName);
-
-               }
-
-       }
-
-       public void applySides(SimpleErgonomics simpleErgonomics) {
-               simpleErgonomics.setHeader(header);
-               simpleErgonomics.setLead(lead);
-               simpleErgonomics.setEnd(end);
-               simpleErgonomics.setFooter(footer);
-       }
-
-       public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) {
-               Hashtable<String, String> props = new Hashtable<>();
-               props.put(CONTEXT_NAME, webPath);
-               appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props);
-       }
-
-       public void reload() {
-               BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext();
-               ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference());
-               appConfigReg.unregister();
-               register(bundleContext, appConfig);
-
-               // BundleContext bundleContext = (BundleContext)
-               // getScriptEngine().get("bundleContext");
-               // try {
-               // Bundle bundle = bundleContext.getBundle();
-               // bundle.stop();
-               // bundle.start();
-               // } catch (BundleException e) {
-               // // TODO Auto-generated catch block
-               // e.printStackTrace();
-               // }
-       }
-
-       private static ResourceLoader createResourceLoader(final String resourceName) {
-               return new ResourceLoader() {
-                       public InputStream getResourceAsStream(String resourceName) throws IOException {
-                               return getClass().getClassLoader().getResourceAsStream(resourceName);
-                       }
-               };
-       }
-
-       public List<String> getResources() {
-               return resources;
-       }
-
-       public AppUi newUi(String name) {
-               if (ui.containsKey(name))
-                       throw new IllegalArgumentException("There is already an UI named " + name);
-               AppUi appUi = new AppUi(this);
-               // appUi.setApp(this);
-               ui.put(name, appUi);
-               return appUi;
-       }
-
-       public void addUi(String name, AppUi appUi) {
-               if (ui.containsKey(name))
-                       throw new IllegalArgumentException("There is already an UI named " + name);
-               // appUi.setApp(this);
-               ui.put(name, appUi);
-       }
-
-       public void applyBranding(Map<String, String> properties) {
-               if (themeId != null)
-                       properties.put(WebClient.THEME_ID, themeId);
-               if (additionalHeaders != null)
-                       properties.put(WebClient.HEAD_HTML, additionalHeaders);
-               if (bodyHtml != null)
-                       properties.put(WebClient.BODY_HTML, bodyHtml);
-               if (pageTitle != null)
-                       properties.put(WebClient.PAGE_TITLE, pageTitle);
-               if (pageOverflow != null)
-                       properties.put(WebClient.PAGE_OVERFLOW, pageOverflow);
-               if (favicon != null)
-                       properties.put(WebClient.FAVICON, favicon);
-       }
-
-       class CmsExceptionHandler implements ExceptionHandler {
-
-               @Override
-               public void handleException(Throwable throwable) {
-                       // TODO be smarter
-                       CmsUiUtils.getCmsView().exception(throwable);
-               }
-
-       }
-
-       // public Branding getBranding() {
-       // return branding;
-       // }
-
-       ScriptEngine getScriptEngine() {
-               return scriptEngine;
-       }
-
-       public static String toJson(Node node) {
-               try {
-                       StringBuilder sb = new StringBuilder();
-                       sb.append('{');
-                       PropertyIterator pit = node.getProperties();
-                       int count = 0;
-                       while (pit.hasNext()) {
-                               Property p = pit.nextProperty();
-                               int type = p.getType();
-                               if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) {
-                                       Node ref = p.getNode();
-                                       if (count != 0)
-                                               sb.append(',');
-                                       // TODO limit depth?
-                                       sb.append(toJson(ref));
-                                       count++;
-                               } else if (!p.isMultiple()) {
-                                       if (count != 0)
-                                               sb.append(',');
-                                       sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"');
-                                       count++;
-                               }
-                       }
-                       sb.append('}');
-                       return sb.toString();
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot convert " + node + " to JSON", e);
-               }
-       }
-
-       public void fromJson(Node node, String json) {
-               // TODO
-       }
-
-       public CmsTheme getTheme() {
-               return theme;
-       }
-
-       public void setTheme(CmsTheme theme) {
-               this.theme = theme;
-       }
-
-       public String getWebPath() {
-               return webPath;
-       }
-
-       public void setWebPath(String context) {
-               this.webPath = context;
-       }
-
-       public String getRepo() {
-               return repo;
-       }
-
-       public void setRepo(String repo) {
-               this.repo = repo;
-       }
-
-       public Map<String, AppUi> getUi() {
-               return ui;
-       }
-
-       public void setUi(Map<String, AppUi> ui) {
-               this.ui = ui;
-       }
-
-       // Branding
-       public String getThemeId() {
-               return themeId;
-       }
-
-       public void setThemeId(String themeId) {
-               this.themeId = themeId;
-       }
-
-       public String getAdditionalHeaders() {
-               return additionalHeaders;
-       }
-
-       public void setAdditionalHeaders(String additionalHeaders) {
-               this.additionalHeaders = additionalHeaders;
-       }
-
-       public String getBodyHtml() {
-               return bodyHtml;
-       }
-
-       public void setBodyHtml(String bodyHtml) {
-               this.bodyHtml = bodyHtml;
-       }
-
-       public String getPageTitle() {
-               return pageTitle;
-       }
-
-       public void setPageTitle(String pageTitle) {
-               this.pageTitle = pageTitle;
-       }
-
-       public String getPageOverflow() {
-               return pageOverflow;
-       }
-
-       public void setPageOverflow(String pageOverflow) {
-               this.pageOverflow = pageOverflow;
-       }
-
-       public String getFavicon() {
-               return favicon;
-       }
-
-       public void setFavicon(String favicon) {
-               this.favicon = favicon;
-       }
-
-       public CmsUiProvider getHeader() {
-               return header;
-       }
-
-       public void setHeader(CmsUiProvider header) {
-               this.header = header;
-       }
-
-       public Integer getHeaderHeight() {
-               return headerHeight;
-       }
-
-       public void setHeaderHeight(Integer headerHeight) {
-               this.headerHeight = headerHeight;
-       }
-
-       public CmsUiProvider getLead() {
-               return lead;
-       }
-
-       public void setLead(CmsUiProvider lead) {
-               this.lead = lead;
-       }
-
-       public CmsUiProvider getEnd() {
-               return end;
-       }
-
-       public void setEnd(CmsUiProvider end) {
-               this.end = end;
-       }
-
-       public CmsUiProvider getFooter() {
-               return footer;
-       }
-
-       public void setFooter(CmsUiProvider footer) {
-               this.footer = footer;
-       }
-
-       static class BundleHttpContext implements HttpContext {
-               private BundleContext bundleContext;
-
-               public BundleHttpContext(BundleContext bundleContext) {
-                       super();
-                       this.bundleContext = bundleContext;
-               }
-
-               @Override
-               public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
-                       // TODO Auto-generated method stub
-                       return true;
-               }
-
-               @Override
-               public URL getResource(String name) {
-
-                       return bundleContext.getBundle().getEntry(name);
-               }
-
-               @Override
-               public String getMimeType(String name) {
-                       return null;
-               }
-
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java
deleted file mode 100644 (file)
index 9879fb0..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
-
-import javax.jcr.Repository;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.swt.CmsException;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.wiring.BundleWiring;
-
-public class CmsScriptRwtApplication implements ApplicationConfiguration {
-       public final static String APP = "APP";
-       public final static String BC = "BC";
-
-       private final CmsLog log = CmsLog.getLog(CmsScriptRwtApplication.class);
-
-       BundleContext bundleContext;
-       Repository repository;
-
-       ScriptEngine engine;
-
-       public void init(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-               ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
-               ClassLoader originalCcl = Thread.currentThread().getContextClassLoader();
-               try {
-//                     Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager
-//                     ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl);
-//                     engine = scriptEngineManager.getEngineByName("JavaScript");
-//                     if (engine == null) {// Nashorn
-//                             Thread.currentThread().setContextClassLoader(originalCcl);
-//                             scriptEngineManager = new ScriptEngineManager();
-//                             Thread.currentThread().setContextClassLoader(bundleCl);
-//                             engine = scriptEngineManager.getEngineByName("JavaScript");
-//                     }
-                       engine = loadScriptEngine(originalCcl, bundleCl);
-
-                       // Load script
-                       URL appUrl = bundleContext.getBundle().getEntry("cms/app.js");
-                       // System.out.println("Loading " + appUrl);
-                       // System.out.println("Loading " + appUrl.getHost());
-                       // System.out.println("Loading " + appUrl.getPath());
-
-                       CmsScriptApp app = new CmsScriptApp(engine);
-                       engine.put(APP, app);
-                       engine.put(BC, bundleContext);
-                       try (Reader reader = new InputStreamReader(appUrl.openStream())) {
-                               engine.eval(reader);
-                       } catch (IOException | ScriptException e) {
-                               throw new CmsException("Cannot execute " + appUrl, e);
-                       }
-
-                       if (log.isDebugEnabled())
-                               log.debug("CMS script app initialized from " + appUrl);
-
-               } catch (Exception e) {
-                       e.printStackTrace();
-               } finally {
-                       Thread.currentThread().setContextClassLoader(originalCcl);
-               }
-       }
-
-       public void destroy(BundleContext bundleContext) {
-               engine = null;
-       }
-
-       @Override
-       public void configure(Application application) {
-               load(application);
-       }
-
-       void load(Application application) {
-               CmsScriptApp app = getApp();
-               app.apply(bundleContext, repository, application);
-               if (log.isDebugEnabled())
-                       log.debug("CMS script app loaded to " + app.getWebPath());
-       }
-
-       CmsScriptApp getApp() {
-               if (engine == null)
-                       throw new IllegalStateException("CMS script app is not initialized");
-               return (CmsScriptApp) engine.get(APP);
-       }
-
-       void update() {
-
-               try {
-                       bundleContext.getBundle().update();
-               } catch (BundleException e) {
-                       e.printStackTrace();
-               }
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) {
-               Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager
-               ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl);
-               ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript");
-               if (engine == null) {// Nashorn
-                       Thread.currentThread().setContextClassLoader(originalCcl);
-                       scriptEngineManager = new ScriptEngineManager();
-                       Thread.currentThread().setContextClassLoader(bundleCl);
-                       engine = scriptEngineManager.getEngineByName("JavaScript");
-               }
-               return engine;
-       }
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java
deleted file mode 100644 (file)
index a550953..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import javax.jcr.Repository;
-
-import org.argeo.api.cms.CmsLog;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-
-public class ScriptAppActivator implements BundleActivator {
-       private final static CmsLog log = CmsLog.getLog(ScriptAppActivator.class);
-
-       @Override
-       public void start(BundleContext context) throws Exception {
-               try {
-                       CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication();
-                       appConfig.init(context);
-                       CmsScriptApp app = appConfig.getApp();
-                       ServiceTracker<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
-                                       FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
-
-                               @Override
-                               public Repository addingService(ServiceReference<Repository> reference) {
-                                       Repository repository = super.addingService(reference);
-                                       appConfig.setRepository(repository);
-                                       CmsScriptApp app = appConfig.getApp();
-                                       app.register(context, appConfig);
-                                       return repository;
-                               }
-
-                       };
-                       repoSt.open();
-               } catch (Exception e) {
-                       log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e);
-                       throw e;
-               }
-       }
-
-       @Override
-       public void stop(BundleContext context) throws Exception {
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/ScriptUi.java
deleted file mode 100644 (file)
index 0c870e1..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-package org.argeo.cms.ui.script;
-
-import java.net.URL;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.framework.BundleContext;
-
-class ScriptUi implements CmsUiProvider {
-       private final static CmsLog log = CmsLog.getLog(ScriptUi.class);
-
-       private boolean development = true;
-       private ScriptEngine scriptEngine;
-
-       private URL appUrl;
-       // private BundleContext bundleContext;
-       // private String path;
-
-       // private Bindings bindings;
-       // private String script;
-
-       public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) {
-               this.scriptEngine = scriptEngine;
-////           ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
-//             ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
-//             ClassLoader originalCcl = Thread.currentThread().getContextClassLoader();
-//             try {
-////                   Thread.currentThread().setContextClassLoader(bundleCl);
-////                   scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
-////                   scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext);
-//                     scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl);
-//
-//             } catch (Exception e) {
-//                     e.printStackTrace();
-//             } finally {
-//                     Thread.currentThread().setContextClassLoader(originalCcl);
-//             }
-               this.appUrl = bundleContext.getBundle().getEntry(path);
-               load();
-       }
-
-       private void load() {
-//             try (Reader reader = new InputStreamReader(appUrl.openStream())) {
-//                     scriptEngine.eval(reader);
-//             } catch (IOException | ScriptException e) {
-//                     log.warn("Cannot execute " + appUrl, e);
-//             }
-
-               try {
-                       scriptEngine.eval("load('" + appUrl + "')");
-               } catch (ScriptException e) {
-                       log.warn("Cannot execute " + appUrl, e);
-               }
-
-       }
-
-       // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws
-       // ScriptException {
-       // super();
-       // this.scriptEngine = scriptEngine;
-       // this.script = script;
-       // bindings = scriptEngine.createBindings();
-       // scriptEngine.eval(script, bindings);
-       // }
-
-       @Override
-       public Control createUi(Composite parent, Node context) throws RepositoryException {
-               long begin = System.currentTimeMillis();
-               // if (bindings == null) {
-               // bindings = scriptEngine.createBindings();
-               // try {
-               // scriptEngine.eval(script, bindings);
-               // } catch (ScriptException e) {
-               // log.warn("Cannot evaluate script", e);
-               // }
-               // }
-               // Bindings bindings = scriptEngine.createBindings();
-               // bindings.put("parent", parent);
-               // bindings.put("context", context);
-               // URL appUrl = bundleContext.getBundle().getEntry(path);
-               // try (Reader reader = new InputStreamReader(appUrl.openStream())) {
-               // scriptEngine.eval(reader,bindings);
-               // } catch (IOException | ScriptException e) {
-               // log.warn("Cannot execute " + appUrl, e);
-               // }
-
-               if (development)
-                       load();
-
-               Invocable invocable = (Invocable) scriptEngine;
-               try {
-                       invocable.invokeFunction("createUi", parent, context);
-               } catch (NoSuchMethodException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-               } catch (ScriptException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-               }
-
-               long duration = System.currentTimeMillis() - begin;
-               if (log.isTraceEnabled())
-                       log.trace(appUrl + " UI in " + duration + " ms");
-               return null;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/cms.js
deleted file mode 100644 (file)
index be9618d..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-// CMS
-var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage');
-
-var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp');
-var AppUi = Java.type('org.argeo.cms.ui.script.AppUi');
-var Theme = Java.type('org.argeo.cms.ui.script.Theme');
-var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi');
-var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils');
-var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader');
-var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink');
-var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink');
-var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink');
-
-// SWT
-var SWT = Java.type('org.eclipse.swt.SWT');
-var Composite = Java.type('org.eclipse.swt.widgets.Composite');
-var Label = Java.type('org.eclipse.swt.widgets.Label');
-var Button = Java.type('org.eclipse.swt.widgets.Button');
-var Text = Java.type('org.eclipse.swt.widgets.Text');
-var Browser = Java.type('org.eclipse.swt.browser.Browser');
-
-var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout');
-var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout');
-var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout');
-var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout');
-var GridData = Java.type('org.eclipse.swt.layout.GridData');
-
-function loadNode(node) {
-       var json = CmsScriptApp.toJson(node)
-       var fromJson = JSON.parse(json)
-       return fromJson
-}
-
-function newArea(parent, style, layout) {
-       var control = new Composite(parent, SWT.NONE)
-       control.setLayout(layout)
-       CmsUtils.style(control, style)
-       return control
-}
-
-function newLabel(parent, style, text) {
-       var control = new Label(parent, SWT.WRAP)
-       control.setText(text)
-       CmsUtils.style(control, style)
-       CmsUtils.markup(control)
-       return control
-}
-
-function newButton(parent, style, text) {
-       var control = new Button(parent, SWT.FLAT)
-       control.setText(text)
-       CmsUtils.style(control, style)
-       CmsUtils.markup(control)
-       return control
-}
-
-function newFormLabel(parent, style, text) {
-       return newLabel(parent, style, '<b>' + text + '</b>')
-}
-
-function newText(parent, style, msg) {
-       var control = new Text(parent, SWT.NONE)
-       control.setMessage(msg)
-       CmsUtils.style(control, style)
-       return control
-}
-
-function newScrolledPage(parent) {
-       var scrolled = new ScrolledPage(parent, SWT.NONE)
-       scrolled.setLayoutData(CmsUtils.fillAll())
-       scrolled.setLayout(CmsUtils.noSpaceGridLayout())
-       var page = new Composite(scrolled, SWT.NONE)
-       page.setLayout(CmsUtils.noSpaceGridLayout())
-       page.setBackgroundMode(SWT.INHERIT_NONE)
-       return page
-}
-
-function gridData(control) {
-       var gridData = new GridData()
-       control.setLayoutData(gridData)
-       return gridData
-}
-
-function gridData(control, hAlign, vAlign) {
-       var gridData = new GridData(hAlign, vAlign, false, false)
-       control.setLayoutData(gridData)
-       return gridData
-}
-
-// print(__FILE__, __LINE__, __DIR__)
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/ui/script/package-info.java
deleted file mode 100644 (file)
index 7440596..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Argeo CMS user interface scripting. */
-package org.argeo.cms.ui.script;
\ No newline at end of file
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java
deleted file mode 100644 (file)
index f3269f2..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-package org.argeo.cms.web;
-
-import static org.argeo.util.directory.ldap.SharedSecret.X_SHARED_SECRET;
-
-import java.io.IOException;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Node;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import javax.servlet.http.HttpServletRequest;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.RemoteAuthCallback;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.CmsStyles;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.util.directory.ldap.AuthPassword;
-import org.argeo.util.directory.ldap.SharedSecret;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.AbstractEntryPoint;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.rap.rwt.client.service.BrowserNavigation;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-/** Manages history and navigation */
-@Deprecated
-public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements CmsView {
-       private static final long serialVersionUID = 906558779562569784L;
-
-       private final CmsLog log = CmsLog.getLog(AbstractCmsEntryPoint.class);
-
-       // private final Subject subject;
-       private LoginContext loginContext;
-
-       private final Repository repository;
-       private final String workspace;
-       private final String defaultPath;
-       private final Map<String, String> factoryProperties;
-
-       // Current state
-       private Session session;
-       private Node node;
-       private String nodePath;// useful when changing auth
-       private String state;
-       private Throwable exception;
-
-       // Client services
-       private final JavaScriptExecutor jsExecutor;
-       private final BrowserNavigation browserNavigation;
-
-       public AbstractCmsEntryPoint(Repository repository, String workspace, String defaultPath,
-                       Map<String, String> factoryProperties) {
-               this.repository = repository;
-               this.workspace = workspace;
-               this.defaultPath = defaultPath;
-               this.factoryProperties = new HashMap<String, String>(factoryProperties);
-               // subject = new Subject();
-
-               // Initial login
-               LoginContext lc;
-               try {
-                       lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
-                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
-                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
-                       lc.login();
-               } catch (LoginException e) {
-                       try {
-                               lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS);
-                               lc.login();
-                       } catch (LoginException e1) {
-                               throw new CmsException("Cannot log in as anonymous", e1);
-                       }
-               }
-               authChange(lc);
-
-               jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
-               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
-               if (browserNavigation != null)
-                       browserNavigation.addBrowserNavigationListener(new CmsNavigationListener());
-       }
-
-       @Override
-       protected Shell createShell(Display display) {
-               Shell shell = super.createShell(display);
-               shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL);
-               display.disposeExec(new Runnable() {
-
-                       @Override
-                       public void run() {
-                               if (log.isTraceEnabled())
-                                       log.trace("Logging out " + session);
-                               JcrUtils.logoutQuietly(session);
-                       }
-               });
-               return shell;
-       }
-
-       @Override
-       protected final void createContents(final Composite parent) {
-               // UiContext.setData(CmsView.KEY, this);
-               CmsSwtUtils.registerCmsView(parent.getShell(), this);
-               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
-                       @Override
-                       public Void run() {
-                               try {
-                                       initUi(parent);
-                               } catch (Exception e) {
-                                       throw new CmsException("Cannot create entrypoint contents", e);
-                               }
-                               return null;
-                       }
-               });
-       }
-
-       /** Create UI */
-       protected abstract void initUi(Composite parent);
-
-       /** Recreate UI after navigation or auth change */
-       protected abstract void refresh();
-
-       /**
-        * The node to return when no node was found (for authenticated users and
-        * anonymous)
-        */
-//     private Node getDefaultNode(Session session) throws RepositoryException {
-//             if (!session.hasPermission(defaultPath, "read")) {
-//                     String userId = session.getUserID();
-//                     if (userId.equals(NodeConstants.ROLE_ANONYMOUS))
-//                             // TODO throw a special exception
-//                             throw new CmsException("Login required");
-//                     else
-//                             throw new CmsException("Unauthorized");
-//             }
-//             return session.getNode(defaultPath);
-//     }
-
-       protected String getBaseTitle() {
-               return factoryProperties.get(WebClient.PAGE_TITLE);
-       }
-
-       public void navigateTo(String state) {
-               exception = null;
-               String title = setState(state);
-               doRefresh();
-               if (browserNavigation != null)
-                       browserNavigation.pushState(state, title);
-       }
-
-       // @Override
-       // public synchronized Subject getSubject() {
-       // return subject;
-       // }
-
-       // @Override
-       // public LoginContext getLoginContext() {
-       // return loginContext;
-       // }
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return CurrentUser.isAnonymous(getSubject());
-       }
-
-       @Override
-       public synchronized void logout() {
-               if (loginContext == null)
-                       throw new CmsException("Login context should not be null");
-               try {
-                       CurrentUser.logoutCmsSession(loginContext.getSubject());
-                       loginContext.logout();
-                       LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS);
-                       anonymousLc.login();
-                       authChange(anonymousLc);
-               } catch (LoginException e) {
-                       log.error("Cannot logout", e);
-               }
-       }
-
-       @Override
-       public synchronized void authChange(LoginContext lc) {
-               if (lc == null)
-                       throw new CmsException("Login context cannot be null");
-               // logout previous login context
-               if (this.loginContext != null)
-                       try {
-                               this.loginContext.logout();
-                       } catch (LoginException e1) {
-                               log.warn("Could not log out: " + e1);
-                       }
-               this.loginContext = lc;
-               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
-
-                       @Override
-                       public Void run() {
-                               try {
-                                       JcrUtils.logoutQuietly(session);
-                                       session = repository.login(workspace);
-                                       if (nodePath != null)
-                                               try {
-                                                       node = session.getNode(nodePath);
-                                               } catch (PathNotFoundException e) {
-                                                       navigateTo("~");
-                                               }
-
-                                       // refresh UI
-                                       doRefresh();
-                               } catch (RepositoryException e) {
-                                       throw new CmsException("Cannot perform auth change", e);
-                               }
-                               return null;
-                       }
-
-               });
-       }
-
-       @Override
-       public void exception(final Throwable e) {
-               AbstractCmsEntryPoint.this.exception = e;
-               log.error("Unexpected exception in CMS", e);
-               doRefresh();
-       }
-
-       protected synchronized void doRefresh() {
-               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
-                       @Override
-                       public Void run() {
-                               refresh();
-                               return null;
-                       }
-               });
-       }
-
-       /** Sets the state of the entry point and retrieve the related JCR node. */
-       protected synchronized String setState(String newState) {
-               String previousState = this.state;
-
-               String newNodePath = null;
-               String prefix = null;
-               this.state = newState;
-               if (newState.equals("~"))
-                       this.state = "";
-
-               try {
-                       int firstSlash = state.indexOf('/');
-                       if (firstSlash == 0) {
-                               newNodePath = state;
-                               prefix = "";
-                       } else if (firstSlash > 0) {
-                               prefix = state.substring(0, firstSlash);
-                               newNodePath = state.substring(firstSlash);
-                       } else {
-                               newNodePath = defaultPath;
-                               prefix = state;
-
-                       }
-
-                       // auth
-                       int colonIndex = prefix.indexOf('$');
-                       if (colonIndex > 0) {
-                               SharedSecret token = new SharedSecret(new AuthPassword(X_SHARED_SECRET + '$' + prefix)) {
-
-                                       @Override
-                                       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
-                                               super.handle(callbacks);
-                                               // handle HTTP context
-                                               for (Callback callback : callbacks) {
-                                                       if (callback instanceof RemoteAuthCallback) {
-                                                               ((RemoteAuthCallback) callback)
-                                                                               .setRequest(new ServletHttpRequest(UiContext.getHttpRequest()));
-                                                               ((RemoteAuthCallback) callback)
-                                                                               .setResponse(new ServletHttpResponse(UiContext.getHttpResponse()));
-                                                       }
-                                               }
-                                       }
-                               };
-                               LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, token);
-                               lc.login();
-                               authChange(lc);// sets the node as well
-                               // } else {
-                               // // TODO check consistency
-                               // }
-                       } else {
-                               Node newNode = null;
-                               if (session.nodeExists(newNodePath))
-                                       newNode = session.getNode(newNodePath);
-                               else {
-//                                     throw new CmsException("Data " + newNodePath + " does not exist");
-                                       newNode = null;
-                               }
-                               setNode(newNode);
-                       }
-                       String title = publishMetaData(getNode());
-
-                       if (log.isTraceEnabled())
-                               log.trace("node=" + newNodePath + ", state=" + state + " (prefix=" + prefix + ")");
-
-                       return title;
-               } catch (Exception e) {
-                       log.error("Cannot set state '" + state + "'", e);
-                       if (state.equals("") || newState.equals("~") || newState.equals(previousState))
-                               return "Unrecoverable exception : " + e.getClass().getSimpleName();
-                       if (previousState.equals(""))
-                               previousState = "~";
-                       navigateTo(previousState);
-                       throw new CmsException("Unexpected issue when accessing #" + newState, e);
-               }
-       }
-
-       private String publishMetaData(Node node) throws RepositoryException {
-               // Title
-               String title;
-               if (node != null && node.isNodeType(NodeType.MIX_TITLE) && node.hasProperty(Property.JCR_TITLE))
-                       title = node.getProperty(Property.JCR_TITLE).getString() + " - " + getBaseTitle();
-               else
-                       title = getBaseTitle();
-
-               HttpServletRequest request = UiContext.getHttpRequest();
-               if (request == null)
-                       return null;
-
-               StringBuilder js = new StringBuilder();
-               if (title == null)
-                       title = "";
-               title = title.replace("'", "\\'");// sanitize
-               js.append("document.title = '" + title + "';");
-               jsExecutor.execute(js.toString());
-               return title;
-       }
-
-       // Simply remove some illegal character
-       // private String clean(String stringToClean) {
-       // return stringToClean.replaceAll("'", "").replaceAll("\\n", "")
-       // .replaceAll("\\t", "");
-       // }
-
-       protected synchronized Node getNode() {
-               return node;
-       }
-
-       private synchronized void setNode(Node node) throws RepositoryException {
-               this.node = node;
-               this.nodePath = node == null ? null : node.getPath();
-       }
-
-       protected String getState() {
-               return state;
-       }
-
-       protected Throwable getException() {
-               return exception;
-       }
-
-       protected void resetException() {
-               exception = null;
-       }
-
-       protected Session getSession() {
-               return session;
-       }
-
-       private class CmsNavigationListener implements BrowserNavigationListener {
-               private static final long serialVersionUID = -3591018803430389270L;
-
-               @Override
-               public void navigated(BrowserNavigationEvent event) {
-                       setState(event.getState());
-                       doRefresh();
-               }
-       }
-}
\ No newline at end of file
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/BundleResourceLoader.java
deleted file mode 100644 (file)
index ca93e62..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.cms.web;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.osgi.framework.Bundle;
-
-/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
-public class BundleResourceLoader implements ResourceLoader {
-       private final Bundle bundle;
-
-       public BundleResourceLoader(Bundle bundle) {
-               this.bundle = bundle;
-       }
-
-       @Override
-       public InputStream getResourceAsStream(String resourceName) throws IOException {
-               URL res = bundle.getEntry(resourceName);
-               if (res == null) {
-                       res = bundle.getResource(resourceName);
-                       if (res == null)
-                               throw new IllegalArgumentException(
-                                               "Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName());
-               }
-               return res.openStream();
-       }
-
-       public Bundle getBundle() {
-               return bundle;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java
deleted file mode 100644 (file)
index 102a4e1..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.argeo.cms.web;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.argeo.api.cms.ux.CmsTheme;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-
-/** A RAP {@link ResourceLoader} based on a {@link CmsTheme}. */
-public class CmsThemeResourceLoader implements ResourceLoader {
-       private final CmsTheme theme;
-
-       public CmsThemeResourceLoader(CmsTheme theme) {
-               super();
-               this.theme = theme;
-       }
-
-       @Override
-       public InputStream getResourceAsStream(String resourceName) throws IOException {
-               return theme.getResourceAsStream(resourceName);
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebApp.java
deleted file mode 100644 (file)
index a1d54cf..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-package org.argeo.cms.web;
-
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.api.cms.CmsAppListener;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.util.LangUtils;
-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.ExceptionHandler;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.EventAdmin;
-
-/** An RWT web app integrating with a {@link CmsApp}. */
-public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, CmsAppListener {
-       private final static CmsLog log = CmsLog.getLog(CmsWebApp.class);
-
-       private BundleContext bundleContext;
-       private CmsApp cmsApp;
-//     private String cmsAppId;
-       private EventAdmin eventAdmin;
-
-       private ServiceRegistration<ApplicationConfiguration> rwtAppReg;
-
-       private final static String CONTEXT_NAME = "contextName";
-       private String contextName;
-
-       private final static String FAVICON_PNG = "favicon.png";
-
-       public void init(BundleContext bundleContext, Map<String, String> properties) {
-               this.bundleContext = bundleContext;
-               contextName = properties.get(CONTEXT_NAME);
-               if (cmsApp != null) {
-                       if (cmsApp.allThemesAvailable())
-                               publishWebApp();
-               }
-       }
-
-       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
-               if (cmsApp != null) {
-                       cmsApp.removeCmsAppListener(this);
-                       cmsApp = null;
-               }
-       }
-
-       @Override
-       public void configure(Application application) {
-               // TODO make it configurable?
-               // SWT compatibility is required for:
-               // - Browser.execute()
-               // - blocking dialogs
-               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-               for (String uiName : cmsApp.getUiNames()) {
-                       CmsTheme theme = cmsApp.getTheme(uiName);
-                       if (theme != null)
-                               WebThemeUtils.apply(application, theme);
-               }
-
-               Map<String, String> properties = new HashMap<>();
-               addEntryPoints(application, properties);
-               application.setExceptionHandler(this);
-       }
-
-       @Override
-       public void handleException(Throwable throwable) {
-               Display display = Display.getCurrent();
-               if (display != null && !display.isDisposed()) {
-                       CmsView cmsView = CmsSwtUtils.getCmsView(display.getActiveShell());
-                       cmsView.exception(throwable);
-               } else {
-                       log.error("Unexpected exception outside an UI thread", throwable);
-               }
-
-       }
-
-       protected void addEntryPoints(Application application, Map<String, String> commonProperties) {
-               for (String uiName : cmsApp.getUiNames()) {
-                       Map<String, String> properties = new HashMap<>(commonProperties);
-                       CmsTheme theme = cmsApp.getTheme(uiName);
-                       if (theme != null) {
-                               properties.put(WebClient.THEME_ID, theme.getThemeId());
-                               properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders());
-                               properties.put(WebClient.BODY_HTML, theme.getBodyHtml());
-                               Set<String> imagePaths = theme.getImagesPaths();
-                               if (imagePaths.contains(FAVICON_PNG)) {
-                                       properties.put(WebClient.FAVICON, FAVICON_PNG);
-                               }
-                       } else {
-                               properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID);
-                       }
-                       String entryPointName = !uiName.equals("") ? "/" + uiName : "/";
-                       application.addEntryPoint(entryPointName, () -> {
-                               CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName);
-                               entryPoint.setEventAdmin(eventAdmin);
-                               return entryPoint;
-                       }, properties);
-                       if (log.isDebugEnabled())
-                               log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName);
-               }
-//             if (log.isDebugEnabled())
-//                     log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
-       }
-
-       CmsApp getCmsApp() {
-               return cmsApp;
-       }
-
-       BundleContext getBundleContext() {
-               return bundleContext;
-       }
-
-       public void setCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               this.cmsApp = cmsApp;
-//             this.cmsAppId = properties.get(Constants.SERVICE_PID);
-               this.cmsApp.addCmsAppListener(this);
-       }
-
-       public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
-               if (!contextName.equals(this.contextName))
-                       return;
-               if (this.cmsApp != null) {
-                       this.cmsApp.removeCmsAppListener(this);
-               }
-               if (rwtAppReg != null)
-                       rwtAppReg.unregister();
-               this.cmsApp = null;
-       }
-
-       @Override
-       public void themingUpdated() {
-               if (cmsApp != null && cmsApp.allThemesAvailable())
-                       publishWebApp();
-       }
-
-       protected void publishWebApp() {
-               Dictionary<String, Object> regProps = LangUtils.dict(CONTEXT_NAME, contextName);
-               if (rwtAppReg != null)
-                       rwtAppReg.unregister();
-               if (bundleContext != null) {
-                       rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps);
-                       if (log.isDebugEnabled())
-                               log.debug("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ...");
-               }
-       }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java
deleted file mode 100644 (file)
index 1597197..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-package org.argeo.cms.web;
-
-import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext;
-
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
-import org.argeo.cms.LocaleUtils;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.auth.RemoteAuthCallbackHandler;
-import org.argeo.cms.servlet.ServletHttpRequest;
-import org.argeo.cms.servlet.ServletHttpResponse;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SimpleSwtUxContext;
-import org.argeo.cms.swt.dialogs.CmsFeedback;
-import org.argeo.cms.ui.util.DefaultImageManager;
-import org.argeo.eclipse.ui.specific.UiContext;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.rap.rwt.client.service.BrowserNavigation;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
-import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.SWTError;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-
-/** The {@link CmsView} for a {@link CmsWebApp}. */
-@SuppressWarnings("restriction")
-public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener {
-       private static final long serialVersionUID = 7733510691684570402L;
-       private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class);
-
-       private EventAdmin eventAdmin;
-
-       private final CmsWebApp cmsWebApp;
-       private final String uiName;
-
-       private LoginContext loginContext;
-       private String state;
-       private Throwable exception;
-       private UxContext uxContext;
-       private CmsImageManager imageManager;
-
-       private Display display;
-       private CmsUi ui;
-
-       private String uid;
-
-       // Client services
-       // private final JavaScriptExecutor jsExecutor;
-       private final BrowserNavigation browserNavigation;
-
-       /** Experimental OS-like multi windows. */
-       private boolean multipleShells = false;
-
-       public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
-               assert cmsWebApp != null;
-               assert uiName != null;
-               this.cmsWebApp = cmsWebApp;
-               this.uiName = uiName;
-               uid = UUID.randomUUID().toString();
-
-               // Initial login
-               LoginContext lc;
-               try {
-                       lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
-                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
-                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
-                       lc.login();
-               } catch (LoginException e) {
-                       try {
-                               lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
-                                               new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
-                                                               new ServletHttpResponse(UiContext.getHttpResponse())));
-                               lc.login();
-                       } catch (LoginException e1) {
-                               throw new IllegalStateException("Cannot log in as anonymous", e1);
-                       }
-               }
-               authChange(lc);
-
-               // jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
-               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
-               if (browserNavigation != null)
-                       browserNavigation.addBrowserNavigationListener(this);
-       }
-
-       protected void createContents(Composite parent) {
-               Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
-                       @Override
-                       public Void run() {
-                               try {
-                                       uxContext = new SimpleSwtUxContext();
-                                       imageManager = new DefaultImageManager();
-                                       CmsSession cmsSession = getCmsSession();
-                                       if (cmsSession != null) {
-                                               UiContext.setLocale(cmsSession.getLocale());
-                                               LocaleUtils.setThreadLocale(cmsSession.getLocale());
-                                       } else {
-                                               Locale rwtLocale = RWT.getUISession().getLocale();
-                                               LocaleUtils.setThreadLocale(rwtLocale);
-                                       }
-                                       parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
-                                       display = parent.getDisplay();
-                                       ui = cmsWebApp.getCmsApp().initUi(parent);
-                                       if (ui instanceof Composite)
-                                               ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
-                                       // we need ui to be set before refresh so that CmsView can store UI context data
-                                       // in it.
-                                       cmsWebApp.getCmsApp().refreshUi(ui, null);
-                               } catch (Exception e) {
-                                       throw new IllegalStateException("Cannot create entrypoint contents", e);
-                               }
-                               return null;
-                       }
-               });
-       }
-
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       public <T> T doAs(PrivilegedAction<T> action) {
-               return Subject.doAs(getSubject(), action);
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return CurrentUser.isAnonymous(getSubject());
-       }
-
-       @Override
-       public synchronized void logout() {
-               if (loginContext == null)
-                       throw new IllegalArgumentException("Login context should not be null");
-               try {
-                       CurrentUser.logoutCmsSession(loginContext.getSubject());
-                       loginContext.logout();
-                       LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
-                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
-                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
-                       anonymousLc.login();
-                       authChange(anonymousLc);
-               } catch (LoginException e) {
-                       log.error("Cannot logout", e);
-               }
-       }
-
-       @Override
-       public synchronized void authChange(LoginContext lc) {
-               if (lc == null)
-                       throw new IllegalArgumentException("Login context cannot be null");
-               // logout previous login context
-               if (this.loginContext != null)
-                       try {
-                               this.loginContext.logout();
-                       } catch (LoginException e1) {
-                               log.warn("Could not log out: " + e1);
-                       }
-               this.loginContext = lc;
-               doRefresh();
-       }
-
-       @Override
-       public void exception(final Throwable e) {
-               if (e instanceof SWTError) {
-                       SWTError swtError = (SWTError) e;
-                       if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED)
-                               return;
-               }
-               display.syncExec(() -> {
-//                     CmsFeedback.show("Unexpected exception in CMS", e);
-                       exception = e;
-                       log.error("Unexpected exception in CMS", e);
-                       doRefresh();
-               });
-       }
-
-       protected synchronized void doRefresh() {
-               if (ui != null)
-                       Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
-                               @Override
-                               public Void run() {
-                                       if (exception != null) {
-                                               // TODO internationalise
-                                               CmsFeedback.show("Unexpected exception", exception);
-                                               exception = null;
-                                               // TODO report
-                                       }
-                                       cmsWebApp.getCmsApp().refreshUi(ui, state);
-                                       return null;
-                               }
-                       });
-       }
-
-       /** Sets the state of the entry point and retrieve the related JCR node. */
-       protected String setState(String newState) {
-               cmsWebApp.getCmsApp().setState(ui, newState);
-               state = newState;
-               return null;
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-       @Override
-       public void navigateTo(String state) {
-               exception = null;
-               String title = setState(state);
-               if (title != null)
-                       doRefresh();
-               if (browserNavigation != null)
-                       browserNavigation.pushState(state, title);
-       }
-
-       public CmsImageManager getImageManager() {
-               return imageManager;
-       }
-
-       @Override
-       public void navigated(BrowserNavigationEvent event) {
-               setState(event.getState());
-               // doRefresh();
-       }
-
-       @Override
-       public void sendEvent(String topic, Map<String, Object> properties) {
-               if (properties == null)
-                       properties = new HashMap<>();
-               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
-                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
-                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
-               properties.put(CMS_VIEW_UID_PROPERTY, uid);
-               eventAdmin.sendEvent(new Event(topic, properties));
-       }
-
-       @Override
-       public void stateChanged(String state, String title) {
-               browserNavigation.pushState(state, title);
-       }
-
-       @Override
-       public CmsSession getCmsSession() {
-               CmsSession cmsSession = cmsWebApp.getCmsApp().getCmsContext().getCmsSession(getSubject());
-               if (cmsSession == null)
-                       throw new IllegalStateException("No CMS session available for " + getSubject());
-               return cmsSession;
-       }
-
-       @Override
-       public Object getData(String key) {
-               if (ui != null) {
-                       return ui.getData(key);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       @Override
-       public void setData(String key, Object value) {
-               if (ui != null) {
-                       ui.setData(key, value);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       /*
-        * EntryPoint IMPLEMENTATION
-        */
-
-       @Override
-       public int createUI() {
-               Display display = new Display();
-               Shell shell = createShell(display);
-               shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               CmsSwtUtils.registerCmsView(shell, this);
-               createContents(shell);
-               shell.layout();
-//             if (shell.getMaximized()) {
-//                     shell.layout();
-//             } else {
-////                   shell.pack();
-//             }
-               shell.open();
-               if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) {
-                       eventLoop: while (!shell.isDisposed()) {
-                               try {
-                                       Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
-                                               @Override
-                                               public Void run() {
-                                                       if (!display.readAndDispatch()) {
-                                                               display.sleep();
-                                                       }
-                                                       return null;
-                                               }
-                                       });
-                               } catch (Throwable e) {
-                                       if (e instanceof SWTError) {
-                                               SWTError swtError = (SWTError) e;
-                                               if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
-                                                       log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
-                                                       continue eventLoop;
-                                               } else {
-                                                       log.error("Unexpected SWT error in event loop, shutting down...", e);
-                                                       break eventLoop;
-                                               }
-                                       } else if (e instanceof ThreadDeath) {
-                                               throw (ThreadDeath) e;
-                                       } else if (e instanceof Error) {
-                                               log.error("Unexpected error in event loop, shutting down...", e);
-                                               break eventLoop;
-                                       } else {
-                                               log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
-                                               continue eventLoop;
-                                       }
-                               }
-                       }
-                       if (!display.isDisposed())
-                               display.dispose();
-               }
-               return 0;
-       }
-
-       protected Shell createShell(Display display) {
-               Shell shell;
-               if (!multipleShells) {
-                       shell = new Shell(display, SWT.NO_TRIM);
-                       shell.setMaximized(true);
-               } else {
-                       shell = new Shell(display, SWT.SHELL_TRIM);
-                       shell.setSize(800, 600);
-               }
-               return shell;
-       }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/MinimalWebApp.java
deleted file mode 100644 (file)
index 2eff71e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.argeo.cms.web;
-
-import static org.argeo.cms.osgi.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.argeo.cms.osgi.BundleCmsTheme;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.osgi.framework.BundleContext;
-
-/** Lightweight web app using only RWT and not the whole Eclipse platform. */
-public class MinimalWebApp implements ApplicationConfiguration {
-
-       private BundleCmsTheme theme;
-
-       public void init(BundleContext bundleContext, Map<String, Object> properties) {
-               if (properties.containsKey(CMS_THEME_BUNDLE_PROPERTY)) {
-                       String cmsThemeBundle = properties.get(CMS_THEME_BUNDLE_PROPERTY).toString();
-                       theme = new BundleCmsTheme(bundleContext, cmsThemeBundle);
-               }
-       }
-
-       public void destroy() {
-
-       }
-
-       /** To be overridden. Does nothing by default. */
-       protected void addEntryPoints(Application application, Map<String, String> properties) {
-
-       }
-
-       @Override
-       public void configure(Application application) {
-               if (theme != null)
-                       WebThemeUtils.apply(application, theme);
-
-               Map<String, String> properties = new HashMap<>();
-               if (theme != null) {
-                       properties.put(WebClient.THEME_ID, theme.getThemeId());
-                       properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders());
-               } else {
-                       properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID);
-               }
-               addEntryPoints(application, properties);
-
-       }
-
-       public void setTheme(BundleCmsTheme theme) {
-               this.theme = theme;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleApp.java
deleted file mode 100644 (file)
index 38a9b44..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-package org.argeo.cms.web;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.security.Privilege;
-import javax.jcr.version.VersionManager;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.jcr.CmsJcrUtils;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.ui.CmsUiConstants;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.LifeCycleUiProvider;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.ui.util.StyleSheetResourceLoader;
-import org.argeo.jcr.JcrUtils;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.application.Application.OperationMode;
-import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.application.EntryPoint;
-import org.eclipse.rap.rwt.application.EntryPointFactory;
-import org.eclipse.rap.rwt.application.ExceptionHandler;
-import org.eclipse.rap.rwt.client.WebClient;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
-/** A basic generic app based on {@link SimpleErgonomics}. */
-@Deprecated
-public class SimpleApp implements CmsUiConstants, ApplicationConfiguration {
-       private final static CmsLog log = CmsLog.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(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER);
-       private List<String> rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER);
-
-       private CmsUiProvider header;
-       private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
-
-       private Integer headerHeight = 40;
-
-       private ServiceRegistration<ApplicationConfiguration> appReg;
-
-       public void configure(Application application) {
-               try {
-                       BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
-
-                       application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
-                       // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
-
-                       application.setExceptionHandler(new CmsExceptionHandler());
-
-                       // loading animated gif
-                       application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE));
-                       // empty image
-                       application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE));
-
-                       for (String resource : resources) {
-                               application.addResource(resource, bundleRL);
-                               if (log.isTraceEnabled())
-                                       log.trace("Resource " + resource);
-                       }
-
-                       Map<String, String> defaultBranding = null;
-                       if (branding.containsKey("*"))
-                               defaultBranding = branding.get("*");
-                       // String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
-
-                       // entry points
-                       for (String page : pages.keySet()) {
-                               Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
-                                               : new HashMap<String, String>();
-                               if (branding.containsKey(page)) {
-                                       properties.putAll(branding.get(page));
-                               }
-                               // favicon
-                               if (properties.containsKey(WebClient.FAVICON)) {
-                                       String themeId = defaultBranding.get(WebClient.THEME_ID);
-                                       Bundle themeBundle = findThemeBundle(bundleContext, themeId);
-                                       String faviconRelPath = properties.get(WebClient.FAVICON);
-                                       application.addResource(faviconRelPath,
-                                                       new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle()));
-                                       if (log.isTraceEnabled())
-                                               log.trace("Favicon " + faviconRelPath);
-
-                               }
-
-                               // page title
-                               if (!properties.containsKey(WebClient.PAGE_TITLE)) {
-                                       if (page.length() > 0)
-                                               properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1));
-                               }
-
-                               // default body HTML
-                               if (!properties.containsKey(WebClient.BODY_HTML))
-                                       properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY);
-
-                               //
-                               // ADD ENTRY POINT
-                               //
-                               application.addEntryPoint("/" + page,
-                                               new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties);
-                               log.info("Page /" + page);
-                       }
-
-                       // stylesheets and themes
-                       Set<Bundle> themeBundles = new HashSet<>();
-                       for (String themeId : styleSheets.keySet()) {
-                               Bundle themeBundle = findThemeBundle(bundleContext, themeId);
-                               StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
-                                               themeBundle != null ? themeBundle : bundleContext.getBundle());
-                               if (themeBundle != null)
-                                       themeBundles.add(themeBundle);
-                               List<String> cssLst = styleSheets.get(themeId);
-                               if (log.isDebugEnabled())
-                                       log.debug("Theme " + themeId);
-                               for (String css : cssLst) {
-                                       application.addStyleSheet(themeId, css, styleSheetRL);
-                                       if (log.isDebugEnabled())
-                                               log.debug(" CSS " + css);
-                               }
-
-                       }
-                       for (Bundle themeBundle : themeBundles) {
-                               BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
-                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.png");
-                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.gif");
-                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.jpg");
-                       }
-               } catch (RuntimeException e) {
-                       // Easier access to initialisation errors
-                       log.error("Unexpected exception when configuring RWT application.", e);
-                       throw e;
-               }
-       }
-
-       public void init() throws RepositoryException {
-               Session session = null;
-               try {
-                       session = CmsJcrUtils.openDataAdminSession(repository, workspace);
-                       // session = JcrUtils.loginOrCreateWorkspace(repository, workspace);
-                       VersionManager vm = session.getWorkspace().getVersionManager();
-                       JcrUtils.mkdirs(session, jcrBasePath);
-                       session.save();
-                       if (!vm.isCheckedOut(jcrBasePath))
-                               vm.checkout(jcrBasePath);
-                       for (String principal : rwPrincipals)
-                               JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE);
-                       for (String principal : roPrincipals)
-                               JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ);
-
-                       for (String pageName : pages.keySet()) {
-                               try {
-                                       initPage(session, pages.get(pageName));
-                                       session.save();
-                               } catch (Exception e) {
-                                       throw new CmsException("Cannot initialize page " + pageName, e);
-                               }
-                       }
-
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-
-               // publish to OSGi
-               register();
-       }
-
-       protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException {
-               if (page instanceof LifeCycleUiProvider)
-                       ((LifeCycleUiProvider) page).init(adminSession);
-       }
-
-       public void destroy() {
-               for (String pageName : pages.keySet()) {
-                       try {
-                               CmsUiProvider page = pages.get(pageName);
-                               if (page instanceof LifeCycleUiProvider)
-                                       ((LifeCycleUiProvider) page).destroy();
-                       } catch (Exception e) {
-                               log.error("Cannot destroy page " + pageName, e);
-                       }
-               }
-       }
-
-       protected void register() {
-               Hashtable<String, String> props = new Hashtable<String, String>();
-               if (contextName != null)
-                       props.put("contextName", contextName);
-               appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props);
-               if (log.isDebugEnabled())
-                       log.debug("Registered " + (contextName == null ? "/" : contextName));
-       }
-
-       protected void unregister() {
-               appReg.unregister();
-               if (log.isDebugEnabled())
-                       log.debug("Unregistered " + (contextName == null ? "/" : contextName));
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setWorkspace(String workspace) {
-               this.workspace = workspace;
-       }
-
-       public void setHeader(CmsUiProvider header) {
-               this.header = header;
-       }
-
-       public void setPages(Map<String, CmsUiProvider> pages) {
-               this.pages = pages;
-       }
-
-       public void setJcrBasePath(String basePath) {
-               this.jcrBasePath = basePath;
-       }
-
-       public void setRoPrincipals(List<String> roPrincipals) {
-               this.roPrincipals = roPrincipals;
-       }
-
-       public void setRwPrincipals(List<String> rwPrincipals) {
-               this.rwPrincipals = rwPrincipals;
-       }
-
-       public void setHeaderHeight(Integer headerHeight) {
-               this.headerHeight = headerHeight;
-       }
-
-       public void setBranding(Map<String, Map<String, String>> branding) {
-               this.branding = branding;
-       }
-
-       public void setStyleSheets(Map<String, List<String>> styleSheets) {
-               this.styleSheets = styleSheets;
-       }
-
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-       public void setResources(List<String> resources) {
-               this.resources = resources;
-       }
-
-       public void setContextName(String contextName) {
-               this.contextName = contextName;
-       }
-
-       private static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL,
-                       String pattern) {
-               Enumeration<URL> themeResources = themeBundle.findEntries("/", pattern, true);
-               if (themeResources == null)
-                       return;
-               while (themeResources.hasMoreElements()) {
-                       String resource = themeResources.nextElement().getPath();
-                       // remove first '/' so that RWT registers it
-                       resource = resource.substring(1);
-                       if (!resource.endsWith("/")) {
-                               application.addResource(resource, themeBRL);
-                               if (log.isTraceEnabled())
-                                       log.trace("Registered " + resource + " from theme " + themeBundle);
-                       }
-
-               }
-
-       }
-
-       private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) {
-               if (themeId == null)
-                       return null;
-               // TODO optimize
-               // TODO deal with multiple versions
-               Bundle themeBundle = null;
-               if (themeId != null) {
-                       for (Bundle bundle : bundleContext.getBundles())
-                               if (themeId.equals(bundle.getSymbolicName())) {
-                                       themeBundle = bundle;
-                                       break;
-                               }
-               }
-               return themeBundle;
-       }
-
-       class CmsExceptionHandler implements ExceptionHandler {
-
-               @Override
-               public void handleException(Throwable throwable) {
-                       // TODO be smarter
-                       CmsUiUtils.getCmsView().exception(throwable);
-               }
-
-       }
-
-       private class CmsEntryPointFactory implements EntryPointFactory {
-               private final CmsUiProvider page;
-               private final Repository repository;
-               private final String workspace;
-               private final Map<String, String> properties;
-
-               public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
-                               Map<String, String> properties) {
-                       this.page = page;
-                       this.repository = repository;
-                       this.workspace = workspace;
-                       this.properties = properties;
-               }
-
-               @Override
-               public EntryPoint create() {
-                       SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) {
-                               private static final long serialVersionUID = -637940404865527290L;
-
-                               @Override
-                               protected void createAdminArea(Composite parent) {
-                                       Composite adminArea = new Composite(parent, SWT.NONE);
-                                       adminArea.setLayout(new FillLayout());
-                                       Button refresh = new Button(adminArea, SWT.PUSH);
-                                       refresh.setText("Reload App");
-                                       refresh.addSelectionListener(new SelectionAdapter() {
-                                               private static final long serialVersionUID = -7671999525536351366L;
-
-                                               @Override
-                                               public void widgetSelected(SelectionEvent e) {
-                                                       long timeBeforeReload = 1000;
-                                                       RWT.getClient().getService(JavaScriptExecutor.class).execute(
-                                                                       "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");");
-                                                       reloadApp();
-                                               }
-                                       });
-                               }
-                       };
-                       // entryPoint.setState("");
-                       entryPoint.setHeader(header);
-                       entryPoint.setHeaderHeight(headerHeight);
-                       // CmsSession.current.set(entryPoint);
-                       return entryPoint;
-               }
-
-               private void reloadApp() {
-                       new Thread("Refresh app") {
-                               @Override
-                               public void run() {
-                                       unregister();
-                                       register();
-                               }
-                       }.start();
-               }
-       }
-
-       private static ResourceLoader createResourceLoader(final String resourceName) {
-               return new ResourceLoader() {
-                       public InputStream getResourceAsStream(String resourceName) throws IOException {
-                               return getClass().getClassLoader().getResourceAsStream(resourceName);
-                       }
-               };
-       }
-
-       // private static ResourceLoader createUrlResourceLoader(final URL url) {
-       // return new ResourceLoader() {
-       // public InputStream getResourceAsStream(String resourceName)
-       // throws IOException {
-       // return url.openStream();
-       // }
-       // };
-       // }
-
-       /*
-        * TEXTS
-        */
-       private static String DEFAULT_LOADING_BODY = "<div"
-                       + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
-                       + "<img src=\"./rwt-resources/" + LOADING_IMAGE
-                       + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/SimpleErgonomics.java
deleted file mode 100644 (file)
index 783f6eb..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-package org.argeo.cms.web;
-
-import java.util.Map;
-import java.util.UUID;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.UxContext;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.CmsStyles;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SimpleSwtUxContext;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.util.DefaultImageManager;
-import org.argeo.cms.ui.util.SystemNotifications;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-
-/** Simple header/body ergonomics. */
-@Deprecated
-public class SimpleErgonomics extends AbstractCmsEntryPoint {
-       private static final long serialVersionUID = 8743413921359548523L;
-
-       private final static CmsLog log = CmsLog.getLog(SimpleErgonomics.class);
-
-       private boolean uiInitialized = false;
-       private Composite headerArea;
-       private Composite leftArea;
-       private Composite rightArea;
-       private Composite footerArea;
-       private Composite bodyArea;
-       private final CmsUiProvider uiProvider;
-
-       private CmsUiProvider header;
-       private Integer headerHeight = 0;
-       private Integer footerHeight = 0;
-       private CmsUiProvider lead;
-       private CmsUiProvider end;
-       private CmsUiProvider footer;
-
-       private CmsImageManager imageManager = new DefaultImageManager();
-       private UxContext uxContext = null;
-       private String uid;
-
-       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) {
-               uid = UUID.randomUUID().toString();
-               parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false)));
-
-               uxContext = new SimpleSwtUxContext();
-               if (!getUxContext().isMasterData())
-                       createAdminArea(parent);
-               headerArea = new Composite(parent, SWT.NONE);
-               headerArea.setLayout(new FillLayout());
-               GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1);
-               headerData.heightHint = headerHeight;
-               headerArea.setLayoutData(headerData);
-
-               // TODO: bi-directional
-               leftArea = new Composite(parent, SWT.NONE);
-               leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false));
-               leftArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               bodyArea = new Composite(parent, SWT.NONE);
-               bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY);
-               bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-               bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               // TODO: bi-directional
-               rightArea = new Composite(parent, SWT.NONE);
-               rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
-               rightArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               footerArea = new Composite(parent, SWT.NONE);
-               // footerArea.setLayout(new FillLayout());
-               GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1);
-               footerData.heightHint = footerHeight;
-               footerArea.setLayoutData(footerData);
-
-               uiInitialized = true;
-               refresh();
-       }
-
-       @Override
-       protected void refresh() {
-               if (!uiInitialized)
-                       return;
-               if (getState() == null)
-                       setState("");
-               refreshSides();
-               refreshBody();
-               if (log.isTraceEnabled())
-                       log.trace("UI refreshed " + getNode());
-       }
-
-       protected void createAdminArea(Composite parent) {
-       }
-
-       @Deprecated
-       protected void refreshHeader() {
-               if (header == null)
-                       return;
-
-               for (Control child : headerArea.getChildren())
-                       child.dispose();
-               try {
-                       header.createUi(headerArea, getNode());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot refresh header", e);
-               }
-               headerArea.layout(true, true);
-       }
-
-       protected void refreshSides() {
-               refresh(headerArea, header, CmsStyles.CMS_HEADER);
-               refresh(leftArea, lead, CmsStyles.CMS_LEAD);
-               refresh(rightArea, end, CmsStyles.CMS_END);
-               refresh(footerArea, footer, CmsStyles.CMS_FOOTER);
-       }
-
-       private void refresh(Composite area, CmsUiProvider uiProvider, String style) {
-               if (uiProvider == null)
-                       return;
-
-               for (Control child : area.getChildren())
-                       child.dispose();
-               CmsSwtUtils.style(area, style);
-               try {
-                       uiProvider.createUi(area, getNode());
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot refresh header", e);
-               }
-               area.layout(true, true);
-       }
-
-       protected void refreshBody() {
-               // Exception
-               Throwable exception = getException();
-               if (exception != null) {
-                       SystemNotifications systemNotifications = new SystemNotifications(bodyArea);
-                       systemNotifications.notifyException(exception);
-                       resetException();
-                       return;
-                       // TODO report
-               }
-
-               // clear
-               for (Control child : bodyArea.getChildren())
-                       child.dispose();
-               bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
-
-               try {
-                       Node node = getNode();
-//                     if (node == null)
-//                             log.error("Context cannot be null");
-//                     else
-                       uiProvider.createUi(bodyArea, node);
-               } catch (RepositoryException e) {
-                       throw new CmsException("Cannot refresh body", e);
-               }
-
-               bodyArea.layout(true, true);
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-       public CmsImageManager getImageManager() {
-               return imageManager;
-       }
-
-       public void setHeader(CmsUiProvider header) {
-               this.header = header;
-       }
-
-       public void setHeaderHeight(Integer headerHeight) {
-               this.headerHeight = headerHeight;
-       }
-
-       public void setImageManager(CmsImageManager imageManager) {
-               this.imageManager = imageManager;
-       }
-
-       public CmsUiProvider getLead() {
-               return lead;
-       }
-
-       public void setLead(CmsUiProvider lead) {
-               this.lead = lead;
-       }
-
-       public CmsUiProvider getEnd() {
-               return end;
-       }
-
-       public void setEnd(CmsUiProvider end) {
-               this.end = end;
-       }
-
-       public CmsUiProvider getFooter() {
-               return footer;
-       }
-
-       public void setFooter(CmsUiProvider footer) {
-               this.footer = footer;
-       }
-
-       public CmsUiProvider getHeader() {
-               return header;
-       }
-
-       public void setFooterHeight(Integer footerHeight) {
-               this.footerHeight = footerHeight;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/WebThemeUtils.java
deleted file mode 100644 (file)
index e51644b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.argeo.cms.web;
-
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.eclipse.rap.rwt.application.Application;
-import org.eclipse.rap.rwt.service.ResourceLoader;
-
-/** Web specific utilities around theming. */
-public class WebThemeUtils {
-       private final static CmsLog log = CmsLog.getLog(WebThemeUtils.class);
-
-       public static void apply(Application application, CmsTheme theme) {
-               ResourceLoader resourceLoader = new CmsThemeResourceLoader(theme);
-               resources: for (String path : theme.getImagesPaths()) {
-                       if (path.startsWith("target/"))
-                               continue resources; // skip maven output
-                       application.addResource(path, resourceLoader);
-                       if (log.isTraceEnabled())
-                               log.trace("Theme " + theme.getThemeId() + ": added resource " + path);
-               }
-               for (String path : theme.getRapCssPaths()) {
-                       application.addStyleSheet(theme.getThemeId(), path, resourceLoader);
-                       if (log.isDebugEnabled())
-                               log.debug("Theme " + theme.getThemeId() + ": added RAP CSS " + path);
-               }
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java
deleted file mode 100644 (file)
index 8380a85..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.argeo.cms.web.osgi;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.cms.web.CmsWebApp;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.service.event.EventAdmin;
-
-/** Publish a CmsApp as a RAP application. */
-public class CmsWebAppFactory {
-       private BundleContext bundleContext = FrameworkUtil.getBundle(CmsWebAppFactory.class).getBundleContext();
-       private final static String CONTEXT_NAME = "contextName";
-
-       private EventAdmin eventAdmin;
-
-       private Map<String, CmsWebApp> registrations = Collections.synchronizedMap(new HashMap<>());
-
-       public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
-               if (contextName != null) {
-                       CmsWebApp cmsWebApp = new CmsWebApp();
-                       cmsWebApp.setEventAdmin(eventAdmin);
-                       cmsWebApp.setCmsApp(cmsApp, properties);
-                       Hashtable<String, String> serviceProperties = new Hashtable<>();
-                       if (!contextName.equals(""))
-                               serviceProperties.put(CONTEXT_NAME, contextName);
-                       cmsWebApp.init(bundleContext, serviceProperties);
-                       registrations.put(contextName, cmsWebApp);
-               }
-       }
-
-       public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
-               if (contextName != null) {
-                       CmsWebApp cmsWebApp = registrations.get(contextName);
-                       if (cmsWebApp != null) {
-                               cmsWebApp.destroy(bundleContext, new HashMap<>());
-                               cmsWebApp.unsetCmsApp(cmsApp, properties);
-                       } else {
-                               // TODO log warning
-                       }
-               }
-       }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
-}
diff --git a/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java b/rap/org.argeo.cms.ui.rap/src/org/argeo/eclipse/ui/jetty/RwtRunner.java
deleted file mode 100644 (file)
index 29165a4..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.argeo.eclipse.ui.jetty;
-
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.resource.Resource;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.rap.rwt.application.AbstractEntryPoint;
-import org.eclipse.rap.rwt.application.ApplicationRunner;
-import org.eclipse.rap.rwt.engine.RWTServlet;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Label;
-
-/** A minimal RWT runner based on embedded Jetty. */
-public class RwtRunner {
-
-       private final Server server;
-       private final ServerConnector serverConnector;
-       private Path tempDir;
-
-       public RwtRunner() {
-               server = new Server(new QueuedThreadPool(10, 1));
-               serverConnector = new ServerConnector(server);
-               serverConnector.setPort(0);
-               server.setConnectors(new Connector[] { serverConnector });
-       }
-
-       protected Control createUi(Composite parent, Object context) {
-               return new Label(parent, SWT.NONE);
-       }
-
-       public void init() {
-               ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
-               context.setContextPath("/");
-               server.setHandler(context);
-
-               String entryPoint = "app";
-
-               // rwt-resources requires a file system
-               try {
-                       tempDir = Files.createTempDirectory("argeo-rwtRunner");
-                       context.setBaseResource(Resource.newResource(tempDir.resolve("www").toString()));
-               } catch (IOException e) {
-                       throw new IllegalStateException("Cannot create temporary directory", e);
-               }
-               context.addEventListener(new ServletContextListener() {
-                       ApplicationRunner applicationRunner;
-
-                       @Override
-                       public void contextInitialized(ServletContextEvent sce) {
-                               applicationRunner = new ApplicationRunner(
-                                               (application) -> application.addEntryPoint("/" + entryPoint, () -> new AbstractEntryPoint() {
-                                                       private static final long serialVersionUID = 5678385921969090733L;
-
-                                                       @Override
-                                                       protected void createContents(Composite parent) {
-                                                               createUi(parent, null);
-                                                       }
-                                               }, null), sce.getServletContext());
-                               applicationRunner.start();
-                       }
-
-                       @Override
-                       public void contextDestroyed(ServletContextEvent sce) {
-                               applicationRunner.stop();
-                       }
-               });
-
-               context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint);
-
-               // Required to serve rwt-resources. It is important that this is last.
-               ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
-               context.addServlet(holderPwd, "/");
-
-               try {
-                       server.start();
-               } catch (Exception e) {
-                       throw new IllegalStateException("Cannot start Jetty server", e);
-               }
-       }
-
-       public void destroy() {
-               try {
-                       serverConnector.close();
-                       server.stop();
-                       // TODO delete temp dir
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-       }
-
-       public Integer getEffectivePort() {
-               return serverConnector.getLocalPort();
-       }
-
-       public void waitFor() throws InterruptedException {
-               server.join();
-       }
-
-       public static void main(String[] args) throws Exception {
-               RwtRunner rwtRunner = new RwtRunner() {
-
-                       @Override
-                       protected Control createUi(Composite parent, Object context) {
-                               Label label = new Label(parent, SWT.NONE);
-                               label.setText("Hello world!");
-                               return label;
-                       }
-               };
-               rwtRunner.init();
-               Runtime.getRuntime().addShutdownHook(new Thread(() -> rwtRunner.destroy(), "Jetty shutdown"));
-
-               long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
-               System.out.println("App available in " + jvmUptime + " ms, on port " + rwtRunner.getEffectivePort());
-
-               // open browser in app mode
-               Thread.sleep(2000);// wait for RWT to be ready
-               Runtime.getRuntime().exec("google-chrome --app=http://localhost:" + rwtRunner.getEffectivePort() + "/app");
-
-               rwtRunner.waitFor();
-       }
-}
diff --git a/rap/org.argeo.swt.specific.rap/.classpath b/rap/org.argeo.swt.specific.rap/.classpath
deleted file mode 100644 (file)
index e03d341..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/rap/org.argeo.swt.specific.rap/.project b/rap/org.argeo.swt.specific.rap/.project
deleted file mode 100644 (file)
index 53d7976..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.swt.specific.rap</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/rap/org.argeo.swt.specific.rap/META-INF/.gitignore b/rap/org.argeo.swt.specific.rap/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rap/org.argeo.swt.specific.rap/bnd.bnd b/rap/org.argeo.swt.specific.rap/bnd.bnd
deleted file mode 100644 (file)
index bcd9b19..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-Import-Package: org.eclipse.swt,\
-org.eclipse.jface.dialogs,\
-org.eclipse.swt.events,\
-javax.servlet.http;version="[3,5)",\
-*
diff --git a/rap/org.argeo.swt.specific.rap/build.properties b/rap/org.argeo.swt.specific.rap/build.properties
deleted file mode 100644 (file)
index fd806ca..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-source.. = src/
-output.. = bin/
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java
deleted file mode 100644 (file)
index ac4e0df..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.awt.image.BufferedImage;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-public class BufferedImageDisplay extends Composite {
-       private static final long serialVersionUID = 4541163690514461514L;
-       private BufferedImage image;
-
-       public BufferedImageDisplay(Composite parent, int style) {
-               super(parent, SWT.NO_BACKGROUND);
-       }
-
-       public void setImage(BufferedImage image) {
-               this.image = image;
-       }
-
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java
deleted file mode 100644 (file)
index 6100c1a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class CmsFileDialog extends FileDialog {
-       private static final long serialVersionUID = -7540791204102318801L;
-
-       public CmsFileDialog(Shell parent, int style) {
-               super(parent, style);
-       }
-
-       public CmsFileDialog(Shell parent) {
-               super(parent);
-       }
-
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java
deleted file mode 100644 (file)
index 3f30bde..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Composite;
-
-public class CmsFileUpload extends FileUpload {
-       private static final long serialVersionUID = 8027963992680980549L;
-
-       public CmsFileUpload(Composite parent, int style) {
-               super(parent, style);
-       }
-
-       @Override
-       public void setText(String text) {
-               super.setText(text);
-       }
-
-       @Override
-       public String getFileName() {
-               return super.getFileName();
-       }
-
-       @Override
-       public String[] getFileNames() {
-               return super.getFileNames();
-       }
-
-       @Override
-       public void addSelectionListener(SelectionListener listener) {
-               super.addSelectionListener(listener);
-       }
-
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
deleted file mode 100644 (file)
index a89b921..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.jface.viewers.AbstractTableViewer;
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.widgets.Widget;
-
-/** Static utilities to bridge differences between RCP and RAP */
-public class EclipseUiSpecificUtils {
-
-       public static void setStyleData(Widget widget, Object data) {
-               widget.setData(RWT.CUSTOM_VARIANT, data);
-       }
-
-       public static Object getStyleData(Widget widget) {
-               return widget.getData(RWT.CUSTOM_VARIANT);
-       }
-
-       public static void setMarkupData(Widget widget) {
-               widget.setData(RWT.MARKUP_ENABLED, true);
-       }
-
-       public static void setMarkupValidationDisabledData(Widget widget) {
-               widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE);
-       }
-
-       /**
-        * TootlTip support is supported only for {@link AbstractTableViewer} in RAP
-        */
-       public static void enableToolTipSupport(Viewer viewer) {
-               if (viewer instanceof ColumnViewer)
-                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
-       }
-
-       private EclipseUiSpecificUtils() {
-       }
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java
deleted file mode 100644 (file)
index f9ca816..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.eclipse.rap.fileupload.FileDetails;
-import org.eclipse.rap.fileupload.FileUploadHandler;
-import org.eclipse.rap.fileupload.FileUploadReceiver;
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.rap.rwt.client.ClientFile;
-import org.eclipse.rap.rwt.client.service.ClientFileUploader;
-import org.eclipse.rap.rwt.dnd.ClientFileTransfer;
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.DropTargetAdapter;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.widgets.Control;
-
-/** Configures a {@link Control} to receive files drop from the client OS. */
-public class FileDropAdapter {
-
-       public void prepareDropTarget(Control control, DropTarget dropTarget) {
-               dropTarget.setTransfer(new Transfer[] { ClientFileTransfer.getInstance() });
-               dropTarget.addDropListener(new DropTargetAdapter() {
-                       private static final long serialVersionUID = 5361645765549463168L;
-
-                       @Override
-                       public void dropAccept(DropTargetEvent event) {
-                               if (!ClientFileTransfer.getInstance().isSupportedType(event.currentDataType)) {
-                                       event.detail = DND.DROP_NONE;
-                               }
-                       }
-
-                       @Override
-                       public void drop(DropTargetEvent event) {
-                               handleFileDrop(control, event);
-                       }
-               });
-       }
-
-       public void handleFileDrop(Control control, DropTargetEvent event) {
-               ClientFile[] clientFiles = (ClientFile[]) event.data;
-               ClientFileUploader service = RWT.getClient().getService(ClientFileUploader.class);
-//             DiskFileUploadReceiver receiver = new DiskFileUploadReceiver();
-               FileUploadReceiver receiver = new FileUploadReceiver() {
-
-                       @Override
-                       public void receive(InputStream stream, FileDetails details) throws IOException {
-                               control.getDisplay().syncExec(() -> {
-                                       try {
-                                               processUpload(stream, details.getFileName(), details.getContentType());
-                                       } catch (IOException e) {
-                                               throw new IllegalStateException("Cannot process upload of " + details.getFileName(), e);
-                                       }
-                               });
-                       }
-               };
-               FileUploadHandler handler = new FileUploadHandler(receiver);
-//                 handler.setMaxFileSize( sizeLimit );
-//                 handler.setUploadTimeLimit( timeLimit );
-               service.submit(handler.getUploadUrl(), clientFiles);
-//             for (File file : receiver.getTargetFiles()) {
-//                     paths.add(file.toPath());
-//             }
-       }
-
-       /** Executed in UI thread */
-       protected void processUpload(InputStream in, String fileName, String contentType) throws IOException {
-
-       }
-
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java
deleted file mode 100644 (file)
index 72e17a2..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.rap.rwt.RWT;
-import org.eclipse.swt.widgets.Display;
-
-/** Singleton class providing single sources infos about the UI context. */
-public class UiContext {
-       /** Can be null, thus indicating that we are not in a web context. */
-       public static HttpServletRequest getHttpRequest() {
-               return RWT.getRequest();
-       }
-
-       public static HttpServletResponse getHttpResponse() {
-               return RWT.getResponse();
-       }
-
-       public static Locale getLocale() {
-               if (Display.getCurrent() != null)
-                       return RWT.getUISession().getLocale();
-               else
-                       return Locale.getDefault();
-       }
-
-       public static void setLocale(Locale locale) {
-               if (Display.getCurrent() != null)
-                       RWT.getUISession().setLocale(locale);
-               else
-                       Locale.setDefault(locale);
-       }
-
-       /** Can always be null */
-       @SuppressWarnings("unchecked")
-       public static <T> T getData(String key) {
-               Display display = getDisplay();
-               if (display == null)
-                       return null;
-               return (T) display.getData(key);
-       }
-
-       public static void setData(String key, Object value) {
-               Display display = getDisplay();
-               if (display == null)
-                       throw new IllegalStateException("Not display available");
-               display.setData(key, value);
-       }
-
-       private static Display getDisplay() {
-               return Display.getCurrent();
-       }
-
-       private UiContext() {
-       }
-
-}
diff --git a/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java b/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java
deleted file mode 100644 (file)
index 4ec451f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Eclipse RAP-specific SWT/JFace utilities, to simplify single-sourcing. */
-package org.argeo.eclipse.ui.specific;
\ No newline at end of file
diff --git a/rcp/org.argeo.cms.e4.rcp/.classpath b/rcp/org.argeo.cms.e4.rcp/.classpath
deleted file mode 100644 (file)
index eca7bdb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/rcp/org.argeo.cms.e4.rcp/.gitignore b/rcp/org.argeo.cms.e4.rcp/.gitignore
deleted file mode 100644 (file)
index 710cd68..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/bin/
-/target/
-/exec
diff --git a/rcp/org.argeo.cms.e4.rcp/.project b/rcp/org.argeo.cms.e4.rcp/.project
deleted file mode 100644 (file)
index 64d5619..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.e4.rcp</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/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs b/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644 (file)
index 0c68a61..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.compliance=1.8
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.8
diff --git a/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs b/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs
deleted file mode 100644 (file)
index f29e940..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-eclipse.preferences.version=1
-pluginProject.extensions=false
-resolve.requirebundle=false
diff --git a/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore b/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi b/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi
deleted file mode 100644 (file)
index 5b250ee..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="ASCII"?>
-<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:advanced="http://www.eclipse.org/ui/2010/UIModel/application/ui/advanced" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmi:id="_c4iAgCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.application">
-  <children xsi:type="basic:TrimmedWindow" xmi:id="_hSGBwCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.trimmedwindow.argeocompanion" label="Argeo Companion">
-    <children xsi:type="advanced:PerspectiveStack" xmi:id="_nxzQICnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.perspectivestack.0">
-      <children xsi:type="advanced:Perspective" xmi:id="_oI_oICnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.perspective.cmsadmin" label="CMS Admin">
-        <children xsi:type="basic:PartSashContainer" xmi:id="_qc16ECnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.partsashcontainer.0" horizontal="true">
-          <children xsi:type="basic:PartStack" xmi:id="_RE87kDsXEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.rcp.partstack.1">
-            <children xsi:type="basic:Part" xmi:id="_V1WvgDsXEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.rcp.part.files" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.files.NodeFsBrowserView" label="Files"/>
-            <children xsi:type="basic:Part" xmi:id="_vOqDQCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.part.jcr" containerData="4000" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR"/>
-          </children>
-          <children xsi:type="basic:PartStack" xmi:id="_0eRiwCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.partstack.0" containerData="6000">
-            <tags>editorArea</tags>
-          </children>
-        </children>
-      </children>
-    </children>
-  </children>
-  <descriptors xmi:id="__9SDsC8JEeq0koquN4xGyg" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" allowMultiple="true" category="editorArea" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
-  <addons xmi:id="_c4iAgSnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
-  <addons xmi:id="_c4iAginCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
-  <addons xmi:id="_c4iAgynCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
-  <addons xmi:id="_c4iAhCnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
-  <addons xmi:id="_c4iAhSnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
-  <addons xmi:id="_c4iAhinCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
-  <addons xmi:id="_c4iAhynCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
-</application:Application>
diff --git a/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties b/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties
deleted file mode 100644 (file)
index 0a0da75..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-argeo.osgi.start.2.node=\
-org.eclipse.equinox.http.servlet,\
-org.eclipse.equinox.metatype,\
-org.eclipse.equinox.cm,\
-org.argeo.init
-
-argeo.osgi.start.3.node=\
-org.argeo.cms,\
-org.argeo.cms.jcr,\
-
-applicationXMI=org.argeo.cms.e4.rcp/argeo-companion.e4xmi
-lifeCycleURI=bundleclass://org.argeo.cms.e4.rcp/org.argeo.cms.e4.rcp.CmsRcpLifeCycle
-clearPersistedState=true
-#argeo.cms.desktop.inTray=true
-
-# Remote node:
-#argeo.node.repo.labeledUri=http://root:demo@localhost:7070/jcr/node
-
-# Logging
-log.org.argeo=DEBUG
-
-argeo.node.useradmin.uris=os:///
-eclipse.application=org.argeo.cms.e4.rcp.CmsE4Application
diff --git a/rcp/org.argeo.cms.e4.rcp/bnd.bnd b/rcp/org.argeo.cms.e4.rcp/bnd.bnd
deleted file mode 100644 (file)
index ff79c80..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-Bundle-SymbolicName: org.argeo.cms.e4.rcp;singleton=true
-
-Require-Bundle: org.eclipse.core.runtime
-
-Import-Package: !org.eclipse.core.runtime,\
-org.eclipse.swt,\
-*
diff --git a/rcp/org.argeo.cms.e4.rcp/build.properties b/rcp/org.argeo.cms.e4.rcp/build.properties
deleted file mode 100644 (file)
index 355413e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               argeo-companion.e4xmi
-source.. = src/
diff --git a/rcp/org.argeo.cms.e4.rcp/log4j.properties b/rcp/org.argeo.cms.e4.rcp/log4j.properties
deleted file mode 100644 (file)
index 13f949f..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-log4j.rootLogger=WARN, development
-
-## Levels
-log4j.logger.org.argeo=DEBUG
-log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN
-log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO
-
-#log4j.logger.org.springframework.security=DEBUG
-#log4j.logger.org.apache.commons.exec=DEBUG
-#log4j.logger.org.apache.jackrabbit.webdav=DEBUG
-#log4j.logger.org.apache.jackrabbit.remote=DEBUG
-#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG
-
-log4j.logger.org.apache.catalina=INFO
-log4j.logger.org.apache.coyote=INFO
-
-log4j.logger.org.apache.directory=INFO
-log4j.logger.org.apache.directory.server=ERROR
-log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR
-
-## Appenders
-# console is set to be a ConsoleAppender.
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-
-# console uses PatternLayout.
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n
-
-# development appender (slow!)
-log4j.appender.development=org.apache.log4j.ConsoleAppender
-log4j.appender.development.layout=org.apache.log4j.PatternLayout
-log4j.appender.development.layout.ConversionPattern=%d{HH:mm:ss} [%16.16t] %5p %m (%F:%L) %c%n
diff --git a/rcp/org.argeo.cms.e4.rcp/plugin.xml b/rcp/org.argeo.cms.e4.rcp/plugin.xml
deleted file mode 100644 (file)
index 3e6896b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.4"?>
-<plugin>
-   <extension
-         id="CmsE4Application"
-         name="CMS E4 Application"
-         point="org.eclipse.core.runtime.applications">
-      <application
-            cardinality="singleton-global"
-            thread="main"
-            visible="true">
-         <run class="org.argeo.cms.e4.rcp.CmsE4Application"></run>
-      </application>
-   </extension>
-</plugin>
diff --git a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java b/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java
deleted file mode 100644 (file)
index b37a765..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-package org.argeo.cms.e4.rcp;
-
-import java.security.PrivilegedExceptionAction;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
-import org.argeo.cms.auth.CurrentUser;
-import org.argeo.cms.swt.CmsException;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.argeo.cms.swt.SimpleSwtUxContext;
-import org.argeo.cms.swt.auth.CmsLoginShell;
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IExtension;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.equinox.app.IApplication;
-import org.eclipse.equinox.app.IApplicationContext;
-import org.eclipse.swt.widgets.Display;
-
-public class CmsE4Application implements IApplication, CmsView {
-       private LoginContext loginContext;
-       private IApplication e4Application;
-       private UxContext uxContext;
-       private String uid;
-
-       @Override
-       public Object start(IApplicationContext context) throws Exception {
-               // TODO wait for CMS to be ready
-               Thread.sleep(5000);
-
-               uid = UUID.randomUUID().toString();
-               Subject subject = new Subject();
-               Display display = createDisplay();
-               CmsLoginShell loginShell = new CmsLoginShell(this, null);
-               // TODO customize CmsLoginShell to be smaller and centered
-               loginShell.setSubject(subject);
-               try {
-                       // try pre-auth
-                       loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_SINGLE_USER, subject, loginShell);
-                       loginContext.login();
-               } catch (LoginException e) {
-                       e.printStackTrace();
-                       loginShell.createUi();
-                       loginShell.open();
-
-                       while (!loginShell.getShell().isDisposed()) {
-                               if (!display.readAndDispatch())
-                                       display.sleep();
-                       }
-               }
-               if (CurrentUser.getUsername(getSubject()) == null)
-                       throw new IllegalStateException("Cannot log in");
-
-               // try {
-               // CallbackHandler callbackHandler = new DefaultLoginDialog(
-               // display.getActiveShell());
-               // loginContext = new LoginContext(
-               // NodeConstants.LOGIN_CONTEXT_SINGLE_USER, subject,
-               // callbackHandler);
-               // } catch (LoginException e1) {
-               // throw new CmsException("Cannot initialize login context", e1);
-               // }
-               //
-               // // login
-               // try {
-               // loginContext.login();
-               // subject = loginContext.getSubject();
-               // } catch (LoginException e) {
-               // e.printStackTrace();
-               // display.dispose();
-               // try {
-               // Thread.sleep(2000);
-               // } catch (InterruptedException e1) {
-               // // silent
-               // }
-               // return null;
-               // }
-
-               uxContext = new SimpleSwtUxContext();
-               // UiContext.setData(CmsView.KEY, this);
-               CmsSwtUtils.registerCmsView(loginShell.getShell(), this);
-               e4Application = getApplication(null);
-               Object res = Subject.doAs(subject, new PrivilegedExceptionAction<Object>() {
-
-                       @Override
-                       public Object run() throws Exception {
-                               return e4Application.start(context);
-                       }
-
-               });
-               return res;
-       }
-
-       @Override
-       public void stop() {
-               if (e4Application != null)
-                       e4Application.stop();
-       }
-
-       static IApplication getApplication(String[] args) {
-               IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME,
-                               Platform.PT_APPLICATIONS, "org.eclipse.e4.ui.workbench.swt.E4Application");
-               try {
-                       IConfigurationElement[] elements = extension.getConfigurationElements();
-                       if (elements.length > 0) {
-                               IConfigurationElement[] runs = elements[0].getChildren("run");
-                               if (runs.length > 0) {
-                                       Object runnable;
-                                       runnable = runs[0].createExecutableExtension("class");
-                                       if (runnable instanceof IApplication)
-                                               return (IApplication) runnable;
-                               }
-                       }
-               } catch (Exception e) {
-                       throw new IllegalStateException("Cannot find e4 application", e);
-               }
-               throw new IllegalStateException("Cannot find e4 application");
-       }
-
-       public static Display createDisplay() {
-               Display.setAppName("Argeo CMS RCP");
-
-               // create the display
-               Display newDisplay = Display.getCurrent();
-               if (newDisplay == null) {
-                       newDisplay = new Display();
-               }
-               // Set the priority higher than normal so as to be higher
-               // than the JobManager.
-               Thread.currentThread().setPriority(Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1));
-               return newDisplay;
-       }
-
-       //
-       // CMS VIEW
-       //
-
-       @Override
-       public UxContext getUxContext() {
-               return uxContext;
-       }
-
-       @Override
-       public void navigateTo(String state) {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public void authChange(LoginContext loginContext) {
-               if (loginContext == null)
-                       throw new CmsException("Login context cannot be null");
-               // logout previous login context
-               // if (this.loginContext != null)
-               // try {
-               // this.loginContext.logout();
-               // } catch (LoginException e1) {
-               // System.err.println("Could not log out: " + e1);
-               // }
-               this.loginContext = loginContext;
-       }
-
-       @Override
-       public void logout() {
-               if (loginContext == null)
-                       throw new CmsException("Login context should not bet null");
-               try {
-                       CurrentUser.logoutCmsSession(loginContext.getSubject());
-                       loginContext.logout();
-               } catch (LoginException e) {
-                       throw new CmsException("Cannot log out", e);
-               }
-       }
-
-       @Override
-       public void exception(Throwable e) {
-               // TODO Auto-generated method stub
-
-       }
-
-       @Override
-       public CmsImageManager getImageManager() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return CurrentUser.isAnonymous(getSubject());
-       }
-
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-}
diff --git a/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java b/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java
deleted file mode 100644 (file)
index 1d38fe7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.argeo.cms.e4.rcp;
-
-import org.eclipse.e4.core.contexts.IEclipseContext;
-import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
-import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
-import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions;
-import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals;
-
-@SuppressWarnings("restriction")
-public class CmsRcpLifeCycle {
-
-       @PostContextCreate
-       void postContextCreate(IEclipseContext workbenchContext) {
-       }
-
-       @PreSave
-       void preSave(IEclipseContext workbenchContext) {
-       }
-
-       @ProcessAdditions
-       void processAdditions(IEclipseContext workbenchContext) {
-       }
-
-       @ProcessRemovals
-       void processRemovals(IEclipseContext workbenchContext) {
-       }
-}
diff --git a/rcp/org.argeo.cms.ui.rcp/.classpath b/rcp/org.argeo.cms.ui.rcp/.classpath
deleted file mode 100644 (file)
index e801ebf..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/rcp/org.argeo.cms.ui.rcp/.gitignore b/rcp/org.argeo.cms.ui.rcp/.gitignore
deleted file mode 100644 (file)
index 09e3bc9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin/
-/target/
diff --git a/rcp/org.argeo.cms.ui.rcp/.project b/rcp/org.argeo.cms.ui.rcp/.project
deleted file mode 100644 (file)
index c9c2a44..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.cms.ui.rcp</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ManifestBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.SchemaBuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.pde.ds.core.builder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.pde.PluginNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore b/rcp/org.argeo.cms.ui.rcp/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpDisplayFactory.xml
deleted file mode 100644 (file)
index a0c0f0f..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="CMS RCP Display Factory">
-   <implementation class="org.argeo.cms.ui.rcp.CmsRcpDisplayFactory"/>
-</scr:component>
diff --git a/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml b/rcp/org.argeo.cms.ui.rcp/OSGI-INF/cmsRcpServletFactory.xml
deleted file mode 100644 (file)
index a1f0b3a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="CMS RCP Servlet Factory">
-   <implementation class="org.argeo.cms.ui.rcp.servlet.CmsRcpServletFactory"/>
-   <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
-   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
-   <reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="static"/>
-</scr:component>
diff --git a/rcp/org.argeo.cms.ui.rcp/bnd.bnd b/rcp/org.argeo.cms.ui.rcp/bnd.bnd
deleted file mode 100644 (file)
index 91f0a8a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-Import-Package:\
-org.argeo.cms.auth,\
-org.eclipse.swt,\
-org.eclipse.swt.graphics,\
-org.w3c.css.sac,\
-*
-
-Service-Component:\
-OSGI-INF/cmsRcpDisplayFactory.xml,\
-OSGI-INF/cmsRcpServletFactory.xml
-
diff --git a/rcp/org.argeo.cms.ui.rcp/build.properties b/rcp/org.argeo.cms.ui.rcp/build.properties
deleted file mode 100644 (file)
index 5eef705..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-output.. = bin/
-bin.includes = META-INF/,\
-               .,\
-               OSGI-INF/,\
-               OSGI-INF/cmsRcpServletFactory.xml
-source.. = src/
diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java
deleted file mode 100644 (file)
index e25a9f7..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-package org.argeo.cms.ui.rcp;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.api.cms.CmsSession;
-import org.argeo.api.cms.ux.CmsImageManager;
-import org.argeo.api.cms.ux.CmsTheme;
-import org.argeo.api.cms.ux.CmsUi;
-import org.argeo.api.cms.ux.CmsView;
-import org.argeo.api.cms.ux.UxContext;
-import org.argeo.cms.swt.CmsSwtUtils;
-import org.eclipse.e4.ui.css.core.engine.CSSEngine;
-import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
-import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-
-/** Runs a {@link CmsApp} as an SWT desktop application. */
-@SuppressWarnings("restriction")
-public class CmsRcpApp implements CmsView {
-       private final static CmsLog log = CmsLog.getLog(CmsRcpApp.class);
-
-       // private BundleContext bundleContext =
-       // FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext();
-
-       private Shell shell;
-       private CmsApp cmsApp;
-
-       // CMS View
-       private String uid;
-       private LoginContext loginContext;
-
-       private EventAdmin eventAdmin;
-
-       private CSSEngine cssEngine;
-
-       private CmsUi ui;
-       // TODO make it configurable
-       private String uiName = "desktop";
-
-       public CmsRcpApp(String uiName) {
-               uid = UUID.randomUUID().toString();
-               this.uiName = uiName;
-       }
-
-       public void initRcpApp() {
-               Display display = Display.getCurrent();
-               shell = new Shell(display);
-               shell.setText("Argeo CMS");
-               Composite parent = shell;
-               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
-               CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this);
-
-               try {
-                       loginContext = new LoginContext(CmsAuth.SINGLE_USER.getLoginContextName());
-                       loginContext.login();
-               } catch (LoginException e) {
-                       throw new IllegalStateException("Could not log in.", e);
-               }
-               if (log.isDebugEnabled())
-                       log.debug("Logged in to desktop: " + loginContext.getSubject());
-
-               Subject.doAs(loginContext.getSubject(), (PrivilegedAction<Void>) () -> {
-
-                       // TODO factorise with web app
-                       parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
-                       ui = cmsApp.initUi(parent);
-                       if (ui instanceof Composite)
-                               ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
-                       // we need ui to be set before refresh so that CmsView can store UI context data
-                       // in it.
-                       cmsApp.refreshUi(ui, null);
-
-                       // Styling
-                       CmsTheme theme = CmsSwtUtils.getCmsTheme(parent);
-                       if (theme != null) {
-                               cssEngine = new CSSSWTEngineImpl(display);
-                               for (String path : theme.getSwtCssPaths()) {
-                                       try (InputStream in = theme.loadPath(path)) {
-                                               cssEngine.parseStyleSheet(in);
-                                       } catch (IOException e) {
-                                               throw new IllegalStateException("Cannot load stylesheet " + path, e);
-                                       }
-                               }
-                               cssEngine.setErrorHandler(new CSSErrorHandler() {
-                                       public void error(Exception e) {
-                                               log.error("SWT styling error: ", e);
-                                       }
-                               });
-                               applyStyles(shell);
-                       }
-                       shell.layout(true, true);
-
-                       shell.open();
-                       return null;
-               });
-       }
-
-       /*
-        * CMS VIEW
-        */
-
-       @Override
-       public String getUid() {
-               return uid;
-       }
-
-       @Override
-       public UxContext getUxContext() {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public void navigateTo(String state) {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public void authChange(LoginContext loginContext) {
-       }
-
-       @Override
-       public void logout() {
-               if (loginContext != null)
-                       try {
-                               loginContext.logout();
-                       } catch (LoginException e) {
-                               log.error("Cannot log out", e);
-                       }
-       }
-
-       @Override
-       public void exception(Throwable e) {
-               log.error("Unexpected exception in CMS RCP", e);
-       }
-
-       @Override
-       public CmsImageManager getImageManager() {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public CmsSession getCmsSession() {
-               CmsSession cmsSession = cmsApp.getCmsContext().getCmsSession(getSubject());
-               return cmsSession;
-       }
-
-       @Override
-       public Object getData(String key) {
-               if (ui != null) {
-                       return ui.getData(key);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       @Override
-       public void setData(String key, Object value) {
-               if (ui != null) {
-                       ui.setData(key, value);
-               } else {
-                       throw new IllegalStateException("UI is not initialized");
-               }
-       }
-
-       @Override
-       public boolean isAnonymous() {
-               return false;
-       }
-
-       @Override
-       public void applyStyles(Object node) {
-               if (cssEngine != null)
-                       cssEngine.applyStyles(node, true);
-       }
-
-       @Override
-       public void sendEvent(String topic, Map<String, Object> properties) {
-               if (properties == null)
-                       properties = new HashMap<>();
-               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
-                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
-                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
-               properties.put(CMS_VIEW_UID_PROPERTY, uid);
-               eventAdmin.sendEvent(new Event(topic, properties));
-       }
-
-       public <T> T doAs(PrivilegedAction<T> action) {
-               return Subject.doAs(getSubject(), action);
-       }
-
-       protected Subject getSubject() {
-               return loginContext.getSubject();
-       }
-
-       public Shell getShell() {
-               return shell;
-       }
-
-       /*
-        * DEPENDENCY INJECTION
-        */
-       public void setCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               this.cmsApp = cmsApp;
-       }
-
-       public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               this.cmsApp = null;
-       }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin = eventAdmin;
-       }
-
-}
diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java
deleted file mode 100644 (file)
index ec471c0..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.argeo.cms.ui.rcp;
-
-import java.nio.file.Path;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.util.OS;
-import org.eclipse.swt.widgets.Display;
-import org.osgi.service.event.EventAdmin;
-import org.eclipse.swt.events.DisposeListener;
-
-/** Creates the SWT {@link Display} in a dedicated thread. */
-public class CmsRcpDisplayFactory {
-       /** File name in a run directory */
-       private final static String ARGEO_RCP_URL = "argeo.rcp.url";
-
-       /** There is only one display in RCP mode */
-       private static Display display;
-
-       private CmsUiThread uiThread;
-
-       private boolean shutdown = false;
-
-       public void init() {
-               uiThread = new CmsUiThread();
-               uiThread.start();
-               while (display == null)
-                       try {
-                               Thread.sleep(100);
-                       } catch (InterruptedException e) {
-                               // silent
-                       }
-       }
-
-       public void destroy() {
-               shutdown = true;
-               display.wake();
-               try {
-                       uiThread.join();
-               } catch (InterruptedException e) {
-                       // silent
-               } finally {
-                       uiThread = null;
-               }
-       }
-
-       class CmsUiThread extends Thread {
-
-               public CmsUiThread() {
-                       super("CMS UI");
-               }
-
-               @Override
-               public void run() {
-                       display = Display.getDefault();
-                       display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
-                       display.setErrorHandler((e) -> e.printStackTrace());
-
-//                     for (String contextName : cmsApps.keySet()) {
-//                             openCmsApp(contextName);
-//                     }
-
-                       while (!shutdown) {
-                               if (!display.readAndDispatch())
-                                       display.sleep();
-                       }
-                       display.dispose();
-                       display = null;
-               }
-       }
-
-       public static Display getDisplay() {
-               return display;
-       }
-
-       public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName,
-                       DisposeListener disposeListener) {
-               CmsRcpDisplayFactory.getDisplay().syncExec(() -> {
-                       CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName);
-                       cmsRcpApp.setEventAdmin(eventAdmin);
-                       cmsRcpApp.setCmsApp(cmsApp, null);
-                       cmsRcpApp.initRcpApp();
-                       if (disposeListener != null)
-                               cmsRcpApp.getShell().addDisposeListener(disposeListener);
-               });
-       }
-
-       public static Path getUrlRunFile() {
-               return OS.getRunDir().resolve(CmsRcpDisplayFactory.ARGEO_RCP_URL);
-       }
-}
diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java
deleted file mode 100644 (file)
index f8aecd3..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.cms.ui.rcp.servlet;
-
-import java.io.IOException;
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.util.Objects;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
-import org.osgi.service.event.EventAdmin;
-
-/** Open the related app when called. */
-public class CmsRcpServlet extends HttpServlet {
-       private static final long serialVersionUID = -3944472431354848923L;
-       private final static Logger logger = System.getLogger(CmsRcpServlet.class.getName());
-
-       private CmsApp cmsApp;
-       private EventAdmin eventAdmin;
-
-       public CmsRcpServlet(EventAdmin eventAdmin, CmsApp cmsApp) {
-               Objects.requireNonNull(eventAdmin);
-               Objects.requireNonNull(cmsApp);
-               this.cmsApp = cmsApp;
-               this.eventAdmin = eventAdmin;
-       }
-
-       @Override
-       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-               String path = req.getPathInfo();
-               String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : "";
-               CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName, null);
-               logger.log(Level.DEBUG, "Opened RCP UI  " + uiName + " of  CMS App " + req.getServletPath());
-       }
-
-}
diff --git a/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/rcp/org.argeo.cms.ui.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java
deleted file mode 100644 (file)
index 7c24f87..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package org.argeo.cms.ui.rcp.servlet;
-
-import java.io.IOException;
-import java.lang.System.Logger;
-import java.lang.System.Logger.Level;
-import java.net.DatagramSocket;
-import java.net.ServerSocket;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-import javax.servlet.Servlet;
-
-import org.argeo.api.cms.CmsApp;
-import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.http.HttpService;
-
-/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */
-public class CmsRcpServletFactory {
-       private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName());
-
-       private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext();
-
-       private CompletableFuture<EventAdmin> eventAdmin = new CompletableFuture<>();
-
-       private Map<String, ServiceRegistration<Servlet>> registrations = Collections.synchronizedMap(new HashMap<>());
-
-       public void init() {
-
-       }
-
-       public void destroy() {
-               Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
-               try {
-                       if (Files.exists(runFile)) {
-                               Files.delete(runFile);
-                       }
-               } catch (IOException e) {
-                       logger.log(Level.ERROR, "Cannot delete " + runFile, e);
-               }
-       }
-
-       public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
-               if (contextName != null) {
-                       eventAdmin.thenAccept((eventAdmin) -> {
-                               CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp);
-                               Hashtable<String, String> serviceProperties = new Hashtable<>();
-                               serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*");
-                               ServiceRegistration<Servlet> sr = bundleContext.registerService(Servlet.class, servlet,
-                                               serviceProperties);
-                               registrations.put(contextName, sr);
-                       });
-               }
-       }
-
-       public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
-               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
-               if (contextName != null) {
-                       ServiceRegistration<Servlet> sr = registrations.get(contextName);
-                       sr.unregister();
-               }
-       }
-
-       public void setEventAdmin(EventAdmin eventAdmin) {
-               this.eventAdmin.complete(eventAdmin);
-       }
-
-       public void setHttpService(HttpService httpService, Map<String, Object> properties) {
-               Integer httpPort = Integer.parseInt(properties.get("http.port").toString());
-               String baseUrl = "http://localhost:" + httpPort + "/";
-               Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
-               try {
-                       if (!Files.exists(runFile)) {
-                               Files.createDirectories(runFile.getParent());
-                               // TODO give read permission only to the owner
-                               Files.createFile(runFile);
-                       } else {
-                               URI uri = URI.create(Files.readString(runFile));
-                               if (!httpPort.equals(uri.getPort()))
-                                       if (!isPortAvailable(uri.getPort())) {
-                                               throw new IllegalStateException("Another CMS is running on " + runFile);
-                                       } else {
-                                               logger.log(Level.WARNING,
-                                                               "Run file " + runFile + " found but port of " + uri + " is available. Overwriting...");
-                                       }
-                       }
-                       Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8);
-               } catch (IOException e) {
-                       throw new RuntimeException("Cannot write run file to " + runFile, e);
-               }
-               logger.log(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile);
-       }
-
-       protected boolean isPortAvailable(int port) {
-               ServerSocket ss = null;
-               DatagramSocket ds = null;
-               try {
-                       ss = new ServerSocket(port);
-                       ss.setReuseAddress(true);
-                       ds = new DatagramSocket(port);
-                       ds.setReuseAddress(true);
-                       return true;
-               } catch (IOException e) {
-               } finally {
-                       if (ds != null) {
-                               ds.close();
-                       }
-
-                       if (ss != null) {
-                               try {
-                                       ss.close();
-                               } catch (IOException e) {
-                                       /* should not be thrown */
-                               }
-                       }
-               }
-
-               return false;
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/.classpath b/rcp/org.argeo.swt.specific.rcp/.classpath
deleted file mode 100644 (file)
index 457b115..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-       <classpathentry kind="src" path="src" />
-       <classpathentry kind="con"
-               path="org.eclipse.pde.core.requiredPlugins" />
-       <classpathentry kind="con"
-               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
-       <classpathentry kind="output" path="bin" />
-</classpath>
diff --git a/rcp/org.argeo.swt.specific.rcp/.gitignore b/rcp/org.argeo.swt.specific.rcp/.gitignore
deleted file mode 100644 (file)
index 5e77890..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/target/
-/bin/
-*.log
\ No newline at end of file
diff --git a/rcp/org.argeo.swt.specific.rcp/.project b/rcp/org.argeo.swt.specific.rcp/.project
deleted file mode 100644 (file)
index c79ee3f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>org.argeo.swt.specific.rcp</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/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore b/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/rcp/org.argeo.swt.specific.rcp/bnd.bnd b/rcp/org.argeo.swt.specific.rcp/bnd.bnd
deleted file mode 100644 (file)
index bb88efd..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-Import-Package: \
-!java.*,\
-org.apache.commons.io,\
-org.eclipse.core.commands,\
-!org.eclipse.core.runtime,\
-!org.eclipse.ui.plugin,\
-org.eclipse.swt,\
-javax.servlet.http;version="[3,5)",\
-javax.servlet;version="[3,5)",\
-*
-
-Export-Package: org.argeo.*,\
-org.eclipse.rap.fileupload.*;version="3.10",\
-org.eclipse.rap.rwt.*;version="3.10"
-
-# Was !org.eclipse.core.commands,\ why ?
-
-#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin
-#Bundle-ActivationPolicy: lazy
-#Ignore-Package: org.eclipse.core.commands
\ No newline at end of file
diff --git a/rcp/org.argeo.swt.specific.rcp/build.properties b/rcp/org.argeo.swt.specific.rcp/build.properties
deleted file mode 100644 (file)
index c6b651a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java
deleted file mode 100644 (file)
index 0d9ce48..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.argeo.eclipse.ui.rcp.internal.rwt;
-
-import org.eclipse.rap.rwt.client.Client;
-import org.eclipse.rap.rwt.client.service.BrowserNavigation;
-import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
-import org.eclipse.rap.rwt.client.service.ClientService;
-import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
-
-public class RcpClient implements Client {
-
-       @Override
-       public <T extends ClientService> T getService(Class<T> type) {
-               if (type.isAssignableFrom(JavaScriptExecutor.class))
-                       return (T) javaScriptExecutor;
-               else if (type.isAssignableFrom(BrowserNavigation.class))
-                       return (T) browserNavigation;
-               else
-                       return null;
-       }
-
-       private JavaScriptExecutor javaScriptExecutor = new JavaScriptExecutor() {
-
-               @Override
-               public void execute(String code) {
-                       // TODO Auto-generated method stub
-
-               }
-       };
-       private BrowserNavigation browserNavigation = new BrowserNavigation() {
-
-               @Override
-               public void pushState(String state, String title) {
-                       // TODO Auto-generated method stub
-
-               }
-
-               @Override
-               public void addBrowserNavigationListener(
-                               BrowserNavigationListener listener) {
-                       // TODO Auto-generated method stub
-
-               }
-       };
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java
deleted file mode 100644 (file)
index 91109a9..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.argeo.eclipse.ui.rcp.internal.rwt;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.apache.commons.io.IOUtils;
-import org.eclipse.rap.rwt.service.ResourceManager;
-
-public class RcpResourceManager implements ResourceManager {
-       private Map<String, byte[]> register = Collections
-                       .synchronizedMap(new TreeMap<String, byte[]>());
-
-       @Override
-       public void register(String name, InputStream in) {
-               try {
-                       register.put(name, IOUtils.toByteArray(in));
-               } catch (IOException e) {
-                       throw new RuntimeException("Cannot register " + name, e);
-               }
-       }
-
-       @Override
-       public boolean unregister(String name) {
-               return register.remove(name) != null;
-       }
-
-       @Override
-       public InputStream getRegisteredContent(String name) {
-               return new ByteArrayInputStream(register.get(name));
-       }
-
-       @Override
-       public String getLocation(String name) {
-               return name;
-       }
-
-       @Override
-       public boolean isRegistered(String name) {
-               return register.containsKey(name);
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java
deleted file mode 100644 (file)
index 7fd2db1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.awt.BorderLayout;
-import java.awt.Frame;
-import java.awt.Graphics;
-import java.awt.image.BufferedImage;
-
-import javax.swing.JPanel;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.awt.SWT_AWT;
-import org.eclipse.swt.widgets.Composite;
-
-public class BufferedImageDisplay extends Composite {
-       private BufferedImage image;
-
-       public BufferedImageDisplay(Composite parent, int style) {
-               super(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND);
-               Frame frame = SWT_AWT.new_Frame(this);
-               frame.setLayout(new BorderLayout());
-               frame.add(new JPanel() {
-                       private static final long serialVersionUID = 8924410573598922364L;
-
-                       public void paintComponent(Graphics g) {
-                               super.paintComponent(g);
-                               if (image != null)
-                                       g.drawImage(image, 0, 0, this);
-                       }
-
-               }, BorderLayout.CENTER);
-               frame.setVisible(true);
-       }
-
-       public void setImage(BufferedImage image) {
-               this.image = image;
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java
deleted file mode 100644 (file)
index 0c5d346..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class CmsFileDialog extends FileDialog {
-       public CmsFileDialog(Shell parent, int style) {
-               super(parent, style);
-       }
-
-       public CmsFileDialog(Shell parent) {
-               super(parent);
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java
deleted file mode 100644 (file)
index 638859a..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.widgets.Composite;
-
-public class CmsFileUpload extends FileUpload {
-       public CmsFileUpload(Composite parent, int style) {
-               super(parent, style);
-       }
-
-       @Override
-       public void setText(String text) {
-               super.setText(text);
-       }
-
-       @Override
-       public String getFileName() {
-               return super.getFileName();
-       }
-
-       @Override
-       public String[] getFileNames() {
-               return super.getFileNames();
-       }
-
-       @Override
-       public void addSelectionListener(SelectionListener listener) {
-               super.addSelectionListener(listener);
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java
deleted file mode 100644 (file)
index fbb4fbf..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-/** RCP specific {@link NLS} to be extended */
-public class DefaultNLS {// extends NLS {
-//     public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin";
-//
-//     public DefaultNLS() {
-//             this(DEFAULT_BUNDLE_LOCATION);
-//     }
-//
-//     public DefaultNLS(String bundleName) {
-//             NLS.initializeMessages(bundleName, getClass());
-//     }
-}
\ No newline at end of file
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java
deleted file mode 100644 (file)
index ac862d7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-/** Constants which are specific to RWT.*/
-public interface EclipseUiConstants {
-       final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName";
-       final static String MARKUP_SUPPORT = null;
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
deleted file mode 100644 (file)
index d1acbcf..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import org.eclipse.jface.viewers.ColumnViewer;
-import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.swt.widgets.Widget;
-
-/** Static utilities to bridge differences between RCP and RAP */
-public class EclipseUiSpecificUtils {
-       private final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName";
-
-       public static void setStyleData(Widget widget, Object data) {
-               widget.setData(CSS_CLASS, data);
-       }
-
-       public static Object getStyleData(Widget widget) {
-               return widget.getData(CSS_CLASS);
-       }
-
-       public static void setMarkupData(Widget widget) {
-               // does nothing
-       }
-
-       public static void setMarkupValidationDisabledData(Widget widget) {
-               // does nothing
-       }
-
-       /**
-        * TootlTip support is supported for {@link ColumnViewer} in RCP
-        * 
-        * @see ColumnViewerToolTipSupport#enableFor(Viewer)
-        */
-       public static void enableToolTipSupport(Viewer viewer) {
-               if (viewer instanceof ColumnViewer)
-                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
-       }
-
-       private EclipseUiSpecificUtils() {
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java
deleted file mode 100644 (file)
index 524447e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-
-import org.eclipse.swt.dnd.DND;
-import org.eclipse.swt.dnd.DropTarget;
-import org.eclipse.swt.dnd.DropTargetAdapter;
-import org.eclipse.swt.dnd.DropTargetEvent;
-import org.eclipse.swt.dnd.FileTransfer;
-import org.eclipse.swt.dnd.Transfer;
-import org.eclipse.swt.widgets.Control;
-
-public class FileDropAdapter {
-
-       public void prepareDropTarget(Control control, DropTarget dropTarget) {
-               dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
-               dropTarget.addDropListener(new DropTargetAdapter() {
-                       @Override
-                       public void dropAccept(DropTargetEvent event) {
-                               if (!FileTransfer.getInstance().isSupportedType(event.currentDataType)) {
-                                       event.detail = DND.DROP_NONE;
-                               }
-                       }
-
-                       @Override
-                       public void drop(DropTargetEvent event) {
-                               handleFileDrop(control, event);
-                       }
-               });
-       }
-
-       public void handleFileDrop(Control control, DropTargetEvent event) {
-               String fileList[] = null;
-               FileTransfer ft = FileTransfer.getInstance();
-               if (ft.isSupportedType(event.currentDataType)) {
-                       fileList = (String[]) event.data;
-               }
-               System.out.println(Arrays.toString(fileList));
-       }
-
-       /** Executed in UI thread */
-       protected void processUpload(InputStream in, String fileName, String contentType) throws IOException {
-
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java b/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java
deleted file mode 100644 (file)
index 20163cf..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.argeo.eclipse.ui.specific;
-
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.swt.widgets.Display;
-
-/** Singleton class providing single sources infos about the UI context. */
-public class UiContext {
-
-       public static HttpServletRequest getHttpRequest() {
-               return null;
-       }
-
-       public static HttpServletResponse getHttpResponse() {
-               return null;
-       }
-
-       public static Locale getLocale() {
-               return Locale.getDefault();
-       }
-
-       public static void setLocale(Locale locale) {
-               Locale.setDefault(locale);
-       }
-
-       /** Can always be null */
-       @SuppressWarnings("unchecked")
-       public static <T> T getData(String key) {
-               Display display = getDisplay();
-               if (display == null)
-                       return null;
-               return (T) display.getData(key);
-       }
-
-       public static void setData(String key, Object value) {
-               Display display = getDisplay();
-               if (display == null)
-                       throw new IllegalStateException("Not display available");
-               display.setData(key, value);
-       }
-
-       private static Display getDisplay() {
-               return Display.getCurrent();
-       }
-
-       private UiContext() {
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java
deleted file mode 100644 (file)
index fbb36dd..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.eclipse.rap.fileupload;
-
-public interface FileDetails {
-       String getContentType();
-
-       long getContentLength();
-
-       String getFileName();
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java
deleted file mode 100644 (file)
index a745280..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.eclipse.rap.fileupload;
-
-import java.util.EventObject;
-
-public abstract class FileUploadEvent extends EventObject {
-
-       private static final long serialVersionUID = 1L;
-
-       protected FileUploadEvent(FileUploadHandler source) {
-               super(source);
-       }
-
-       public abstract FileDetails[] getFileDetails();
-
-       public abstract long getContentLength();
-
-       public abstract long getBytesRead();
-
-       public abstract Exception getException();
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java
deleted file mode 100644 (file)
index 7d89300..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011, 2012 EclipseSource and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    EclipseSource - initial API and implementation
- ******************************************************************************/
-package org.eclipse.rap.fileupload;
-
-/**
- * A file upload handler is used to accept file uploads from a client. After
- * creating a file upload handler, the server will accept file uploads to the
- * URL returned by <code>getUploadUrl()</code>. Upload listeners can be attached
- * to react on progress. When the upload has finished, a FileUploadHandler has
- * to be disposed of by calling its <code>dispose()</code> method.
- *
- * @noextend This class is not intended to be subclassed by clients.
- */
-public class FileUploadHandler {
-
-       public FileUploadHandler(FileUploadReceiver fileUploadReceiver) {
-       }
-
-       public void dispose() {
-
-       }
-
-       public void addUploadListener(FileUploadListener listener) {
-
-       }
-
-       public String getUploadUrl() {
-               return null;
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java
deleted file mode 100644 (file)
index b59fd39..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011, 2012 EclipseSource and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    EclipseSource - initial API and implementation
- ******************************************************************************/
-package org.eclipse.rap.fileupload;
-
-import org.eclipse.swt.widgets.Display;
-
-
-/**
- * Listener to react on progress and completion of a file upload.
- * <p>
- * <strong>Note:</strong> This listener will be called from a different thread than the UI thread.
- * Implementations must use {@link Display#asyncExec(Runnable)} to access the UI.
- * </p>
- *
- * @see FileUploadEvent
- */
-public interface FileUploadListener {
-
-  /**
-   * Called when new information about an in-progress upload is available.
-   *
-   * @param event event object that contains information about the uploaded file
-   * @see FileUploadEvent#getBytesRead()
-   */
-  void uploadProgress( FileUploadEvent event );
-
-  /**
-   * Called when a file upload has finished successfully.
-   *
-   * @param event event object that contains information about the uploaded file
-   * @see FileUploadEvent
-   */
-  void uploadFinished( FileUploadEvent event );
-
-  /**
-   * Called when a file upload failed.
-   *
-   * @param event event object that contains information about the uploaded file
-   * @see FileUploadEvent#getErrorMessage()
-   */
-  void uploadFailed( FileUploadEvent event );
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java
deleted file mode 100644 (file)
index 3f4cf47..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011, 2013 EclipseSource and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    EclipseSource - initial API and implementation
- ******************************************************************************/
-package org.eclipse.rap.fileupload;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-
-/**
- * Instances of this interface are responsible for reading and processing the data from a file
- * upload.
- */
-public abstract class FileUploadReceiver {
-
-  /**
-   * Reads and processes all data from the provided input stream.
-   *
-   * @param stream the stream to read from
-   * @param details the details of the uploaded file like file name, content-type and size
-   * @throws IOException if an input / output error occurs
-   */
-  public abstract void receive( InputStream stream, FileDetails details ) throws IOException;
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java
deleted file mode 100644 (file)
index 1688594..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.eclipse.rap.rwt;
-
-import java.util.Locale;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.argeo.eclipse.ui.rcp.internal.rwt.RcpClient;
-import org.argeo.eclipse.ui.rcp.internal.rwt.RcpResourceManager;
-import org.eclipse.rap.rwt.client.Client;
-import org.eclipse.rap.rwt.service.ResourceManager;
-
-public class RWT {
-       public final static String CUSTOM_VARIANT = "argeo-rcp:CUSTOM_VARIANT";
-       public final static String MARKUP_ENABLED = "argeo-rcp:MARKUP_ENABLED";
-       public static final String TOOLTIP_MARKUP_ENABLED = "argeo-rcp:TOOLTIP_MARKUP_ENABLED";
-       public final static String CUSTOM_ITEM_HEIGHT = "argeo-rcp:CUSTOM_ITEM_HEIGHT";
-       public final static String ACTIVE_KEYS = "argeo-rcp:ACTIVE_KEYS";
-       public final static String CANCEL_KEYS = "argeo-rcp:CANCEL_KEYS";
-       public final static String DEFAULT_THEME_ID  = "argeo-rcp:DEFAULT_THEME_ID";
-
-       public final static int HYPERLINK = 0;
-
-       private static Locale locale = Locale.getDefault();
-       private static RcpClient client = new RcpClient();
-       private static ResourceManager resourceManager = new RcpResourceManager();
-       static {
-
-       }
-
-       public static Locale getLocale() {
-               return locale;
-       }
-
-       public static HttpServletRequest getRequest() {
-               return null;
-       }
-
-       public static ResourceManager getResourceManager() {
-               return resourceManager;
-       }
-
-       public static Client getClient() {
-               return client;
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java
deleted file mode 100644 (file)
index 6e30aa6..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.eclipse.rap.rwt;
-
-public class SingletonUtil {
-       public static <T> T getSessionInstance(Class<T> clss) {
-               return null;
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java
deleted file mode 100644 (file)
index 980a818..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-public abstract class AbstractEntryPoint implements EntryPoint {
-       private Display display;
-       private Shell shell;
-
-       protected Shell createShell(Display display) {
-               return new Shell(display);
-       }
-
-       protected void createContents(Composite parent) {
-
-       }
-
-       public int createUI() {
-               display = new Display();
-               shell = createShell(display);
-               shell.setLayout(new GridLayout(1, false));
-               createContents(shell);
-               if (shell.getMaximized()) {
-                       shell.layout();
-               } else {
-                       shell.pack();
-               }
-               shell.open();
-               while (!shell.isDisposed()) {
-                       if (!display.readAndDispatch()) {
-                               display.sleep();
-                       }
-               }
-               display.dispose();
-               return 0;
-       }
-
-       protected Shell getShell() {
-               return shell;
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java
deleted file mode 100644 (file)
index 6cb5f29..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-import java.util.Map;
-
-import org.eclipse.rap.rwt.service.ResourceLoader;
-
-public interface Application {
-       public static enum OperationMode {
-               JEE_COMPATIBILITY, SWT_COMPATIBILITY,
-       }
-
-       void setOperationMode(OperationMode operationMode);
-
-       void addResource(String name, ResourceLoader resourceLoader);
-
-       void setExceptionHandler(ExceptionHandler exceptionHandler);
-
-       void addEntryPoint(String path, EntryPointFactory entryPointFactory,
-                       Map<String, String> properties);
-
-       void addEntryPoint(String path, Class<? extends EntryPoint> entryPoint,
-                       Map<String, String> properties);
-
-       void addStyleSheet(String themeId, String styleSheetLocation,
-                       ResourceLoader resourceLoader);
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java
deleted file mode 100644 (file)
index 961ad70..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-public interface ApplicationConfiguration {
-       void configure(Application application);
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java
deleted file mode 100644 (file)
index c0d559a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-public interface EntryPoint {
-       int createUI();
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java
deleted file mode 100644 (file)
index d5b24d8..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-public interface EntryPointFactory {
-       public EntryPoint create();
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java
deleted file mode 100644 (file)
index 13daf21..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.application;
-
-public interface ExceptionHandler {
-       public void handleException(Throwable throwable);
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java
deleted file mode 100644 (file)
index 934feae..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.eclipse.rap.rwt.client;
-
-import java.io.Serializable;
-
-import org.eclipse.rap.rwt.client.service.ClientService;
-
-public interface Client extends Serializable {
-
-  /**
-   * Returns this client's implementation of a given service, if available.
-   *
-   * @param type the type of the requested service, must be a subtype of ClientService
-   * @return the requested service if provided by this client, otherwise <code>null</code>
-   * @see ClientService
-   */
-  <T extends ClientService> T getService( Class<T> type );
-
-}
\ No newline at end of file
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java
deleted file mode 100644 (file)
index 1f19bdd..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.eclipse.rap.rwt.client;
-
-public interface WebClient {
-       public final static String FAVICON = "rcp:FAVICON";
-       public final static String PAGE_TITLE = "rcp:PAGE_TITLE";
-       public final static String BODY_HTML = "rcp:BODY_HTML";
-       public final static String THEME_ID = "rcp:THEME_ID";
-       public final static String HEAD_HTML = "rcp:HEAD_HTML";
-       public final static String PAGE_OVERFLOW = "rcp:PAGE_OVERFLOW";
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java
deleted file mode 100644 (file)
index ffba4e4..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.eclipse.rap.rwt.client.service;
-
-public interface BrowserNavigation extends ClientService {
-       void pushState(String state, String title);
-
-       void addBrowserNavigationListener(BrowserNavigationListener listener);
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java
deleted file mode 100644 (file)
index 3e1b3eb..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.eclipse.rap.rwt.client.service;
-
-public class BrowserNavigationEvent {
-       private String state;
-
-       public String getState() {
-               return state;
-       }
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java
deleted file mode 100644 (file)
index 8319c03..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.client.service;
-
-public interface BrowserNavigationListener {
-       public void navigated(BrowserNavigationEvent event);
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java
deleted file mode 100644 (file)
index 9f479d1..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.eclipse.rap.rwt.client.service;
-
-import java.io.Serializable;
-
-public interface ClientService extends Serializable {
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java
deleted file mode 100644 (file)
index 6c44c72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.eclipse.rap.rwt.client.service;
-
-public interface JavaScriptExecutor extends ClientService {
-       public void execute( String code );
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java
deleted file mode 100644 (file)
index 9dae811..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2012 EclipseSource and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *    EclipseSource - initial API and implementation
- ******************************************************************************/
-package org.eclipse.rap.rwt.client.service;
-
-/**
- * The UrlLauncher service allows loading an URL in an external window, application or save dialog.
- *
- * @since 2.0
- * @noimplement This interface is not intended to be implemented by clients.
- */
-public interface UrlLauncher extends ClientService {
-
-  /**
-   * Opens the given URL.
-   *
-   * Any HTTP URL or relative URL will be opened in a new window.
-   * Modern browser may block any attempt to open new windows, but will usually prompt the user to
-   * accept or ignore. Even if accepted, the decision may be applied to only this attempt, or only
-   * to future attempts. It could also trigger a document reload, causing a session restart.
-   *
-   * Non-HTTP URLs like "mailto" will not create a new browser window, but require the client
-   * to have a matching protocol handler registered.
-   *
-   * @param url the URL to open
-   */
-  void openURL( String url );
-
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java
deleted file mode 100644 (file)
index 7e7116c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.eclipse.rap.rwt.service;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public interface ResourceLoader {
-       public InputStream getResourceAsStream(String resourceName)
-                       throws IOException;
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java
deleted file mode 100644 (file)
index c3379ea..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.eclipse.rap.rwt.service;
-
-import java.io.InputStream;
-
-public interface ResourceManager {
-       public void register(String name, InputStream in);
-
-       boolean unregister(String name);
-
-       public InputStream getRegisteredContent(String name);
-
-       public String getLocation(String name);
-
-       public boolean isRegistered(String name);
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java
deleted file mode 100644 (file)
index bed194f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.eclipse.rap.rwt.service;
-
-/** Mock, does nothing as this is irrelevant for RCP. */
-public class ServerPushSession {
-       public void start() {
-
-       }
-
-       public void stop() {
-
-       }
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java
deleted file mode 100644 (file)
index b2a2005..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.eclipse.rap.rwt.widgets;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Widget;
-
-public class DropDown {
-       private boolean visible=false;
-
-       public DropDown(Widget parent, int style) {
-               // FIXME implement a shell
-       }
-
-       public DropDown(Widget parent) {
-               this(parent, SWT.NONE);
-       }
-
-       public void setVisible(boolean visible) {
-               this.visible = visible;
-       }
-
-       public boolean isVisible() {
-               return visible;
-       }
-       
-       public void setItems( String[] items ) {
-               
-       }
-       
-       public void setSelectionIndex( int selection ) {
-               
-       }
-       
-}
diff --git a/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java b/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java
deleted file mode 100644 (file)
index cbf1449..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.eclipse.rap.rwt.widgets;
-
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
-
-public class FileUpload extends Composite {
-
-       public FileUpload(Composite parent, int style) {
-               super(parent, style);
-       }
-
-       public void addSelectionListener(SelectionListener listener) {
-
-       }
-
-       public void submit(String url) {
-
-       }
-
-       public void setImage(Image image) {
-
-       }
-
-       public void setText(String text) {
-
-       }
-
-       public String getFileName() {
-               return null;
-       }
-
-       public String[] getFileNames() {
-               return null;
-       }
-
-}
diff --git a/swt/org.argeo.cms.e4/.classpath b/swt/org.argeo.cms.e4/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /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-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/org.argeo.cms.e4/.project b/swt/org.argeo.cms.e4/.project
new file mode 100644 (file)
index 0000000..0c04069
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.e4</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/swt/org.argeo.cms.e4/META-INF/.gitignore b/swt/org.argeo.cms.e4/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml b/swt/org.argeo.cms.e4/OSGI-INF/defaultCallbackHandler.xml
new file mode 100644 (file)
index 0000000..fcd3ae5
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Default CallbackHandler">
+   <implementation class="org.argeo.cms.swt.auth.DynamicCallbackHandler"/>
+   <service>
+      <provide interface="javax.security.auth.callback.CallbackHandler"/>
+   </service>
+</scr:component>
diff --git a/swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml b/swt/org.argeo.cms.e4/OSGI-INF/homeRepository.xml
new file mode 100644 (file)
index 0000000..65690f2
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="Home Repository">
+   <implementation class="org.argeo.cms.e4.OsgiFilterContextFunction"/>
+   <property name="service.context.key" type="String" value="(cn=home)"/>
+   <service>
+      <provide interface="org.eclipse.e4.core.contexts.IContextFunction"/>
+   </service>
+</scr:component>
diff --git a/swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/swt/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml
new file mode 100644 (file)
index 0000000..cc7087b
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="User Admin Wrapper">
+   <implementation class="org.argeo.cms.e4.users.UserAdminWrapper"/>
+   <reference bind="setUserTransaction" cardinality="1..1" interface="org.argeo.util.transaction.WorkTransaction" name="UserTransaction" policy="static"/>
+   <reference bind="setUserAdmin" cardinality="1..1" interface="org.osgi.service.useradmin.UserAdmin" name="UserAdmin" policy="static"/>
+   <service>
+      <provide interface="org.argeo.cms.e4.users.UserAdminWrapper"/>
+   </service>
+   <reference bind="addUserDirectory" cardinality="0..n" interface="org.argeo.osgi.useradmin.UserDirectory" name="UserDirectory" policy="static" unbind="removeUserDirectory"/>
+</scr:component>
diff --git a/swt/org.argeo.cms.e4/bnd.bnd b/swt/org.argeo.cms.e4/bnd.bnd
new file mode 100644 (file)
index 0000000..8026f98
--- /dev/null
@@ -0,0 +1,19 @@
+Service-Component: OSGI-INF/homeRepository.xml,\
+OSGI-INF/userAdminWrapper.xml,\
+OSGI-INF/defaultCallbackHandler.xml
+Bundle-ActivationPolicy: lazy
+
+Import-Package: \
+org.argeo.api.acr,\
+org.eclipse.swt,\
+org.eclipse.swt.widgets;version="0.0.0",\
+org.eclipse.e4.ui.model.application.ui,\
+org.eclipse.e4.ui.model.application,\
+javax.jcr.nodetype,\
+org.argeo.cms,\
+org.eclipse.core.commands.common,\
+org.eclipse.jface.window,\
+org.argeo.cms.swt.auth,\
+org.apache.jackrabbit.*;version="[2,3)",\
+javax.servlet.*;version="[3,5)",\
+*
diff --git a/swt/org.argeo.cms.e4/build.properties b/swt/org.argeo.cms.e4/build.properties
new file mode 100644 (file)
index 0000000..e46a7ba
--- /dev/null
@@ -0,0 +1,9 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               OSGI-INF/,\
+               .,\
+               OSGI-INF/homeRepository.xml,\
+               OSGI-INF/userAdminWrapper.xml,\
+               OSGI-INF/defaultCallbackHandler.xml,\
+               e4xmi/cms-demo.e4xmi
+source.. = src/
diff --git a/swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi b/swt/org.argeo.cms.e4/e4xmi/cms-devops.e4xmi
new file mode 100644 (file)
index 0000000..89bcc37
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="ASCII"?>
+<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:advanced="http://www.eclipse.org/ui/2010/UIModel/application/ui/advanced" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_XqkCQKknEeObFrG_clJBYA" elementId="">
+  <children xsi:type="basic:TrimmedWindow" xmi:id="_Zdy6cKknEeObFrG_clJBYA" elementId="org.argeo.cms.e4.apps.admin.trimmedwindow.0" label="" x="10" y="10" width="500" height="500">
+    <persistedState key="styleOverride" value="8"/>
+    <tags>shellMaximized</tags>
+    <tags>auth.cn=admin,ou=roles,ou=node</tags>
+    <children xsi:type="advanced:PerspectiveStack" xmi:id="_jXVqsCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.perspectivestack.0" selectedElement="_xOVlsDvOEeiF1foPJZSZkw">
+      <children xsi:type="advanced:Perspective" xmi:id="_xOVlsDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.perspective.users" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png">
+        <tags>auth.cn=admin,ou=roles,ou=node</tags>
+        <children xsi:type="basic:PartSashContainer" xmi:id="_1tQoEDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partsashcontainer.2" horizontal="true">
+          <children xsi:type="basic:PartStack" xmi:id="_vtbKkDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.4" containerData="4000" selectedElement="_9gukYDvOEeiF1foPJZSZkw">
+            <children xsi:type="basic:Part" xmi:id="_9gukYDvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.part.users" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.UsersView" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person.png">
+              <handlers xmi:id="_0mN68DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.4" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.NewUser" command="_uL5i4DvjEeiF1foPJZSZkw"/>
+              <handlers xmi:id="_ODLdgDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.5" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.DeleteUsers" command="_xkcMADvjEeiF1foPJZSZkw"/>
+              <toolbar xmi:id="_jLWmkDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.toolbar.1">
+                <children xsi:type="menu:HandledToolItem" xmi:id="_jy_OUDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.new" label="New" iconURI="platform:/plugin/org.argeo.cms.swt/icons/add.png" command="_uL5i4DvjEeiF1foPJZSZkw"/>
+                <children xsi:type="menu:HandledToolItem" xmi:id="_9qszMDvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/delete.png" command="_xkcMADvjEeiF1foPJZSZkw"/>
+              </toolbar>
+            </children>
+          </children>
+          <children xsi:type="basic:PartStack" xmi:id="__g1a8DvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.3" containerData="4000">
+            <tags>usersEditorArea</tags>
+          </children>
+          <children xsi:type="basic:PartStack" xmi:id="_-mFn8DvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partstack.5" containerData="2000">
+            <children xsi:type="basic:Part" xmi:id="_6etk4DvOEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.part.groups" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.GroupsView" label="Groups" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png">
+              <handlers xmi:id="_cmShoDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.6" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.NewGroup" command="_uL5i4DvjEeiF1foPJZSZkw"/>
+              <handlers xmi:id="_fbYfcDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.7" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.handlers.DeleteGroups" command="_xkcMADvjEeiF1foPJZSZkw"/>
+              <toolbar xmi:id="_Us0rADvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.toolbar.2">
+                <children xsi:type="menu:HandledToolItem" xmi:id="_VQTLgDvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.new" label="New" iconURI="platform:/plugin/org.argeo.cms.swt/icons/add.png" command="_uL5i4DvjEeiF1foPJZSZkw"/>
+                <children xsi:type="menu:HandledToolItem" xmi:id="_XfME8DvkEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/delete.png" command="_xkcMADvjEeiF1foPJZSZkw"/>
+              </toolbar>
+            </children>
+          </children>
+        </children>
+      </children>
+      <children xsi:type="advanced:Perspective" xmi:id="_jvjWYCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.perspective.data" label="Data" iconURI="platform:/plugin/org.argeo.cms.swt/icons/nodes.gif">
+        <children xsi:type="basic:PartSashContainer" xmi:id="_h3tvMCkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partsashcontainer.0" selectedElement="_0B9SECkxEein5vuhpK-Dew" horizontal="true">
+          <children xsi:type="basic:PartStack" xmi:id="_0B9SECkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.0" containerData="4000" selectedElement="_WAjPkCkTEein5vuhpK-Dew">
+            <children xsi:type="basic:Part" xmi:id="_WAjPkCkTEein5vuhpK-Dew" elementId="org.argeo.cms.e4.jcrbrowser" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR" iconURI="platform:/plugin/org.argeo.cms.swt/icons/browser.gif">
+              <menus xsi:type="menu:PopupMenu" xmi:id="_eXiUECqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.popupmenu.nodeViewer">
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_GVeO8CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.swt/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_fU238CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_U4o9cCqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.swt/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_Ncxo0CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.remove" label="Remove" iconURI="platform:/plugin/org.argeo.cms.swt/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+              </menus>
+              <menus xmi:id="_oRg_ACqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.menu.0">
+                <tags>ViewMenu</tags>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_yJR8ECqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.swt/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_o6HQECqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_5D7aACqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.swt/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_7rR2wCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.swt/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+                <children xsi:type="menu:HandledMenuItem" xmi:id="_XsHLgFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledmenuitem.0" iconURI="platform:/plugin/org.argeo.cms.swt/icons/addRepo.gif" command="_ZWpasFgQEeiknZQLx-vtnA"/>
+              </menus>
+            </children>
+          </children>
+          <children xsi:type="basic:PartStack" xmi:id="_mHrEUCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.1" containerData="6000">
+            <tags>dataExplorer</tags>
+          </children>
+        </children>
+      </children>
+      <children xsi:type="advanced:Perspective" xmi:id="_u5ZakFhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.perspective.monitoring" label="Monitoring" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif">
+        <children xsi:type="basic:PartStack" xmi:id="_7i7t8FhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.partstack.6">
+          <children xsi:type="basic:Part" xmi:id="_Z-3cMFhbEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.osgiConfigurations" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.OsgiConfigurationsView" label="OSGi Configurations" iconURI="platform:/plugin/org.argeo.cms.swt/icons/node.gif"/>
+          <children xsi:type="basic:Part" xmi:id="_8dM90FhJEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.cmsSessions" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.CmsSessionsView" label="CMS Sessions" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person-logged-in.png"/>
+          <children xsi:type="basic:Part" xmi:id="_KqRZIFhNEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.modules" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.ModulesView" label="Modules" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif"/>
+          <children xsi:type="basic:Part" xmi:id="_dXtIoFhNEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.part.bundles" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.monitoring.BundlesView" label="Bundles" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif"/>
+        </children>
+      </children>
+      <children xsi:type="advanced:Perspective" xmi:id="_ABK2ADsNEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.perspective.files" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif">
+        <children xsi:type="basic:PartSashContainer" xmi:id="_FPimEDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.partsashcontainer.1" horizontal="true">
+          <children xsi:type="basic:PartStack" xmi:id="_H93NgDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.partstack.2" containerData="4000">
+            <children xsi:type="basic:Part" xmi:id="_Izxh0DsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.part.files" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.files.NodeFsBrowserView" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif"/>
+          </children>
+          <children xsi:type="basic:Part" xmi:id="_TMqBMDsSEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.part.0" containerData="6000"/>
+        </children>
+      </children>
+    </children>
+    <handlers xmi:id="_Vwax0DvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handler.8" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.OpenPerspective" command="_AF1UsDvrEeiF1foPJZSZkw"/>
+    <trimBars xmi:id="_euVxMCk2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.trimbar.0" side="Left">
+      <children xsi:type="menu:ToolBar" xmi:id="_fotHsCk2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.toolbar.0">
+        <children xsi:type="menu:HandledToolItem" xmi:id="_jCSQgDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.users" label="Users" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png" command="_AF1UsDvrEeiF1foPJZSZkw">
+          <tags>auth.cn=admin,ou=roles,ou=node</tags>
+          <parameters xmi:id="_lu_uYDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.2" name="perspectiveId" value="org.argeo.cms.e4.perspective.users"/>
+        </children>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_jfUM4Ck2Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.handledtoolitem.test" label="Data" iconURI="platform:/plugin/org.argeo.cms.swt/icons/nodes.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
+          <parameters xmi:id="_KDlXQDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.0" name="perspectiveId" value="org.argeo.cms.e4.perspective.data"/>
+        </children>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_dhv80FhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledtoolitem.monitoring" label="Monitoring" iconURI="platform:/plugin/org.argeo.cms.swt/icons/bundles.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
+          <parameters xmi:id="_kjN0cFhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.parameter.3" name="perspectiveId" value="org.argeo.cms.e4.perspective.monitoring"/>
+        </children>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_b0OHUDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.handledtoolitem.files" label="Files" iconURI="platform:/plugin/org.argeo.cms.swt/icons/file.gif" command="_AF1UsDvrEeiF1foPJZSZkw">
+          <parameters xmi:id="_fXvRYDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.parameter.1" name="perspectiveId" value="org.argeo.cms.e4.perspective.files"/>
+        </children>
+        <children xsi:type="menu:ToolBarSeparator" xmi:id="_wuoL8FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.toolbarseparator.0"/>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_2v8DkFhKEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledtoolitem.logout" label="Log out" iconURI="platform:/plugin/org.argeo.cms.swt/icons/logout.png" command="_PsWd0FhLEeiknZQLx-vtnA"/>
+      </children>
+    </trimBars>
+  </children>
+  <handlers xmi:id="_Xp-P4CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.0" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddFolderNode" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_jbnNwCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.1" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.DeleteNodes" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_loxB0CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.2" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.Refresh" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_omPfkCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.3" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.RenameNode" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_dUg-cFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.9" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddRemoteRepository" command="_ZWpasFgQEeiknZQLx-vtnA"/>
+  <handlers xmi:id="_RQyFAFhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.10" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.CloseWorkbench" command="_PsWd0FhLEeiknZQLx-vtnA"/>
+  <descriptors xmi:id="_XzfoMCqlEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" label="Node Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/node.gif" allowMultiple="true" category="dataExplorer" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
+  <descriptors xmi:id="_sAdNwDvdEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partdescriptor.userEditor" label="User Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/person.png" allowMultiple="true" category="usersEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.UserEditor"/>
+  <descriptors xmi:id="_5nK7EDvdEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.partdescriptor.groupEditor" label="Group Editor" iconURI="platform:/plugin/org.argeo.cms.swt/icons/group.png" allowMultiple="true" category="usersEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.users.GroupEditor"/>
+  <commands xmi:id="_RgE5cCqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.addFolderNode" commandName="Add folder node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_ChJ-4CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.deleteNodes" commandName="Delete nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_TOKHsCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.refreshNodes" commandName="Refresh nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_ZrcUMCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.renameNode" commandName="Rename node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_uL5i4DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.add" commandName="Add"/>
+  <commands xmi:id="_xkcMADvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.delete" commandName="Delete"/>
+  <commands xmi:id="_AF1UsDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.openPerspective" commandName="Open Perspective">
+    <parameters xmi:id="_F3WAUDvrEeiF1foPJZSZkw" elementId="perspectiveId" name="Perspective Id" optional="false"/>
+  </commands>
+  <commands xmi:id="_ZWpasFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.addRemoteRepository" commandName="Add Remote Repository"/>
+  <commands xmi:id="_PsWd0FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.logout" commandName="Log out"/>
+  <addons xmi:id="_XqkCQaknEeObFrG_clJBYA" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
+  <addons xmi:id="_XqkCQqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
+  <addons xmi:id="_XqkCQ6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
+  <addons xmi:id="_XqkCRKknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
+  <addons xmi:id="_XqkCRaknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
+  <addons xmi:id="_XqkCRqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
+  <addons xmi:id="_XqkCR6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
+  <addons xmi:id="_8VnK8OdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.locale" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.LocaleAddon"/>
+  <addons xmi:id="_-xeJYOdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.auth" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.AuthAddon"/>
+  <categories xmi:id="_MDkwUCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.category.jcrBrowser" name="JCR Browser"/>
+</application:Application>
diff --git a/swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi b/swt/org.argeo.cms.e4/e4xmi/cms-ego.e4xmi
new file mode 100644 (file)
index 0000000..ef659fc
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="ASCII"?>
+<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_XqkCQKknEeObFrG_clJBYA" elementId="">
+  <children xsi:type="basic:TrimmedWindow" xmi:id="_Zdy6cKknEeObFrG_clJBYA" elementId="org.argeo.cms.e4.apps.admin.trimmedwindow.0" label="" x="10" y="10" width="500" height="500">
+    <persistedState key="styleOverride" value="8"/>
+    <tags>shellMaximized</tags>
+    <tags>auth.cn=user,ou=roles,ou=node</tags>
+    <children xsi:type="basic:PartSashContainer" xmi:id="_h3tvMCkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partsashcontainer.0" selectedElement="_0B9SECkxEein5vuhpK-Dew" horizontal="true">
+      <children xsi:type="basic:PartStack" xmi:id="_0B9SECkxEein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.0" containerData="4000" selectedElement="_WAjPkCkTEein5vuhpK-Dew">
+        <children xsi:type="basic:Part" xmi:id="_WAjPkCkTEein5vuhpK-Dew" elementId="org.argeo.cms.e4.jcrbrowser" containerData="" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/browser.gif">
+          <menus xsi:type="menu:PopupMenu" xmi:id="_eXiUECqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.popupmenu.nodeViewer">
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_GVeO8CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_fU238CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_U4o9cCqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_Ncxo0CqhEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.remove" label="Remove" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+          </menus>
+          <menus xmi:id="_oRg_ACqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.menu.0">
+            <tags>ViewMenu</tags>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_yJR8ECqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.refresh" label="Refresh" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/refresh.png" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_o6HQECqTEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.addfoldernode" label="Add folder" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addFolder.gif" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_5D7aACqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.rename" label="Rename" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/rename.gif" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_7rR2wCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handledmenuitem.delete" label="Delete" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/remove.gif" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+            <children xsi:type="menu:HandledMenuItem" xmi:id="_XsHLgFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handledmenuitem.0" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/addRepo.gif" command="_ZWpasFgQEeiknZQLx-vtnA"/>
+          </menus>
+        </children>
+      </children>
+      <children xsi:type="basic:PartStack" xmi:id="_mHrEUCk4Eein5vuhpK-Dew" elementId="org.argeo.cms.e4.partstack.1" containerData="6000">
+        <tags>dataExplorer</tags>
+        <children xsi:type="basic:Part" xmi:id="_LyT80MKKEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.part.egoDashboard" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.parts.EgoDashboard" label="" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/home.png">
+          <toolbar xmi:id="_Ut8wMMKMEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.toolbar.0">
+            <children xsi:type="menu:HandledToolItem" xmi:id="_nElwUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.cms.e4.handledtoolitem.changepassword" label="Change password" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/actions/edit.png" command="_jEjCUMKMEeq1Ytjq4ALs6g"/>
+            <children xsi:type="menu:HandledToolItem" xmi:id="_WAD4UMKMEeqaPNgZ5fEKYw" elementId="org.argeo.cms.e4.handledtoolitem.logout" label="Log out" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/logout.png" command="_PsWd0FhLEeiknZQLx-vtnA"/>
+          </toolbar>
+        </children>
+      </children>
+    </children>
+  </children>
+  <handlers xmi:id="_Xp-P4CqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.0" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddFolderNode" command="_RgE5cCqREeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_jbnNwCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.1" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.DeleteNodes" command="_ChJ-4CqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_loxB0CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.2" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.Refresh" command="_TOKHsCqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_omPfkCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.handler.3" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.RenameNode" command="_ZrcUMCqYEeidr6NYQH6GbQ"/>
+  <handlers xmi:id="_dUg-cFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.9" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.handlers.AddRemoteRepository" command="_ZWpasFgQEeiknZQLx-vtnA"/>
+  <handlers xmi:id="_RQyFAFhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.handler.logout" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.CloseWorkbench" command="_PsWd0FhLEeiknZQLx-vtnA"/>
+  <handlers xmi:id="_lN4GUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.suite.e4.handler.changePassword" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangePassword" command="_jEjCUMKMEeq1Ytjq4ALs6g"/>
+  <descriptors xmi:id="_XzfoMCqlEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" label="Node Editor" iconURI="platform:/plugin/org.argeo.cms.ui.theme/icons/node.gif" allowMultiple="true" category="dataExplorer" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
+  <commands xmi:id="_RgE5cCqREeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.addFolderNode" commandName="Add folder node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_ChJ-4CqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.deleteNodes" commandName="Delete nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_TOKHsCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.refreshNodes" commandName="Refresh nodes" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_ZrcUMCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.command.renameNode" commandName="Rename node" category="_MDkwUCqYEeidr6NYQH6GbQ"/>
+  <commands xmi:id="_uL5i4DvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.add" commandName="Add"/>
+  <commands xmi:id="_xkcMADvjEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.delete" commandName="Delete"/>
+  <commands xmi:id="_AF1UsDvrEeiF1foPJZSZkw" elementId="org.argeo.cms.e4.command.openPerspective" commandName="Open Perspective">
+    <parameters xmi:id="_F3WAUDvrEeiF1foPJZSZkw" elementId="perspectiveId" name="Perspective Id" optional="false"/>
+  </commands>
+  <commands xmi:id="_ZWpasFgQEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.addRemoteRepository" commandName="Add Remote Repository"/>
+  <commands xmi:id="_PsWd0FhLEeiknZQLx-vtnA" elementId="org.argeo.cms.e4.command.logout" commandName="Log out"/>
+  <commands xmi:id="_jEjCUMKMEeq1Ytjq4ALs6g" elementId="org.argeo.cms.e4.command.changePassword" commandName="Change Password"/>
+  <addons xmi:id="_XqkCQaknEeObFrG_clJBYA" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
+  <addons xmi:id="_XqkCQqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
+  <addons xmi:id="_XqkCQ6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
+  <addons xmi:id="_XqkCRKknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
+  <addons xmi:id="_XqkCRaknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
+  <addons xmi:id="_XqkCRqknEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
+  <addons xmi:id="_XqkCR6knEeObFrG_clJBYA" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
+  <addons xmi:id="_8VnK8OdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.locale" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.LocaleAddon"/>
+  <addons xmi:id="_-xeJYOdKEeijEOqYKRSeoQ" elementId="org.argeo.cms.e4.addon.auth" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.addons.AuthAddon"/>
+  <categories xmi:id="_MDkwUCqYEeidr6NYQH6GbQ" elementId="org.argeo.cms.e4.category.jcrBrowser" name="JCR Browser"/>
+</application:Application>
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/CmsE4Utils.java
new file mode 100644 (file)
index 0000000..a997de7
--- /dev/null
@@ -0,0 +1,77 @@
+package org.argeo.cms.e4;
+
+import java.util.List;
+
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.commands.MCommand;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
+import org.eclipse.e4.ui.model.application.ui.menu.MHandledMenuItem;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/** Static utilities simplifying recurring Eclipse 4 patterns. */
+public class CmsE4Utils {
+       /** Open an editor based on its id. */
+       public static void openEditor(EPartService partService, String editorId, String key, String state) {
+               for (MPart part : partService.getParts()) {
+                       String id = part.getPersistedState().get(key);
+                       if (id != null && state.equals(id)) {
+                               partService.showPart(part, PartState.ACTIVATE);
+                               return;
+                       }
+               }
+
+               // new part
+               MPart part = partService.createPart(editorId);
+               if (part == null)
+                       throw new CmsException("No editor found with id " + editorId);
+               part.getPersistedState().put(key, state);
+               partService.showPart(part, PartState.ACTIVATE);
+       }
+
+       /** Dynamically creates an handled menu item from a command ID. */
+       public static MHandledMenuItem createHandledMenuItem(EModelService modelService, MApplication app,
+                       String commandId) {
+               MCommand command = findCommand(modelService, app, commandId);
+               if (command == null)
+                       return null;
+               MHandledMenuItem handledItem = modelService.createModelElement(MHandledMenuItem.class);
+               handledItem.setCommand(command);
+               return handledItem;
+
+       }
+
+       /**
+        * Finds a command by ID.
+        * 
+        * @return the {@link MCommand} or <code>null</code> if not found.
+        */
+       public static MCommand findCommand(EModelService modelService, MApplication app, String commandId) {
+               List<MCommand> cmds = modelService.findElements(app, null, MCommand.class, null);
+               for (MCommand cmd : cmds) {
+                       if (cmd.getElementId().equals(commandId)) {
+                               return cmd;
+                       }
+               }
+               return null;
+       }
+
+       /** Dynamically creates a direct menu item from a class. */
+       public static MDirectMenuItem createDirectMenuItem(EModelService modelService, Class<?> clss, String label) {
+               MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class);
+               dynamicItem.setLabel(label);
+               Bundle bundle = FrameworkUtil.getBundle(clss);
+               dynamicItem.setContributionURI("bundleclass://" + bundle.getSymbolicName() + "/" + clss.getName());
+               return dynamicItem;
+       }
+
+       /** Singleton. */
+       private CmsE4Utils() {
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/OsgiFilterContextFunction.java
new file mode 100644 (file)
index 0000000..1e3e75c
--- /dev/null
@@ -0,0 +1,33 @@
+package org.argeo.cms.e4;
+
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.e4.core.contexts.ContextFunction;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.IInjector;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/** An Eclipse 4 {@link ContextFunction} based on an OSGi filter. */
+public class OsgiFilterContextFunction extends ContextFunction {
+
+       private BundleContext bc = FrameworkUtil.getBundle(OsgiFilterContextFunction.class).getBundleContext();
+
+       @Override
+       public Object compute(IEclipseContext context, String contextKey) {
+               ServiceReference<?>[] srs;
+               try {
+                       srs = bc.getServiceReferences((String) null, contextKey);
+               } catch (InvalidSyntaxException e) {
+                       throw new CmsException("Context key " + contextKey + " must be a valid osgi filter", e);
+               }
+               if (srs == null || srs.length == 0) {
+                       return IInjector.NOT_A_VALUE;
+               } else {
+                       // return the first one
+                       return bc.getService(srs[0]);
+               }
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/PrivilegedJob.java
new file mode 100644 (file)
index 0000000..89055d2
--- /dev/null
@@ -0,0 +1,49 @@
+package org.argeo.cms.e4;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Propagate authentication to an eclipse job. Typically to execute a privileged
+ * action outside the UI thread
+ */
+public abstract class PrivilegedJob extends Job {
+       private final Subject subject;
+
+       public PrivilegedJob(String jobName) {
+               this(jobName, AccessController.getContext());
+       }
+
+       public PrivilegedJob(String jobName,
+                       AccessControlContext accessControlContext) {
+               super(jobName);
+               subject = Subject.getSubject(accessControlContext);
+
+               // Must be called *before* the job is scheduled,
+               // it is required for the progress window to appear
+               setUser(true);
+       }
+
+       @Override
+       protected IStatus run(final IProgressMonitor progressMonitor) {
+               PrivilegedAction<IStatus> privilegedAction = new PrivilegedAction<IStatus>() {
+                       public IStatus run() {
+                               return doRun(progressMonitor);
+                       }
+               };
+               return Subject.doAs(subject, privilegedAction);
+       }
+
+       /**
+        * Implement here what should be executed with default context
+        * authentication
+        */
+       protected abstract IStatus doRun(IProgressMonitor progressMonitor);
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/AuthAddon.java
new file mode 100644 (file)
index 0000000..e84b18c
--- /dev/null
@@ -0,0 +1,104 @@
+package org.argeo.cms.e4.addons;
+
+import java.security.AccessController;
+import java.util.Iterator;
+
+import javax.annotation.PostConstruct;
+import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletRequest;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.ui.MElementContainer;
+import org.eclipse.e4.ui.model.application.ui.MUIElement;
+import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
+import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
+import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
+
+public class AuthAddon {
+       private final static CmsLog log = CmsLog.getLog(AuthAddon.class);
+
+       public final static String AUTH = "auth.";
+
+       @PostConstruct
+       void init(MApplication application) {
+               Iterator<MWindow> windows = application.getChildren().iterator();
+               boolean atLeastOneTopLevelWindowVisible = false;
+               windows: while (windows.hasNext()) {
+                       MWindow window = windows.next();
+                       // main window
+                       boolean windowVisible = process(window);
+                       if (!windowVisible) {
+//                             windows.remove();
+                               continue windows;
+                       }
+                       atLeastOneTopLevelWindowVisible = true;
+                       // trim bars
+                       if (window instanceof MTrimmedWindow) {
+                               Iterator<MTrimBar> trimBars = ((MTrimmedWindow) window).getTrimBars().iterator();
+                               while (trimBars.hasNext()) {
+                                       MTrimBar trimBar = trimBars.next();
+                                       if (!process(trimBar)) {
+                                               trimBars.remove();
+                                       }
+                               }
+                       }
+               }
+
+               if (!atLeastOneTopLevelWindowVisible) {
+                       log.warn("No top-level window is authorized for user " + CurrentUser.getUsername() + ", logging out..");
+                       logout();
+               }
+       }
+
+       protected boolean process(MUIElement element) {
+               for (String tag : element.getTags()) {
+                       if (tag.startsWith(AUTH)) {
+                               String role = tag.substring(AUTH.length(), tag.length());
+                               if (!CurrentUser.isInRole(role)) {
+                                       element.setVisible(false);
+                                       element.setToBeRendered(false);
+                                       return false;
+                               }
+                       }
+               }
+
+               // children
+               if (element instanceof MElementContainer) {
+                       @SuppressWarnings("unchecked")
+                       MElementContainer<? extends MUIElement> container = (MElementContainer<? extends MUIElement>) element;
+                       Iterator<? extends MUIElement> children = container.getChildren().iterator();
+                       while (children.hasNext()) {
+                               MUIElement child = children.next();
+                               boolean visible = process(child);
+                               if (!visible)
+                                       children.remove();
+                       }
+
+                       for (Object child : container.getChildren()) {
+                               if (child instanceof MUIElement) {
+                                       boolean visible = process((MUIElement) child);
+                                       if (!visible)
+                                               container.getChildren().remove(child);
+                               }
+                       }
+               }
+
+               return true;
+       }
+
+       protected void logout() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               try {
+                       CurrentUser.logoutCmsSession(subject);
+               } catch (Exception e) {
+                       throw new CmsException("Cannot log out", e);
+               }
+               HttpServletRequest request = org.argeo.eclipse.ui.specific.UiContext.getHttpRequest();
+               if (request != null)
+                       request.getSession().setMaxInactiveInterval(0);
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/LocaleAddon.java
new file mode 100644 (file)
index 0000000..5bc0d69
--- /dev/null
@@ -0,0 +1,51 @@
+package org.argeo.cms.e4.addons;
+
+import java.security.AccessController;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+import javax.security.auth.Subject;
+
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.e4.core.services.nls.ILocaleChangeService;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.e4.ui.workbench.modeling.ElementMatcher;
+import org.eclipse.swt.SWT;
+
+/** Integrate workbench with the locale provided at log in. */
+public class LocaleAddon {
+       private final static String STYLE_OVERRIDE = "styleOverride";
+
+       // Right to left languages
+       private final static String ARABIC = "ar";
+       private final static String HEBREW = "he";
+
+       @PostConstruct
+       public void init(ILocaleChangeService localeChangeService, EModelService modelService, MApplication application) {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               Set<Locale> locales = subject.getPublicCredentials(Locale.class);
+               if (!locales.isEmpty()) {
+                       Locale locale = locales.iterator().next();
+                       localeChangeService.changeApplicationLocale(locale);
+                       UiContext.setLocale(locale);
+
+                       if (locale.getLanguage().equals(ARABIC) || locale.getLanguage().equals(HEBREW)) {
+                               List<MWindow> windows = modelService.findElements(application, MWindow.class, EModelService.ANYWHERE,
+                                               new ElementMatcher(null, null, (String) null));
+                               for (MWindow window : windows) {
+                                       String currentStyle = window.getPersistedState().get(STYLE_OVERRIDE);
+                                       int style = 0;
+                                       if (currentStyle != null) {
+                                               style = Integer.parseInt(currentStyle);
+                                       }
+                                       style = style | SWT.RIGHT_TO_LEFT;
+                                       window.getPersistedState().put(STYLE_OVERRIDE, Integer.toString(style));
+                               }
+                       }
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/addons/package-info.java
new file mode 100644 (file)
index 0000000..6367b42
--- /dev/null
@@ -0,0 +1,2 @@
+/** Eclipse 4 addons to integrate with Argeo CMS. */
+package org.argeo.cms.e4.addons;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/NodeFsBrowserView.java
new file mode 100644 (file)
index 0000000..579d35d
--- /dev/null
@@ -0,0 +1,50 @@
+package org.argeo.cms.e4.files;
+
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.eclipse.ui.fs.AdvancedFsBrowser;
+import org.argeo.eclipse.ui.fs.SimpleFsBrowser;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+/** Browse the node file system. */
+public class NodeFsBrowserView {
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID +
+       // ".nodeFsBrowserView";
+
+       @Inject
+       FileSystemProvider nodeFileSystemProvider;
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               try {
+                       //URI uri = new URI("node://root:demo@localhost:7070/");
+                       URI uri = new URI("node:///");
+                       FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri);
+                       if (fileSystem == null)
+                               fileSystem = nodeFileSystemProvider.newFileSystem(uri, null);
+                       Path nodePath = fileSystem.getPath("/");
+
+                       Path localPath = Paths.get(System.getProperty("user.home"));
+
+                       SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS);
+                       browser.setInput(nodePath, localPath);
+//                     AdvancedFsBrowser browser = new AdvancedFsBrowser();
+//                     browser.createUi(parent, localPath);
+               } catch (Exception e) {
+                       throw new CmsException("Cannot open file system browser", e);
+               }
+       }
+
+       public void setFocus() {
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/files/package-info.java
new file mode 100644 (file)
index 0000000..b481dd4
--- /dev/null
@@ -0,0 +1,2 @@
+/** Files browser perspective. */
+package org.argeo.cms.e4.files;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangeLanguage.java
new file mode 100644 (file)
index 0000000..416df7d
--- /dev/null
@@ -0,0 +1,13 @@
+package org.argeo.cms.e4.handlers;
+
+import java.util.Locale;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.core.services.nls.ILocaleChangeService;
+
+public class ChangeLanguage {
+       @Execute
+       public void execute(ILocaleChangeService localeChangeService) {
+               localeChangeService.changeApplicationLocale(Locale.FRENCH);
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java
new file mode 100644 (file)
index 0000000..7ef8c59
--- /dev/null
@@ -0,0 +1,137 @@
+package org.argeo.cms.e4.handlers;
+
+import static org.argeo.cms.CmsMsg.changePassword;
+import static org.argeo.cms.CmsMsg.currentPassword;
+import static org.argeo.cms.CmsMsg.newPassword;
+import static org.argeo.cms.CmsMsg.passwordChanged;
+import static org.argeo.cms.CmsMsg.repeatNewPassword;
+
+import java.util.Arrays;
+
+import javax.inject.Inject;
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.security.CryptoKeyring;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.dialogs.CmsMessageDialog;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.util.transaction.WorkTransaction;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.core.di.annotations.Optional;
+import org.eclipse.jface.dialogs.Dialog;
+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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/** Change the password of the logged-in user. */
+public class ChangePassword {
+       @Inject
+       private UserAdmin userAdmin;
+       @Inject
+       private WorkTransaction userTransaction;
+       @Inject
+       @Optional
+       private CryptoKeyring keyring = null;
+
+       @Execute
+       public void execute() {
+               ChangePasswordDialog dialog = new ChangePasswordDialog(Display.getCurrent().getActiveShell(), userAdmin);
+               if (dialog.open() == Dialog.OK) {
+                       new CmsMessageDialog(Display.getCurrent().getActiveShell(), passwordChanged.lead(),
+                                       CmsMessageDialog.INFORMATION).open();
+               }
+       }
+
+       protected void changePassword(char[] oldPassword, char[] newPassword) {
+               String name = CurrentUser.getUsername();
+               LdapName dn;
+               try {
+                       dn = new LdapName(name);
+               } catch (InvalidNameException e) {
+                       throw new CmsException("Invalid user dn " + name, e);
+               }
+               User user = (User) userAdmin.getRole(dn.toString());
+               if (!user.hasCredential(null, oldPassword))
+                       throw new CmsException("Invalid password");
+               if (Arrays.equals(newPassword, new char[0]))
+                       throw new CmsException("New password empty");
+               try {
+                       userTransaction.begin();
+                       user.getCredentials().put(null, newPassword);
+                       if (keyring != null) {
+                               keyring.changePassword(oldPassword, newPassword);
+                               // TODO change secret keys in the CMS session
+                       }
+                       userTransaction.commit();
+               } catch (Exception e) {
+                       try {
+                               userTransaction.rollback();
+                       } catch (Exception e1) {
+                               e1.printStackTrace();
+                       }
+                       if (e instanceof RuntimeException)
+                               throw (RuntimeException) e;
+                       else
+                               throw new CmsException("Cannot change password", e);
+               }
+       }
+
+       class ChangePasswordDialog extends CmsMessageDialog {
+               private Text oldPassword, newPassword1, newPassword2;
+
+               public ChangePasswordDialog(Shell parentShell, UserAdmin securityService) {
+                       super(parentShell, changePassword.lead(), CONFIRM);
+               }
+
+//             protected Point getInitialSize() {
+//                     return new Point(400, 450);
+//             }
+
+               protected Control createDialogArea(Composite parent) {
+                       Composite dialogarea = (Composite) super.createDialogArea(parent);
+                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       Composite composite = new Composite(dialogarea, SWT.NONE);
+                       composite.setLayout(new GridLayout(2, false));
+                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+                       oldPassword = createLP(composite, currentPassword.lead());
+                       newPassword1 = createLP(composite, newPassword.lead());
+                       newPassword2 = createLP(composite, repeatNewPassword.lead());
+
+//                     parent.pack();
+                       oldPassword.setFocus();
+                       return composite;
+               }
+
+               @Override
+               protected void okPressed() {
+                       try {
+                               if (!newPassword1.getText().equals(newPassword2.getText()))
+                                       throw new CmsException("New passwords are different");
+                               changePassword(oldPassword.getTextChars(), newPassword1.getTextChars());
+                               closeShell(OK);
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot change password", e);
+                       }
+               }
+
+               /** Creates label and password. */
+               protected Text createLP(Composite parent, String label) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+                       return text;
+               }
+
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseAllParts.java
new file mode 100644 (file)
index 0000000..d11c041
--- /dev/null
@@ -0,0 +1,37 @@
+package org.argeo.cms.e4.handlers;
+
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+
+public class CloseAllParts {
+
+       @Execute
+       void execute(EPartService partService) {
+               for (MPart part : partService.getParts()) {
+                       if (part.isCloseable()) {
+                               if (part.isDirty()) {
+                                       if (partService.savePart(part, true)) {
+                                               partService.hidePart(part, true);
+                                       }
+                               } else {
+                                       partService.hidePart(part, true);
+                               }
+                       }
+               }
+       }
+
+       @CanExecute
+       boolean canExecute(EPartService partService) {
+               boolean atLeastOnePart = false;
+               for (MPart part : partService.getParts()) {
+                       if (part.isVisible() && part.isCloseable()) {
+                               atLeastOnePart = true;
+                               break;
+                       }
+               }
+               return atLeastOnePart;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/CloseWorkbench.java
new file mode 100644 (file)
index 0000000..c2ae4bf
--- /dev/null
@@ -0,0 +1,28 @@
+package org.argeo.cms.e4.handlers;
+
+import java.security.AccessController;
+
+import javax.security.auth.Subject;
+
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+
+public class CloseWorkbench {
+       @Execute
+       public void execute(IWorkbench workbench) {
+               logout();
+               workbench.close();
+       }
+
+       protected void logout() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               try {
+                       CurrentUser.logoutCmsSession(subject);
+               } catch (Exception e) {
+                       throw new CmsException("Cannot log out", e);
+               }
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/DoNothing.java
new file mode 100644 (file)
index 0000000..358494c
--- /dev/null
@@ -0,0 +1,10 @@
+package org.argeo.cms.e4.handlers;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+
+public class DoNothing {
+       @Execute
+       public void execute() {
+
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/LanguageMenuContribution.java
new file mode 100644 (file)
index 0000000..ac825bb
--- /dev/null
@@ -0,0 +1,29 @@
+
+package org.argeo.cms.e4.handlers;
+
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.e4.ui.di.AboutToHide;
+import org.eclipse.e4.ui.di.AboutToShow;
+import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem;
+import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+
+public class LanguageMenuContribution {
+       @AboutToShow
+       public void aboutToShow(List<MMenuElement> items, EModelService modelService) {
+               MDirectMenuItem dynamicItem = modelService.createModelElement(MDirectMenuItem.class);
+               dynamicItem.setLabel("Dynamic Menu Item (" + new Date() + ")");
+               //dynamicItem.setContributorURI("platform:/plugin/org.argeo.cms.e4");
+               //dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/" + ChangeLanguage.class.getName());
+               dynamicItem.setEnabled(true);
+               dynamicItem.setContributionURI("bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.handlers.ChangeLanguage");
+               items.add(dynamicItem);
+       }
+
+       @AboutToHide
+       public void aboutToHide() {
+               
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/OpenPerspective.java
new file mode 100644 (file)
index 0000000..ac544b1
--- /dev/null
@@ -0,0 +1,31 @@
+package org.argeo.cms.e4.handlers;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.MApplication;
+import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+
+public class OpenPerspective {
+       @Inject
+       MApplication application;
+       @Inject
+       EPartService partService;
+       @Inject
+       EModelService modelService;
+
+       @Execute
+       public void execute(@Named("perspectiveId") String perspectiveId) {
+               List<MPerspective> perspectives = modelService.findElements(application, perspectiveId, MPerspective.class,
+                               null);
+               if (perspectives.size() == 0)
+                       return;
+               MPerspective perspective = perspectives.get(0);
+               partService.switchPerspective(perspective);
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SaveAllParts.java
new file mode 100644 (file)
index 0000000..3b60abd
--- /dev/null
@@ -0,0 +1,19 @@
+package org.argeo.cms.e4.handlers;
+
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+
+public class SaveAllParts {
+
+       @Execute
+       void execute(EPartService partService) {
+               partService.saveAll(false);
+       }
+
+       @CanExecute
+       boolean canExecute(EPartService partService) {
+               return partService.getDirtyParts().size() > 0;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/SavePart.java
new file mode 100644 (file)
index 0000000..73486f3
--- /dev/null
@@ -0,0 +1,18 @@
+package org.argeo.cms.e4.handlers;
+
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+
+public class SavePart {
+       @Execute
+       void execute(EPartService partService, MPart part) {
+               partService.savePart(part, false);
+       }
+
+       @CanExecute
+       boolean canExecute(MPart part) {
+               return part.isDirty();
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/package-info.java
new file mode 100644 (file)
index 0000000..a44ca90
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic Eclipse 4 handlers. */
+package org.argeo.cms.e4.handlers;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/EclipseJcrMonitor.java
new file mode 100644 (file)
index 0000000..e10738e
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.cms.e4.jcr;
+
+import org.argeo.jcr.JcrMonitor;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Wraps an Eclipse {@link IProgressMonitor} so that it can be passed to
+ * framework agnostic Argeo routines.
+ */
+public class EclipseJcrMonitor implements JcrMonitor {
+       private final IProgressMonitor progressMonitor;
+
+       public EclipseJcrMonitor(IProgressMonitor progressMonitor) {
+               this.progressMonitor = progressMonitor;
+       }
+
+       public void beginTask(String name, int totalWork) {
+               progressMonitor.beginTask(name, totalWork);
+       }
+
+       public void done() {
+               progressMonitor.done();
+       }
+
+       public boolean isCanceled() {
+               return progressMonitor.isCanceled();
+       }
+
+       public void setCanceled(boolean value) {
+               progressMonitor.setCanceled(value);
+       }
+
+       public void setTaskName(String name) {
+               progressMonitor.setTaskName(name);
+       }
+
+       public void subTask(String name) {
+               progressMonitor.subTask(name);
+       }
+
+       public void worked(int work) {
+               progressMonitor.worked(work);
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/GenericPropertyPage.java
new file mode 100644 (file)
index 0000000..e17f17b
--- /dev/null
@@ -0,0 +1,141 @@
+package org.argeo.cms.e4.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.jcr.PropertyLabelProvider;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.eclipse.jface.layout.TreeColumnLayout;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * Generic editor property page. Lists all properties of current node as a
+ * complex tree. TODO: enable editing
+ */
+public class GenericPropertyPage {
+
+       // Main business Objects
+       private Node currentNode;
+
+       public GenericPropertyPage(Node currentNode) {
+               this.currentNode = currentNode;
+       }
+
+       protected void createFormContent(Composite parent) {
+               Composite innerBox = new Composite(parent, SWT.NONE);
+               // Composite innerBox = new Composite(body, SWT.NO_FOCUS);
+               FillLayout layout = new FillLayout();
+               layout.marginHeight = 5;
+               layout.marginWidth = 5;
+               innerBox.setLayout(layout);
+               createComplexTree(innerBox);
+               // TODO TreeColumnLayout triggers a scroll issue with the form:
+               // The inside body is always to big and a scroll bar is shown
+               // Composite tableCmp = new Composite(body, SWT.NO_FOCUS);
+               // createComplexTree(tableCmp);
+       }
+
+       private TreeViewer createComplexTree(Composite parent) {
+               int style = SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION;
+               Tree tree = new Tree(parent, style);
+               TreeColumnLayout tableColumnLayout = new TreeColumnLayout();
+
+               createColumn(tree, tableColumnLayout, "Property", SWT.LEFT, 200, 30);
+               createColumn(tree, tableColumnLayout, "Value(s)", SWT.LEFT, 300, 60);
+               createColumn(tree, tableColumnLayout, "Type", SWT.LEFT, 75, 10);
+               createColumn(tree, tableColumnLayout, "Attributes", SWT.LEFT, 75, 0);
+               // Do not apply the treeColumnLayout it does not work yet
+               // parent.setLayout(tableColumnLayout);
+
+               tree.setLinesVisible(true);
+               tree.setHeaderVisible(true);
+
+               TreeViewer treeViewer = new TreeViewer(tree);
+               treeViewer.setContentProvider(new TreeContentProvider());
+               treeViewer.setLabelProvider((IBaseLabelProvider) new PropertyLabelProvider());
+               treeViewer.setInput(currentNode);
+               treeViewer.expandAll();
+               return treeViewer;
+       }
+
+       private static TreeColumn createColumn(Tree parent, TreeColumnLayout tableColumnLayout, String name, int style,
+                       int width, int weight) {
+               TreeColumn column = new TreeColumn(parent, style);
+               column.setText(name);
+               column.setWidth(width);
+               column.setMoveable(true);
+               column.setResizable(true);
+               tableColumnLayout.setColumnData(column, new ColumnWeightData(weight, width, true));
+               return column;
+       }
+
+       private class TreeContentProvider implements ITreeContentProvider {
+               private static final long serialVersionUID = -6162736530019406214L;
+
+               public Object[] getElements(Object parent) {
+                       Object[] props = null;
+                       try {
+
+                               if (parent instanceof Node) {
+                                       Node node = (Node) parent;
+                                       PropertyIterator pi;
+                                       pi = node.getProperties();
+                                       List<Property> propList = new ArrayList<Property>();
+                                       while (pi.hasNext()) {
+                                               propList.add(pi.nextProperty());
+                                       }
+                                       props = propList.toArray();
+                               }
+                       } catch (RepositoryException e) {
+                               throw new EclipseUiException("Unexpected exception while listing node properties", e);
+                       }
+                       return props;
+               }
+
+               public Object getParent(Object child) {
+                       return null;
+               }
+
+               public Object[] getChildren(Object parent) {
+                       if (parent instanceof Property) {
+                               Property prop = (Property) parent;
+                               try {
+                                       if (prop.isMultiple())
+                                               return prop.getValues();
+                               } catch (RepositoryException e) {
+                                       throw new EclipseUiException("Cannot get multi-prop values on " + prop, e);
+                               }
+                       }
+                       return null;
+               }
+
+               public boolean hasChildren(Object parent) {
+                       try {
+                               return (parent instanceof Property && ((Property) parent).isMultiple());
+                       } catch (RepositoryException e) {
+                               throw new EclipseUiException("Cannot check if property is multiple for " + parent, e);
+                       }
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+               public void dispose() {
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrBrowserView.java
new file mode 100644 (file)
index 0000000..0b77c07
--- /dev/null
@@ -0,0 +1,349 @@
+package org.argeo.cms.e4.jcr;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.security.CryptoKeyring;
+import org.argeo.cms.security.Keyring;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.jcr.JcrBrowserUtils;
+import org.argeo.cms.ui.jcr.NodeContentProvider;
+import org.argeo.cms.ui.jcr.NodeLabelProvider;
+import org.argeo.cms.ui.jcr.OsgiRepositoryRegister;
+import org.argeo.cms.ui.jcr.PropertiesContentProvider;
+import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.jcr.AsyncUiEventListener;
+import org.argeo.eclipse.ui.jcr.util.NodeViewerComparer;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.core.di.annotations.Optional;
+import org.eclipse.e4.ui.services.EMenuService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+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.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Basic View to display a sash form to browse a JCR compliant multiple
+ * repository environment
+ */
+public class JcrBrowserView {
+       final static String ID = "org.argeo.cms.e4.jcrbrowser";
+       final static String NODE_VIEWER_POPUP_MENU_ID = "org.argeo.cms.e4.popupmenu.nodeViewer";
+
+       private boolean sortChildNodes = true;
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       @Optional
+       private Keyring keyring;
+       @Inject
+       private RepositoryFactory repositoryFactory;
+       @Inject
+       private Repository nodeRepository;
+
+       // Current user session on the home repository default workspace
+       private Session userSession;
+
+       private OsgiRepositoryRegister repositoryRegister = new OsgiRepositoryRegister();
+
+       // This page widgets
+       private TreeViewer nodesViewer;
+       private NodeContentProvider nodeContentProvider;
+       private TableViewer propertiesViewer;
+       private EventListener resultsObserver;
+
+       @PostConstruct
+       public void createPartControl(Composite parent, IEclipseContext context, EPartService partService,
+                       ESelectionService selectionService, EMenuService menuService) {
+               repositoryRegister.init();
+
+               parent.setLayout(new FillLayout());
+               SashForm sashForm = new SashForm(parent, SWT.VERTICAL);
+               // sashForm.setSashWidth(4);
+               // sashForm.setLayout(new FillLayout());
+
+               // Create the tree on top of the view
+               Composite top = new Composite(sashForm, SWT.NONE);
+               // GridLayout gl = new GridLayout(1, false);
+               top.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               try {
+                       this.userSession = this.nodeRepository.login(CmsConstants.HOME_WORKSPACE);
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot open user session", e);
+               }
+
+               nodeContentProvider = new NodeContentProvider(userSession, keyring, repositoryRegister, repositoryFactory,
+                               sortChildNodes);
+
+               // nodes viewer
+               nodesViewer = createNodeViewer(top, nodeContentProvider);
+
+               // context menu : it is completely defined in the plugin.xml file.
+               // MenuManager menuManager = new MenuManager();
+               // Menu menu = menuManager.createContextMenu(nodesViewer.getTree());
+
+               // nodesViewer.getTree().setMenu(menu);
+
+               nodesViewer.setInput("");
+
+               // Create the property viewer on the bottom
+               Composite bottom = new Composite(sashForm, SWT.NONE);
+               bottom.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               propertiesViewer = createPropertiesViewer(bottom);
+
+               sashForm.setWeights(getWeights());
+               nodesViewer.setComparer(new NodeViewerComparer());
+               nodesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+                       public void selectionChanged(SelectionChangedEvent event) {
+                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
+                               selectionService.setSelection(selection.toList());
+                       }
+               });
+               nodesViewer.addDoubleClickListener(new JcrE4DClickListener(nodesViewer, partService));
+               menuService.registerContextMenu(nodesViewer.getControl(), NODE_VIEWER_POPUP_MENU_ID);
+               // getSite().registerContextMenu(menuManager, nodesViewer);
+               // getSite().setSelectionProvider(nodesViewer);
+       }
+
+       @PreDestroy
+       public void dispose() {
+               JcrUtils.logoutQuietly(userSession);
+               repositoryRegister.destroy();
+       }
+
+       public void refresh(Object obj) {
+               // Enable full refresh from a command when no element of the tree is
+               // selected
+               if (obj == null) {
+                       Object[] elements = nodeContentProvider.getElements(null);
+                       for (Object el : elements) {
+                               if (el instanceof TreeParent)
+                                       JcrBrowserUtils.forceRefreshIfNeeded((TreeParent) el);
+                               getNodeViewer().refresh(el);
+                       }
+               } else
+                       getNodeViewer().refresh(obj);
+       }
+
+       /**
+        * To be overridden to adapt size of form and result frames.
+        */
+       protected int[] getWeights() {
+               return new int[] { 70, 30 };
+       }
+
+       protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) {
+
+               final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
+
+               tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               tmpNodeViewer.setContentProvider(nodeContentProvider);
+               tmpNodeViewer.setLabelProvider((IBaseLabelProvider) new NodeLabelProvider());
+               tmpNodeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+                       public void selectionChanged(SelectionChangedEvent event) {
+                               if (!event.getSelection().isEmpty()) {
+                                       IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+                                       Object firstItem = sel.getFirstElement();
+                                       if (firstItem instanceof SingleJcrNodeElem)
+                                               propertiesViewer.setInput(((SingleJcrNodeElem) firstItem).getNode());
+                               } else {
+                                       propertiesViewer.setInput("");
+                               }
+                       }
+               });
+
+               resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay());
+               if (keyring != null)
+                       try {
+                               ObservationManager observationManager = userSession.getWorkspace().getObservationManager();
+                               observationManager.addEventListener(resultsObserver, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/",
+                                               true, null, null, false);
+                       } catch (RepositoryException e) {
+                               throw new EclipseUiException("Cannot register listeners", e);
+                       }
+
+               // tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer));
+               return tmpNodeViewer;
+       }
+
+       protected TableViewer createPropertiesViewer(Composite parent) {
+               propertiesViewer = new TableViewer(parent, SWT.NONE);
+               propertiesViewer.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               propertiesViewer.getTable().setHeaderVisible(true);
+               propertiesViewer.setContentProvider(new PropertiesContentProvider());
+               TableViewerColumn col = new TableViewerColumn(propertiesViewer, SWT.NONE);
+               col.getColumn().setText("Name");
+               col.getColumn().setWidth(200);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -6684361063107478595L;
+
+                       public String getText(Object element) {
+                               try {
+                                       return ((Property) element).getName();
+                               } catch (RepositoryException e) {
+                                       throw new EclipseUiException("Unexpected exception in label provider", e);
+                               }
+                       }
+               });
+               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
+               col.getColumn().setText("Value");
+               col.getColumn().setWidth(400);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -8201994187693336657L;
+
+                       public String getText(Object element) {
+                               try {
+                                       Property property = (Property) element;
+                                       if (property.getType() == PropertyType.BINARY)
+                                               return "<binary>";
+                                       else if (property.isMultiple()) {
+                                               StringBuffer buf = new StringBuffer("[");
+                                               Value[] values = property.getValues();
+                                               for (int i = 0; i < values.length; i++) {
+                                                       if (i != 0)
+                                                               buf.append(", ");
+                                                       buf.append(values[i].getString());
+                                               }
+                                               buf.append(']');
+                                               return buf.toString();
+                                       } else
+                                               return property.getValue().getString();
+                               } catch (RepositoryException e) {
+                                       throw new EclipseUiException("Unexpected exception in label provider", e);
+                               }
+                       }
+               });
+               col = new TableViewerColumn(propertiesViewer, SWT.NONE);
+               col.getColumn().setText("Type");
+               col.getColumn().setWidth(200);
+               col.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -6009599998150286070L;
+
+                       public String getText(Object element) {
+                               return JcrBrowserUtils.getPropertyTypeAsString((Property) element);
+                       }
+               });
+               propertiesViewer.setInput("");
+               return propertiesViewer;
+       }
+
+       protected TreeViewer getNodeViewer() {
+               return nodesViewer;
+       }
+
+       /**
+        * Resets the tree content provider
+        * 
+        * @param sortChildNodes if true the content provider will use a comparer to
+        *                       sort nodes that might slow down the display
+        */
+       public void setSortChildNodes(boolean sortChildNodes) {
+               this.sortChildNodes = sortChildNodes;
+               ((NodeContentProvider) nodesViewer.getContentProvider()).setSortChildren(sortChildNodes);
+               nodesViewer.setInput("");
+       }
+
+       /** Notifies the current view that a node has been added */
+       public void nodeAdded(TreeParent parentNode) {
+               // insure that Ui objects have been correctly created:
+               JcrBrowserUtils.forceRefreshIfNeeded(parentNode);
+               getNodeViewer().refresh(parentNode);
+               getNodeViewer().expandToLevel(parentNode, 1);
+       }
+
+       /** Notifies the current view that a node has been removed */
+       public void nodeRemoved(TreeParent parentNode) {
+               IStructuredSelection newSel = new StructuredSelection(parentNode);
+               getNodeViewer().setSelection(newSel, true);
+               // Force refresh
+               IStructuredSelection tmpSel = (IStructuredSelection) getNodeViewer().getSelection();
+               getNodeViewer().refresh(tmpSel.getFirstElement());
+       }
+
+       class TreeObserver extends AsyncUiEventListener {
+
+               public TreeObserver(Display display) {
+                       super(display);
+               }
+
+               @Override
+               protected Boolean willProcessInUiThread(List<Event> events) throws RepositoryException {
+                       for (Event event : events) {
+                               if (getLog().isTraceEnabled())
+                                       getLog().debug("Received event " + event);
+                               String path = event.getPath();
+                               int index = path.lastIndexOf('/');
+                               String propertyName = path.substring(index + 1);
+                               if (getLog().isTraceEnabled())
+                                       getLog().debug("Concerned property " + propertyName);
+                       }
+                       return false;
+               }
+
+               protected void onEventInUiThread(List<Event> events) throws RepositoryException {
+                       if (getLog().isTraceEnabled())
+                               getLog().trace("Refresh result list");
+                       nodesViewer.refresh();
+               }
+
+       }
+
+       public boolean getSortChildNodes() {
+               return sortChildNodes;
+       }
+
+       public void setFocus() {
+               getNodeViewer().getTree().setFocus();
+       }
+
+       /* DEPENDENCY INJECTION */
+       // public void setRepositoryRegister(RepositoryRegister repositoryRegister) {
+       // this.repositoryRegister = repositoryRegister;
+       // }
+
+       public void setKeyring(CryptoKeyring keyring) {
+               this.keyring = keyring;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setNodeRepository(Repository nodeRepository) {
+               this.nodeRepository = nodeRepository;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrE4DClickListener.java
new file mode 100644 (file)
index 0000000..f4ee2e8
--- /dev/null
@@ -0,0 +1,36 @@
+package org.argeo.cms.e4.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.ui.jcr.JcrDClickListener;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.EPartService.PartState;
+import org.eclipse.jface.viewers.TreeViewer;
+
+public class JcrE4DClickListener extends JcrDClickListener {
+       EPartService partService;
+
+       public JcrE4DClickListener(TreeViewer nodeViewer, EPartService partService) {
+               super(nodeViewer);
+               this.partService = partService;
+       }
+
+       @Override
+       protected void openNode(Node node) {
+               MPart part = partService.createPart(JcrNodeEditor.DESCRIPTOR_ID);
+               try {
+                       part.setLabel(node.getName());
+                       part.getPersistedState().put("nodeWorkspace", node.getSession().getWorkspace().getName());
+                       part.getPersistedState().put("nodePath", node.getPath());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot open " + node, e);
+               }
+
+               // the provided part is be shown
+               partService.showPart(part, PartState.ACTIVATE);
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/JcrNodeEditor.java
new file mode 100644 (file)
index 0000000..ae2b325
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.cms.e4.jcr;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.jcr.Node;
+
+import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+
+public class JcrNodeEditor {
+       final static String DESCRIPTOR_ID = "org.argeo.cms.e4.partdescriptor.nodeEditor";
+
+       @PostConstruct
+       public void createUi(Composite parent, MPart part, ESelectionService selectionService) {
+               parent.setLayout(new FillLayout());
+               List<?> selection = (List<?>) selectionService.getSelection();
+               Node node = ((SingleJcrNodeElem) selection.get(0)).getNode();
+               GenericPropertyPage propertyPage = new GenericPropertyPage(node);
+               propertyPage.createFormContent(parent);
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/SimplePart.java
new file mode 100644 (file)
index 0000000..17d8d2a
--- /dev/null
@@ -0,0 +1,19 @@
+package org.argeo.cms.e4.jcr;
+
+import javax.annotation.PostConstruct;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class SimplePart {
+
+       @PostConstruct
+       void init(Composite parent) {
+               parent.setLayout(new GridLayout());
+               Label label = new Label(parent, SWT.NONE);
+               label.setText("Hello e4 World");
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddFolderNode.java
new file mode 100644 (file)
index 0000000..09fa760
--- /dev/null
@@ -0,0 +1,67 @@
+package org.argeo.cms.e4.jcr.handlers;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.argeo.cms.e4.jcr.JcrBrowserView;
+import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
+import org.argeo.cms.ui.jcr.model.WorkspaceElem;
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.eclipse.ui.dialogs.SingleValue;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+
+/**
+ * Adds a node of type nt:folder, only on {@link SingleJcrNodeElem} and
+ * {@link WorkspaceElem} TreeObject types.
+ * 
+ * This handler assumes that a selection provider is available and picks only
+ * first selected item. It is UI's job to enable the command only when the
+ * selection contains one and only one element. Thus no parameter is passed
+ * through the command.
+ */
+public class AddFolderNode {
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               List<?> selection = (List<?>) selectionService.getSelection();
+               JcrBrowserView view = (JcrBrowserView) part.getObject();
+
+               if (selection != null && selection.size() == 1) {
+                       TreeParent treeParentNode = null;
+                       Node jcrParentNode = null;
+                       Object obj = selection.get(0);
+
+                       if (obj instanceof SingleJcrNodeElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((SingleJcrNodeElem) treeParentNode).getNode();
+                       } else if (obj instanceof WorkspaceElem) {
+                               treeParentNode = (TreeParent) obj;
+                               jcrParentNode = ((WorkspaceElem) treeParentNode).getRootNode();
+                       } else
+                               return;
+
+                       String folderName = SingleValue.ask("Folder name", "Enter folder name");
+                       if (folderName != null) {
+                               try {
+                                       jcrParentNode.addNode(folderName, NodeType.NT_FOLDER);
+                                       jcrParentNode.getSession().save();
+                                       view.nodeAdded(treeParentNode);
+                               } catch (RepositoryException e) {
+                                       ErrorFeedback.show("Cannot create folder " + folderName + " under " + treeParentNode, e);
+                               }
+                       }
+               } else {
+                       // ErrorFeedback.show(WorkbenchUiPlugin
+                       // .getMessage("errorUnvalidNtFolderNodeType"));
+                       ErrorFeedback.show("Invalid NT folder node type");
+               }
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/AddRemoteRepository.java
new file mode 100644 (file)
index 0000000..dc47f6e
--- /dev/null
@@ -0,0 +1,210 @@
+package org.argeo.cms.e4.jcr.handlers;
+
+import java.net.URI;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.ArgeoNames;
+import org.argeo.cms.ArgeoTypes;
+import org.argeo.cms.e4.jcr.JcrBrowserView;
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.security.Keyring;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.core.di.annotations.Optional;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+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.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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Connect to a remote repository and, if successful publish it as an OSGi
+ * service.
+ */
+public class AddRemoteRepository {
+
+       @Inject
+       private RepositoryFactory repositoryFactory;
+       @Inject
+       private Repository nodeRepository;
+       @Inject
+       @Optional
+       private Keyring keyring;
+
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part) {
+               JcrBrowserView view = (JcrBrowserView) part.getObject();
+               RemoteRepositoryLoginDialog dlg = new RemoteRepositoryLoginDialog(Display.getDefault().getActiveShell());
+               if (dlg.open() == Dialog.OK) {
+                       view.refresh(null);
+               }
+       }
+
+       // public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+       // this.repositoryFactory = repositoryFactory;
+       // }
+       //
+       // public void setKeyring(Keyring keyring) {
+       // this.keyring = keyring;
+       // }
+       //
+       // public void setNodeRepository(Repository nodeRepository) {
+       // this.nodeRepository = nodeRepository;
+       // }
+
+       class RemoteRepositoryLoginDialog extends TitleAreaDialog {
+               private static final long serialVersionUID = 2234006887750103399L;
+               private Text name;
+               private Text uri;
+               private Text username;
+               private Text password;
+               private Button saveInKeyring;
+
+               public RemoteRepositoryLoginDialog(Shell parentShell) {
+                       super(parentShell);
+               }
+
+               protected Point getInitialSize() {
+                       return new Point(600, 400);
+               }
+
+               protected Control createDialogArea(Composite parent) {
+                       Composite dialogarea = (Composite) super.createDialogArea(parent);
+                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       Composite composite = new Composite(dialogarea, SWT.NONE);
+                       composite.setLayout(new GridLayout(2, false));
+                       composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+                       setMessage("Login to remote repository", IMessageProvider.NONE);
+                       name = createLT(composite, "Name", "remoteRepository");
+                       uri = createLT(composite, "URI", "http://localhost:7070/jcr/node");
+                       username = createLT(composite, "User", "");
+                       password = createLP(composite, "Password");
+
+                       saveInKeyring = createLC(composite, "Remember password", false);
+                       parent.pack();
+                       return composite;
+               }
+
+               @Override
+               protected void createButtonsForButtonBar(Composite parent) {
+                       super.createButtonsForButtonBar(parent);
+                       Button test = createButton(parent, 2, "Test", false);
+                       test.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -1829962269440419560L;
+
+                               public void widgetSelected(SelectionEvent arg0) {
+                                       testConnection();
+                               }
+                       });
+               }
+
+               void testConnection() {
+                       Session session = null;
+                       try {
+                               URI checkedUri = new URI(uri.getText());
+                               String checkedUriStr = checkedUri.toString();
+
+                               Hashtable<String, String> params = new Hashtable<String, String>();
+                               params.put(CmsConstants.LABELED_URI, checkedUriStr);
+                               Repository repository = repositoryFactory.getRepository(params);
+                               if (username.getText().trim().equals("")) {// anonymous
+                                       // FIXME make it more generic
+                                       session = repository.login(CmsConstants.SYS_WORKSPACE);
+                               } else {
+                                       // FIXME use getTextChars() when upgrading to 3.7
+                                       // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=297412
+                                       char[] pwd = password.getText().toCharArray();
+                                       SimpleCredentials sc = new SimpleCredentials(username.getText(), pwd);
+                                       session = repository.login(sc, "main");
+                                       MessageDialog.openInformation(getParentShell(), "Success",
+                                                       "Connection to '" + uri.getText() + "' successful");
+                               }
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Connection test failed for " + uri.getText(), e);
+                       } finally {
+                               JcrUtils.logoutQuietly(session);
+                       }
+               }
+
+               @Override
+               protected void okPressed() {
+                       Session nodeSession = null;
+                       try {
+                               nodeSession = nodeRepository.login();
+                               Node home = CmsJcrUtils.getUserHome(nodeSession);
+
+                               Node remote = home.hasNode(ArgeoNames.ARGEO_REMOTE) ? home.getNode(ArgeoNames.ARGEO_REMOTE)
+                                               : home.addNode(ArgeoNames.ARGEO_REMOTE);
+                               if (remote.hasNode(name.getText()))
+                                       throw new EclipseUiException("There is already a remote repository named " + name.getText());
+                               Node remoteRepository = remote.addNode(name.getText(), ArgeoTypes.ARGEO_REMOTE_REPOSITORY);
+                               remoteRepository.setProperty(ArgeoNames.ARGEO_URI, uri.getText());
+                               remoteRepository.setProperty(ArgeoNames.ARGEO_USER_ID, username.getText());
+                               nodeSession.save();
+                               if (saveInKeyring.getSelection()) {
+                                       String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD;
+                                       keyring.set(pwdPath, password.getText().toCharArray());
+                               }
+                               nodeSession.save();
+                               MessageDialog.openInformation(getParentShell(), "Repository Added",
+                                               "Remote repository '" + username.getText() + "@" + uri.getText() + "' added");
+
+                               super.okPressed();
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot add remote repository", e);
+                       } finally {
+                               JcrUtils.logoutQuietly(nodeSession);
+                       }
+               }
+
+               /** Creates label and text. */
+               protected Text createLT(Composite parent, String label, String initial) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       text.setText(initial);
+                       return text;
+               }
+
+               /** Creates label and check. */
+               protected Button createLC(Composite parent, String label, Boolean initial) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Button check = new Button(parent, SWT.CHECK);
+                       check.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       check.setSelection(initial);
+                       return check;
+               }
+
+               protected Text createLP(Composite parent, String label) {
+                       new Label(parent, SWT.NONE).setText(label);
+                       Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       return text;
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/DeleteNodes.java
new file mode 100644 (file)
index 0000000..b8de06b
--- /dev/null
@@ -0,0 +1,95 @@
+package org.argeo.cms.e4.jcr.handlers;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.e4.jcr.JcrBrowserView;
+import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
+import org.argeo.cms.ui.jcr.model.WorkspaceElem;
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Delete the selected nodes: both in the JCR repository and in the UI view.
+ * Warning no check is done, except implementation dependent native checks,
+ * handle with care.
+ * 
+ * This handler is still 'hard linked' to a GenericJcrBrowser view to enable
+ * correct tree refresh when a node is added. This must be corrected in future
+ * versions.
+ */
+public class DeleteNodes {
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               List<?> selection = (List<?>) selectionService.getSelection();
+               if (selection == null)
+                       return;
+
+               JcrBrowserView view = (JcrBrowserView) part.getObject();
+
+               // confirmation
+               StringBuffer buf = new StringBuffer("");
+               for (Object o : selection) {
+                       SingleJcrNodeElem sjn = (SingleJcrNodeElem) o;
+                       buf.append(sjn.getName()).append(' ');
+               }
+               Boolean doRemove = MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), "Confirm deletion",
+                               "Do you want to delete " + buf + "?");
+
+               // operation
+               if (doRemove) {
+                       SingleJcrNodeElem ancestor = null;
+                       WorkspaceElem rootAncestor = null;
+                       try {
+                               for (Object obj : selection) {
+                                       if (obj instanceof SingleJcrNodeElem) {
+                                               // Cache objects
+                                               SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj;
+                                               TreeParent tp = (TreeParent) sjn.getParent();
+                                               Node node = sjn.getNode();
+
+                                               // Jcr Remove
+                                               node.remove();
+                                               node.getSession().save();
+                                               // UI remove
+                                               tp.removeChild(sjn);
+
+                                               // Check if the parent is the root node
+                                               if (tp instanceof WorkspaceElem)
+                                                       rootAncestor = (WorkspaceElem) tp;
+                                               else
+                                                       ancestor = getOlder(ancestor, (SingleJcrNodeElem) tp);
+                                       }
+                               }
+                               if (rootAncestor != null)
+                                       view.nodeRemoved(rootAncestor);
+                               else if (ancestor != null)
+                                       view.nodeRemoved(ancestor);
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot delete selected node ", e);
+                       }
+               }
+       }
+
+       private SingleJcrNodeElem getOlder(SingleJcrNodeElem A, SingleJcrNodeElem B) {
+               try {
+                       if (A == null)
+                               return B == null ? null : B;
+                       // Todo enhanced this method
+                       else
+                               return A.getNode().getDepth() <= B.getNode().getDepth() ? A : B;
+               } catch (RepositoryException re) {
+                       throw new EclipseUiException("Cannot find ancestor", re);
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/Refresh.java
new file mode 100644 (file)
index 0000000..036e70a
--- /dev/null
@@ -0,0 +1,43 @@
+package org.argeo.cms.e4.jcr.handlers;
+
+import java.util.List;
+
+import javax.inject.Named;
+
+import org.argeo.cms.e4.jcr.JcrBrowserView;
+import org.argeo.cms.ui.jcr.JcrBrowserUtils;
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+
+/**
+ * Force the selected objects of the active view to be refreshed doing the
+ * following:
+ * <ol>
+ * <li>The model objects are recomputed</li>
+ * <li>the view is refreshed</li>
+ * </ol>
+ */
+public class Refresh {
+
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService,
+                       ESelectionService selectionService) {
+
+               JcrBrowserView view = (JcrBrowserView) part.getObject();
+               List<?> selection = (List<?>) selectionService.getSelection();
+
+               if (selection != null && !selection.isEmpty()) {
+                       for (Object obj : selection)
+                               if (obj instanceof TreeParent) {
+                                       TreeParent tp = (TreeParent) obj;
+                                       JcrBrowserUtils.forceRefreshIfNeeded(tp);
+                                       view.refresh(obj);
+                               }
+               } else if (view instanceof JcrBrowserView)
+                       view.refresh(null); // force full refresh
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/RenameNode.java
new file mode 100644 (file)
index 0000000..97674ab
--- /dev/null
@@ -0,0 +1,59 @@
+package org.argeo.cms.e4.jcr.handlers;
+
+import java.util.List;
+
+import javax.inject.Named;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.cms.e4.jcr.JcrBrowserView;
+import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.dialogs.SingleValue;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+
+/**
+ * Canonically call JCR Session#move(String, String) on the first element
+ * returned by HandlerUtil#getActiveWorkbenchWindow()
+ * (...getActivePage().getSelection()), if it is a {@link SingleJcrNodeElem}.
+ * The user must then fill a new name in and confirm
+ */
+public class RenameNode {
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, EPartService partService,
+                       ESelectionService selectionService) {
+               List<?> selection = (List<?>) selectionService.getSelection();
+               if (selection == null || selection.size() != 1)
+                       return;
+               JcrBrowserView view = (JcrBrowserView) part.getObject();
+
+               Object element = selection.get(0);
+               if (element instanceof SingleJcrNodeElem) {
+                       SingleJcrNodeElem sjn = (SingleJcrNodeElem) element;
+                       Node node = sjn.getNode();
+                       Session session = null;
+                       String newName = null;
+                       String oldPath = null;
+                       try {
+                               newName = SingleValue.ask("New node name", "Please provide a new name for [" + node.getName() + "]");
+                               // TODO sanity check and user feedback
+                               newName = JcrUtils.replaceInvalidChars(newName);
+                               oldPath = node.getPath();
+                               session = node.getSession();
+                               session.move(oldPath, JcrUtils.parentPath(oldPath) + "/" + newName);
+                               session.save();
+
+                               // Manually refresh the browser view. Must be enhanced
+                               view.refresh(sjn);
+                       } catch (RepositoryException e) {
+                               throw new EclipseUiException("Unable to rename " + node + " to " + newName, e);
+                       }
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/handlers/package-info.java
new file mode 100644 (file)
index 0000000..4e075e2
--- /dev/null
@@ -0,0 +1,2 @@
+/** JCR browser handlers. */
+package org.argeo.cms.e4.jcr.handlers;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/jcr/package-info.java
new file mode 100644 (file)
index 0000000..3e92fb0
--- /dev/null
@@ -0,0 +1,2 @@
+/** JCR browser perspective. */
+package org.argeo.cms.e4.jcr;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/AbstractOsgiComposite.java
new file mode 100644 (file)
index 0000000..4fd1d68
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.cms.e4.maintenance;
+
+import java.util.Collection;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+abstract class AbstractOsgiComposite extends Composite {
+       private static final long serialVersionUID = -4097415973477517137L;
+       protected final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+       protected final CmsLog log = CmsLog.getLog(getClass());
+
+       public AbstractOsgiComposite(Composite parent, int style) {
+               super(parent, style);
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               setLayout(CmsSwtUtils.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/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/Browse.java
new file mode 100644 (file)
index 0000000..a536da0
--- /dev/null
@@ -0,0 +1,576 @@
+package org.argeo.cms.e4.maintenance;
+
+import static org.eclipse.swt.SWT.RIGHT;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.LinkedHashMap;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.argeo.api.cms.ux.Cms2DSize;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsLink;
+import org.argeo.cms.ui.widgets.EditableImage;
+import org.argeo.cms.ui.widgets.Img;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+public class Browse implements CmsUiProvider {
+
+       // Some local constants to experiment. should be cleaned
+       private final static String BROWSE_PREFIX = "browse#";
+       private final static int THUMBNAIL_WIDTH = 400;
+       private final static int COLUMN_WIDTH = 160;
+       private DateFormat timeFormatter = new SimpleDateFormat("dd-MM-yyyy', 'HH:mm");
+
+       // keep a cache of the opened nodes
+       // Key is the path
+       private LinkedHashMap<String, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<String, Browse.FilterEntitiesVirtualTable>();
+       private Composite nodeDisplayParent;
+       private Composite colViewer;
+       private ScrolledComposite scrolledCmp;
+       private Text parentPathTxt;
+       private Text filterTxt;
+       private Node currEdited;
+
+       private String initialPath;
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               if (context == null)
+                       // return null;
+                       throw new CmsException("Context cannot be null");
+               GridLayout layout = CmsSwtUtils.noSpaceGridLayout();
+               layout.numColumns = 2;
+               parent.setLayout(layout);
+
+               // Left
+               Composite leftCmp = new Composite(parent, SWT.NO_FOCUS);
+               leftCmp.setLayoutData(CmsSwtUtils.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 = CmsSwtUtils.noSpaceGridLayout();
+               parent.setLayout(layout);
+               Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
+               filterCmp.setLayoutData(CmsSwtUtils.fillWidth());
+
+               // top filter
+               addFilterPanel(filterCmp);
+
+               // scrolled composite
+               scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS);
+               scrolledCmp.setLayoutData(CmsSwtUtils.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(CmsSwtUtils.noSpaceGridLayout());
+               createBrowserColumn(parent, context);
+               return null;
+       }
+
+       private Control createBrowserColumn(Composite parent, Node context) throws RepositoryException {
+               // TODO style is not correctly managed.
+               FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context);
+               // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style());
+               table.filterList("*");
+               table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
+               browserCols.put(context.getPath(), table);
+               return null;
+       }
+
+       public void addFilterPanel(Composite parent) {
+
+               parent.setLayout(CmsSwtUtils.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(CmsSwtUtils.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;
+                       CmsSwtUtils.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(CmsSwtUtils.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 Cms2DSize imageWidth = new Cms2DSize(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);
+               CmsSwtUtils.markup(contextL);
+               contextL.setText("<b>" + context.getName() + "</b>");
+               new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType().getName());
+
+               // Children
+               for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) {
+                       Node child = nIt.nextNode();
+                       new CmsLink(child.getName(), BROWSE_PREFIX + child.getPath()).createUi(parent, context);
+                       new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType().getName());
+               }
+
+               // Properties
+               for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) {
+                       Property property = pIt.nextProperty();
+                       Label label = new Label(parent, SWT.NONE);
+                       label.setText(property.getName());
+                       label.setToolTipText(JcrUtils.getPropertyDefinitionAsString(property));
+                       new Label(parent, SWT.NONE).setText(getPropAsString(property));
+               }
+
+               return null;
+       }
+
+       private boolean isImg(Node node) throws RepositoryException {
+               // TODO support images
+               return false;
+//             return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE);
+       }
+
+       private String getPropAsString(Property property) throws RepositoryException {
+               String result = "";
+               if (property.isMultiple()) {
+                       result = getMultiAsString(property, ", ");
+               } else {
+                       Value value = property.getValue();
+                       if (value.getType() == PropertyType.BINARY)
+                               result = "<binary>";
+                       else if (value.getType() == PropertyType.DATE)
+                               result = timeFormatter.format(value.getDate().getTime());
+                       else
+                               result = value.getString();
+               }
+               return result;
+       }
+
+       private String getMultiAsString(Property property, String separator) throws RepositoryException {
+               if (separator == null)
+                       separator = "; ";
+               Value[] values = property.getValues();
+               StringBuilder builder = new StringBuilder();
+               for (Value val : values) {
+                       String currStr = val.getString();
+                       if (!"".equals(currStr.trim()))
+                               builder.append(currStr).append(separator);
+               }
+               if (builder.lastIndexOf(separator) >= 0)
+                       return builder.substring(0, builder.length() - separator.length());
+               else
+                       return builder.toString();
+       }
+
+       /** Almost canonical implementation of a table that display entities */
+       private class FilterEntitiesVirtualTable extends Composite {
+               private static final long serialVersionUID = 8798147431706283824L;
+
+               // Context
+               private Node context;
+
+               // UI Objects
+               private TableViewer entityViewer;
+
+               // enable management of multiple columns
+               Node getNode() {
+                       return context;
+               }
+
+               @Override
+               public boolean setFocus() {
+                       if (entityViewer.getTable().isDisposed())
+                               return false;
+                       if (entityViewer.getSelection().isEmpty()) {
+                               Object first = entityViewer.getElementAt(0);
+                               if (first != null) {
+                                       entityViewer.setSelection(new StructuredSelection(first), true);
+                               }
+                       }
+                       return entityViewer.getTable().setFocus();
+               }
+
+               void filterList(String filter) {
+                       try {
+                               NodeIterator nit = context.getNodes(filter);
+                               refreshFilteredList(nit);
+                       } catch (RepositoryException e) {
+                               throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter, e);
+                       }
+
+               }
+
+               public FilterEntitiesVirtualTable(Composite parent, int style, Node context) {
+                       super(parent, SWT.NO_FOCUS);
+                       this.context = context;
+                       populate();
+               }
+
+               protected void populate() {
+                       Composite parent = this;
+                       GridLayout layout = CmsSwtUtils.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(CmsSwtUtils.noSpaceGridLayout());
+
+                       entityViewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.SINGLE);
+                       Table table = entityViewer.getTable();
+
+                       table.setLayoutData(CmsSwtUtils.fillAll());
+                       table.setLinesVisible(true);
+                       table.setHeaderVisible(false);
+                       CmsSwtUtils.markup(table);
+
+                       CmsSwtUtils.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/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/ConnectivityDeploymentUi.java
new file mode 100644 (file)
index 0000000..97f3e67
--- /dev/null
@@ -0,0 +1,48 @@
+package org.argeo.cms.e4.maintenance;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+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));
+               CmsSwtUtils.markup(label);
+               label.setText(text.toString());
+       }
+
+       protected boolean isDeployed() {
+               return bc.getServiceReference(UserAdmin.class) != null;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DataDeploymentUi.java
new file mode 100644 (file)
index 0000000..ef95bde
--- /dev/null
@@ -0,0 +1,139 @@
+package org.argeo.cms.e4.maintenance;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.swt.CmsSwtUtils;
+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,
+                               "(" + CmsConstants.CN + "=*)");
+               StringBuffer text = new StringBuffer();
+               text.append("<span style='font-variant: small-caps;'>Jackrabbit Repositories</span><br/>");
+               for (ServiceReference<RepositoryContext> sr : contexts) {
+                       RepositoryContext repositoryContext = bc.getService(sr);
+                       String alias = sr.getProperty(CmsConstants.CN).toString();
+                       String rootNodeId = repositoryContext.getRootNodeId().toString();
+                       RepositoryConfig repositoryConfig = repositoryContext.getRepositoryConfig();
+                       Path repoHomePath = new File(repositoryConfig.getHomeDir()).toPath().toAbsolutePath();
+                       // TODO check data store
+
+                       text.append("<b>" + alias + "</b><br/>");
+                       text.append("rootNodeId: " + rootNodeId + "<br/>");
+                       try {
+                               FileStore fileStore = Files.getFileStore(repoHomePath);
+                               text.append("partition: " + fileStore.toString() + "<br/>");
+                               text.append(
+                                               percentUsed(fileStore) + " used (" + humanReadable(fileStore.getUsableSpace()) + " free)<br/>");
+                       } catch (IOException e) {
+                               log.error("Cannot check fileStore for " + repoHomePath, e);
+                       }
+               }
+               Label label = new Label(parent, SWT.NONE);
+               label.setData(new GridData(SWT.FILL, SWT.FILL, false, false));
+               CmsSwtUtils.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/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/DeploymentEntryPoint.java
new file mode 100644 (file)
index 0000000..e713f53
--- /dev/null
@@ -0,0 +1,96 @@
+package org.argeo.cms.e4.maintenance;
+
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsDeployment;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+class DeploymentEntryPoint {
+       private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+       protected void createContents(Composite parent) {
+               // FIXME manage authentication if needed
+               // if (!CurrentUser.roles().contains(AuthConstants.ROLE_ADMIN))
+               // return;
+
+               // parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               if (isDesktop()) {
+                       parent.setLayout(new GridLayout(2, true));
+               } else {
+                       // TODO add scrolling
+                       parent.setLayout(new GridLayout(1, true));
+               }
+
+               initHighLevelSummary(parent);
+
+               Group securityGroup = createHighLevelGroup(parent, "Security");
+               securityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+               new SecurityDeploymentUi(securityGroup, SWT.NONE);
+
+               Group dataGroup = createHighLevelGroup(parent, "Data");
+               dataGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+               new DataDeploymentUi(dataGroup, SWT.NONE);
+
+               Group logGroup = createHighLevelGroup(parent, "Notifications");
+               logGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
+               new LogDeploymentUi(logGroup, SWT.NONE);
+
+               Group connectivityGroup = createHighLevelGroup(parent, "Connectivity");
+               new ConnectivityDeploymentUi(connectivityGroup, SWT.NONE);
+               connectivityGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true));
+
+       }
+
+       private void initHighLevelSummary(Composite parent) {
+               Composite composite = new Composite(parent, SWT.NONE);
+               GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
+               if (isDesktop())
+                       gridData.horizontalSpan = 3;
+               composite.setLayoutData(gridData);
+               composite.setLayout(new FillLayout());
+
+               ServiceReference<CmsState> nodeStateRef = bc.getServiceReference(CmsState.class);
+               if (nodeStateRef == null)
+                       throw new IllegalStateException("No CMS state available");
+               CmsState nodeState = bc.getService(nodeStateRef);
+               ServiceReference<CmsContext> nodeDeploymentRef = bc.getServiceReference(CmsContext.class);
+               Label label = new Label(composite, SWT.WRAP);
+               CmsSwtUtils.markup(label);
+               if (nodeDeploymentRef == null) {
+                       label.setText("Not yet deployed on <br>" + nodeState.getHostname() + "</br>, please configure below.");
+               } else {
+                       Object stateUuid = nodeStateRef.getProperty(CmsConstants.CN);
+                       CmsContext 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);
+               CmsSwtUtils.markup(group);
+               return group;
+       }
+
+       private boolean isDesktop() {
+               return true;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/LogDeploymentUi.java
new file mode 100644 (file)
index 0000000..fa5d3da
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.cms.e4.maintenance;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+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));
+               CmsSwtUtils.markup(logDisplay);
+               Enumeration<LogEntry> logEntries = (Enumeration<LogEntry>) logReader.getLog();
+               while (logEntries.hasMoreElements())
+                       logDisplay.append(printEntry(logEntries.nextElement()));
+       }
+
+       private String printEntry(LogEntry entry) {
+               StringBuilder sb = new StringBuilder();
+               GregorianCalendar calendar = new GregorianCalendar(TimeZone.getDefault());
+               calendar.setTimeInMillis(entry.getTime());
+               sb.append(dateFormat.format(calendar.getTime())).append(' ');
+               sb.append(entry.getMessage());
+               sb.append('\n');
+               return sb.toString();
+       }
+
+       @Override
+       public void logged(LogEntry entry) {
+               if (display.isDisposed())
+                       return;
+               display.asyncExec(() -> {
+                       if (logDisplay.isDisposed())
+                               return;
+                       logDisplay.append(printEntry(entry));
+               });
+               display.wake();
+       }
+
+       // @Override
+       // public void dispose() {
+       // super.dispose();
+       // getService(LogReaderService.class).removeLogListener(this);
+       // }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/MaintenanceStyles.java
new file mode 100644 (file)
index 0000000..df1be51
--- /dev/null
@@ -0,0 +1,10 @@
+package org.argeo.cms.e4.maintenance;
+
+/** Specific styles used by the various maintenance pages . */
+public interface MaintenanceStyles {
+       // General
+       public final static String PREFIX = "maintenance_";
+
+       // Browser
+       public final static String BROWSER_COLUMN = "browser_column";
+       }
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/NonAdminPage.java
new file mode 100644 (file)
index 0000000..cb38ce8
--- /dev/null
@@ -0,0 +1,30 @@
+package org.argeo.cms.e4.maintenance;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.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(CmsSwtUtils.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/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/SecurityDeploymentUi.java
new file mode 100644 (file)
index 0000000..3492c54
--- /dev/null
@@ -0,0 +1,85 @@
+package org.argeo.cms.e4.maintenance;
+
+import java.net.URI;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+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));
+               CmsSwtUtils.markup(label);
+               label.setText(text.toString());
+       }
+
+       protected boolean isDeployed() {
+               return bc.getServiceReference(UserAdmin.class) != null;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/maintenance/package-info.java
new file mode 100644 (file)
index 0000000..e4d2ad4
--- /dev/null
@@ -0,0 +1,2 @@
+/** Maintenance perspective. */
+package org.argeo.cms.e4.maintenance;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundleNode.java
new file mode 100644 (file)
index 0000000..e953683
--- /dev/null
@@ -0,0 +1,46 @@
+package org.argeo.cms.e4.monitoring;
+
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+/** A tree element representing a {@link Bundle} */
+class BundleNode extends TreeParent {
+       private final Bundle bundle;
+
+       public BundleNode(Bundle bundle) {
+               this(bundle, false);
+       }
+
+       @SuppressWarnings("rawtypes")
+       public BundleNode(Bundle bundle, boolean hasChildren) {
+               super(bundle.getSymbolicName());
+               this.bundle = bundle;
+
+               if (hasChildren) {
+                       // REFERENCES
+                       ServiceReference[] usedServices = bundle.getServicesInUse();
+                       if (usedServices != null) {
+                               for (ServiceReference sr : usedServices) {
+                                       if (sr != null)
+                                               addChild(new ServiceReferenceNode(sr, false));
+                               }
+                       }
+
+                       // SERVICES
+                       ServiceReference[] registeredServices = bundle
+                                       .getRegisteredServices();
+                       if (registeredServices != null) {
+                               for (ServiceReference sr : registeredServices) {
+                                       if (sr != null)
+                                               addChild(new ServiceReferenceNode(sr, true));
+                               }
+                       }
+               }
+
+       }
+
+       Bundle getBundle() {
+               return bundle;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/BundlesView.java
new file mode 100644 (file)
index 0000000..c639255
--- /dev/null
@@ -0,0 +1,114 @@
+//package org.argeo.eclipse.ui.workbench.osgi;
+//public class BundlesView {}
+
+package org.argeo.cms.e4.monitoring;
+
+import javax.annotation.PostConstruct;
+
+import org.argeo.eclipse.ui.ColumnViewerComparator;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * Overview of the bundles as a table. Equivalent to Equinox 'ss' console
+ * command.
+ */
+public class BundlesView {
+       private final static BundleContext bc = FrameworkUtil.getBundle(BundlesView.class).getBundleContext();
+       private TableViewer viewer;
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               viewer = new TableViewer(parent);
+               viewer.setContentProvider(new BundleContentProvider());
+               viewer.getTable().setHeaderVisible(true);
+
+               EclipseUiSpecificUtils.enableToolTipSupport(viewer);
+
+               // ID
+               TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(30);
+               column.getColumn().setText("ID");
+               column.getColumn().setAlignment(SWT.RIGHT);
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -3122136344359358605L;
+
+                       public String getText(Object element) {
+                               return Long.toString(((Bundle) element).getBundleId());
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // State
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(18);
+               column.getColumn().setText("State");
+               column.setLabelProvider(new StateLabelProvider());
+               new ColumnViewerComparator(column);
+
+               // Symbolic name
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(250);
+               column.getColumn().setText("Symbolic Name");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -4280840684440451080L;
+
+                       public String getText(Object element) {
+                               return ((Bundle) element).getSymbolicName();
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // Version
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(250);
+               column.getColumn().setText("Version");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = 6871926308708629989L;
+
+                       public String getText(Object element) {
+                               Bundle bundle = (org.osgi.framework.Bundle) element;
+                               return bundle.getVersion().toString();
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               viewer.setInput(bc);
+
+       }
+
+       @Focus
+       public void setFocus() {
+               if (viewer != null)
+                       viewer.getControl().setFocus();
+       }
+
+       /** Content provider managing the array of bundles */
+       private static class BundleContentProvider implements IStructuredContentProvider {
+               private static final long serialVersionUID = -8533792785725875977L;
+
+               public Object[] getElements(Object inputElement) {
+                       if (inputElement instanceof BundleContext) {
+                               BundleContext bc = (BundleContext) inputElement;
+                               return bc.getBundles();
+                       }
+                       return null;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/CmsSessionsView.java
new file mode 100644 (file)
index 0000000..95b1eb2
--- /dev/null
@@ -0,0 +1,173 @@
+//package org.argeo.eclipse.ui.workbench.osgi;
+//public class BundlesView {}
+
+package org.argeo.cms.e4.monitoring;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.argeo.api.cms.CmsSession;
+import org.argeo.cms.auth.RoleNameUtils;
+import org.argeo.eclipse.ui.ColumnViewerComparator;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.argeo.util.LangUtils;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Overview of the active CMS sessions.
+ */
+public class CmsSessionsView {
+       private final static BundleContext bc = FrameworkUtil.getBundle(CmsSessionsView.class).getBundleContext();
+
+       private TableViewer viewer;
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               viewer = new TableViewer(parent);
+               viewer.setContentProvider(new CmsSessionContentProvider());
+               viewer.getTable().setHeaderVisible(true);
+
+               EclipseUiSpecificUtils.enableToolTipSupport(viewer);
+
+               int longColWidth = 150;
+               int smallColWidth = 100;
+
+               // Display name
+               TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(longColWidth);
+               column.getColumn().setText("User");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -5234573509093747505L;
+
+                       public String getText(Object element) {
+                               return ((CmsSession) element).getDisplayName();
+                       }
+
+                       public String getToolTipText(Object element) {
+                               return ((CmsSession) element).getUserDn().toString();
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // Creation time
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(smallColWidth);
+               column.getColumn().setText("Since");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -5234573509093747505L;
+
+                       public String getText(Object element) {
+                               return LangUtils.since(((CmsSession) element).getCreationTime());
+                       }
+
+                       public String getToolTipText(Object element) {
+                               return ((CmsSession) element).getCreationTime().toString();
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // Username
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(smallColWidth);
+               column.getColumn().setText("Username");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -5234573509093747505L;
+
+                       public String getText(Object element) {
+                               String userDn = ((CmsSession) element).getUserDn();
+                               return RoleNameUtils.getLastRdnValue(userDn);
+                       }
+
+                       public String getToolTipText(Object element) {
+                               return ((CmsSession) element).getUserDn().toString();
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // UUID
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(smallColWidth);
+               column.getColumn().setText("UUID");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -5234573509093747505L;
+
+                       public String getText(Object element) {
+                               return ((CmsSession) element).getUuid().toString();
+                       }
+
+                       public String getToolTipText(Object element) {
+                               return getText(element);
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               // Local ID
+               column = new TableViewerColumn(viewer, SWT.NONE);
+               column.getColumn().setWidth(smallColWidth);
+               column.getColumn().setText("Local ID");
+               column.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = -5234573509093747505L;
+
+                       public String getText(Object element) {
+                               return ((CmsSession) element).getLocalId();
+                       }
+
+                       public String getToolTipText(Object element) {
+                               return getText(element);
+                       }
+               });
+               new ColumnViewerComparator(column);
+
+               viewer.setInput(bc);
+
+       }
+
+       @Focus
+       public void setFocus() {
+               if (viewer != null)
+                       viewer.getControl().setFocus();
+       }
+
+       /** Content provider managing the array of bundles */
+       private static class CmsSessionContentProvider implements IStructuredContentProvider {
+               private static final long serialVersionUID = -8533792785725875977L;
+
+               public Object[] getElements(Object inputElement) {
+                       if (inputElement instanceof BundleContext) {
+                               BundleContext bc = (BundleContext) inputElement;
+                               Collection<ServiceReference<CmsSession>> srs;
+                               try {
+                                       srs = bc.getServiceReferences(CmsSession.class, null);
+                               } catch (InvalidSyntaxException e) {
+                                       throw new IllegalArgumentException("Cannot retrieve CMS sessions", e);
+                               }
+                               List<CmsSession> res = new ArrayList<>();
+                               for (ServiceReference<CmsSession> sr : srs) {
+                                       res.add(bc.getService(sr));
+                               }
+                               return res.toArray();
+                       }
+                       return null;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ModulesView.java
new file mode 100644 (file)
index 0000000..6317882
--- /dev/null
@@ -0,0 +1,91 @@
+package org.argeo.cms.e4.monitoring;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/** The OSGi runtime from a module perspective. */
+public class ModulesView {
+       private final static BundleContext bc = FrameworkUtil.getBundle(ModulesView.class).getBundleContext();
+       private TreeViewer viewer;
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+               viewer.setContentProvider(new ModulesContentProvider());
+               viewer.setLabelProvider(new ModulesLabelProvider());
+               viewer.setInput(bc);
+       }
+
+       @Focus
+       public void setFocus() {
+               viewer.getTree().setFocus();
+       }
+
+       private class ModulesContentProvider implements ITreeContentProvider {
+               private static final long serialVersionUID = 3819934804640641721L;
+
+               public Object[] getElements(Object inputElement) {
+                       return getChildren(inputElement);
+               }
+
+               public Object[] getChildren(Object parentElement) {
+                       if (parentElement instanceof BundleContext) {
+                               BundleContext bundleContext = (BundleContext) parentElement;
+                               Bundle[] bundles = bundleContext.getBundles();
+
+                               List<BundleNode> modules = new ArrayList<BundleNode>();
+                               for (Bundle bundle : bundles) {
+                                       if (bundle.getState() == Bundle.ACTIVE)
+                                               modules.add(new BundleNode(bundle, true));
+                               }
+                               return modules.toArray();
+                       } else if (parentElement instanceof TreeParent) {
+                               return ((TreeParent) parentElement).getChildren();
+                       } else {
+                               return null;
+                       }
+               }
+
+               public Object getParent(Object element) {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public boolean hasChildren(Object element) {
+                       if (element instanceof TreeParent) {
+                               return ((TreeParent) element).hasChildren();
+                       }
+                       return false;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+
+       private class ModulesLabelProvider extends StateLabelProvider {
+               private static final long serialVersionUID = 5290046145534824722L;
+
+               @Override
+               public String getText(Object element) {
+                       if (element instanceof BundleNode)
+                               return element.toString() + " [" + ((BundleNode) element).getBundle().getBundleId() + "]";
+                       return element.toString();
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiConfigurationsView.java
new file mode 100644 (file)
index 0000000..5db8bd1
--- /dev/null
@@ -0,0 +1,163 @@
+package org.argeo.cms.e4.monitoring;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.argeo.cms.swt.CmsException;
+import org.argeo.util.LangUtils;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class OsgiConfigurationsView {
+       private final static BundleContext bc = FrameworkUtil.getBundle(OsgiConfigurationsView.class).getBundleContext();
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               ConfigurationAdmin configurationAdmin = bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
+
+               TreeViewer viewer = new TreeViewer(parent);
+               // viewer.getTree().setHeaderVisible(true);
+
+               TreeViewerColumn tvc = new TreeViewerColumn(viewer, SWT.NONE);
+               tvc.getColumn().setWidth(400);
+               tvc.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = 835407996597566763L;
+
+                       @Override
+                       public String getText(Object element) {
+                               if (element instanceof Configuration) {
+                                       return ((Configuration) element).getPid();
+                               } else if (element instanceof Prop) {
+                                       return ((Prop) element).key;
+                               }
+                               return super.getText(element);
+                       }
+
+                       @Override
+                       public Image getImage(Object element) {
+                               if (element instanceof Configuration)
+                                       return OsgiExplorerImages.CONFIGURATION;
+                               return null;
+                       }
+
+               });
+
+               tvc = new TreeViewerColumn(viewer, SWT.NONE);
+               tvc.getColumn().setWidth(400);
+               tvc.setLabelProvider(new ColumnLabelProvider() {
+                       private static final long serialVersionUID = 6999659261190014687L;
+
+                       @Override
+                       public String getText(Object element) {
+                               if (element instanceof Configuration) {
+                                       // return ((Configuration) element).getFactoryPid();
+                                       return null;
+                               } else if (element instanceof Prop) {
+                                       return ((Prop) element).value.toString();
+                               }
+                               return super.getText(element);
+                       }
+               });
+
+               viewer.setContentProvider(new ConfigurationsContentProvider());
+               viewer.setInput(configurationAdmin);
+       }
+
+       static class ConfigurationsContentProvider implements ITreeContentProvider {
+               private static final long serialVersionUID = -4892768279440981042L;
+               private ConfigurationComparator configurationComparator = new ConfigurationComparator();
+
+               @Override
+               public void dispose() {
+               }
+
+               @Override
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+
+               @Override
+               public Object[] getElements(Object inputElement) {
+                       ConfigurationAdmin configurationAdmin = (ConfigurationAdmin) inputElement;
+                       try {
+                               Configuration[] configurations = configurationAdmin.listConfigurations(null);
+                               Arrays.sort(configurations, configurationComparator);
+                               return configurations;
+                       } catch (IOException | InvalidSyntaxException e) {
+                               throw new CmsException("Cannot list configurations", e);
+                       }
+               }
+
+               @Override
+               public Object[] getChildren(Object parentElement) {
+                       if (parentElement instanceof Configuration) {
+                               List<Prop> res = new ArrayList<>();
+                               Configuration configuration = (Configuration) parentElement;
+                               Dictionary<String, Object> props = configuration.getProperties();
+                               keys: for (String key : LangUtils.keys(props)) {
+                                       if (Constants.SERVICE_PID.equals(key))
+                                               continue keys;
+                                       if (ConfigurationAdmin.SERVICE_FACTORYPID.equals(key))
+                                               continue keys;
+                                       res.add(new Prop(configuration, key, props.get(key)));
+                               }
+                               return res.toArray(new Prop[res.size()]);
+                       }
+                       return null;
+               }
+
+               @Override
+               public Object getParent(Object element) {
+                       if (element instanceof Prop)
+                               return ((Prop) element).configuration;
+                       return null;
+               }
+
+               @Override
+               public boolean hasChildren(Object element) {
+                       if (element instanceof Configuration)
+                               return true;
+                       return false;
+               }
+
+       }
+
+       static class Prop {
+               final Configuration configuration;
+               final String key;
+               final Object value;
+
+               public Prop(Configuration configuration, String key, Object value) {
+                       this.configuration = configuration;
+                       this.key = key;
+                       this.value = value;
+               }
+
+       }
+
+       static class ConfigurationComparator implements Comparator<Configuration> {
+
+               @Override
+               public int compare(Configuration o1, Configuration o2) {
+                       return o1.getPid().compareTo(o2.getPid());
+               }
+
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/OsgiExplorerImages.java
new file mode 100644 (file)
index 0000000..7217fe6
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.cms.e4.monitoring;
+
+import org.argeo.cms.ui.theme.CmsImages;
+import org.eclipse.swt.graphics.Image;
+
+/** Shared icons. */
+public class OsgiExplorerImages extends CmsImages {
+       public final static Image INSTALLED = createIcon("installed.gif");
+       public final static Image RESOLVED = createIcon("resolved.gif");
+       public final static Image STARTING = createIcon("starting.gif");
+       public final static Image ACTIVE = createIcon("active.gif");
+       public final static Image SERVICE_PUBLISHED = createIcon("service_published.gif");
+       public final static Image SERVICE_REFERENCED = createIcon("service_referenced.gif");
+       public final static Image CONFIGURATION = createIcon("node.gif");
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/ServiceReferenceNode.java
new file mode 100644 (file)
index 0000000..1c60811
--- /dev/null
@@ -0,0 +1,46 @@
+package org.argeo.cms.e4.monitoring;
+
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+/** A tree element representing a {@link ServiceReference} */
+@SuppressWarnings({ "rawtypes" })
+class ServiceReferenceNode extends TreeParent {
+       private final ServiceReference serviceReference;
+       private final boolean published;
+
+       public ServiceReferenceNode(ServiceReference serviceReference,
+                       boolean published) {
+               super(serviceReference.toString());
+               this.serviceReference = serviceReference;
+               this.published = published;
+
+               if (isPublished()) {
+                       Bundle[] usedBundles = serviceReference.getUsingBundles();
+                       if (usedBundles != null) {
+                               for (Bundle b : usedBundles) {
+                                       if (b != null)
+                                               addChild(new BundleNode(b));
+                               }
+                       }
+               } else {
+                       Bundle provider = serviceReference.getBundle();
+                       addChild(new BundleNode(provider));
+               }
+
+               for (String key : serviceReference.getPropertyKeys()) {
+                       addChild(new TreeParent(key + "="
+                                       + serviceReference.getProperty(key)));
+               }
+
+       }
+
+       public ServiceReference getServiceReference() {
+               return serviceReference;
+       }
+
+       public boolean isPublished() {
+               return published;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/StateLabelProvider.java
new file mode 100644 (file)
index 0000000..5cb5b65
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.cms.e4.monitoring;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/** Label provider showing the sate of bundles */
+class StateLabelProvider extends ColumnLabelProvider {
+       private static final long serialVersionUID = -7885583135316000733L;
+
+       @Override
+       public Image getImage(Object element) {
+               int state;
+               if (element instanceof Bundle)
+                       state = ((Bundle) element).getState();
+               else if (element instanceof BundleNode)
+                       state = ((BundleNode) element).getBundle().getState();
+               else if (element instanceof ServiceReferenceNode)
+                       if (((ServiceReferenceNode) element).isPublished())
+                               return OsgiExplorerImages.SERVICE_PUBLISHED;
+                       else
+                               return OsgiExplorerImages.SERVICE_REFERENCED;
+               else
+                       return null;
+
+               switch (state) {
+               case Bundle.UNINSTALLED:
+                       return OsgiExplorerImages.INSTALLED;
+               case Bundle.INSTALLED:
+                       return OsgiExplorerImages.INSTALLED;
+               case Bundle.RESOLVED:
+                       return OsgiExplorerImages.RESOLVED;
+               case Bundle.STARTING:
+                       return OsgiExplorerImages.STARTING;
+               case Bundle.STOPPING:
+                       return OsgiExplorerImages.STARTING;
+               case Bundle.ACTIVE:
+                       return OsgiExplorerImages.ACTIVE;
+               default:
+                       return null;
+               }
+       }
+
+       @Override
+       public String getText(Object element) {
+               return null;
+       }
+
+       @Override
+       public String getToolTipText(Object element) {
+               Bundle bundle = (Bundle) element;
+               Integer state = bundle.getState();
+               switch (state) {
+               case Bundle.UNINSTALLED:
+                       return "UNINSTALLED";
+               case Bundle.INSTALLED:
+                       return "INSTALLED";
+               case Bundle.RESOLVED:
+                       return "RESOLVED";
+               case Bundle.STARTING:
+                       String activationPolicy = bundle.getHeaders()
+                                       .get(Constants.BUNDLE_ACTIVATIONPOLICY).toString();
+
+                       // .get("Bundle-ActivationPolicy").toString();
+                       // FIXME constant triggers the compilation failure
+                       if (activationPolicy != null
+                                       && activationPolicy.equals(Constants.ACTIVATION_LAZY))
+                               // && activationPolicy.equals("lazy"))
+                               // FIXME constant triggers the compilation failure
+                               // && activationPolicy.equals(Constants.ACTIVATION_LAZY))
+                               return "<<LAZY>>";
+                       return "STARTING";
+               case Bundle.STOPPING:
+                       return "STOPPING";
+               case Bundle.ACTIVE:
+                       return "ACTIVE";
+               default:
+                       return null;
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/monitoring/package-info.java
new file mode 100644 (file)
index 0000000..873bf31
--- /dev/null
@@ -0,0 +1,2 @@
+/** Monitoring perspective. */
+package org.argeo.cms.e4.monitoring;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/package-info.java
new file mode 100644 (file)
index 0000000..233119c
--- /dev/null
@@ -0,0 +1,2 @@
+/** Eclipse 4 user interfaces. */
+package org.argeo.cms.e4;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/parts/EgoDashboard.java
new file mode 100644 (file)
index 0000000..f2a73f2
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.cms.e4.parts;
+
+import java.time.ZonedDateTime;
+
+import javax.annotation.PostConstruct;
+
+import org.argeo.api.cms.CmsSession;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** A canonical view of the logged in user. */
+public class EgoDashboard {
+//     private BundleContext bc = FrameworkUtil.getBundle(EgoDashboard.class).getBundleContext();
+
+       @PostConstruct
+       public void createPartControl(Composite p) {
+               p.setLayout(new GridLayout());
+               String username = CurrentUser.getUsername();
+
+               CmsSwtUtils.lbl(p, "<strong>" + CurrentUser.getDisplayName() + "</strong>");
+               CmsSwtUtils.txt(p, username);
+               CmsSwtUtils.lbl(p, "Roles:");
+               roles: for (String role : CurrentUser.roles()) {
+                       if (username.equals(role))
+                               continue roles;
+                       CmsSwtUtils.txt(p, role);
+               }
+
+//             Subject subject = Subject.getSubject(AccessController.getContext());
+//             if (subject != null) {
+               CmsSession cmsSession = CurrentUser.getCmsSession();
+               ZonedDateTime loggedIndSince = cmsSession.getCreationTime();
+               CmsSwtUtils.lbl(p, "Session:");
+               CmsSwtUtils.txt(p, cmsSession.getUuid().toString());
+               CmsSwtUtils.lbl(p, "Logged in since:");
+               CmsSwtUtils.txt(p, loggedIndSince.toString());
+//             }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/AbstractRoleEditor.java
new file mode 100644 (file)
index 0000000..137f762
--- /dev/null
@@ -0,0 +1,287 @@
+package org.argeo.cms.e4.users;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
+import org.argeo.cms.ui.eclipse.forms.IManagedForm;
+import org.argeo.cms.ui.eclipse.forms.ManagedForm;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.e4.ui.di.Persist;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+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.Text;
+import org.osgi.service.useradmin.Authorization;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Editor for a user, might be a user or a group. */
+public abstract class AbstractRoleEditor {
+
+       // public final static String USER_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID +
+       // ".userEditor";
+       // public final static String GROUP_EDITOR_ID = WorkbenchUiPlugin.PLUGIN_ID +
+       // ".groupEditor";
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       protected UserAdminWrapper userAdminWrapper;
+
+       @Inject
+       private MPart mPart;
+
+       // @Inject
+       // Composite parent;
+
+       private UserAdmin userAdmin;
+
+       // Context
+       private User user;
+       private String username;
+
+       private NameChangeListener listener;
+
+       private ManagedForm managedForm;
+
+       // public void init(IEditorSite site, IEditorInput input) throws
+       // PartInitException {
+       @PostConstruct
+       public void init(Composite parent) {
+               this.userAdmin = userAdminWrapper.getUserAdmin();
+               username = mPart.getPersistedState().get(LdapAttrs.uid.name());
+               user = (User) userAdmin.getRole(username);
+
+               listener = new NameChangeListener(Display.getCurrent());
+               userAdminWrapper.addListener(listener);
+               updateEditorTitle(null);
+
+               managedForm = new ManagedForm(parent) {
+
+                       @Override
+                       public void staleStateChanged() {
+                               refresh();
+                       }
+               };
+               ScrolledComposite scrolled = managedForm.getForm();
+               Composite body = new Composite(scrolled, SWT.NONE);
+               scrolled.setContent(body);
+               createUi(body);
+               managedForm.refresh();
+       }
+
+       abstract void createUi(Composite parent);
+
+       /**
+        * returns the list of all authorizations for the given user or of the current
+        * displayed user if parameter is null
+        */
+       protected List<User> getFlatGroups(User aUser) {
+               Authorization currAuth;
+               if (aUser == null)
+                       currAuth = userAdmin.getAuthorization(this.user);
+               else
+                       currAuth = userAdmin.getAuthorization(aUser);
+
+               String[] roles = currAuth.getRoles();
+
+               List<User> groups = new ArrayList<User>();
+               for (String roleStr : roles) {
+                       User currRole = (User) userAdmin.getRole(roleStr);
+                       if (currRole != null && !groups.contains(currRole))
+                               groups.add(currRole);
+               }
+               return groups;
+       }
+
+       protected IManagedForm getManagedForm() {
+               return managedForm;
+       }
+
+       /** Exposes the user (or group) that is displayed by the current editor */
+       protected User getDisplayedUser() {
+               return user;
+       }
+
+       private void setDisplayedUser(User user) {
+               this.user = user;
+       }
+
+       void updateEditorTitle(String title) {
+               if (title == null) {
+                       String commonName = UserAdminUtils.getProperty(user, LdapAttrs.cn.name());
+                       title = "".equals(commonName) ? user.getName() : commonName;
+               }
+               setPartName(title);
+       }
+
+       protected void setPartName(String name) {
+               mPart.setLabel(name);
+       }
+
+       // protected void addPages() {
+       // try {
+       // if (user.getType() == Role.GROUP)
+       // addPage(new GroupMainPage(this, userAdminWrapper, repository, nodeInstance));
+       // else
+       // addPage(new UserMainPage(this, userAdminWrapper));
+       // } catch (Exception e) {
+       // throw new CmsException("Cannot add pages", e);
+       // }
+       // }
+
+       @Persist
+       public void doSave(IProgressMonitor monitor) {
+               userAdminWrapper.beginTransactionIfNeeded();
+               commitPages(true);
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+               // firePropertyChange(PROP_DIRTY);
+               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user));
+       }
+
+       protected void commitPages(boolean b) {
+               managedForm.commit(b);
+       }
+
+       @PreDestroy
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+               managedForm.dispose();
+       }
+
+       // CONTROLERS FOR THIS EDITOR AND ITS PAGES
+
+       class NameChangeListener extends UiUserAdminListener {
+               public NameChangeListener(Display display) {
+                       super(display);
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       Role changedRole = event.getRole();
+                       if (changedRole == null || changedRole.equals(user)) {
+                               updateEditorTitle(null);
+                               User reloadedUser = (User) userAdminWrapper.getUserAdmin().getRole(user.getName());
+                               setDisplayedUser(reloadedUser);
+                       }
+               }
+       }
+
+       class MainInfoListener extends UiUserAdminListener {
+               private final AbstractFormPart part;
+
+               public MainInfoListener(Display display, AbstractFormPart part) {
+                       super(display);
+                       this.part = part;
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       // Rollback
+                       if (event.getRole() == null)
+                               part.markStale();
+               }
+       }
+
+       class GroupChangeListener extends UiUserAdminListener {
+               private final AbstractFormPart part;
+
+               public GroupChangeListener(Display display, AbstractFormPart part) {
+                       super(display);
+                       this.part = part;
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       // always mark as stale
+                       part.markStale();
+               }
+       }
+
+       /** Registers a listener that will notify this part */
+       class FormPartML implements ModifyListener {
+               private static final long serialVersionUID = 6299808129505381333L;
+               private AbstractFormPart formPart;
+
+               public FormPartML(AbstractFormPart generalPart) {
+                       this.formPart = generalPart;
+               }
+
+               public void modifyText(ModifyEvent e) {
+                       // Discard event when the control does not have the focus, typically
+                       // to avoid all editors being marked as dirty during a Rollback
+                       if (((Control) e.widget).isFocusControl())
+                               formPart.markDirty();
+               }
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+
+       /** Creates label and multiline text. */
+       Text createLMT(Composite parent, String label, String value) {
+               Label lbl = new Label(parent, SWT.NONE);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+               Text text = new Text(parent, SWT.NONE);
+               text.setText(value);
+               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, true));
+               return text;
+       }
+
+       /** Creates label and password. */
+       Text createLP(Composite parent, String label, String value) {
+               Label lbl = new Label(parent, SWT.NONE);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+               Text text = new Text(parent, SWT.PASSWORD | SWT.BORDER);
+               text.setText(value);
+               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false));
+               return text;
+       }
+
+       /** Creates label and text. */
+       Text createLT(Composite parent, String label, String value) {
+               Label lbl = new Label(parent, SWT.NONE);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+               lbl.setFont(EclipseUiUtils.getBoldFont(parent));
+               Text text = new Text(parent, SWT.BORDER);
+               text.setText(value);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
+               return text;
+       }
+
+       Text createReadOnlyLT(Composite parent, String label, String value) {
+               Label lbl = new Label(parent, SWT.NONE);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+               lbl.setFont(EclipseUiUtils.getBoldFont(parent));
+               Text text = new Text(parent, SWT.NONE);
+               text.setText(value);
+               text.setLayoutData(new GridData(SWT.LEAD, SWT.FILL, true, false));
+               text.setEditable(false);
+               // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
+               return text;
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/CmsWorkbenchStyles.java
new file mode 100644 (file)
index 0000000..07df312
--- /dev/null
@@ -0,0 +1,8 @@
+package org.argeo.cms.e4.users;
+
+/** Centralize the declaration of Workbench specific CSS Styles */
+interface CmsWorkbenchStyles {
+
+       // Specific People layouting
+       String WORKBENCH_FORM_TEXT = "workbench_form_text";
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java
new file mode 100644 (file)
index 0000000..d54f8bc
--- /dev/null
@@ -0,0 +1,566 @@
+package org.argeo.cms.e4.users;
+
+import static org.argeo.api.cms.CmsContext.WORKGROUP;
+import static org.argeo.cms.auth.UserAdminUtils.setProperty;
+import static org.argeo.util.naming.LdapAttrs.businessCategory;
+import static org.argeo.util.naming.LdapAttrs.description;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.providers.CommonNameLP;
+import org.argeo.cms.e4.users.providers.MailLP;
+import org.argeo.cms.e4.users.providers.RoleIconLP;
+import org.argeo.cms.e4.users.providers.UserFilter;
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
+import org.argeo.cms.ui.eclipse.forms.IManagedForm;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.jcr.JcrException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.transaction.WorkTransaction;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+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.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+//import org.eclipse.ui.forms.AbstractFormPart;
+//import org.eclipse.ui.forms.IManagedForm;
+//import org.eclipse.ui.forms.SectionPart;
+//import org.eclipse.ui.forms.editor.FormEditor;
+//import org.eclipse.ui.forms.editor.FormPage;
+//import org.eclipse.ui.forms.widgets.FormToolkit;
+//import org.eclipse.ui.forms.widgets.ScrolledForm;
+//import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Display/edit main properties of a given group */
+public class GroupEditor extends AbstractRoleEditor {
+       // final static String ID = "GroupEditor.mainPage";
+
+       @Inject
+       private EPartService partService;
+
+       // private final UserEditor editor;
+       @Inject
+       private Repository repository;
+       @Inject
+       private CmsContext nodeInstance;
+       // private final UserAdminWrapper userAdminWrapper;
+       private Session groupsSession;
+
+       // public GroupMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper,
+       // Repository repository,
+       // NodeInstance nodeInstance) {
+       // super(editor, ID, "Main");
+       // try {
+       // session = repository.login();
+       // } catch (RepositoryException e) {
+       // throw new CmsException("Cannot retrieve session of in MainGroupPage
+       // constructor", e);
+       // }
+       // this.editor = (UserEditor) editor;
+       // this.userAdminWrapper = userAdminWrapper;
+       // this.nodeInstance = nodeInstance;
+       // }
+
+       // protected void createFormContent(final IManagedForm mf) {
+       // ScrolledForm form = mf.getForm();
+       // Composite body = form.getBody();
+       // GridLayout mainLayout = new GridLayout();
+       // body.setLayout(mainLayout);
+       // Group group = (Group) editor.getDisplayedUser();
+       // appendOverviewPart(body, group);
+       // appendMembersPart(body, group);
+       // }
+
+       @Override
+       protected void createUi(Composite parent) {
+               try {
+                       groupsSession = repository.login(CmsConstants.SRV_WORKSPACE);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot retrieve session", e);
+               }
+               // ScrolledForm form = mf.getForm();
+               // Composite body = form.getBody();
+               // Composite body = new Composite(parent, SWT.NONE);
+               Composite body = parent;
+               GridLayout mainLayout = new GridLayout();
+               body.setLayout(mainLayout);
+               Group group = (Group) getDisplayedUser();
+               appendOverviewPart(body, group);
+               appendMembersPart(body, group);
+       }
+
+       @PreDestroy
+       public void dispose() {
+               JcrUtils.logoutQuietly(groupsSession);
+               super.dispose();
+       }
+
+       /** Creates the general section */
+       protected void appendOverviewPart(final Composite parent, final Group group) {
+               Composite body = new Composite(parent, SWT.NONE);
+               // GridLayout layout = new GridLayout(5, false);
+               GridLayout layout = new GridLayout(2, false);
+               body.setLayout(layout);
+               body.setLayoutData(CmsSwtUtils.fillWidth());
+
+               String cn = UserAdminUtils.getProperty(group, LdapAttrs.cn.name());
+               createReadOnlyLT(body, "Name", cn);
+               createReadOnlyLT(body, "DN", group.getName());
+               createReadOnlyLT(body, "Domain", UserAdminUtils.getDomainName(group));
+
+               // Description
+               Label descLbl = new Label(body, SWT.LEAD);
+               descLbl.setFont(EclipseUiUtils.getBoldFont(body));
+               descLbl.setText("Description");
+               descLbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, true, false, 2, 1));
+               final Text descTxt = new Text(body, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER);
+               GridData gd = EclipseUiUtils.fillWidth();
+               gd.heightHint = 50;
+               gd.horizontalSpan = 2;
+               descTxt.setLayoutData(gd);
+
+               // Mark as workgroup
+               Link markAsWorkgroupLk = new Link(body, SWT.NONE);
+               markAsWorkgroupLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
+
+               // create form part (controller)
+               final AbstractFormPart part = new AbstractFormPart() {
+
+                       private MainInfoListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = new MainInfoListener(parent.getDisplay(), this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       public void commit(boolean onSave) {
+                               // group.getProperties().put(LdapAttrs.description.name(), descTxt.getText());
+                               setProperty(group, description, descTxt.getText());
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void refresh() {
+                               // dnTxt.setText(group.getName());
+                               // cnTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.cn.name()));
+                               descTxt.setText(UserAdminUtils.getProperty(group, LdapAttrs.description.name()));
+                               Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn);
+                               if (workgroupHome == null)
+                                       markAsWorkgroupLk.setText("<a>Mark as workgroup</a>");
+                               else
+                                       markAsWorkgroupLk.setText("Configured as workgroup");
+                               parent.layout(true, true);
+                               super.refresh();
+                       }
+               };
+
+               markAsWorkgroupLk.addSelectionListener(new SelectionAdapter() {
+                       private static final long serialVersionUID = -6439340898096365078L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+
+                               boolean confirmed = MessageDialog.openConfirm(parent.getShell(), "Mark as workgroup",
+                                               "Are you sure you want to mark " + cn + " as being a workgroup? ");
+                               if (confirmed) {
+                                       Node workgroupHome = CmsJcrUtils.getGroupHome(groupsSession, cn);
+                                       if (workgroupHome != null)
+                                               return; // already marked as workgroup, do nothing
+                                       else {
+                                               // improve transaction management
+                                               userAdminWrapper.beginTransactionIfNeeded();
+                                               nodeInstance.createWorkgroup(group.getName());
+                                               setProperty(group, businessCategory, WORKGROUP);
+                                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
+                                               part.refresh();
+                                       }
+                               }
+                       }
+               });
+
+               ModifyListener defaultListener = new FormPartML(part);
+               descTxt.addModifyListener(defaultListener);
+               getManagedForm().addPart(part);
+       }
+
+       /** Filtered table with members. Has drag and drop ability */
+       protected void appendMembersPart(Composite parent, Group group) {
+               // Section section = tk.createSection(parent, Section.TITLE_BAR);
+               // section.setText("Members");
+               // section.setLayoutData(EclipseUiUtils.fillAll());
+
+               Composite body = new Composite(parent, SWT.BORDER);
+               body.setLayout(new GridLayout());
+               // section.setClient(body);
+               body.setLayoutData(EclipseUiUtils.fillAll());
+
+               // Define the displayed columns
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
+               columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150));
+               // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
+               // 240));
+
+               // Create and configure the table
+               LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL,
+                               userAdminWrapper.getUserAdmin());
+
+               userViewerCmp.setColumnDefinitions(columnDefs);
+               userViewerCmp.populate(true, false);
+               userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+
+               // Controllers
+               TableViewer userViewer = userViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDropSupport(operations, tt,
+                               new GroupDropListener(userAdminWrapper, userViewerCmp, (Group) getDisplayedUser()));
+
+               AbstractFormPart part = new GroupMembersPart(userViewerCmp);
+               getManagedForm().addPart(part);
+
+               // remove button
+               // addRemoveAbility(toolBarManager, userViewerCmp.getTableViewer(), group);
+               Action action = new RemoveMembershipAction(userViewer, group, "Remove selected items from this group",
+                               SecurityAdminImages.ICON_REMOVE_DESC);
+
+               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+               ToolBar toolBar = toolBarManager.createControl(body);
+               toolBar.setLayoutData(CmsSwtUtils.fillWidth());
+
+               toolBarManager.add(action);
+               toolBarManager.update(true);
+
+       }
+
+       // private LdifUsersTable createMemberPart(Composite parent, Group group) {
+       //
+       // // Define the displayed columns
+       // List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+       // columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
+       // columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
+       // columnDefs.add(new ColumnDefinition(new MailLP(), "Mail", 150));
+       // // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished
+       // Name",
+       // // 240));
+       //
+       // // Create and configure the table
+       // LdifUsersTable userViewerCmp = new MyUserTableViewer(parent, SWT.MULTI |
+       // SWT.H_SCROLL | SWT.V_SCROLL,
+       // userAdminWrapper.getUserAdmin());
+       //
+       // userViewerCmp.setColumnDefinitions(columnDefs);
+       // userViewerCmp.populate(true, false);
+       // userViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+       //
+       // // Controllers
+       // TableViewer userViewer = userViewerCmp.getTableViewer();
+       // userViewer.addDoubleClickListener(new
+       // UserTableDefaultDClickListener(partService));
+       // int operations = DND.DROP_COPY | DND.DROP_MOVE;
+       // Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+       // userViewer.addDropSupport(operations, tt,
+       // new GroupDropListener(userAdminWrapper, userViewerCmp, (Group)
+       // getDisplayedUser()));
+       //
+       // // userViewerCmp.refresh();
+       // return userViewerCmp;
+       // }
+
+       // Local viewers
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private final UserFilter userFilter;
+
+               public MyUserTableViewer(Composite parent, int style, UserAdmin userAdmin) {
+                       super(parent, style, true);
+                       userFilter = new UserFilter();
+
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       // reload user and set it in the editor
+                       Group group = (Group) getDisplayedUser();
+                       Role[] roles = group.getMembers();
+                       List<User> users = new ArrayList<User>();
+                       userFilter.setSearchText(filter);
+                       // userFilter.setShowSystemRole(true);
+                       for (Role role : roles)
+                               // if (role.getType() == Role.GROUP)
+                               if (userFilter.select(null, null, role))
+                                       users.add((User) role);
+                       return users;
+               }
+       }
+
+       // private void addRemoveAbility(ToolBarManager toolBarManager, TableViewer
+       // userViewer, Group group) {
+       // // Section section = sectionPart.getSection();
+       // // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+       // // ToolBar toolbar = toolBarManager.createControl(parent);
+       // // ToolBar toolbar = toolBarManager.getControl();
+       // // final Cursor handCursor = new Cursor(toolbar.getDisplay(),
+       // SWT.CURSOR_HAND);
+       // // toolbar.setCursor(handCursor);
+       // // toolbar.addDisposeListener(new DisposeListener() {
+       // // private static final long serialVersionUID = 3882131405820522925L;
+       // //
+       // // public void widgetDisposed(DisposeEvent e) {
+       // // if ((handCursor != null) && (handCursor.isDisposed() == false)) {
+       // // handCursor.dispose();
+       // // }
+       // // }
+       // // });
+       //
+       // Action action = new RemoveMembershipAction(userViewer, group, "Remove
+       // selected items from this group",
+       // SecurityAdminImages.ICON_REMOVE_DESC);
+       // toolBarManager.add(action);
+       // toolBarManager.update(true);
+       // // section.setTextClient(toolbar);
+       // }
+
+       private class RemoveMembershipAction extends Action {
+               private static final long serialVersionUID = -1337713097184522588L;
+
+               private final TableViewer userViewer;
+               private final Group group;
+
+               RemoveMembershipAction(TableViewer userViewer, Group group, String name, ImageDescriptor img) {
+                       super(name, img);
+                       this.userViewer = userViewer;
+                       this.group = group;
+               }
+
+               @Override
+               public void run() {
+                       ISelection selection = userViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+
+                       @SuppressWarnings("unchecked")
+                       Iterator<User> it = ((IStructuredSelection) selection).iterator();
+                       List<User> users = new ArrayList<User>();
+                       while (it.hasNext()) {
+                               User currUser = it.next();
+                               users.add(currUser);
+                       }
+
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       for (User user : users) {
+                               group.removeMember(user);
+                       }
+                       userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
+               }
+       }
+
+       // LOCAL CONTROLLERS
+       private class GroupMembersPart extends AbstractFormPart {
+               private final LdifUsersTable userViewer;
+               // private final Group group;
+
+               private GroupChangeListener listener;
+
+               public GroupMembersPart(LdifUsersTable userViewer) {
+                       // super(section);
+                       this.userViewer = userViewer;
+                       // this.group = group;
+               }
+
+               @Override
+               public void initialize(IManagedForm form) {
+                       super.initialize(form);
+                       listener = new GroupChangeListener(userViewer.getDisplay(), GroupMembersPart.this);
+                       userAdminWrapper.addListener(listener);
+               }
+
+               @Override
+               public void dispose() {
+                       userAdminWrapper.removeListener(listener);
+                       super.dispose();
+               }
+
+               @Override
+               public void refresh() {
+                       userViewer.refresh();
+                       super.refresh();
+               }
+       }
+
+       /**
+        * Defines this table as being a potential target to add group membership
+        * (roles) to this group
+        */
+       private class GroupDropListener extends ViewerDropAdapter {
+               private static final long serialVersionUID = 2893468717831451621L;
+
+               private final UserAdminWrapper userAdminWrapper;
+               // private final LdifUsersTable myUserViewerCmp;
+               private final Group myGroup;
+
+               public GroupDropListener(UserAdminWrapper userAdminWrapper, LdifUsersTable userTableViewerCmp, Group group) {
+                       super(userTableViewerCmp.getTableViewer());
+                       this.userAdminWrapper = userAdminWrapper;
+                       this.myGroup = group;
+                       // this.myUserViewerCmp = userTableViewerCmp;
+               }
+
+               @Override
+               public boolean validateDrop(Object target, int operation, TransferData transferType) {
+                       // Target is always OK in a list only view
+                       // TODO check if not a string
+                       boolean validDrop = true;
+                       return validDrop;
+               }
+
+               @Override
+               public void drop(DropTargetEvent event) {
+                       // TODO Is there an opportunity to perform the check before?
+                       String newUserName = (String) event.data;
+                       UserAdmin myUserAdmin = userAdminWrapper.getUserAdmin();
+                       Role role = myUserAdmin.getRole(newUserName);
+                       if (role.getType() == Role.GROUP) {
+                               Group newGroup = (Group) role;
+                               Shell shell = getViewer().getControl().getShell();
+                               // Sanity checks
+                               if (myGroup == newGroup) { // Equality
+                                       MessageDialog.openError(shell, "Forbidden addition ", "A group cannot be a member of itself.");
+                                       return;
+                               }
+
+                               // Cycle
+                               String myName = myGroup.getName();
+                               List<User> myMemberships = getFlatGroups(myGroup);
+                               if (myMemberships.contains(newGroup)) {
+                                       MessageDialog.openError(shell, "Forbidden addition: cycle",
+                                                       "Cannot add " + newUserName + " to group " + myName + ". This would create a cycle");
+                                       return;
+                               }
+
+                               // Already member
+                               List<User> newGroupMemberships = getFlatGroups(newGroup);
+                               if (newGroupMemberships.contains(myGroup)) {
+                                       MessageDialog.openError(shell, "Forbidden addition",
+                                                       "Cannot add " + newUserName + " to group " + myName + ", this membership already exists");
+                                       return;
+                               }
+                               userAdminWrapper.beginTransactionIfNeeded();
+                               myGroup.addMember(newGroup);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup));
+                       } else if (role.getType() == Role.USER) {
+                               // TODO check if the group is already member of this group
+                               WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded();
+                               User user = (User) role;
+                               myGroup.addMember(user);
+                               if (UserAdminWrapper.COMMIT_ON_SAVE)
+                                       try {
+                                               transaction.commit();
+                                       } catch (Exception e) {
+                                               throw new IllegalStateException(
+                                                               "Cannot commit transaction " + "after user group membership update", e);
+                                       }
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup));
+                       }
+                       super.drop(event);
+               }
+
+               @Override
+               public boolean performDrop(Object data) {
+                       // myUserViewerCmp.refresh();
+                       return true;
+               }
+       }
+
+       // LOCAL HELPERS
+       // private Composite addSection(FormToolkit tk, Composite parent) {
+       // Section section = tk.createSection(parent, SWT.NO_FOCUS);
+       // section.setLayoutData(EclipseUiUtils.fillWidth());
+       // Composite body = tk.createComposite(section, SWT.WRAP);
+       // body.setLayoutData(EclipseUiUtils.fillAll());
+       // section.setClient(body);
+       // return body;
+       // }
+
+       /** Creates label and text. */
+       // private Text createLT(Composite parent, String label, String value) {
+       // FormToolkit toolkit = getManagedForm().getToolkit();
+       // Label lbl = toolkit.createLabel(parent, label);
+       // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+       // lbl.setFont(EclipseUiUtils.getBoldFont(parent));
+       // Text text = toolkit.createText(parent, value, SWT.BORDER);
+       // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+       // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
+       // return text;
+       // }
+       //
+       // Text createReadOnlyLT(Composite parent, String label, String value) {
+       // FormToolkit toolkit = getManagedForm().getToolkit();
+       // Label lbl = toolkit.createLabel(parent, label);
+       // lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
+       // lbl.setFont(EclipseUiUtils.getBoldFont(parent));
+       // Text text = toolkit.createText(parent, value, SWT.NONE);
+       // text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+       // text.setEditable(false);
+       // CmsUiUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
+       // return text;
+       // }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupsView.java
new file mode 100644 (file)
index 0000000..3bf4891
--- /dev/null
@@ -0,0 +1,251 @@
+package org.argeo.cms.e4.users;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.e4.users.providers.CommonNameLP;
+import org.argeo.cms.e4.users.providers.DomainNameLP;
+import org.argeo.cms.e4.users.providers.RoleIconLP;
+import org.argeo.cms.e4.users.providers.UserDragListener;
+import org.argeo.cms.swt.CmsException;
+//import org.argeo.cms.ui.workbench.WorkbenchUiPlugin;
+//import org.argeo.cms.ui.workbench.internal.useradmin.UiUserAdminListener;
+//import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper;
+//import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP;
+//import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP;
+//import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP;
+//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserDragListener;
+//import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+//import org.eclipse.ui.part.ViewPart;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** List all groups with filter */
+public class GroupsView {
+       private final static CmsLog log = CmsLog.getLog(GroupsView.class);
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".groupsView";
+
+       @Inject
+       private EPartService partService;
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+
+       // UI Objects
+       private LdifUsersTable groupTableViewerCmp;
+       private TableViewer userViewer;
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+       private UserAdminListener listener;
+
+       @PostConstruct
+       public void createPartControl(Composite parent, ESelectionService selectionService) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
+
+               // Define the displayed columns
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 19));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
+               // Only show technical DN to admin
+               // if (isAdmin)
+               // columnDefs.add(new ColumnDefinition(new UserNameLP(),
+               // "Distinguished Name", 300));
+
+               // Create and configure the table
+               groupTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+
+               groupTableViewerCmp.setColumnDefinitions(columnDefs);
+               // if (isAdmin)
+               // groupTableViewerCmp.populateWithStaticFilters(false, false);
+               // else
+               groupTableViewerCmp.populate(true, false);
+
+               groupTableViewerCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               // Links
+               userViewer = groupTableViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
+               // getViewSite().setSelectionProvider(userViewer);
+               userViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+
+                       @Override
+                       public void selectionChanged(SelectionChangedEvent event) {
+                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
+                               selectionService.setSelection(selection.toList());
+                       }
+               });
+
+               // Really?
+               groupTableViewerCmp.refresh();
+
+               // Drag and drop
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer));
+
+               // // Register a useradmin listener
+               // listener = new UserAdminListener() {
+               // @Override
+               // public void roleChanged(UserAdminEvent event) {
+               // if (userViewer != null && !userViewer.getTable().isDisposed())
+               // refresh();
+               // }
+               // };
+               // userAdminWrapper.addListener(listener);
+               // }
+
+               // Register a useradmin listener
+               listener = new MyUiUAListener(parent.getDisplay());
+               userAdminWrapper.addListener(listener);
+       }
+
+       private class MyUiUAListener extends UiUserAdminListener {
+               public MyUiUAListener(Display display) {
+                       super(display);
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       if (userViewer != null && !userViewer.getTable().isDisposed())
+                               refresh();
+               }
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private boolean showSystemRoles = true;
+
+               private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.cn.name(), LdapAttrs.DN };
+
+               public MyUserTableViewer(Composite parent, int style) {
+                       super(parent, style);
+                       showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
+               }
+
+               protected void populateStaticFilters(Composite staticFilterCmp) {
+                       staticFilterCmp.setLayout(new GridLayout());
+                       final Button showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
+                       showSystemRoleBtn.setText("Show system roles");
+                       showSystemRoles = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
+                       showSystemRoleBtn.setSelection(showSystemRoles);
+
+                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -7033424592697691676L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       showSystemRoles = showSystemRoleBtn.getSelection();
+                                       refresh();
+                               }
+
+                       });
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Role[] roles;
+                       try {
+                               StringBuilder builder = new StringBuilder();
+                               StringBuilder tmpBuilder = new StringBuilder();
+                               if (EclipseUiUtils.notEmpty(filter))
+                                       for (String prop : knownProps) {
+                                               tmpBuilder.append("(");
+                                               tmpBuilder.append(prop);
+                                               tmpBuilder.append("=*");
+                                               tmpBuilder.append(filter);
+                                               tmpBuilder.append("*)");
+                                       }
+                               if (tmpBuilder.length() > 1) {
+                                       builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
+                                                       .append(LdapObjs.groupOfNames.name()).append(")");
+                                       // hide tokens
+                                       builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.TOKENS_BASEDN)
+                                                       .append("))");
+
+                                       if (!showSystemRoles)
+                                               builder.append("(!(").append(LdapAttrs.DN).append("=*").append(CmsConstants.ROLES_BASEDN)
+                                                               .append("))");
+                                       builder.append("(|");
+                                       builder.append(tmpBuilder.toString());
+                                       builder.append("))");
+                               } else {
+                                       if (!showSystemRoles)
+                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
+                                                               .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*")
+                                                               .append(CmsConstants.ROLES_BASEDN).append("))(!(").append(LdapAttrs.DN).append("=*")
+                                                               .append(CmsConstants.TOKENS_BASEDN).append(")))");
+                                       else
+                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
+                                                               .append(LdapObjs.groupOfNames.name()).append(")(!(").append(LdapAttrs.DN).append("=*")
+                                                               .append(CmsConstants.TOKENS_BASEDN).append(")))");
+
+                               }
+                               roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
+                       } catch (InvalidSyntaxException e) {
+                               throw new CmsException("Unable to get roles with filter: " + filter, e);
+                       }
+                       List<User> users = new ArrayList<User>();
+                       for (Role role : roles)
+                               if (!users.contains(role))
+                                       users.add((User) role);
+                               else
+                                       log.warn("Duplicated role: " + role);
+
+                       return users;
+               }
+       }
+
+       public void refresh() {
+               groupTableViewerCmp.refresh();
+       }
+
+       @PreDestroy
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+       }
+
+       @Focus
+       public void setFocus() {
+               groupTableViewerCmp.setFocus();
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/SecurityAdminImages.java
new file mode 100644 (file)
index 0000000..7bbe3c7
--- /dev/null
@@ -0,0 +1,19 @@
+package org.argeo.cms.e4.users;
+
+import org.argeo.cms.ui.theme.CmsImages;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+
+/** Shared icons that must be declared programmatically . */
+public class SecurityAdminImages extends CmsImages {
+       private final static String PREFIX = "icons/";
+
+       public final static ImageDescriptor ICON_REMOVE_DESC = createDesc(PREFIX + "delete.png");
+       public final static ImageDescriptor ICON_USER_DESC = createDesc(PREFIX + "person.png");
+
+       public final static Image ICON_USER = ICON_USER_DESC.createImage();
+       public final static Image ICON_GROUP = createImg(PREFIX + "group.png");
+       public final static Image ICON_WORKGROUP = createImg(PREFIX + "workgroup.png");
+       public final static Image ICON_ROLE = createImg(PREFIX + "role.gif");
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java
new file mode 100644 (file)
index 0000000..fb48a47
--- /dev/null
@@ -0,0 +1,34 @@
+package org.argeo.cms.e4.users;
+
+import org.argeo.util.transaction.WorkTransaction;
+
+/** First effort to centralize back end methods used by the user admin UI */
+public class UiAdminUtils {
+       /*
+        * INTERNAL METHODS: Below methods are meant to stay here and are not part
+        * of a potential generic backend to manage the useradmin
+        */
+       /** Easily notify the ActiveWindow that the transaction had a state change */
+       public final static void notifyTransactionStateChange(
+                       WorkTransaction userTransaction) {
+//             try {
+//                     IWorkbenchWindow aww = PlatformUI.getWorkbench()
+//                                     .getActiveWorkbenchWindow();
+//                     ISourceProviderService sourceProviderService = (ISourceProviderService) aww
+//                                     .getService(ISourceProviderService.class);
+//                     UserTransactionProvider esp = (UserTransactionProvider) sourceProviderService
+//                                     .getSourceProvider(UserTransactionProvider.TRANSACTION_STATE);
+//                     esp.fireTransactionStateChange();
+//             } catch (Exception e) {
+//                     throw new CmsException("Unable to begin transaction", e);
+//             }
+       }
+
+       /**
+        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
+        * Thanks to <a href=
+        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
+        * >this tip</a>.
+        */
+       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiUserAdminListener.java
new file mode 100644 (file)
index 0000000..eb64aba
--- /dev/null
@@ -0,0 +1,27 @@
+package org.argeo.cms.e4.users;
+
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** Convenience class to insure the call to refresh is done in the UI thread */
+public abstract class UiUserAdminListener implements UserAdminListener {
+
+       private final Display display;
+
+       public UiUserAdminListener(Display display) {
+               this.display = display;
+       }
+
+       @Override
+       public void roleChanged(final UserAdminEvent event) {
+               display.asyncExec(new Runnable() {
+                       @Override
+                       public void run() {
+                               roleChangedToUiThread(event);
+                       }
+               });
+       }
+
+       public abstract void roleChangedToUiThread(UserAdminEvent event);
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java
new file mode 100644 (file)
index 0000000..d120ae9
--- /dev/null
@@ -0,0 +1,153 @@
+package org.argeo.cms.e4.users;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.osgi.useradmin.UserDirectory;
+import org.argeo.util.directory.DirectoryConf;
+import org.argeo.util.transaction.WorkTransaction;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** Centralise interaction with the UserAdmin in this bundle */
+public class UserAdminWrapper {
+
+       private UserAdmin userAdmin;
+       // private ServiceReference<UserAdmin> userAdminServiceReference;
+//     private Set<String> uris;
+       private Map<UserDirectory, Hashtable<String, String>> userDirectories = Collections
+                       .synchronizedMap(new LinkedHashMap<>());
+       private WorkTransaction userTransaction;
+
+       // First effort to simplify UX while managing users and groups
+       public final static boolean COMMIT_ON_SAVE = true;
+
+       // Registered listeners
+       List<UserAdminListener> listeners = new ArrayList<UserAdminListener>();
+
+       /**
+        * Starts a transaction if necessary. Should always been called together with
+        * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the
+        * security model changes have been performed.
+        */
+       public WorkTransaction beginTransactionIfNeeded() {
+               try {
+                       // UserTransaction userTransaction = getUserTransaction();
+                       if (userTransaction.isNoTransactionStatus()) {
+                               userTransaction.begin();
+                               // UiAdminUtils.notifyTransactionStateChange(userTransaction);
+                       }
+                       return userTransaction;
+               } catch (Exception e) {
+                       throw new CmsException("Unable to begin transaction", e);
+               }
+       }
+
+       /**
+        * Depending on the current application configuration, it will either commit the
+        * current transaction or throw a notification that the transaction state has
+        * changed (In the later case, it must be called from the UI thread).
+        */
+       public void commitOrNotifyTransactionStateChange() {
+               try {
+                       // UserTransaction userTransaction = getUserTransaction();
+                       if (userTransaction.isNoTransactionStatus())
+                               return;
+
+                       if (UserAdminWrapper.COMMIT_ON_SAVE)
+                               userTransaction.commit();
+                       else
+                               UiAdminUtils.notifyTransactionStateChange(userTransaction);
+               } catch (Exception e) {
+                       throw new CmsException("Unable to clean transaction", e);
+               }
+       }
+
+       // TODO implement safer mechanism
+       public void addListener(UserAdminListener userAdminListener) {
+               if (!listeners.contains(userAdminListener))
+                       listeners.add(userAdminListener);
+       }
+
+       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 (UserDirectory userDirectory : userDirectories.keySet()) {
+                       Boolean readOnly = userDirectory.isReadOnly();
+                       String baseDn = userDirectory.getContext();
+
+                       if (onlyWritable && readOnly)
+                               continue;
+                       if (baseDn.equalsIgnoreCase(CmsConstants.ROLES_BASEDN))
+                               continue;
+                       if (baseDn.equalsIgnoreCase(CmsConstants.TOKENS_BASEDN))
+                               continue;
+                       dns.put(baseDn, DirectoryConf.propertiesAsUri(userDirectories.get(userDirectory)).toString());
+
+               }
+//             for (String uri : uris) {
+//                     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(NodeConstants.ROLES_BASEDN))
+//                             continue;
+//                     if (baseDn.equalsIgnoreCase(NodeConstants.TOKENS_BASEDN))
+//                             continue;
+//                     dns.put(baseDn, uri);
+//             }
+               return dns;
+       }
+
+       public UserAdmin getUserAdmin() {
+               return userAdmin;
+       }
+
+       public WorkTransaction getUserTransaction() {
+               return userTransaction;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdmin(UserAdmin userAdmin, Map<String, String> properties) {
+               this.userAdmin = userAdmin;
+//             this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet()));
+       }
+
+       public void setUserTransaction(WorkTransaction userTransaction) {
+               this.userTransaction = userTransaction;
+       }
+
+       public void addUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
+               userDirectories.put(userDirectory, new Hashtable<>(properties));
+       }
+
+       public void removeUserDirectory(UserDirectory userDirectory, Map<String, String> properties) {
+               userDirectories.remove(userDirectory);
+       }
+
+       // public void setUserAdminServiceReference(
+       // ServiceReference<UserAdmin> userAdminServiceReference) {
+       // this.userAdminServiceReference = userAdminServiceReference;
+       // }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java
new file mode 100644 (file)
index 0000000..4fc59d3
--- /dev/null
@@ -0,0 +1,606 @@
+package org.argeo.cms.e4.users;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.providers.CommonNameLP;
+import org.argeo.cms.e4.users.providers.DomainNameLP;
+import org.argeo.cms.e4.users.providers.MailLP;
+import org.argeo.cms.e4.users.providers.UserNameLP;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.argeo.util.transaction.WorkTransaction;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Wizard to update users */
+public class UserBatchUpdateWizard extends Wizard {
+
+       private final static CmsLog log = CmsLog.getLog(UserBatchUpdateWizard.class);
+       private UserAdminWrapper userAdminWrapper;
+
+       // pages
+       private ChooseCommandWizardPage chooseCommandPage;
+       private ChooseUsersWizardPage userListPage;
+       private ValidateAndLaunchWizardPage validatePage;
+
+       // Various implemented commands keys
+       private final static String CMD_UPDATE_PASSWORD = "resetPassword";
+       private final static String CMD_UPDATE_EMAIL = "resetEmail";
+       private final static String CMD_GROUP_MEMBERSHIP = "groupMembership";
+
+       private final Map<String, String> commands = new HashMap<String, String>() {
+               private static final long serialVersionUID = 1L;
+               {
+                       put("Reset password(s)", CMD_UPDATE_PASSWORD);
+                       put("Reset email(s)", CMD_UPDATE_EMAIL);
+                       // TODO implement role / group management
+                       // put("Add/Remove from group", CMD_GROUP_MEMBERSHIP);
+               }
+       };
+
+       public UserBatchUpdateWizard(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+
+       @Override
+       public void addPages() {
+               chooseCommandPage = new ChooseCommandWizardPage();
+               addPage(chooseCommandPage);
+               userListPage = new ChooseUsersWizardPage();
+               addPage(userListPage);
+               validatePage = new ValidateAndLaunchWizardPage();
+               addPage(validatePage);
+       }
+
+       @Override
+       public boolean performFinish() {
+               if (!canFinish())
+                       return false;
+               WorkTransaction ut = userAdminWrapper.getUserTransaction();
+               if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction",
+                               "A user transaction is already existing, " + "are you sure you want to proceed ?"))
+                       return false;
+
+               // We cannot use jobs, user modifications are still meant to be done in
+               // the UIThread
+               // UpdateJob job = null;
+               // if (job != null)
+               // job.schedule();
+
+               if (CMD_UPDATE_PASSWORD.equals(chooseCommandPage.getCommand())) {
+                       char[] newValue = chooseCommandPage.getPwdValue();
+                       if (newValue == null)
+                               throw new CmsException("Password cannot be null or an empty string");
+                       ResetPassword job = new ResetPassword(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
+                       job.doUpdate();
+               } else if (CMD_UPDATE_EMAIL.equals(chooseCommandPage.getCommand())) {
+                       String newValue = chooseCommandPage.getEmailValue();
+                       if (newValue == null)
+                               throw new CmsException("Password cannot be null or an empty string");
+                       ResetEmail job = new ResetEmail(userAdminWrapper, userListPage.getSelectedUsers(), newValue);
+                       job.doUpdate();
+               }
+               return true;
+       }
+
+       public boolean canFinish() {
+               if (this.getContainer().getCurrentPage() == validatePage)
+                       return true;
+               return false;
+       }
+
+       private class ResetPassword {
+               private char[] newPwd;
+               private UserAdminWrapper userAdminWrapper;
+               private List<User> usersToUpdate;
+
+               public ResetPassword(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, char[] newPwd) {
+                       this.newPwd = newPwd;
+                       this.usersToUpdate = usersToUpdate;
+                       this.userAdminWrapper = userAdminWrapper;
+               }
+
+               @SuppressWarnings("unchecked")
+               protected void doUpdate() {
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       try {
+                               for (User user : usersToUpdate) {
+                                       // the char array is emptied after being used.
+                                       user.getCredentials().put(null, newPwd.clone());
+                               }
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot perform batch update on users", e);
+                       } finally {
+                               WorkTransaction ut = userAdminWrapper.getUserTransaction();
+                               if (!ut.isNoTransactionStatus())
+                                       ut.rollback();
+                       }
+               }
+       }
+
+       private class ResetEmail {
+               private String newEmail;
+               private UserAdminWrapper userAdminWrapper;
+               private List<User> usersToUpdate;
+
+               public ResetEmail(UserAdminWrapper userAdminWrapper, List<User> usersToUpdate, String newEmail) {
+                       this.newEmail = newEmail;
+                       this.usersToUpdate = usersToUpdate;
+                       this.userAdminWrapper = userAdminWrapper;
+               }
+
+               @SuppressWarnings("unchecked")
+               protected void doUpdate() {
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       try {
+                               for (User user : usersToUpdate) {
+                                       // the char array is emptied after being used.
+                                       user.getProperties().put(LdapAttrs.mail.name(), newEmail);
+                               }
+
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               if (!usersToUpdate.isEmpty())
+                                       userAdminWrapper.notifyListeners(
+                                                       new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, usersToUpdate.get(0)));
+                       } catch (Exception e) {
+                               throw new CmsException("Cannot perform batch update on users", e);
+                       } finally {
+                               WorkTransaction ut = userAdminWrapper.getUserTransaction();
+                               if (!ut.isNoTransactionStatus())
+                                       ut.rollback();
+                       }
+               }
+       }
+
+       // @SuppressWarnings("unused")
+       // private class AddToGroup extends UpdateJob {
+       // private String groupID;
+       // private Session session;
+       //
+       // public AddToGroup(Session session, List<Node> nodesToUpdate,
+       // String groupID) {
+       // super(session, nodesToUpdate);
+       // this.session = session;
+       // this.groupID = groupID;
+       // }
+       //
+       // protected void doUpdate(Node node) {
+       // log.info("Add/Remove to group actions are not yet implemented");
+       // // TODO implement this
+       // // try {
+       // // throw new CmsException("Not yet implemented");
+       // // } catch (RepositoryException re) {
+       // // throw new CmsException(
+       // // "Unable to update boolean value for node " + node, re);
+       // // }
+       // }
+       // }
+
+       // /**
+       // * Base privileged job that will be run asynchronously to perform the
+       // batch
+       // * update
+       // */
+       // private abstract class UpdateJob extends PrivilegedJob {
+       //
+       // private final UserAdminWrapper userAdminWrapper;
+       // private final List<User> usersToUpdate;
+       //
+       // protected abstract void doUpdate(User user);
+       //
+       // public UpdateJob(UserAdminWrapper userAdminWrapper,
+       // List<User> usersToUpdate) {
+       // super("Perform update");
+       // this.usersToUpdate = usersToUpdate;
+       // this.userAdminWrapper = userAdminWrapper;
+       // }
+       //
+       // @Override
+       // protected IStatus doRun(IProgressMonitor progressMonitor) {
+       // try {
+       // JcrMonitor monitor = new EclipseJcrMonitor(progressMonitor);
+       // int total = usersToUpdate.size();
+       // monitor.beginTask("Performing change", total);
+       // userAdminWrapper.beginTransactionIfNeeded();
+       // for (User user : usersToUpdate) {
+       // doUpdate(user);
+       // monitor.worked(1);
+       // }
+       // userAdminWrapper.getUserTransaction().commit();
+       // } catch (Exception e) {
+       // throw new CmsException(
+       // "Cannot perform batch update on users", e);
+       // } finally {
+       // UserTransaction ut = userAdminWrapper.getUserTransaction();
+       // try {
+       // if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION)
+       // ut.rollback();
+       // } catch (IllegalStateException | SecurityException
+       // | SystemException e) {
+       // log.error("Unable to rollback session in 'finally', "
+       // + "the system might be in a dirty state");
+       // e.printStackTrace();
+       // }
+       // }
+       // return Status.OK_STATUS;
+       // }
+       // }
+
+       // PAGES
+       /**
+        * Displays a combo box that enables user to choose which action to perform
+        */
+       private class ChooseCommandWizardPage extends WizardPage {
+               private static final long serialVersionUID = -8069434295293996633L;
+               private Combo chooseCommandCmb;
+               private Button trueChk;
+               private Text valueTxt;
+               private Text pwdTxt;
+               private Text pwd2Txt;
+
+               public ChooseCommandWizardPage() {
+                       super("Choose a command to run.");
+                       setTitle("Choose a command to run.");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       GridLayout gl = new GridLayout();
+                       Composite container = new Composite(parent, SWT.NO_FOCUS);
+                       container.setLayout(gl);
+
+                       chooseCommandCmb = new Combo(container, SWT.READ_ONLY);
+                       chooseCommandCmb.setLayoutData(EclipseUiUtils.fillWidth());
+                       String[] values = commands.keySet().toArray(new String[0]);
+                       chooseCommandCmb.setItems(values);
+
+                       final Composite bottomPart = new Composite(container, SWT.NO_FOCUS);
+                       bottomPart.setLayoutData(EclipseUiUtils.fillAll());
+                       bottomPart.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       chooseCommandCmb.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       if (getCommand().equals(CMD_UPDATE_PASSWORD))
+                                               populatePasswordCmp(bottomPart);
+                                       else if (getCommand().equals(CMD_UPDATE_EMAIL))
+                                               populateEmailCmp(bottomPart);
+                                       else if (getCommand().equals(CMD_GROUP_MEMBERSHIP))
+                                               populateGroupCmp(bottomPart);
+                                       else
+                                               populateBooleanFlagCmp(bottomPart);
+                                       checkPageComplete();
+                                       bottomPart.layout(true, true);
+                               }
+                       });
+                       setControl(container);
+               }
+
+               private void populateBooleanFlagCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       trueChk = new Button(parent, SWT.CHECK);
+                       trueChk.setText("Do it. (It will to the contrary if unchecked)");
+                       trueChk.setSelection(true);
+                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+               }
+
+               private void populatePasswordCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       Composite body = new Composite(parent, SWT.NO_FOCUS);
+
+                       ModifyListener ml = new ModifyListener() {
+                               private static final long serialVersionUID = -1558726363536729634L;
+
+                               @Override
+                               public void modifyText(ModifyEvent event) {
+                                       checkPageComplete();
+                               }
+                       };
+
+                       body.setLayout(new GridLayout(2, false));
+                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       pwdTxt = EclipseUiUtils.createGridLP(body, "New password", ml);
+                       pwd2Txt = EclipseUiUtils.createGridLP(body, "Repeat password", ml);
+               }
+
+               private void populateEmailCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       Composite body = new Composite(parent, SWT.NO_FOCUS);
+
+                       ModifyListener ml = new ModifyListener() {
+                               private static final long serialVersionUID = 2147704227294268317L;
+
+                               @Override
+                               public void modifyText(ModifyEvent event) {
+                                       checkPageComplete();
+                               }
+                       };
+
+                       body.setLayout(new GridLayout(2, false));
+                       body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       valueTxt = EclipseUiUtils.createGridLT(body, "New e-mail", ml);
+               }
+
+               private void checkPageComplete() {
+                       String errorMsg = null;
+                       if (chooseCommandCmb.getSelectionIndex() < 0)
+                               errorMsg = "Please select an action";
+                       else if (CMD_UPDATE_EMAIL.equals(getCommand())) {
+                               if (!valueTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN))
+                                       errorMsg = "Not a valid e-mail address";
+                       } else if (CMD_UPDATE_PASSWORD.equals(getCommand())) {
+                               if (EclipseUiUtils.isEmpty(pwdTxt.getText()) || pwdTxt.getText().length() < 4)
+                                       errorMsg = "Please enter a password that is at least 4 character long";
+                               else if (!pwdTxt.getText().equals(pwd2Txt.getText()))
+                                       errorMsg = "Passwords are different";
+                       }
+                       if (EclipseUiUtils.notEmpty(errorMsg)) {
+                               setMessage(errorMsg, WizardPage.ERROR);
+                               setPageComplete(false);
+                       } else {
+                               setMessage("Page complete, you can proceed to user choice", WizardPage.INFORMATION);
+                               setPageComplete(true);
+                       }
+
+                       getContainer().updateButtons();
+               }
+
+               private void populateGroupCmp(Composite parent) {
+                       EclipseUiUtils.clear(parent);
+                       trueChk = new Button(parent, SWT.CHECK);
+                       trueChk.setText("Add to group. (It will remove user(s) from the " + "corresponding group if unchecked)");
+                       trueChk.setSelection(true);
+                       trueChk.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+               }
+
+               protected String getCommand() {
+                       return commands.get(chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex()));
+               }
+
+               protected String getCommandLbl() {
+                       return chooseCommandCmb.getItem(chooseCommandCmb.getSelectionIndex());
+               }
+
+               @SuppressWarnings("unused")
+               protected boolean getBoleanValue() {
+                       // FIXME this is not consistent and will lead to errors.
+                       if ("argeo:enabled".equals(getCommand()))
+                               return trueChk.getSelection();
+                       else
+                               return !trueChk.getSelection();
+               }
+
+               @SuppressWarnings("unused")
+               protected String getStringValue() {
+                       String value = null;
+                       if (valueTxt != null) {
+                               value = valueTxt.getText();
+                               if ("".equals(value.trim()))
+                                       value = null;
+                       }
+                       return value;
+               }
+
+               protected char[] getPwdValue() {
+                       // We do not directly reset the password text fields: There is no
+                       // need to over secure this process: setting a pwd to multi users
+                       // at the same time is anyhow a bad practice and should be used only
+                       // in test environment or for temporary access
+                       if (pwdTxt == null || pwdTxt.isDisposed())
+                               return null;
+                       else
+                               return pwdTxt.getText().toCharArray();
+               }
+
+               protected String getEmailValue() {
+                       // We do not directly reset the password text fields: There is no
+                       // need to over secure this process: setting a pwd to multi users
+                       // at the same time is anyhow a bad practice and should be used only
+                       // in test environment or for temporary access
+                       if (valueTxt == null || valueTxt.isDisposed())
+                               return null;
+                       else
+                               return valueTxt.getText();
+               }
+       }
+
+       /**
+        * Displays a list of users with a check box to be able to choose some of them
+        */
+       private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener {
+               private static final long serialVersionUID = 7651807402211214274L;
+               private ChooseUserTableViewer userTableCmp;
+
+               public ChooseUsersWizardPage() {
+                       super("Choose Users");
+                       setTitle("Select users who will be impacted");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       Composite pageCmp = new Composite(parent, SWT.NONE);
+                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       // Define the displayed columns
+                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+                       columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
+                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
+
+                       // Only show technical DN to admin
+                       if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
+                               columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
+
+                       userTableCmp = new ChooseUserTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
+                       userTableCmp.setColumnDefinitions(columnDefs);
+                       userTableCmp.populate(true, true);
+                       userTableCmp.refresh();
+
+                       setControl(pageCmp);
+
+                       // Add listener to update message when shown
+                       final IWizardContainer wContainer = this.getContainer();
+                       if (wContainer instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
+                       }
+
+               }
+
+               @Override
+               public void pageChanged(PageChangedEvent event) {
+                       if (event.getSelectedPage() == this) {
+                               String msg = "Chosen batch action: " + chooseCommandPage.getCommandLbl();
+                               ((WizardPage) event.getSelectedPage()).setMessage(msg);
+                       }
+               }
+
+               protected List<User> getSelectedUsers() {
+                       return userTableCmp.getSelectedUsers();
+               }
+
+               private class ChooseUserTableViewer extends LdifUsersTable {
+                       private static final long serialVersionUID = 5080437561015853124L;
+                       private final String[] knownProps = { LdapAttrs.uid.name(), LdapAttrs.DN, LdapAttrs.cn.name(),
+                                       LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() };
+
+                       public ChooseUserTableViewer(Composite parent, int style) {
+                               super(parent, style);
+                       }
+
+                       @Override
+                       protected List<User> listFilteredElements(String filter) {
+                               Role[] roles;
+
+                               try {
+                                       StringBuilder builder = new StringBuilder();
+
+                                       StringBuilder tmpBuilder = new StringBuilder();
+                                       if (EclipseUiUtils.notEmpty(filter))
+                                               for (String prop : knownProps) {
+                                                       tmpBuilder.append("(");
+                                                       tmpBuilder.append(prop);
+                                                       tmpBuilder.append("=*");
+                                                       tmpBuilder.append(filter);
+                                                       tmpBuilder.append("*)");
+                                               }
+                                       if (tmpBuilder.length() > 1) {
+                                               builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
+                                                               .append(LdapObjs.inetOrgPerson.name()).append(")(|");
+                                               builder.append(tmpBuilder.toString());
+                                               builder.append("))");
+                                       } else
+                                               builder.append("(").append(LdapAttrs.objectClass.name()).append("=")
+                                                               .append(LdapObjs.inetOrgPerson.name()).append(")");
+                                       roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
+                               } catch (InvalidSyntaxException e) {
+                                       throw new CmsException("Unable to get roles with filter: " + filter, e);
+                               }
+                               List<User> users = new ArrayList<User>();
+                               for (Role role : roles)
+                                       // Prevent current logged in user to perform batch on
+                                       // himself
+                                       if (!UserAdminUtils.isCurrentUser((User) role))
+                                               users.add((User) role);
+                               return users;
+                       }
+               }
+       }
+
+       /** Summary of input data before launching the process */
+       private class ValidateAndLaunchWizardPage extends WizardPage implements IPageChangedListener {
+               private static final long serialVersionUID = 7098918351451743853L;
+               private ChosenUsersTableViewer userTableCmp;
+
+               public ValidateAndLaunchWizardPage() {
+                       super("Validate and launch");
+                       setTitle("Validate and launch");
+               }
+
+               @Override
+               public void createControl(Composite parent) {
+                       Composite pageCmp = new Composite(parent, SWT.NO_FOCUS);
+                       pageCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+                       columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
+                       columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+                       columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
+                       // Only show technical DN to admin
+                       if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
+                               columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
+                       userTableCmp = new ChosenUsersTableViewer(pageCmp, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+                       userTableCmp.setLayoutData(EclipseUiUtils.fillAll());
+                       userTableCmp.setColumnDefinitions(columnDefs);
+                       userTableCmp.populate(false, false);
+                       userTableCmp.refresh();
+                       setControl(pageCmp);
+                       // Add listener to update message when shown
+                       final IWizardContainer wContainer = this.getContainer();
+                       if (wContainer instanceof IPageChangeProvider) {
+                               ((IPageChangeProvider) wContainer).addPageChangedListener(this);
+                       }
+               }
+
+               @Override
+               public void pageChanged(PageChangedEvent event) {
+                       if (event.getSelectedPage() == this) {
+                               @SuppressWarnings({ "unchecked", "rawtypes" })
+                               Object[] values = ((ArrayList) userListPage.getSelectedUsers())
+                                               .toArray(new Object[userListPage.getSelectedUsers().size()]);
+                               userTableCmp.getTableViewer().setInput(values);
+                               String msg = "Following batch action: [" + chooseCommandPage.getCommandLbl()
+                                               + "] will be perfomed on the users listed below.\n";
+                               // + "Are you sure you want to proceed?";
+                               setMessage(msg);
+                       }
+               }
+
+               private class ChosenUsersTableViewer extends LdifUsersTable {
+                       private static final long serialVersionUID = 7814764735794270541L;
+
+                       public ChosenUsersTableViewer(Composite parent, int style) {
+                               super(parent, style);
+                       }
+
+                       @Override
+                       protected List<User> listFilteredElements(String filter) {
+                               return userListPage.getSelectedUsers();
+                       }
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserEditor.java
new file mode 100644 (file)
index 0000000..66f4420
--- /dev/null
@@ -0,0 +1,535 @@
+package org.argeo.cms.e4.users;
+
+import static org.argeo.cms.auth.UserAdminUtils.getProperty;
+import static org.argeo.util.naming.LdapAttrs.cn;
+import static org.argeo.util.naming.LdapAttrs.givenName;
+import static org.argeo.util.naming.LdapAttrs.mail;
+import static org.argeo.util.naming.LdapAttrs.sn;
+import static org.argeo.util.naming.LdapAttrs.uid;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.providers.CommonNameLP;
+import org.argeo.cms.e4.users.providers.DomainNameLP;
+import org.argeo.cms.e4.users.providers.RoleIconLP;
+import org.argeo.cms.e4.users.providers.UserFilter;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
+//import org.argeo.cms.ui.eclipse.forms.FormToolkit;
+import org.argeo.cms.ui.eclipse.forms.IManagedForm;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerDropAdapter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+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.Display;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Display/edit the properties of a given user */
+public class UserEditor extends AbstractRoleEditor {
+       // final static String ID = "UserEditor.mainPage";
+
+       @Inject
+       private EPartService partService;
+
+       // private final UserEditor editor;
+       // private UserAdminWrapper userAdminWrapper;
+
+       // Local configuration
+       // private final int PRE_TITLE_INDENT = 10;
+
+       // public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
+       // super(editor, ID, "Main");
+       // this.editor = (UserEditor) editor;
+       // this.userAdminWrapper = userAdminWrapper;
+       // }
+
+       // protected void createFormContent(final IManagedForm mf) {
+       // ScrolledForm form = mf.getForm();
+       // Composite body = form.getBody();
+       // GridLayout mainLayout = new GridLayout();
+       // // mainLayout.marginRight = 10;
+       // body.setLayout(mainLayout);
+       // User user = editor.getDisplayedUser();
+       // appendOverviewPart(body, user);
+       // // Remove to ability to force the password for his own user. The user
+       // // must then use the change pwd feature
+       // appendMemberOfPart(body, user);
+       // }
+
+       @Override
+       protected void createUi(Composite body) {
+               // Composite body = new Composite(parent, SWT.BORDER);
+               GridLayout mainLayout = new GridLayout();
+               // mainLayout.marginRight = 10;
+               body.setLayout(mainLayout);
+               // body.getParent().setLayout(new GridLayout());
+               // body.setLayoutData(CmsUiUtils.fillAll());
+               User user = getDisplayedUser();
+               appendOverviewPart(body, user);
+               // Remove to ability to force the password for his own user. The user
+               // must then use the change pwd feature
+               appendMemberOfPart(body, user);
+       }
+
+       /** Creates the general section */
+       private void appendOverviewPart(final Composite parent, final User user) {
+               // FormToolkit tk = getManagedForm().getToolkit();
+
+               // Section section = tk.createSection(parent, SWT.NO_FOCUS);
+               // GridData gd = EclipseUiUtils.fillWidth();
+               // // gd.verticalAlignment = PRE_TITLE_INDENT;
+               // section.setLayoutData(gd);
+               Composite body = new Composite(parent, SWT.NONE);
+               body.setLayoutData(EclipseUiUtils.fillWidth());
+               // section.setClient(body);
+               // body.setLayout(new GridLayout(6, false));
+               body.setLayout(new GridLayout(2, false));
+
+               Text commonName = createReadOnlyLT(body, "Name", getProperty(user, cn));
+               Text distinguishedName = createReadOnlyLT(body, "Login", getProperty(user, uid));
+               Text firstName = createLT(body, "First name", getProperty(user, givenName));
+               Text lastName = createLT(body, "Last name", getProperty(user, sn));
+               Text email = createLT(body, "Email", getProperty(user, mail));
+
+               Link resetPwdLk = new Link(body, SWT.NONE);
+               if (!UserAdminUtils.isCurrentUser(user)) {
+                       resetPwdLk.setText("<a>Reset password</a>");
+               }
+               resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+
+               // create form part (controller)
+               AbstractFormPart part = new AbstractFormPart() {
+                       private MainInfoListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = new MainInfoListener(parent.getDisplay(), this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       @SuppressWarnings("unchecked")
+                       public void commit(boolean onSave) {
+                               // TODO Sanity checks (mail validity...)
+                               user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText());
+                               user.getProperties().put(LdapAttrs.sn.name(), lastName.getText());
+                               user.getProperties().put(LdapAttrs.cn.name(), commonName.getText());
+                               user.getProperties().put(LdapAttrs.mail.name(), email.getText());
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void refresh() {
+                               distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name()));
+                               commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name()));
+                               firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name()));
+                               lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name()));
+                               email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name()));
+                               refreshFormTitle(user);
+                               super.refresh();
+                       }
+               };
+
+               // Improve this: automatically generate CN when first or last name
+               // changes
+               ModifyListener cnML = new ModifyListener() {
+                       private static final long serialVersionUID = 4298649222869835486L;
+
+                       @Override
+                       public void modifyText(ModifyEvent event) {
+                               String first = firstName.getText();
+                               String last = lastName.getText();
+                               String cn = first.trim() + " " + last.trim() + " ";
+                               cn = cn.trim();
+                               commonName.setText(cn);
+                               // getManagedForm().getForm().setText(cn);
+                               updateEditorTitle(cn);
+                       }
+               };
+               firstName.addModifyListener(cnML);
+               lastName.addModifyListener(cnML);
+
+               ModifyListener defaultListener = new FormPartML(part);
+               firstName.addModifyListener(defaultListener);
+               lastName.addModifyListener(defaultListener);
+               email.addModifyListener(defaultListener);
+
+               if (!UserAdminUtils.isCurrentUser(user))
+                       resetPwdLk.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = 5881800534589073787L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       new ChangePasswordDialog(user, "Reset password").open();
+                               }
+                       });
+
+               getManagedForm().addPart(part);
+       }
+
+       private class ChangePasswordDialog extends TrayDialog {
+               private static final long serialVersionUID = 2843538207460082349L;
+
+               private User user;
+               private Text password1;
+               private Text password2;
+               private String title;
+               // private FormToolkit tk;
+
+               public ChangePasswordDialog(User user, String title) {
+                       super(Display.getDefault().getActiveShell());
+                       // this.tk = tk;
+                       this.user = user;
+                       this.title = title;
+               }
+
+               protected Control createDialogArea(Composite parent) {
+                       Composite dialogarea = (Composite) super.createDialogArea(parent);
+                       dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       Composite body = new Composite(dialogarea, SWT.NO_FOCUS);
+                       body.setLayoutData(EclipseUiUtils.fillAll());
+                       GridLayout layout = new GridLayout(2, false);
+                       body.setLayout(layout);
+
+                       password1 = createLP(body, "New password", "");
+                       password2 = createLP(body, "Repeat password", "");
+                       parent.pack();
+                       return body;
+               }
+
+               @SuppressWarnings("unchecked")
+               @Override
+               protected void okPressed() {
+                       String msg = null;
+
+                       if (password1.getText().equals(""))
+                               msg = "Password cannot be empty";
+                       else if (password1.getText().equals(password2.getText())) {
+                               char[] newPassword = password1.getText().toCharArray();
+                               // userAdminWrapper.beginTransactionIfNeeded();
+                               userAdminWrapper.beginTransactionIfNeeded();
+                               user.getCredentials().put(null, newPassword);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               super.okPressed();
+                       } else {
+                               msg = "Passwords are not equals";
+                       }
+
+                       if (EclipseUiUtils.notEmpty(msg))
+                               MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg);
+               }
+
+               protected void configureShell(Shell shell) {
+                       super.configureShell(shell);
+                       shell.setText(title);
+               }
+       }
+
+       private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
+               // Section section = addSection(tk, parent, "Roles");
+               // Composite body = (Composite) section.getClient();
+               // Composite body= parent;
+               Composite body = new Composite(parent, SWT.BORDER);
+               body.setLayout(new GridLayout());
+               body.setLayoutData(CmsSwtUtils.fillAll());
+
+               // boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
+
+               // Displayed columns
+               List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+               columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
+               // Only show technical DN to administrators
+               // if (isAdmin)
+               // columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
+               // 300));
+
+               // Create and configure the table
+               final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
+
+               userViewerCmp.setColumnDefinitions(columnDefs);
+               // if (isAdmin)
+               // userViewerCmp.populateWithStaticFilters(false, false);
+               // else
+               userViewerCmp.populate(true, false);
+               GridData gd = EclipseUiUtils.fillAll();
+               gd.heightHint = 500;
+               userViewerCmp.setLayoutData(gd);
+
+               // Controllers
+               TableViewer userViewer = userViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user);
+               userViewer.addDropSupport(operations, tt, dropL);
+
+               AbstractFormPart part = new AbstractFormPart() {
+
+                       private GroupChangeListener listener;
+
+                       @Override
+                       public void initialize(IManagedForm form) {
+                               super.initialize(form);
+                               listener = new GroupChangeListener(parent.getDisplay(), this);
+                               userAdminWrapper.addListener(listener);
+                       }
+
+                       public void commit(boolean onSave) {
+                               super.commit(onSave);
+                       }
+
+                       @Override
+                       public void dispose() {
+                               userAdminWrapper.removeListener(listener);
+                               super.dispose();
+                       }
+
+                       @Override
+                       public void refresh() {
+                               userViewerCmp.refresh();
+                               super.refresh();
+                       }
+               };
+               getManagedForm().addPart(part);
+               // addRemoveAbitily(body, userViewer, user);
+               // userViewerCmp.refresh();
+               String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups";
+               Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC);
+               ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+               ToolBar toolBar = toolBarManager.createControl(body);
+               toolBar.setLayoutData(CmsSwtUtils.fillWidth());
+               toolBarManager.add(action);
+               toolBarManager.update(true);
+               return userViewerCmp;
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 2653790051461237329L;
+
+               private Button showSystemRoleBtn;
+
+               private final User user;
+               private final UserFilter userFilter;
+
+               public MyUserTableViewer(Composite parent, int style, User user) {
+                       super(parent, style, true);
+                       this.user = user;
+                       userFilter = new UserFilter();
+               }
+
+               protected void populateStaticFilters(Composite staticFilterCmp) {
+                       staticFilterCmp.setLayout(new GridLayout());
+                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
+                       showSystemRoleBtn.setText("Show system roles");
+                       boolean showSysRole = CurrentUser.isInRole(CmsConstants.ROLE_ADMIN);
+                       showSystemRoleBtn.setSelection(showSysRole);
+                       userFilter.setShowSystemRole(showSysRole);
+                       showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -7033424592697691676L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       userFilter.setShowSystemRole(showSystemRoleBtn.getSelection());
+                                       refresh();
+                               }
+                       });
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       List<User> users = (List<User>) getFlatGroups(null);
+                       List<User> filteredUsers = new ArrayList<User>();
+                       if (users.contains(user))
+                               users.remove(user);
+                       userFilter.setSearchText(filter);
+                       for (User user : users)
+                               if (userFilter.select(null, null, user))
+                                       filteredUsers.add(user);
+                       return filteredUsers;
+               }
+       }
+
+       // private void addRemoveAbility(Composite parent, TableViewer userViewer, User
+       // user) {
+       // // Section section = sectionPart.getSection();
+       // ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+       // ToolBar toolbar = toolBarManager.createControl(parent);
+       // final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
+       // toolbar.setCursor(handCursor);
+       // toolbar.addDisposeListener(new DisposeListener() {
+       // private static final long serialVersionUID = 3882131405820522925L;
+       //
+       // public void widgetDisposed(DisposeEvent e) {
+       // if ((handCursor != null) && (handCursor.isDisposed() == false)) {
+       // handCursor.dispose();
+       // }
+       // }
+       // });
+       //
+       // String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) +
+       // " from the below selected groups";
+       // Action action = new RemoveMembershipAction(userViewer, user, tooltip,
+       // SecurityAdminImages.ICON_REMOVE_DESC);
+       // toolBarManager.add(action);
+       // toolBarManager.update(true);
+       // // section.setTextClient(toolbar);
+       // }
+
+       private class RemoveMembershipAction extends Action {
+               private static final long serialVersionUID = -1337713097184522588L;
+
+               private final TableViewer userViewer;
+               private final User user;
+
+               RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) {
+                       super(name, img);
+                       this.userViewer = userViewer;
+                       this.user = user;
+               }
+
+               @Override
+               public void run() {
+                       ISelection selection = userViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+
+                       @SuppressWarnings("unchecked")
+                       Iterator<Group> it = ((IStructuredSelection) selection).iterator();
+                       List<Group> groups = new ArrayList<Group>();
+                       while (it.hasNext()) {
+                               Group currGroup = it.next();
+                               groups.add(currGroup);
+                       }
+
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       for (Group group : groups) {
+                               group.removeMember(user);
+                       }
+                       userAdminWrapper.commitOrNotifyTransactionStateChange();
+                       for (Group group : groups) {
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
+                       }
+               }
+       }
+
+       /**
+        * Defines the table as being a potential target to add group memberships
+        * (roles) to this user
+        */
+       private class GroupDropListener extends ViewerDropAdapter {
+               private static final long serialVersionUID = 2893468717831451621L;
+
+               private final UserAdminWrapper myUserAdminWrapper;
+               private final User myUser;
+
+               public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) {
+                       super(userViewer);
+                       this.myUserAdminWrapper = userAdminWrapper;
+                       this.myUser = user;
+               }
+
+               @Override
+               public boolean validateDrop(Object target, int operation, TransferData transferType) {
+                       // Target is always OK in a list only view
+                       // TODO check if not a string
+                       boolean validDrop = true;
+                       return validDrop;
+               }
+
+               @Override
+               public void drop(DropTargetEvent event) {
+                       String name = (String) event.data;
+                       UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
+                       Role role = myUserAdmin.getRole(name);
+                       // TODO this check should be done before.
+                       if (role.getType() == Role.GROUP) {
+                               // TODO check if the user is already member of this group
+
+                               myUserAdminWrapper.beginTransactionIfNeeded();
+                               Group group = (Group) role;
+                               group.addMember(myUser);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
+                       }
+                       super.drop(event);
+               }
+
+               @Override
+               public boolean performDrop(Object data) {
+                       // userTableViewerCmp.refresh();
+                       return true;
+               }
+       }
+
+       // LOCAL HELPERS
+       private void refreshFormTitle(User group) {
+               // getManagedForm().getForm().setText(UserAdminUtils.getProperty(group,
+               // LdapAttrs.cn.name()));
+       }
+
+       /** Appends a section with a title */
+       // private Section addSection(FormToolkit tk, Composite parent, String title) {
+       // Section section = tk.createSection(parent, Section.TITLE_BAR);
+       // GridData gd = EclipseUiUtils.fillWidth();
+       // gd.verticalAlignment = PRE_TITLE_INDENT;
+       // section.setLayoutData(gd);
+       // section.setText(title);
+       // // section.getMenu().setVisible(true);
+       //
+       // Composite body = tk.createComposite(section, SWT.WRAP);
+       // body.setLayoutData(EclipseUiUtils.fillAll());
+       // section.setClient(body);
+       //
+       // return section;
+       // }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserTableDefaultDClickListener.java
new file mode 100644 (file)
index 0000000..c6d024e
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.cms.e4.users;
+
+import org.argeo.cms.e4.CmsE4Utils;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Default double click listener for the various user tables, will open the
+ * clicked item in the editor
+ */
+public class UserTableDefaultDClickListener implements IDoubleClickListener {
+       private final EPartService partService;
+
+       public UserTableDefaultDClickListener(EPartService partService) {
+               this.partService = partService;
+       }
+
+       public void doubleClick(DoubleClickEvent evt) {
+               if (evt.getSelection().isEmpty())
+                       return;
+               Object obj = ((IStructuredSelection) evt.getSelection()).getFirstElement();
+               User user = (User) obj;
+
+               String editorId = getEditorId(user);
+               CmsE4Utils.openEditor(partService, editorId, LdapAttrs.uid.name(), user.getName());
+       }
+
+       protected String getEditorId(User user) {
+               if (user instanceof Group)
+                       return "org.argeo.cms.e4.partdescriptor.groupEditor";
+               else
+                       return "org.argeo.cms.e4.partdescriptor.userEditor";
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UsersView.java
new file mode 100644 (file)
index 0000000..720945c
--- /dev/null
@@ -0,0 +1,182 @@
+package org.argeo.cms.e4.users;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.e4.users.providers.CommonNameLP;
+import org.argeo.cms.e4.users.providers.DomainNameLP;
+import org.argeo.cms.e4.users.providers.MailLP;
+import org.argeo.cms.e4.users.providers.UserDragListener;
+import org.argeo.cms.e4.users.providers.UserNameLP;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+import org.osgi.service.useradmin.UserAdminListener;
+
+/** List all users with filter - based on Ldif userAdmin */
+public class UsersView {
+       // private final static Log log = LogFactory.getLog(UsersView.class);
+
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".usersView";
+
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+       @Inject
+       private EPartService partService;
+
+       // UI Objects
+       private LdifUsersTable userTableViewerCmp;
+       private TableViewer userViewer;
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+       private UserAdminListener listener;
+
+       @PostConstruct
+       public void createPartControl(Composite parent, ESelectionService selectionService) {
+
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               // Define the displayed columns
+               columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Common Name", 150));
+               columnDefs.add(new ColumnDefinition(new MailLP(), "E-mail", 150));
+               columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 200));
+               // Only show technical DN to admin
+               if (CurrentUser.isInRole(CmsConstants.ROLE_ADMIN))
+                       columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name", 300));
+
+               // Create and configure the table
+               userTableViewerCmp = new MyUserTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+               userTableViewerCmp.setLayoutData(EclipseUiUtils.fillAll());
+               userTableViewerCmp.setColumnDefinitions(columnDefs);
+               userTableViewerCmp.populate(true, false);
+
+               // Links
+               userViewer = userTableViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new UserTableDefaultDClickListener(partService));
+               userViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+
+                       @Override
+                       public void selectionChanged(SelectionChangedEvent event) {
+                               IStructuredSelection selection = (IStructuredSelection) event.getSelection();
+                               selectionService.setSelection(selection.toList());
+                       }
+               });
+               // getViewSite().setSelectionProvider(userViewer);
+
+               // Really?
+               userTableViewerCmp.refresh();
+
+               // Drag and drop
+               int operations = DND.DROP_COPY | DND.DROP_MOVE;
+               Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
+               userViewer.addDragSupport(operations, tt, new UserDragListener(userViewer));
+
+               // Register a useradmin listener
+               listener = new MyUiUAListener(parent.getDisplay());
+               userAdminWrapper.addListener(listener);
+       }
+
+       private class MyUiUAListener extends UiUserAdminListener {
+               public MyUiUAListener(Display display) {
+                       super(display);
+               }
+
+               @Override
+               public void roleChangedToUiThread(UserAdminEvent event) {
+                       if (userViewer != null && !userViewer.getTable().isDisposed())
+                               refresh();
+               }
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.uid.name(), LdapAttrs.cn.name(),
+                               LdapAttrs.givenName.name(), LdapAttrs.sn.name(), LdapAttrs.mail.name() };
+
+               public MyUserTableViewer(Composite parent, int style) {
+                       super(parent, style);
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Role[] roles;
+
+                       try {
+                               StringBuilder builder = new StringBuilder();
+
+                               StringBuilder tmpBuilder = new StringBuilder();
+                               if (EclipseUiUtils.notEmpty(filter))
+                                       for (String prop : knownProps) {
+                                               tmpBuilder.append("(");
+                                               tmpBuilder.append(prop);
+                                               tmpBuilder.append("=*");
+                                               tmpBuilder.append(filter);
+                                               tmpBuilder.append("*)");
+                                       }
+                               if (tmpBuilder.length() > 1) {
+                                       builder.append("(&(").append(LdapAttrs.objectClass.name()).append("=")
+                                                       .append(LdapObjs.inetOrgPerson.name()).append(")(|");
+                                       builder.append(tmpBuilder.toString());
+                                       builder.append("))");
+                               } else
+                                       builder.append("(").append(LdapAttrs.objectClass.name()).append("=")
+                                                       .append(LdapObjs.inetOrgPerson.name()).append(")");
+                               roles = userAdminWrapper.getUserAdmin().getRoles(builder.toString());
+                       } catch (InvalidSyntaxException e) {
+                               throw new CmsException("Unable to get roles with filter: " + filter, e);
+                       }
+                       List<User> users = new ArrayList<User>();
+                       for (Role role : roles)
+                               // if (role.getType() == Role.USER && role.getType() !=
+                               // Role.GROUP)
+                               users.add((User) role);
+                       return users;
+               }
+       }
+
+       public void refresh() {
+               userTableViewerCmp.refresh();
+       }
+
+       // Override generic view methods
+       @PreDestroy
+       public void dispose() {
+               userAdminWrapper.removeListener(listener);
+       }
+
+       @Focus
+       public void setFocus() {
+               userTableViewerCmp.setFocus();
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteGroups.java
new file mode 100644 (file)
index 0000000..742bc3f
--- /dev/null
@@ -0,0 +1,95 @@
+package org.argeo.cms.e4.users.handlers;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.GroupsView;
+import org.argeo.cms.e4.users.UserAdminWrapper;
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Delete the selected groups */
+public class DeleteGroups {
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID +
+       // ".deleteGroups";
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+
+       @Inject
+       ESelectionService selectionService;
+
+       @SuppressWarnings("unchecked")
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               // ISelection selection = null;// HandlerUtil.getCurrentSelection(event);
+               // if (selection.isEmpty())
+               // return null;
+               //
+               // List<Group> groups = new ArrayList<Group>();
+               // Iterator<Group> it = ((IStructuredSelection) selection).iterator();
+
+               List<Group> selection = (List<Group>) selectionService.getSelection();
+               if (selection == null)
+                       return;
+
+               StringBuilder builder = new StringBuilder();
+               for (Group group : selection) {
+                       Group currGroup = group;
+                       String groupName = UserAdminUtils.getUserLocalId(currGroup.getName());
+                       // TODO add checks
+                       builder.append(groupName).append("; ");
+                       // groups.add(currGroup);
+               }
+
+               if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Groups", "Are you sure that you "
+                               + "want to delete these groups?\n" + builder.substring(0, builder.length() - 2)))
+                       return;
+
+               userAdminWrapper.beginTransactionIfNeeded();
+               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
+               // IWorkbenchPage iwp =
+               // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
+               for (Group group : selection) {
+                       String groupName = group.getName();
+                       // TODO find a way to close the editor cleanly if opened. Cannot be
+                       // done through the UserAdminListeners, it causes a
+                       // java.util.ConcurrentModificationException because disposing the
+                       // editor unregisters and disposes the listener
+                       // IEditorPart part = iwp.findEditor(new UserEditorInput(groupName));
+                       // if (part != null)
+                       // iwp.closeEditor(part, false);
+                       userAdmin.removeRole(groupName);
+               }
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+
+               // Update the view
+               for (Group group : selection) {
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, group));
+               }
+
+               // return null;
+       }
+
+       @CanExecute
+       public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               return part.getObject() instanceof GroupsView && selectionService.getSelection() != null;
+       }
+
+       /* DEPENDENCY INJECTION */
+       // public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+       // this.userAdminWrapper = userAdminWrapper;
+       // }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/DeleteUsers.java
new file mode 100644 (file)
index 0000000..d1afd22
--- /dev/null
@@ -0,0 +1,88 @@
+package org.argeo.cms.e4.users.handlers;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.UserAdminWrapper;
+import org.argeo.cms.e4.users.UsersView;
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.services.IServiceConstants;
+import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Delete the selected users */
+public class DeleteUsers {
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".deleteUsers";
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+
+       @SuppressWarnings("unchecked")
+       @Execute
+       public void execute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               // ISelection selection = null;// HandlerUtil.getCurrentSelection(event);
+               // if (selection.isEmpty())
+               // return null;
+               List<User> selection = (List<User>) selectionService.getSelection();
+               if (selection == null)
+                       return;
+
+//             Iterator<User> it = ((IStructuredSelection) selection).iterator();
+//             List<User> users = new ArrayList<User>();
+               StringBuilder builder = new StringBuilder();
+
+               for(User user:selection) {
+                       User currUser = user;
+//                     User currUser = it.next();
+                       String userName = UserAdminUtils.getUserLocalId(currUser.getName());
+                       if (UserAdminUtils.isCurrentUser(currUser)) {
+                               MessageDialog.openError(Display.getCurrent().getActiveShell(), "Deletion forbidden",
+                                               "You cannot delete your own user this way.");
+                               return;
+                       }
+                       builder.append(userName).append("; ");
+//                     users.add(currUser);
+               }
+
+               if (!MessageDialog.openQuestion(Display.getCurrent().getActiveShell(), "Delete Users",
+                               "Are you sure that you want to delete these users?\n" + builder.substring(0, builder.length() - 2)))
+                       return;
+
+               userAdminWrapper.beginTransactionIfNeeded();
+               UserAdmin userAdmin = userAdminWrapper.getUserAdmin();
+               // IWorkbenchPage iwp =
+               // HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();
+
+               for (User user : selection) {
+                       String userName = user.getName();
+                       // TODO find a way to close the editor cleanly if opened. Cannot be
+                       // done through the UserAdminListeners, it causes a
+                       // java.util.ConcurrentModificationException because disposing the
+                       // editor unregisters and disposes the listener
+                       // IEditorPart part = iwp.findEditor(new UserEditorInput(userName));
+                       // if (part != null)
+                       // iwp.closeEditor(part, false);
+                       userAdmin.removeRole(userName);
+               }
+               userAdminWrapper.commitOrNotifyTransactionStateChange();
+
+               for (User user : selection) {
+                       userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_REMOVED, user));
+               }
+       }
+
+       @CanExecute
+       public boolean canExecute(@Named(IServiceConstants.ACTIVE_PART) MPart part, ESelectionService selectionService) {
+               return part.getObject() instanceof UsersView && selectionService.getSelection() != null;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewGroup.java
new file mode 100644 (file)
index 0000000..41e14e0
--- /dev/null
@@ -0,0 +1,212 @@
+package org.argeo.cms.e4.users.handlers;
+
+import java.util.Dictionary;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.argeo.cms.e4.users.UserAdminWrapper;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.util.directory.DirectoryConf;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Create a new group */
+public class NewGroup {
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newGroup";
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+
+       @Execute
+       public Object execute() {
+               NewGroupWizard newGroupWizard = new NewGroupWizard();
+               newGroupWizard.setWindowTitle("Group creation");
+               WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newGroupWizard);
+               dialog.open();
+               return null;
+       }
+
+       private class NewGroupWizard extends Wizard {
+
+               // Pages
+               private MainGroupInfoWizardPage mainGroupInfo;
+
+               // UI fields
+               private Text dNameTxt, commonNameTxt, descriptionTxt;
+               private Combo baseDnCmb;
+
+               public NewGroupWizard() {
+               }
+
+               @Override
+               public void addPages() {
+                       mainGroupInfo = new MainGroupInfoWizardPage();
+                       addPage(mainGroupInfo);
+               }
+
+               @SuppressWarnings({ "rawtypes", "unchecked" })
+               @Override
+               public boolean performFinish() {
+                       if (!canFinish())
+                               return false;
+                       String commonName = commonNameTxt.getText();
+                       try {
+                               userAdminWrapper.beginTransactionIfNeeded();
+                               String dn = getDn(commonName);
+                               Group group = (Group) userAdminWrapper.getUserAdmin().createRole(dn, Role.GROUP);
+                               Dictionary props = group.getProperties();
+                               String descStr = descriptionTxt.getText();
+                               if (EclipseUiUtils.notEmpty(descStr))
+                                       props.put(LdapAttrs.description.name(), descStr);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, group));
+                               return true;
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot create new group " + commonName, e);
+                               return false;
+                       }
+               }
+
+               private class MainGroupInfoWizardPage extends WizardPage implements FocusListener {
+                       private static final long serialVersionUID = -3150193365151601807L;
+
+                       public MainGroupInfoWizardPage() {
+                               super("Main");
+                               setTitle("General information");
+                               setMessage("Please choose a domain, provide a common name " + "and a free description");
+                       }
+
+                       @Override
+                       public void createControl(Composite parent) {
+                               Composite bodyCmp = new Composite(parent, SWT.NONE);
+                               setControl(bodyCmp);
+                               bodyCmp.setLayout(new GridLayout(2, false));
+
+                               dNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Distinguished name");
+                               dNameTxt.setEnabled(false);
+
+                               baseDnCmb = createGridLC(bodyCmp, "Base DN");
+                               // Initialise before adding the listener to avoid NPE
+                               initialiseDnCmb(baseDnCmb);
+                               baseDnCmb.addFocusListener(this);
+
+                               commonNameTxt = EclipseUiUtils.createGridLT(bodyCmp, "Common name");
+                               commonNameTxt.addFocusListener(this);
+
+                               Label descLbl = new Label(bodyCmp, SWT.LEAD);
+                               descLbl.setText("Description");
+                               descLbl.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
+                               descriptionTxt = new Text(bodyCmp, SWT.LEAD | SWT.MULTI | SWT.WRAP | SWT.BORDER);
+                               descriptionTxt.setLayoutData(EclipseUiUtils.fillAll());
+                               descriptionTxt.addFocusListener(this);
+
+                               // Initialize buttons
+                               setPageComplete(false);
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void focusLost(FocusEvent event) {
+                               String name = commonNameTxt.getText();
+                               if (EclipseUiUtils.isEmpty(name))
+                                       dNameTxt.setText("");
+                               else
+                                       dNameTxt.setText(getDn(name));
+
+                               String message = checkComplete();
+                               if (message != null) {
+                                       setMessage(message, WizardPage.ERROR);
+                                       setPageComplete(false);
+                               } else {
+                                       setMessage("Complete", WizardPage.INFORMATION);
+                                       setPageComplete(true);
+                               }
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void focusGained(FocusEvent event) {
+                       }
+
+                       /** @return the error message or null if complete */
+                       protected String checkComplete() {
+                               String name = commonNameTxt.getText();
+
+                               if (name.trim().equals(""))
+                                       return "Common name must not be empty";
+                               Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name));
+                               if (role != null)
+                                       return "Group " + name + " already exists";
+                               return null;
+                       }
+
+                       @Override
+                       public void setVisible(boolean visible) {
+                               super.setVisible(visible);
+                               if (visible)
+                                       if (baseDnCmb.getSelectionIndex() == -1)
+                                               baseDnCmb.setFocus();
+                                       else
+                                               commonNameTxt.setFocus();
+                       }
+               }
+
+               private Map<String, String> getDns() {
+                       return userAdminWrapper.getKnownBaseDns(true);
+               }
+
+               private String getDn(String cn) {
+                       Map<String, String> dns = getDns();
+                       String bdn = baseDnCmb.getText();
+                       if (EclipseUiUtils.notEmpty(bdn)) {
+                               Dictionary<String, ?> props = DirectoryConf.uriAsProperties(dns.get(bdn));
+                               String dn = LdapAttrs.cn.name() + "=" + cn + "," + DirectoryConf.groupBase.getValue(props) + "," + bdn;
+                               return dn;
+                       }
+                       return null;
+               }
+
+               private void initialiseDnCmb(Combo combo) {
+                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
+                       if (dns.isEmpty())
+                               throw new CmsException("No writable base dn found. Cannot create group");
+                       combo.setItems(dns.keySet().toArray(new String[0]));
+                       if (dns.size() == 1)
+                               combo.select(0);
+               }
+       }
+
+       private Combo createGridLC(Composite parent, String label) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return combo;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/NewUser.java
new file mode 100644 (file)
index 0000000..40a4460
--- /dev/null
@@ -0,0 +1,287 @@
+package org.argeo.cms.e4.users.handlers;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.UiAdminUtils;
+import org.argeo.cms.e4.users.UserAdminWrapper;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
+import org.argeo.util.directory.DirectoryConf;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdminEvent;
+
+/** Open a wizard that enables creation of a new user. */
+public class NewUser {
+       // private final static Log log = LogFactory.getLog(NewUser.class);
+       // public final static String ID = WorkbenchUiPlugin.PLUGIN_ID + ".newUser";
+
+       /* DEPENDENCY INJECTION */
+       @Inject
+       private UserAdminWrapper userAdminWrapper;
+
+       @Execute
+       public Object execute() {
+               NewUserWizard newUserWizard = new NewUserWizard();
+               newUserWizard.setWindowTitle("User creation");
+               WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), newUserWizard);
+               dialog.open();
+               return null;
+       }
+
+       private class NewUserWizard extends Wizard {
+
+               // pages
+               private MainUserInfoWizardPage mainUserInfo;
+
+               // End user fields
+               private Text dNameTxt, usernameTxt, firstNameTxt, lastNameTxt, primaryMailTxt, pwd1Txt, pwd2Txt;
+               private Combo baseDnCmb;
+
+               public NewUserWizard() {
+
+               }
+
+               @Override
+               public void addPages() {
+                       mainUserInfo = new MainUserInfoWizardPage();
+                       addPage(mainUserInfo);
+                       String message = "Default wizard that also eases user creation tests:\n "
+                                       + "Mail and last name are automatically "
+                                       + "generated form the uid. Password are defauted to 'demo'.";
+                       mainUserInfo.setMessage(message, WizardPage.WARNING);
+               }
+
+               @SuppressWarnings({ "rawtypes", "unchecked" })
+               @Override
+               public boolean performFinish() {
+                       if (!canFinish())
+                               return false;
+                       String username = mainUserInfo.getUsername();
+                       userAdminWrapper.beginTransactionIfNeeded();
+                       try {
+                               User user = (User) userAdminWrapper.getUserAdmin().createRole(getDn(username), Role.USER);
+
+                               Dictionary props = user.getProperties();
+
+                               String lastNameStr = lastNameTxt.getText();
+                               if (EclipseUiUtils.notEmpty(lastNameStr))
+                                       props.put(LdapAttrs.sn.name(), lastNameStr);
+
+                               String firstNameStr = firstNameTxt.getText();
+                               if (EclipseUiUtils.notEmpty(firstNameStr))
+                                       props.put(LdapAttrs.givenName.name(), firstNameStr);
+
+                               String cn = UserAdminUtils.buildDefaultCn(firstNameStr, lastNameStr);
+                               if (EclipseUiUtils.notEmpty(cn))
+                                       props.put(LdapAttrs.cn.name(), cn);
+
+                               String mailStr = primaryMailTxt.getText();
+                               if (EclipseUiUtils.notEmpty(mailStr))
+                                       props.put(LdapAttrs.mail.name(), mailStr);
+
+                               char[] password = mainUserInfo.getPassword();
+                               user.getCredentials().put(null, password);
+                               userAdminWrapper.commitOrNotifyTransactionStateChange();
+                               userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CREATED, user));
+                               return true;
+                       } catch (Exception e) {
+                               ErrorFeedback.show("Cannot create new user " + username, e);
+                               return false;
+                       }
+               }
+
+               private class MainUserInfoWizardPage extends WizardPage implements ModifyListener {
+                       private static final long serialVersionUID = -3150193365151601807L;
+
+                       public MainUserInfoWizardPage() {
+                               super("Main");
+                               setTitle("Required Information");
+                       }
+
+                       @Override
+                       public void createControl(Composite parent) {
+                               Composite composite = new Composite(parent, SWT.NONE);
+                               composite.setLayout(new GridLayout(2, false));
+                               dNameTxt = EclipseUiUtils.createGridLT(composite, "Distinguished name", this);
+                               dNameTxt.setEnabled(false);
+
+                               baseDnCmb = createGridLC(composite, "Base DN");
+                               initialiseDnCmb(baseDnCmb);
+                               baseDnCmb.addModifyListener(this);
+                               baseDnCmb.addModifyListener(new ModifyListener() {
+                                       private static final long serialVersionUID = -1435351236582736843L;
+
+                                       @Override
+                                       public void modifyText(ModifyEvent event) {
+                                               String name = usernameTxt.getText();
+                                               dNameTxt.setText(getDn(name));
+                                       }
+                               });
+
+                               usernameTxt = EclipseUiUtils.createGridLT(composite, "Local ID", this);
+                               usernameTxt.addModifyListener(new ModifyListener() {
+                                       private static final long serialVersionUID = -1435351236582736843L;
+
+                                       @Override
+                                       public void modifyText(ModifyEvent event) {
+                                               String name = usernameTxt.getText();
+                                               if (name.trim().equals("")) {
+                                                       dNameTxt.setText("");
+                                                       lastNameTxt.setText("");
+                                                       primaryMailTxt.setText("");
+                                                       pwd1Txt.setText("");
+                                                       pwd2Txt.setText("");
+                                               } else {
+                                                       dNameTxt.setText(getDn(name));
+                                                       lastNameTxt.setText(name.toUpperCase());
+                                                       primaryMailTxt.setText(getMail(name));
+                                                       pwd1Txt.setText("demo");
+                                                       pwd2Txt.setText("demo");
+                                               }
+                                       }
+                               });
+
+                               primaryMailTxt = EclipseUiUtils.createGridLT(composite, "Email", this);
+                               firstNameTxt = EclipseUiUtils.createGridLT(composite, "First name", this);
+                               lastNameTxt = EclipseUiUtils.createGridLT(composite, "Last name", this);
+                               pwd1Txt = EclipseUiUtils.createGridLP(composite, "Password", this);
+                               pwd2Txt = EclipseUiUtils.createGridLP(composite, "Repeat password", this);
+                               setControl(composite);
+
+                               // Initialize buttons
+                               setPageComplete(false);
+                               getContainer().updateButtons();
+                       }
+
+                       @Override
+                       public void modifyText(ModifyEvent event) {
+                               String message = checkComplete();
+                               if (message != null) {
+                                       setMessage(message, WizardPage.ERROR);
+                                       setPageComplete(false);
+                               } else {
+                                       setMessage("Complete", WizardPage.INFORMATION);
+                                       setPageComplete(true);
+                               }
+                               getContainer().updateButtons();
+                       }
+
+                       /** @return error message or null if complete */
+                       protected String checkComplete() {
+                               String name = usernameTxt.getText();
+
+                               if (name.trim().equals(""))
+                                       return "User name must not be empty";
+                               Role role = userAdminWrapper.getUserAdmin().getRole(getDn(name));
+                               if (role != null)
+                                       return "User " + name + " already exists";
+                               if (!primaryMailTxt.getText().matches(UiAdminUtils.EMAIL_PATTERN))
+                                       return "Not a valid email address";
+                               if (lastNameTxt.getText().trim().equals(""))
+                                       return "Specify a last name";
+                               if (pwd1Txt.getText().trim().equals(""))
+                                       return "Specify a password";
+                               if (pwd2Txt.getText().trim().equals(""))
+                                       return "Repeat the password";
+                               if (!pwd2Txt.getText().equals(pwd1Txt.getText()))
+                                       return "Passwords are different";
+                               return null;
+                       }
+
+                       @Override
+                       public void setVisible(boolean visible) {
+                               super.setVisible(visible);
+                               if (visible)
+                                       if (baseDnCmb.getSelectionIndex() == -1)
+                                               baseDnCmb.setFocus();
+                                       else
+                                               usernameTxt.setFocus();
+                       }
+
+                       public String getUsername() {
+                               return usernameTxt.getText();
+                       }
+
+                       public char[] getPassword() {
+                               return pwd1Txt.getTextChars();
+                       }
+
+               }
+
+               private Map<String, String> getDns() {
+                       return userAdminWrapper.getKnownBaseDns(true);
+               }
+
+               private String getDn(String uid) {
+                       Map<String, String> dns = getDns();
+                       String bdn = baseDnCmb.getText();
+                       if (EclipseUiUtils.notEmpty(bdn)) {
+                               Dictionary<String, ?> props = DirectoryConf.uriAsProperties(dns.get(bdn));
+                               String dn = LdapAttrs.uid.name() + "=" + uid + "," + DirectoryConf.userBase.getValue(props) + "," + bdn;
+                               return dn;
+                       }
+                       return null;
+               }
+
+               private void initialiseDnCmb(Combo combo) {
+                       Map<String, String> dns = userAdminWrapper.getKnownBaseDns(true);
+                       if (dns.isEmpty())
+                               throw new CmsException("No writable base dn found. Cannot create user");
+                       combo.setItems(dns.keySet().toArray(new String[0]));
+                       if (dns.size() == 1)
+                               combo.select(0);
+               }
+
+               private String getMail(String username) {
+                       if (baseDnCmb.getSelectionIndex() == -1)
+                               return null;
+                       String baseDn = baseDnCmb.getText();
+                       try {
+                               LdapName name = new LdapName(baseDn);
+                               List<Rdn> rdns = name.getRdns();
+                               return username + "@" + (String) rdns.get(1).getValue() + '.' + (String) rdns.get(0).getValue();
+                       } catch (InvalidNameException e) {
+                               throw new CmsException("Unable to generate mail for " + username + " with base dn " + baseDn, e);
+                       }
+               }
+       }
+
+       private Combo createGridLC(Composite parent, String label) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Combo combo = new Combo(parent, SWT.LEAD | SWT.BORDER | SWT.READ_ONLY);
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               return combo;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setUserAdminWrapper(UserAdminWrapper userAdminWrapper) {
+               this.userAdminWrapper = userAdminWrapper;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/handlers/package-info.java
new file mode 100644 (file)
index 0000000..cf3db1d
--- /dev/null
@@ -0,0 +1,2 @@
+/** Users management handlers. */
+package org.argeo.cms.e4.users.handlers;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/package-info.java
new file mode 100644 (file)
index 0000000..c6f14b0
--- /dev/null
@@ -0,0 +1,2 @@
+/** Users management perspective. */
+package org.argeo.cms.e4.users;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/CommonNameLP.java
new file mode 100644 (file)
index 0000000..2d8db67
--- /dev/null
@@ -0,0 +1,21 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the common name of a user */
+public class CommonNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 5256703081044911941L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getProperty(user, LdapAttrs.cn.name());
+       }
+
+       @Override
+       public String getToolTipText(Object element) {
+               return UserAdminUtils.getProperty((User) element, LdapAttrs.DN);
+       }
+
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/DomainNameLP.java
new file mode 100644 (file)
index 0000000..e23729d
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.osgi.service.useradmin.User;
+
+/** The human friendly domain name for the corresponding user. */
+public class DomainNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 5256703081044911941L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getDomainName(user);
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/MailLP.java
new file mode 100644 (file)
index 0000000..52d3b85
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the Primary Mail of a user */
+public class MailLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 8329764452141982707L;
+
+       @Override
+       public String getText(User user) {
+               return UserAdminUtils.getProperty(user, LdapAttrs.mail.name());
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/RoleIconLP.java
new file mode 100644 (file)
index 0000000..8c94093
--- /dev/null
@@ -0,0 +1,35 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.e4.users.SecurityAdminImages;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Provide a bundle specific image depending on the current user type */
+public class RoleIconLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 6550449442061090388L;
+
+       @Override
+       public String getText(User user) {
+               return "";
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               User user = (User) element;
+               String dn = user.getName();
+               if (dn.endsWith(CmsConstants.ROLES_BASEDN))
+                       return SecurityAdminImages.ICON_ROLE;
+               else if (user.getType() == Role.GROUP) {
+                       String businessCategory = UserAdminUtils.getProperty(user, LdapAttrs.businessCategory);
+                       if (businessCategory != null && businessCategory.equals(CmsContext.WORKGROUP))
+                               return SecurityAdminImages.ICON_WORKGROUP;
+                       return SecurityAdminImages.ICON_GROUP;
+               } else
+                       return SecurityAdminImages.ICON_USER;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserAdminAbstractLP.java
new file mode 100644 (file)
index 0000000..29873db
--- /dev/null
@@ -0,0 +1,66 @@
+package org.argeo.cms.e4.users.providers;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Utility class that add font modifications to a column label provider
+ * depending on the given user properties
+ */
+public abstract class UserAdminAbstractLP extends ColumnLabelProvider {
+       private static final long serialVersionUID = 137336765024922368L;
+
+       // private Font italic;
+       private Font bold;
+
+       @Override
+       public Font getFont(Object element) {
+               // Self as bold
+               try {
+                       LdapName selfUserName = UserAdminUtils.getCurrentUserLdapName();
+                       String userName = ((User) element).getName();
+                       LdapName userLdapName = new LdapName(userName);
+                       if (userLdapName.equals(selfUserName)) {
+                               if (bold == null)
+                                       bold = JFaceResources.getFontRegistry()
+                                                       .defaultFontDescriptor().setStyle(SWT.BOLD)
+                                                       .createFont(Display.getCurrent());
+                               return bold;
+                       }
+               } catch (InvalidNameException e) {
+                       throw new CmsException("cannot parse dn for " + element, e);
+               }
+
+               // Disabled as Italic
+               // Node userProfile = (Node) elem;
+               // if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
+               // return italic;
+
+               return null;
+               // return super.getFont(element);
+       }
+
+       @Override
+       public String getText(Object element) {
+               User user = (User) element;
+               return getText(user);
+       }
+
+       public void setDisplay(Display display) {
+               // italic = JFaceResources.getFontRegistry().defaultFontDescriptor()
+               // .setStyle(SWT.ITALIC).createFont(display);
+               bold = JFaceResources.getFontRegistry().defaultFontDescriptor()
+                               .setStyle(SWT.BOLD).createFont(Display.getCurrent());
+       }
+
+       public abstract String getText(User user);
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserDragListener.java
new file mode 100644 (file)
index 0000000..56a2624
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.DragSourceListener;
+import org.osgi.service.useradmin.User;
+
+/** Default drag listener to modify group and users via the UI */
+public class UserDragListener implements DragSourceListener {
+       private static final long serialVersionUID = -2074337775033781454L;
+       private final Viewer viewer;
+
+       public UserDragListener(Viewer viewer) {
+               this.viewer = viewer;
+       }
+
+       public void dragStart(DragSourceEvent event) {
+               // TODO implement finer checks
+               IStructuredSelection selection = (IStructuredSelection) viewer
+                               .getSelection();
+               if (selection.isEmpty() || selection.size() > 1)
+                       event.doit = false;
+               else
+                       event.doit = true;
+       }
+
+       public void dragSetData(DragSourceEvent event) {
+               // TODO Support multiple selection
+               Object obj = ((IStructuredSelection) viewer.getSelection())
+                               .getFirstElement();
+               if (obj != null) {
+                       User user = (User) obj;
+                       event.data = user.getName();
+               }
+       }
+
+       public void dragFinished(DragSourceEvent event) {
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserFilter.java
new file mode 100644 (file)
index 0000000..154b047
--- /dev/null
@@ -0,0 +1,58 @@
+package org.argeo.cms.e4.users.providers;
+
+import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.argeo.util.naming.LdapAttrs;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Filter user list using JFace mechanism on the client (yet on the server) side
+ * rather than having the UserAdmin to process the search
+ */
+public class UserFilter extends ViewerFilter {
+       private static final long serialVersionUID = 5082509381672880568L;
+
+       private String searchString;
+       private boolean showSystemRole = true;
+
+       private final String[] knownProps = { LdapAttrs.DN, LdapAttrs.cn.name(), LdapAttrs.givenName.name(),
+                       LdapAttrs.sn.name(), LdapAttrs.uid.name(), LdapAttrs.description.name(), LdapAttrs.mail.name() };
+
+       public void setSearchText(String s) {
+               // ensure that the value can be used for matching
+               if (notEmpty(s))
+                       searchString = ".*" + s.toLowerCase() + ".*";
+               else
+                       searchString = ".*";
+       }
+
+       public void setShowSystemRole(boolean showSystemRole) {
+               this.showSystemRole = showSystemRole;
+       }
+
+       @Override
+       public boolean select(Viewer viewer, Object parentElement, Object element) {
+               User user = (User) element;
+               if (!showSystemRole && user.getName().matches(".*(" + CmsConstants.ROLES_BASEDN + ")"))
+                       // UserAdminUtils.getProperty(user, LdifName.dn.name())
+                       // .toLowerCase().endsWith(AuthConstants.ROLES_BASEDN))
+                       return false;
+
+               if (searchString == null || searchString.length() == 0)
+                       return true;
+
+               if (user.getName().matches(searchString))
+                       return true;
+
+               for (String key : knownProps) {
+                       String currVal = UserAdminUtils.getProperty(user, key);
+                       if (notEmpty(currVal) && currVal.toLowerCase().matches(searchString))
+                               return true;
+               }
+               return false;
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/UserNameLP.java
new file mode 100644 (file)
index 0000000..3cd00eb
--- /dev/null
@@ -0,0 +1,13 @@
+package org.argeo.cms.e4.users.providers;
+
+import org.osgi.service.useradmin.User;
+
+/** Simply declare a label provider that returns the username of a user */
+public class UserNameLP extends UserAdminAbstractLP {
+       private static final long serialVersionUID = 6550449442061090388L;
+
+       @Override
+       public String getText(User user) {
+               return user.getName();
+       }
+}
diff --git a/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java b/swt/org.argeo.cms.e4/src/org/argeo/cms/e4/users/providers/package-info.java
new file mode 100644 (file)
index 0000000..33bef8d
--- /dev/null
@@ -0,0 +1,2 @@
+/** Users management content providers. */
+package org.argeo.cms.e4.users.providers;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/.classpath b/swt/org.argeo.cms.swt/.classpath
new file mode 100644 (file)
index 0000000..e03d341
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/swt/org.argeo.cms.swt/.project b/swt/org.argeo.cms.swt/.project
new file mode 100644 (file)
index 0000000..8ac021b
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.swt</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/swt/org.argeo.cms.swt/META-INF/.gitignore b/swt/org.argeo.cms.swt/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml b/swt/org.argeo.cms.swt/OSGI-INF/cmsUserApp.xml
new file mode 100644 (file)
index 0000000..4f2a405
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS User App">
+   <implementation class="org.argeo.cms.swt.app.CmsUserApp"/>
+   <property name="argeo.cms.app.contextName" type="String" value="cms/user"/>
+   <service>
+      <provide interface="org.argeo.api.cms.CmsApp"/>
+   </service>
+   <reference bind="setCmsContext" cardinality="1..1" interface="org.argeo.api.cms.CmsContext" name="CmsContext" policy="static"/>
+   <reference bind="setContentRepository" cardinality="1..1" interface="org.argeo.api.acr.ContentRepository" name="ContentRepository" policy="static"/>
+</scr:component>
diff --git a/swt/org.argeo.cms.swt/bnd.bnd b/swt/org.argeo.cms.swt/bnd.bnd
new file mode 100644 (file)
index 0000000..2dda08b
--- /dev/null
@@ -0,0 +1,11 @@
+Import-Package: org.eclipse.swt,\
+org.eclipse.jface.window,\
+org.eclipse.core.commands.common,\
+javax.servlet.*;version="[3,5)",\
+*
+
+Bundle-ActivationPolicy: lazy
+                       
+Service-Component: \
+OSGI-INF/cmsUserApp.xml
+                       
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/build.properties b/swt/org.argeo.cms.swt/build.properties
new file mode 100644 (file)
index 0000000..5f0f21a
--- /dev/null
@@ -0,0 +1,5 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               OSGI-INF/cmsUserApp.xml
+source.. = src/
diff --git a/swt/org.argeo.cms.swt/icons/actions/add.png b/swt/org.argeo.cms.swt/icons/actions/add.png
new file mode 100644 (file)
index 0000000..5c06bf0
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/add.png differ
diff --git a/swt/org.argeo.cms.swt/icons/actions/close-all.png b/swt/org.argeo.cms.swt/icons/actions/close-all.png
new file mode 100644 (file)
index 0000000..81bfc95
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/close-all.png differ
diff --git a/swt/org.argeo.cms.swt/icons/actions/delete.png b/swt/org.argeo.cms.swt/icons/actions/delete.png
new file mode 100644 (file)
index 0000000..9712723
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/delete.png differ
diff --git a/swt/org.argeo.cms.swt/icons/actions/edit.png b/swt/org.argeo.cms.swt/icons/actions/edit.png
new file mode 100644 (file)
index 0000000..ad3db9f
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/edit.png differ
diff --git a/swt/org.argeo.cms.swt/icons/actions/save-all.png b/swt/org.argeo.cms.swt/icons/actions/save-all.png
new file mode 100644 (file)
index 0000000..f48ed32
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/save-all.png differ
diff --git a/swt/org.argeo.cms.swt/icons/actions/save.png b/swt/org.argeo.cms.swt/icons/actions/save.png
new file mode 100644 (file)
index 0000000..1c58ada
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/actions/save.png differ
diff --git a/swt/org.argeo.cms.swt/icons/active.gif b/swt/org.argeo.cms.swt/icons/active.gif
new file mode 100644 (file)
index 0000000..7d24707
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/active.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/add.gif b/swt/org.argeo.cms.swt/icons/add.gif
new file mode 100644 (file)
index 0000000..252d7eb
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/add.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/add.png b/swt/org.argeo.cms.swt/icons/add.png
new file mode 100644 (file)
index 0000000..c7edfec
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/add.png differ
diff --git a/swt/org.argeo.cms.swt/icons/addFolder.gif b/swt/org.argeo.cms.swt/icons/addFolder.gif
new file mode 100644 (file)
index 0000000..d3f43d9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addFolder.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/addPrivileges.gif b/swt/org.argeo.cms.swt/icons/addPrivileges.gif
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addPrivileges.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/addRepo.gif b/swt/org.argeo.cms.swt/icons/addRepo.gif
new file mode 100644 (file)
index 0000000..26d81c0
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addRepo.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/addWorkspace.png b/swt/org.argeo.cms.swt/icons/addWorkspace.png
new file mode 100644 (file)
index 0000000..bbee775
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/addWorkspace.png differ
diff --git a/swt/org.argeo.cms.swt/icons/adminLog.gif b/swt/org.argeo.cms.swt/icons/adminLog.gif
new file mode 100644 (file)
index 0000000..6ef3bca
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/adminLog.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/batch.gif b/swt/org.argeo.cms.swt/icons/batch.gif
new file mode 100644 (file)
index 0000000..b8ca14a
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/batch.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/begin.gif b/swt/org.argeo.cms.swt/icons/begin.gif
new file mode 100755 (executable)
index 0000000..feb8e94
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/begin.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/binary.png b/swt/org.argeo.cms.swt/icons/binary.png
new file mode 100644 (file)
index 0000000..fdf4f82
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/binary.png differ
diff --git a/swt/org.argeo.cms.swt/icons/browser.gif b/swt/org.argeo.cms.swt/icons/browser.gif
new file mode 100644 (file)
index 0000000..6c7320c
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/browser.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/bundles.gif b/swt/org.argeo.cms.swt/icons/bundles.gif
new file mode 100644 (file)
index 0000000..e9a6bd9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/bundles.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/changePassword.gif b/swt/org.argeo.cms.swt/icons/changePassword.gif
new file mode 100644 (file)
index 0000000..274a850
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/changePassword.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/clear.gif b/swt/org.argeo.cms.swt/icons/clear.gif
new file mode 100644 (file)
index 0000000..6bc10f9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/clear.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/close-all.png b/swt/org.argeo.cms.swt/icons/close-all.png
new file mode 100644 (file)
index 0000000..85d4d42
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/close-all.png differ
diff --git a/swt/org.argeo.cms.swt/icons/commit.gif b/swt/org.argeo.cms.swt/icons/commit.gif
new file mode 100755 (executable)
index 0000000..876f3eb
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/commit.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/delete.png b/swt/org.argeo.cms.swt/icons/delete.png
new file mode 100644 (file)
index 0000000..676a39d
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/delete.png differ
diff --git a/swt/org.argeo.cms.swt/icons/dumpNode.gif b/swt/org.argeo.cms.swt/icons/dumpNode.gif
new file mode 100644 (file)
index 0000000..14eb1be
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/dumpNode.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/file.gif b/swt/org.argeo.cms.swt/icons/file.gif
new file mode 100644 (file)
index 0000000..ef30288
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/file.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/folder.gif b/swt/org.argeo.cms.swt/icons/folder.gif
new file mode 100644 (file)
index 0000000..42e027c
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/folder.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/getSize.gif b/swt/org.argeo.cms.swt/icons/getSize.gif
new file mode 100644 (file)
index 0000000..b05bf3e
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/getSize.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/group.png b/swt/org.argeo.cms.swt/icons/group.png
new file mode 100644 (file)
index 0000000..cc6683a
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/group.png differ
diff --git a/swt/org.argeo.cms.swt/icons/home.gif b/swt/org.argeo.cms.swt/icons/home.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/home.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/home.png b/swt/org.argeo.cms.swt/icons/home.png
new file mode 100644 (file)
index 0000000..5eb0967
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/home.png differ
diff --git a/swt/org.argeo.cms.swt/icons/import_fs.png b/swt/org.argeo.cms.swt/icons/import_fs.png
new file mode 100644 (file)
index 0000000..d7c890c
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/import_fs.png differ
diff --git a/swt/org.argeo.cms.swt/icons/installed.gif b/swt/org.argeo.cms.swt/icons/installed.gif
new file mode 100644 (file)
index 0000000..2988716
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/installed.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/log.gif b/swt/org.argeo.cms.swt/icons/log.gif
new file mode 100644 (file)
index 0000000..e3ecc55
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/log.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/logout.png b/swt/org.argeo.cms.swt/icons/logout.png
new file mode 100644 (file)
index 0000000..f2952fa
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/logout.png differ
diff --git a/swt/org.argeo.cms.swt/icons/maintenance.gif b/swt/org.argeo.cms.swt/icons/maintenance.gif
new file mode 100644 (file)
index 0000000..e5690ec
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/maintenance.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/node.gif b/swt/org.argeo.cms.swt/icons/node.gif
new file mode 100644 (file)
index 0000000..364c0e7
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/node.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/nodes.gif b/swt/org.argeo.cms.swt/icons/nodes.gif
new file mode 100644 (file)
index 0000000..bba3dbc
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/nodes.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/osgi_explorer.gif b/swt/org.argeo.cms.swt/icons/osgi_explorer.gif
new file mode 100644 (file)
index 0000000..e9a6bd9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/osgi_explorer.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/password.gif b/swt/org.argeo.cms.swt/icons/password.gif
new file mode 100644 (file)
index 0000000..a6b251f
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/password.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/person-logged-in.png b/swt/org.argeo.cms.swt/icons/person-logged-in.png
new file mode 100644 (file)
index 0000000..87acc14
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/person-logged-in.png differ
diff --git a/swt/org.argeo.cms.swt/icons/person.png b/swt/org.argeo.cms.swt/icons/person.png
new file mode 100644 (file)
index 0000000..7d979a5
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/person.png differ
diff --git a/swt/org.argeo.cms.swt/icons/query.png b/swt/org.argeo.cms.swt/icons/query.png
new file mode 100644 (file)
index 0000000..54c089d
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/query.png differ
diff --git a/swt/org.argeo.cms.swt/icons/refresh.png b/swt/org.argeo.cms.swt/icons/refresh.png
new file mode 100644 (file)
index 0000000..71b3481
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/refresh.png differ
diff --git a/swt/org.argeo.cms.swt/icons/remote_connected.gif b/swt/org.argeo.cms.swt/icons/remote_connected.gif
new file mode 100644 (file)
index 0000000..1492b4e
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remote_connected.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/remote_disconnected.gif b/swt/org.argeo.cms.swt/icons/remote_disconnected.gif
new file mode 100644 (file)
index 0000000..6c54da9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remote_disconnected.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/remove.gif b/swt/org.argeo.cms.swt/icons/remove.gif
new file mode 100644 (file)
index 0000000..0ae6dec
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/remove.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/removePrivileges.gif b/swt/org.argeo.cms.swt/icons/removePrivileges.gif
new file mode 100644 (file)
index 0000000..aa78fd2
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/removePrivileges.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/rename.gif b/swt/org.argeo.cms.swt/icons/rename.gif
new file mode 100644 (file)
index 0000000..8048405
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/rename.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/repositories.gif b/swt/org.argeo.cms.swt/icons/repositories.gif
new file mode 100644 (file)
index 0000000..c13bea1
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repositories.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/repository_connected.gif b/swt/org.argeo.cms.swt/icons/repository_connected.gif
new file mode 100644 (file)
index 0000000..a15fa55
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repository_connected.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/repository_disconnected.gif b/swt/org.argeo.cms.swt/icons/repository_disconnected.gif
new file mode 100644 (file)
index 0000000..4576dc5
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/repository_disconnected.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/resolved.gif b/swt/org.argeo.cms.swt/icons/resolved.gif
new file mode 100644 (file)
index 0000000..f4a1ea1
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/resolved.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/role.gif b/swt/org.argeo.cms.swt/icons/role.gif
new file mode 100644 (file)
index 0000000..274a850
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/role.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/rollback.gif b/swt/org.argeo.cms.swt/icons/rollback.gif
new file mode 100755 (executable)
index 0000000..c753995
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/rollback.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/save-all.png b/swt/org.argeo.cms.swt/icons/save-all.png
new file mode 100644 (file)
index 0000000..b68a29b
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save-all.png differ
diff --git a/swt/org.argeo.cms.swt/icons/save.gif b/swt/org.argeo.cms.swt/icons/save.gif
new file mode 100644 (file)
index 0000000..654ad7b
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/save.png b/swt/org.argeo.cms.swt/icons/save.png
new file mode 100644 (file)
index 0000000..f27ef2d
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save.png differ
diff --git a/swt/org.argeo.cms.swt/icons/save_security.png b/swt/org.argeo.cms.swt/icons/save_security.png
new file mode 100644 (file)
index 0000000..ca41dc9
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save_security.png differ
diff --git a/swt/org.argeo.cms.swt/icons/save_security_disabled.png b/swt/org.argeo.cms.swt/icons/save_security_disabled.png
new file mode 100644 (file)
index 0000000..fb7d08d
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/save_security_disabled.png differ
diff --git a/swt/org.argeo.cms.swt/icons/security.gif b/swt/org.argeo.cms.swt/icons/security.gif
new file mode 100644 (file)
index 0000000..57fb95e
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/security.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/service_published.gif b/swt/org.argeo.cms.swt/icons/service_published.gif
new file mode 100644 (file)
index 0000000..17f771a
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/service_published.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/service_referenced.gif b/swt/org.argeo.cms.swt/icons/service_referenced.gif
new file mode 100644 (file)
index 0000000..c24a95f
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/service_referenced.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/sort.gif b/swt/org.argeo.cms.swt/icons/sort.gif
new file mode 100644 (file)
index 0000000..23c5d0b
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/sort.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/starting.gif b/swt/org.argeo.cms.swt/icons/starting.gif
new file mode 100644 (file)
index 0000000..563743d
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/starting.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/sync.gif b/swt/org.argeo.cms.swt/icons/sync.gif
new file mode 100644 (file)
index 0000000..b4fa052
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/sync.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/user.gif b/swt/org.argeo.cms.swt/icons/user.gif
new file mode 100644 (file)
index 0000000..90a0014
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/user.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/users.gif b/swt/org.argeo.cms.swt/icons/users.gif
new file mode 100644 (file)
index 0000000..2de7edd
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/users.gif differ
diff --git a/swt/org.argeo.cms.swt/icons/workgroup.png b/swt/org.argeo.cms.swt/icons/workgroup.png
new file mode 100644 (file)
index 0000000..7fef996
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workgroup.png differ
diff --git a/swt/org.argeo.cms.swt/icons/workgroup.xcf b/swt/org.argeo.cms.swt/icons/workgroup.xcf
new file mode 100644 (file)
index 0000000..f517c82
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workgroup.xcf differ
diff --git a/swt/org.argeo.cms.swt/icons/workspace_connected.png b/swt/org.argeo.cms.swt/icons/workspace_connected.png
new file mode 100644 (file)
index 0000000..0430baa
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workspace_connected.png differ
diff --git a/swt/org.argeo.cms.swt/icons/workspace_disconnected.png b/swt/org.argeo.cms.swt/icons/workspace_disconnected.png
new file mode 100644 (file)
index 0000000..fddcb8c
Binary files /dev/null and b/swt/org.argeo.cms.swt/icons/workspace_disconnected.png differ
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/jface/dialog/CmsWizardDialog.java
new file mode 100644 (file)
index 0000000..33841a1
--- /dev/null
@@ -0,0 +1,222 @@
+package org.argeo.cms.jface.dialog;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.swt.dialogs.LightweightDialog;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardContainer2;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.SWT;
+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.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;
+
+/** A wizard dialog based on {@link LightweightDialog}. */
+public class CmsWizardDialog extends LightweightDialog implements IWizardContainer2 {
+       private static final long serialVersionUID = -2123153353654812154L;
+
+       private IWizard wizard;
+       private IWizardPage currentPage;
+       private int currentPageIndex;
+
+       private Label titleBar;
+       private Label message;
+       private Composite[] pageBodies;
+       private Composite buttons;
+       private Button back;
+       private Button next;
+       private Button finish;
+
+       public CmsWizardDialog(Shell parentShell, IWizard wizard) {
+               super(parentShell);
+               this.wizard = wizard;
+               wizard.setContainer(this);
+               // create the pages
+               wizard.addPages();
+               currentPage = wizard.getStartingPage();
+               if (currentPage == null)
+                       throw new IllegalArgumentException("At least one wizard page is required");
+       }
+
+       @Override
+       protected Control createDialogArea(Composite parent) {
+               updateWindowTitle();
+
+               Composite messageArea = new Composite(parent, SWT.NONE);
+               messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               {
+                       messageArea.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(2, false)));
+                       titleBar = new Label(messageArea, SWT.WRAP);
+                       titleBar.setFont(EclipseUiUtils.getBoldFont(parent));
+                       titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
+                       updateTitleBar();
+                       Button cancelButton = new Button(messageArea, SWT.FLAT);
+                       cancelButton.setText(CmsMsg.cancel.lead());
+                       cancelButton.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false, 1, 3));
+                       cancelButton.addSelectionListener((Selected) (e) -> closeShell(CANCEL));
+                       message = new Label(messageArea, SWT.WRAP);
+                       message.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2));
+                       updateMessage();
+               }
+
+               Composite body = new Composite(parent, SWT.BORDER);
+               body.setLayout(new FormLayout());
+               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               pageBodies = new Composite[wizard.getPageCount()];
+               IWizardPage[] pages = wizard.getPages();
+               for (int i = 0; i < pages.length; i++) {
+                       pageBodies[i] = new Composite(body, SWT.NONE);
+                       pageBodies[i].setLayout(CmsSwtUtils.noSpaceGridLayout());
+                       setSwitchingFormData(pageBodies[i]);
+                       pages[i].createControl(pageBodies[i]);
+               }
+               showPage(currentPage);
+
+               buttons = new Composite(parent, SWT.NONE);
+               buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+               {
+                       boolean singlePage = wizard.getPageCount() == 1;
+                       // singlePage = false;// dev
+                       GridLayout layout = new GridLayout(singlePage ? 1 : 3, true);
+                       layout.marginWidth = 0;
+                       layout.marginHeight = 0;
+                       buttons.setLayout(layout);
+                       // TODO revert order for right-to-left languages
+
+                       if (!singlePage) {
+                               back = new Button(buttons, SWT.PUSH);
+                               back.setText(CmsMsg.wizardBack.lead());
+                               back.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                               back.addSelectionListener((Selected) (e) -> backPressed());
+
+                               next = new Button(buttons, SWT.PUSH);
+                               next.setText(CmsMsg.wizardNext.lead());
+                               next.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                               next.addSelectionListener((Selected) (e) -> nextPressed());
+                       }
+                       finish = new Button(buttons, SWT.PUSH);
+                       finish.setText(CmsMsg.wizardFinish.lead());
+                       finish.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                       finish.addSelectionListener((Selected) (e) -> finishPressed());
+
+                       updateButtons();
+               }
+               return body;
+       }
+
+       @Override
+       public IWizardPage getCurrentPage() {
+               return currentPage;
+       }
+
+       @Override
+       public Shell getShell() {
+               return getForegoundShell();
+       }
+
+       @Override
+       public void showPage(IWizardPage page) {
+               IWizardPage[] pages = wizard.getPages();
+               int index = -1;
+               for (int i = 0; i < pages.length; i++) {
+                       if (page == pages[i]) {
+                               index = i;
+                               break;
+                       }
+               }
+               if (index < 0)
+                       throw new IllegalArgumentException("Cannot find index of wizard page " + page);
+               pageBodies[index].moveAbove(pageBodies[currentPageIndex]);
+
+               // // clear
+               // for (Control c : body.getChildren())
+               // c.dispose();
+               // page.createControl(body);
+               // body.layout(true, true);
+               currentPageIndex = index;
+               currentPage = page;
+       }
+
+       @Override
+       public void updateButtons() {
+               if (back != null)
+                       back.setEnabled(wizard.getPreviousPage(currentPage) != null);
+               if (next != null)
+                       next.setEnabled(wizard.getNextPage(currentPage) != null && currentPage.canFlipToNextPage());
+               if (finish != null) {
+                       finish.setEnabled(wizard.canFinish());
+               }
+       }
+
+       @Override
+       public void updateMessage() {
+               if (currentPage.getMessage() != null)
+                       message.setText(currentPage.getMessage());
+       }
+
+       @Override
+       public void updateTitleBar() {
+               if (currentPage.getTitle() != null)
+                       titleBar.setText(currentPage.getTitle());
+       }
+
+       @Override
+       public void updateWindowTitle() {
+               setTitle(wizard.getWindowTitle());
+       }
+
+       @Override
+       public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
+                       throws InvocationTargetException, InterruptedException {
+               // FIXME it creates a dependency to Eclipse Core Runtime
+               // runnable.run(null);
+       }
+
+       @Override
+       public void updateSize() {
+               // TODO pack?
+       }
+
+       protected boolean onCancel() {
+               return wizard.performCancel();
+       }
+
+       protected void nextPressed() {
+               IWizardPage page = wizard.getNextPage(currentPage);
+               showPage(page);
+               updateButtons();
+       }
+
+       protected void backPressed() {
+               IWizardPage page = wizard.getPreviousPage(currentPage);
+               showPage(page);
+               updateButtons();
+       }
+
+       protected void finishPressed() {
+               if (wizard.performFinish())
+                       closeShell(OK);
+       }
+
+       private static void setSwitchingFormData(Composite composite) {
+               FormData fdLabel = new FormData();
+               fdLabel.top = new FormAttachment(0, 0);
+               fdLabel.left = new FormAttachment(0, 0);
+               fdLabel.right = new FormAttachment(100, 0);
+               fdLabel.bottom = new FormAttachment(100, 0);
+               composite.setLayoutData(fdLabel);
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsException.java
new file mode 100644 (file)
index 0000000..874ea96
--- /dev/null
@@ -0,0 +1,16 @@
+package org.argeo.cms.swt;
+
+/** @deprecated Use standard Java {@link RuntimeException} instead. */
+@Deprecated
+public class CmsException extends RuntimeException {
+       private static final long serialVersionUID = -5341764743356771313L;
+
+       public CmsException(String message) {
+               super(message);
+       }
+
+       public CmsException(String message, Throwable e) {
+               super(message, e);
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsStyles.java
new file mode 100644 (file)
index 0000000..9eba6f6
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.cms.swt;
+
+/** Styles references in the CSS. */
+@Deprecated
+public interface CmsStyles {
+       // General
+       public final static String CMS_SHELL = "cms_shell";
+       public final static String CMS_MENU_LINK = "cms_menu_link";
+
+       // Header
+       public final static String CMS_HEADER = "cms_header";
+       public final static String CMS_HEADER_LEAD = "cms_header-lead";
+       public final static String CMS_HEADER_CENTER = "cms_header-center";
+       public final static String CMS_HEADER_END = "cms_header-end";
+
+       public final static String CMS_LEAD = "cms_lead";
+       public final static String CMS_END = "cms_end";
+       public final static String CMS_FOOTER = "cms_footer";
+
+       public final static String CMS_USER_MENU = "cms_user_menu";
+       public final static String CMS_USER_MENU_LINK = "cms_user_menu-link";
+       public final static String CMS_USER_MENU_ITEM = "cms_user_menu-item";
+       public final static String CMS_LOGIN_DIALOG = "cms_login_dialog";
+       public final static String CMS_LOGIN_DIALOG_USERNAME = "cms_login_dialog-username";
+       public final static String CMS_LOGIN_DIALOG_PASSWORD = "cms_login_dialog-password";
+
+       // Body
+       public final static String CMS_SCROLLED_AREA = "cms_scrolled_area";
+       public final static String CMS_BODY = "cms_body";
+       public final static String CMS_STATIC_TEXT = "cms_static-text";
+       public final static String CMS_LINK = "cms_link";
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtTheme.java
new file mode 100644 (file)
index 0000000..7669b15
--- /dev/null
@@ -0,0 +1,25 @@
+package org.argeo.cms.swt;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.eclipse.swt.graphics.Image;
+
+/** SWT specific {@link CmsTheme}. */
+public interface CmsSwtTheme extends CmsTheme {
+//     /** The image registered at this path, or <code>null</code> if not found. */
+//     Image getImage(String path);
+
+       /**
+        * And icon with this file name (without the extension), with a best effort to
+        * find the appropriate size, or <code>null</code> if not found.
+        * 
+        * @param name          An icon file name without path and extension.
+        * @param preferredSize the preferred size, if <code>null</code>,
+        *                      {@link #getDefaultIconSize()} will be tried.
+        */
+       Image getIcon(String name, Integer preferredSize);
+
+       Image getSmallIcon(CmsIcon icon);
+       
+       Image getBigIcon(CmsIcon icon);
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUi.java
new file mode 100644 (file)
index 0000000..2fb79f4
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.cms.swt;
+
+import org.argeo.api.cms.ux.CmsUi;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** A basic {@link CmsUi}, based on an SWT {@link Composite}. */
+public class CmsSwtUi extends Composite implements CmsUi {
+
+       private static final long serialVersionUID = -107939076610406448L;
+
+       public CmsSwtUi(Composite parent, int style) {
+               super(parent, style);
+               setLayout(new GridLayout());
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/CmsSwtUtils.java
new file mode 100644 (file)
index 0000000..5d96409
--- /dev/null
@@ -0,0 +1,315 @@
+package org.argeo.cms.swt;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.api.cms.ux.CmsStyle;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+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.RowData;
+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.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/** SWT utilities. */
+public class CmsSwtUtils {
+       /*
+        * THEME AND VIEW
+        */
+
+       public static CmsSwtTheme getCmsTheme(Composite parent) {
+               CmsSwtTheme theme = (CmsSwtTheme) parent.getData(CmsTheme.class.getName());
+               if (theme == null) {
+                       // find parent shell
+                       Shell topShell = parent.getShell();
+                       while (topShell.getParent() != null)
+                               topShell = (Shell) topShell.getParent();
+                       theme = (CmsSwtTheme) topShell.getData(CmsTheme.class.getName());
+                       parent.setData(CmsTheme.class.getName(), theme);
+               }
+               return theme;
+       }
+
+       public static void registerCmsTheme(Shell shell, CmsTheme theme) {
+               // find parent shell
+               Shell topShell = shell;
+               while (topShell.getParent() != null)
+                       topShell = (Shell) topShell.getParent();
+               // check if already set
+               if (topShell.getData(CmsTheme.class.getName()) != null) {
+                       CmsTheme registeredTheme = (CmsTheme) topShell.getData(CmsTheme.class.getName());
+                       throw new IllegalArgumentException(
+                                       "Theme " + registeredTheme.getThemeId() + " already registered in this shell");
+               }
+               topShell.setData(CmsTheme.class.getName(), theme);
+       }
+
+       public static CmsView getCmsView(Control parent) {
+               // find parent shell
+               Shell topShell = parent.getShell();
+               while (topShell.getParent() != null)
+                       topShell = (Shell) topShell.getParent();
+               return (CmsView) topShell.getData(CmsView.class.getName());
+       }
+
+       public static void registerCmsView(Shell shell, CmsView view) {
+               // find parent shell
+               Shell topShell = shell;
+               while (topShell.getParent() != null)
+                       topShell = (Shell) topShell.getParent();
+               // check if already set
+               if (topShell.getData(CmsView.class.getName()) != null) {
+                       CmsView registeredView = (CmsView) topShell.getData(CmsView.class.getName());
+                       throw new IllegalArgumentException("Cms view " + registeredView + " already registered in this shell");
+               }
+               shell.setData(CmsView.class.getName(), view);
+       }
+
+       /*
+        * EVENTS
+        */
+
+       /** Sends an event via {@link CmsView#sendEvent(String, Map)}. */
+       public static void sendEventOnSelect(Control control, String topic, Map<String, Object> properties) {
+               SelectionListener listener = (Selected) (e) -> {
+                       getCmsView(control.getParent()).sendEvent(topic, properties);
+               };
+               if (control instanceof Button) {
+                       ((Button) control).addSelectionListener(listener);
+               } else
+                       throw new UnsupportedOperationException("Control type " + control.getClass() + " is not supported.");
+       }
+
+       /**
+        * Convenience method to sends an event via
+        * {@link CmsView#sendEvent(String, Map)}.
+        */
+       public static void sendEventOnSelect(Control control, String topic, String key, Object value) {
+               Map<String, Object> properties = new HashMap<>();
+               properties.put(key, value);
+               sendEventOnSelect(control, topic, properties);
+       }
+
+       /*
+        * ICONS
+        */
+       /** Get a small icon from this theme. */
+       public static Image getSmallIcon(CmsTheme theme, CmsIcon icon) {
+               return ((CmsSwtTheme) theme).getSmallIcon(icon);
+       }
+
+       /** Get a big icon from this theme. */
+       public static Image getBigIcon(CmsTheme theme, CmsIcon icon) {
+               return ((CmsSwtTheme) theme).getBigIcon(icon);
+       }
+
+       /*
+        * LAYOUT INDEPENDENT
+        */
+       /** Takes the most space possible, depending on parent layout. */
+       public static void fill(Control control) {
+               Layout parentLayout = control.getParent().getLayout();
+               if (parentLayout == null)
+                       throw new IllegalStateException("Parent layout is not set");
+               if (parentLayout instanceof GridLayout) {
+                       control.setLayoutData(fillAll());
+               } else if (parentLayout instanceof FormLayout) {
+                       control.setLayoutData(coverAll());
+               } else {
+                       throw new IllegalArgumentException("Unsupported parent layout  " + parentLayout.getClass().getName());
+               }
+       }
+
+       /*
+        * GRID LAYOUT
+        */
+       /** A {@link GridLayout} without any spacing and one column. */
+       public static GridLayout noSpaceGridLayout() {
+               return noSpaceGridLayout(new GridLayout());
+       }
+
+       /**
+        * A {@link GridLayout} without any spacing and multiple columns of unequal
+        * width.
+        */
+       public static GridLayout noSpaceGridLayout(int columns) {
+               return noSpaceGridLayout(new GridLayout(columns, false));
+       }
+
+       /** @return the same layout, with spaces removed. */
+       public static GridLayout noSpaceGridLayout(GridLayout layout) {
+               layout.horizontalSpacing = 0;
+               layout.verticalSpacing = 0;
+               layout.marginWidth = 0;
+               layout.marginHeight = 0;
+               return layout;
+       }
+
+       public static GridData fillAll() {
+               return new GridData(SWT.FILL, SWT.FILL, true, true);
+       }
+
+       public static GridData fillWidth() {
+               return grabWidth(SWT.FILL, SWT.FILL);
+       }
+
+       public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
+               return new GridData(horizontalAlignment, horizontalAlignment, true, false);
+       }
+
+       public static GridData fillHeight() {
+               return grabHeight(SWT.FILL, SWT.FILL);
+       }
+
+       public static GridData grabHeight(int horizontalAlignment, int verticalAlignment) {
+               return new GridData(horizontalAlignment, horizontalAlignment, false, true);
+       }
+
+       /*
+        * ROW LAYOUT
+        */
+       /** @return the same layout, with margins removed. */
+       public static RowLayout noMarginsRowLayout(RowLayout rowLayout) {
+               rowLayout.marginTop = 0;
+               rowLayout.marginBottom = 0;
+               rowLayout.marginLeft = 0;
+               rowLayout.marginRight = 0;
+               return rowLayout;
+       }
+
+       public static RowLayout noMarginsRowLayout(int type) {
+               return noMarginsRowLayout(new RowLayout(type));
+       }
+
+       public static RowData rowData16px() {
+               return new RowData(16, 16);
+       }
+
+       /*
+        * FORM LAYOUT
+        */
+       public static FormData coverAll() {
+               FormData fdLabel = new FormData();
+               fdLabel.top = new FormAttachment(0, 0);
+               fdLabel.left = new FormAttachment(0, 0);
+               fdLabel.right = new FormAttachment(100, 0);
+               fdLabel.bottom = new FormAttachment(100, 0);
+               return fdLabel;
+       }
+
+       /*
+        * STYLING
+        */
+
+       /** Style widget */
+       public static <T extends Widget> T style(T widget, String style) {
+               if (style == null)
+                       return widget;// does nothing
+               EclipseUiSpecificUtils.setStyleData(widget, style);
+               if (widget instanceof Control) {
+                       CmsView cmsView = getCmsView((Control) widget);
+                       if (cmsView != null)
+                               cmsView.applyStyles(widget);
+               }
+               return widget;
+       }
+
+       /** Style widget */
+       public static <T extends Widget> T style(T widget, CmsStyle style) {
+               return style(widget, style.style());
+       }
+
+       /** Enable markups on widget */
+       public static <T extends Widget> T markup(T widget) {
+               EclipseUiSpecificUtils.setMarkupData(widget);
+               return widget;
+       }
+
+       /** Disable markup validation. */
+       public static <T extends Widget> T disableMarkupValidation(T widget) {
+               EclipseUiSpecificUtils.setMarkupValidationDisabledData(widget);
+               return widget;
+       }
+
+       /**
+        * Apply markup and set text on {@link Label}, {@link Button}, {@link Text}.
+        * 
+        * @param widget the widget to style and to use in order to display text
+        * @param txt    the object to display via its <code>toString()</code> method.
+        *               This argument should not be null, but if it is null and
+        *               assertions are disabled "<null>" is displayed instead; if
+        *               assertions are enabled the call will fail.
+        * 
+        * @see markup
+        */
+       public static <T extends Widget> T text(T widget, Object txt) {
+               assert txt != null;
+               String str = txt != null ? txt.toString() : "<null>";
+               markup(widget);
+               if (widget instanceof Label)
+                       ((Label) widget).setText(str);
+               else if (widget instanceof Button)
+                       ((Button) widget).setText(str);
+               else if (widget instanceof Text)
+                       ((Text) widget).setText(str);
+               else
+                       throw new IllegalArgumentException("Unsupported widget type " + widget.getClass());
+               return widget;
+       }
+
+       /** A {@link Label} with markup activated. */
+       public static Label lbl(Composite parent, Object txt) {
+               return text(new Label(parent, SWT.NONE), txt);
+       }
+
+       /** A read-only {@link Text} whose content can be copy/pasted. */
+       public static Text txt(Composite parent, Object txt) {
+               return text(new Text(parent, SWT.NONE), txt);
+       }
+
+       /** Dispose all children of a Composite */
+       public static void clear(Composite composite) {
+               if (composite.isDisposed())
+                       return;
+               for (Control child : composite.getChildren())
+                       child.dispose();
+       }
+
+       /** Clean reserved URL characters for use in HTTP links. */
+       public static String cleanPathForUrl(String path) {
+               StringTokenizer st = new StringTokenizer(path, "/");
+               StringBuilder sb = new StringBuilder();
+               while (st.hasMoreElements()) {
+                       sb.append('/');
+                       String encoded = URLEncoder.encode(st.nextToken(), StandardCharsets.UTF_8);
+                       encoded = encoded.replace("+", "%20");
+                       sb.append(encoded);
+
+               }
+               return sb.toString();
+       }
+
+       /** Singleton. */
+       private CmsSwtUtils() {
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDoubleClick.java
new file mode 100644 (file)
index 0000000..b818b06
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.cms.swt;
+
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+
+/**
+ * {@link MouseListener#mouseDoubleClick(MouseEvent)} as a functional interface
+ * in order to use as a short lambda expression in UI code.
+ * {@link MouseListener#mouseDownouseEvent)} and
+ * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default.
+ */
+@FunctionalInterface
+public interface MouseDoubleClick extends MouseListener {
+       @Override
+       void mouseDoubleClick(MouseEvent e);
+
+       @Override
+       default void mouseDown(MouseEvent e) {
+               // does nothing
+       }
+
+       @Override
+       default void mouseUp(MouseEvent e) {
+               // does nothing
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/MouseDown.java
new file mode 100644 (file)
index 0000000..baecb00
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.cms.swt;
+
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+
+/**
+ * {@link MouseListener#mouseDown(MouseEvent)} as a functional interface in
+ * order to use as a short lambda expression in UI code.
+ * {@link MouseListener#mouseDoubleClick(MouseEvent)} and
+ * {@link MouseListener#mouseUp(MouseEvent)} do nothing by default.
+ */
+@FunctionalInterface
+public interface MouseDown extends MouseListener {
+       @Override
+       void mouseDown(MouseEvent e);
+
+       @Override
+       default void mouseDoubleClick(MouseEvent e) {
+               // does nothing
+       }
+
+       @Override
+       default void mouseUp(MouseEvent e) {
+               // does nothing
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/Selected.java
new file mode 100644 (file)
index 0000000..03fbad0
--- /dev/null
@@ -0,0 +1,21 @@
+package org.argeo.cms.swt;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+
+/**
+ * {@link SelectionListener} as a functional interface in order to use as a
+ * short lambda expression in UI code.
+ * {@link SelectionListener#widgetDefaultSelected(SelectionEvent)} does nothing
+ * by default.
+ */
+@FunctionalInterface
+public interface Selected extends SelectionListener {
+       @Override
+       public void widgetSelected(SelectionEvent e);
+
+       default public void widgetDefaultSelected(SelectionEvent e) {
+               // does nothing
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SimpleSwtUxContext.java
new file mode 100644 (file)
index 0000000..e468c6d
--- /dev/null
@@ -0,0 +1,50 @@
+package org.argeo.cms.swt;
+
+import org.argeo.api.cms.ux.UxContext;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+public class SimpleSwtUxContext implements UxContext {
+       private Point size;
+       private Point small = new Point(400, 400);
+
+       public SimpleSwtUxContext() {
+               this(Display.getCurrent().getBounds());
+       }
+
+       public SimpleSwtUxContext(Rectangle rect) {
+               this.size = new Point(rect.width, rect.height);
+       }
+
+       public SimpleSwtUxContext(Point size) {
+               this.size = size;
+       }
+
+       @Override
+       public boolean isPortrait() {
+               return size.x >= size.y;
+       }
+
+       @Override
+       public boolean isLandscape() {
+               return size.x < size.y;
+       }
+
+       @Override
+       public boolean isSquare() {
+               return size.x == size.y;
+       }
+
+       @Override
+       public boolean isSmall() {
+               return size.x <= small.x || size.y <= small.y;
+       }
+
+       @Override
+       public boolean isMasterData() {
+               // TODO make it configurable
+               return true;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/SwtEditablePart.java
new file mode 100644 (file)
index 0000000..f2cceef
--- /dev/null
@@ -0,0 +1,9 @@
+package org.argeo.cms.swt;
+
+import org.argeo.cms.ux.widgets.EditablePart;
+import org.eclipse.swt.widgets.Control;
+
+/** Manages whether an editable or non editable control is shown. */
+public interface SwtEditablePart extends EditablePart {
+       public Control getControl();
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/ContentComposite.java
new file mode 100644 (file)
index 0000000..951889e
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.cms.swt.acr;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.eclipse.swt.widgets.Composite;
+
+/** A composite which can (optionally) manage a content. */
+public class ContentComposite extends Composite {
+       private static final long serialVersionUID = -1447009015451153367L;
+
+       public ContentComposite(Composite parent, int style, Content item) {
+               super(parent, style);
+               setData(item);
+       }
+
+       public Content getContent() {
+               return (Content) getData();
+       }
+
+       @Deprecated
+       public Content getNode() {
+               return getContent();
+       }
+
+       protected ProvidedContent getProvidedContent() {
+               return (ProvidedContent) getContent();
+       }
+
+       public String getSessionLocalId() {
+               return getProvidedContent().getSessionLocalId();
+       }
+
+       protected void itemUpdated() {
+               layout();
+       }
+
+       public void setContent(Content content) {
+               setData(content);
+               itemUpdated();
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSection.java
new file mode 100644 (file)
index 0000000..89d0038
--- /dev/null
@@ -0,0 +1,158 @@
+package org.argeo.cms.swt.acr;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.argeo.api.acr.Content;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ux.widgets.EditablePart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** A structured UI related to a JCR context. */
+public class SwtSection extends ContentComposite {
+       private static final long serialVersionUID = -5933796173755739207L;
+
+       private final SwtSection parentSection;
+       private Composite sectionHeader;
+       private final Integer relativeDepth;
+
+       public SwtSection(Composite parent, int style, Content node) {
+               this(parent, findSection(parent), style, node);
+       }
+
+       public SwtSection(SwtSection section, int style, Content node) {
+               this(section, section, style, node);
+       }
+
+       protected SwtSection(Composite parent, SwtSection parentSection, int style, Content node) {
+               super(parent, style, node);
+               this.parentSection = parentSection;
+               if (parentSection != null) {
+                       relativeDepth = getProvidedContent().getDepth() - parentSection.getProvidedContent().getDepth();
+               } else {
+                       relativeDepth = 0;
+               }
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+       }
+
+       public Map<String, SwtSection> getSubSections() {
+               LinkedHashMap<String, SwtSection> result = new LinkedHashMap<String, SwtSection>();
+               for (Control child : getChildren()) {
+                       if (child instanceof Composite) {
+                               collectDirectSubSections((Composite) child, result);
+                       }
+               }
+               return Collections.unmodifiableMap(result);
+       }
+
+       private void collectDirectSubSections(Composite composite, LinkedHashMap<String, SwtSection> subSections) {
+               if (composite == sectionHeader || composite instanceof EditablePart)
+                       return;
+               if (composite instanceof SwtSection) {
+                       SwtSection section = (SwtSection) composite;
+                       subSections.put(section.getProvidedContent().getSessionLocalId(), section);
+                       return;
+               }
+
+               for (Control child : composite.getChildren())
+                       if (child instanceof Composite)
+                               collectDirectSubSections((Composite) child, subSections);
+       }
+
+       public Composite createHeader() {
+               return createHeader(this);
+       }
+
+       public Composite createHeader(Composite parent) {
+               if (sectionHeader != null)
+                       sectionHeader.dispose();
+
+               sectionHeader = new Composite(parent, SWT.NONE);
+               sectionHeader.setLayoutData(CmsSwtUtils.fillWidth());
+               sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               // sectionHeader.moveAbove(null);
+               // layout();
+               return sectionHeader;
+       }
+
+       public Composite getHeader() {
+               if (sectionHeader != null && sectionHeader.isDisposed())
+                       sectionHeader = null;
+               return sectionHeader;
+       }
+
+       // SECTION PARTS
+       public SwtSectionPart getSectionPart(String partId) {
+               for (Control child : getChildren()) {
+                       if (child instanceof SwtSectionPart) {
+                               SwtSectionPart sectionPart = (SwtSectionPart) child;
+                               if (sectionPart.getPartId().equals(partId))
+                                       return sectionPart;
+                       }
+               }
+               return null;
+       }
+
+       public SwtSectionPart nextSectionPart(SwtSectionPart sectionPart) {
+               Control[] children = getChildren();
+               for (int i = 0; i < children.length; i++) {
+                       if (sectionPart == children[i]) {
+                               for (int j = i + 1; j < children.length; j++) {
+                                       if (children[i + 1] instanceof SwtSectionPart) {
+                                               return (SwtSectionPart) children[i + 1];
+                                       }
+                               }
+
+//                             if (i + 1 < children.length) {
+//                                     Composite next = (Composite) children[i + 1];
+//                                     return (SectionPart) next;
+//                             } else {
+//                                     // next section
+//                             }
+                       }
+               }
+               return null;
+       }
+
+       public SwtSectionPart previousSectionPart(SwtSectionPart 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 (SwtSectionPart) previous;
+                               } else {
+                                       // previous section
+                               }
+               }
+               return null;
+       }
+
+       @Override
+       public String toString() {
+               if (parentSection == null)
+                       return "Main section " + getContent();
+               return "Section " + getContent();
+       }
+
+       public SwtSection getParentSection() {
+               return parentSection;
+       }
+
+       public Integer getRelativeDepth() {
+               return relativeDepth;
+       }
+
+       /** Recursively finds the related section in the parents (can be itself) */
+       public static SwtSection findSection(Control control) {
+               if (control == null)
+                       return null;
+               if (control instanceof SwtSection)
+                       return (SwtSection) control;
+               else
+                       return findSection(control.getParent());
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtSectionPart.java
new file mode 100644 (file)
index 0000000..7fbf4bb
--- /dev/null
@@ -0,0 +1,11 @@
+package org.argeo.cms.swt.acr;
+
+import org.argeo.cms.ux.acr.ContentPart;
+import org.argeo.cms.ux.widgets.EditablePart;
+
+/** An editable part dynamically related to a Section */
+public interface SwtSectionPart extends EditablePart, ContentPart {
+       public String getPartId();
+
+       public SwtSection getSection();
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtTabbedArea.java
new file mode 100644 (file)
index 0000000..cd4e37d
--- /dev/null
@@ -0,0 +1,258 @@
+package org.argeo.cms.swt.acr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.graphics.Image;
+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.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/** Manages {@link SwtSection} in a tab-like structure. */
+public class SwtTabbedArea extends Composite {
+       private static final long serialVersionUID = 8659669229482033444L;
+
+       private Composite headers;
+       private Composite body;
+
+       private List<SwtSection> sections = new ArrayList<>();
+
+       private ProvidedContent previousNode;
+       private SwtUiProvider previousUiProvider;
+       private SwtUiProvider currentUiProvider;
+
+       private String tabStyle;
+       private String tabSelectedStyle;
+       private String bodyStyle;
+       private Image closeIcon;
+
+       private StackLayout stackLayout;
+
+       private boolean singleTab = false;
+
+       public SwtTabbedArea(Composite parent, int style) {
+               super(parent, SWT.NONE);
+               CmsSwtUtils.style(parent, bodyStyle);
+
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               // TODO manage tabs at bottom or sides
+               headers = new Composite(this, SWT.NONE);
+               headers.setLayoutData(CmsSwtUtils.fillWidth());
+               body = new Composite(this, SWT.NONE);
+               body.setLayoutData(CmsSwtUtils.fillAll());
+               // body.setLayout(new FormLayout());
+               stackLayout = new StackLayout();
+               body.setLayout(stackLayout);
+               emptyState();
+       }
+
+       protected void refreshTabHeaders() {
+               int tabCount = sections.size() > 0 ? sections.size() : 1;
+               for (Control tab : headers.getChildren())
+                       tab.dispose();
+
+               headers.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(tabCount, true)));
+
+               if (sections.size() == 0) {
+                       Composite emptyHeader = new Composite(headers, SWT.NONE);
+                       emptyHeader.setLayoutData(CmsSwtUtils.fillAll());
+                       emptyHeader.setLayout(new GridLayout());
+                       Label lbl = new Label(emptyHeader, SWT.NONE);
+                       lbl.setText("");
+                       lbl.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+
+               }
+
+               SwtSection currentSection = getCurrentSection();
+               for (SwtSection section : sections) {
+                       boolean selected = section == currentSection;
+                       Composite sectionHeader = section.createHeader(headers);
+                       CmsSwtUtils.style(sectionHeader, selected ? tabSelectedStyle : tabStyle);
+                       int headerColumns = singleTab ? 1 : 2;
+                       sectionHeader.setLayout(new GridLayout(headerColumns, false));
+                       sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout(headerColumns));
+                       Button title = new Button(sectionHeader, SWT.FLAT);
+                       CmsSwtUtils.style(title, selected ? tabSelectedStyle : tabStyle);
+                       title.setLayoutData(CmsSwtUtils.fillWidth());
+                       title.addSelectionListener((Selected) (e) -> showTab(tabIndex(section.getNode())));
+                       Content node = section.getContent();
+
+                       // FIXME find a standard way to display titles
+                       String titleStr = node.getName().getLocalPart();
+                       
+                       // TODO internationalize
+                       title.setText(titleStr);
+                       if (!singleTab) {
+                               ToolBar toolBar = new ToolBar(sectionHeader, SWT.NONE);
+                               ToolItem closeItem = new ToolItem(toolBar, SWT.FLAT);
+                               if (closeIcon != null)
+                                       closeItem.setImage(closeIcon);
+                               else
+                                       closeItem.setText("X");
+                               CmsSwtUtils.style(closeItem, selected ? tabSelectedStyle : tabStyle);
+                               closeItem.addSelectionListener((Selected) (e) -> closeTab(section));
+                       }
+               }
+
+       }
+
+       public void view(SwtUiProvider uiProvider, Content context) {
+               if (body.isDisposed())
+                       return;
+               int index = tabIndex(context);
+               if (index >= 0) {
+                       showTab(index);
+                       previousNode = (ProvidedContent) context;
+                       previousUiProvider = uiProvider;
+                       return;
+               }
+               SwtSection section = (SwtSection) body.getChildren()[0];
+               previousNode = (ProvidedContent) section.getNode();
+               if (previousNode == null) {// empty state
+                       previousNode = (ProvidedContent) context;
+                       previousUiProvider = uiProvider;
+               } else {
+                       previousUiProvider = currentUiProvider;
+               }
+               currentUiProvider = uiProvider;
+               section.setContent(context);
+               // section.setLayoutData(CmsUiUtils.coverAll());
+               build(section, uiProvider, context);
+               if (sections.size() == 0)
+                       sections.add(section);
+               refreshTabHeaders();
+               index = tabIndex(context);
+               showTab(index);
+               layout(true, true);
+       }
+
+       public void open(SwtUiProvider uiProvider, Content context) {
+               if (singleTab)
+                       throw new UnsupportedOperationException("Open is not supported in single tab mode.");
+
+               if (previousNode != null
+                               && previousNode.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId())) {
+                       // does nothing
+                       return;
+               }
+               if (sections.size() == 0)
+                       CmsSwtUtils.clear(body);
+               SwtSection currentSection = getCurrentSection();
+               int currentIndex = sections.indexOf(currentSection);
+               SwtSection previousSection = new SwtSection(body, SWT.NONE, context);
+               build(previousSection, previousUiProvider, previousNode);
+               // previousSection.setLayoutData(CmsUiUtils.coverAll());
+               int newIndex = currentIndex + 1;
+               sections.add(currentIndex, previousSection);
+//             sections.add(newIndex, previousSection);
+               showTab(newIndex);
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       public void showTab(int index) {
+               SwtSection sectionToShow = sections.get(index);
+               // sectionToShow.moveAbove(null);
+               stackLayout.topControl = sectionToShow;
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       protected void build(SwtSection section, SwtUiProvider uiProvider, Content context) {
+               for (Control child : section.getChildren())
+                       child.dispose();
+               CmsSwtUtils.style(section, bodyStyle);
+               section.setContent(context);
+               uiProvider.createUiPart(section, context);
+
+       }
+
+       private int tabIndex(Content context) {
+               for (int i = 0; i < sections.size(); i++) {
+                       SwtSection section = sections.get(i);
+                       if (section.getSessionLocalId().equals(((ProvidedContent) context).getSessionLocalId()))
+                               return i;
+               }
+               return -1;
+       }
+
+       public void closeTab(SwtSection section) {
+               int currentIndex = sections.indexOf(section);
+               int nextIndex = currentIndex == 0 ? 0 : currentIndex - 1;
+               sections.remove(section);
+               section.dispose();
+               if (sections.size() == 0) {
+                       emptyState();
+                       refreshTabHeaders();
+                       layout(true, true);
+                       return;
+               }
+               refreshTabHeaders();
+               showTab(nextIndex);
+       }
+
+       public void closeAllTabs() {
+               for (SwtSection section : sections) {
+                       section.dispose();
+               }
+               sections.clear();
+               emptyState();
+               refreshTabHeaders();
+               layout(true, true);
+       }
+
+       protected void emptyState() {
+               new SwtSection(body, SWT.NONE, null);
+               refreshTabHeaders();
+       }
+
+       public Composite getCurrent() {
+               return getCurrentSection();
+       }
+
+       protected SwtSection getCurrentSection() {
+               return (SwtSection) stackLayout.topControl;
+       }
+
+       public Content getCurrentContext() {
+               SwtSection section = getCurrentSection();
+               if (section != null) {
+                       return section.getNode();
+               } else {
+                       return null;
+               }
+       }
+
+       public void setTabStyle(String tabStyle) {
+               this.tabStyle = tabStyle;
+       }
+
+       public void setTabSelectedStyle(String tabSelectedStyle) {
+               this.tabSelectedStyle = tabSelectedStyle;
+       }
+
+       public void setBodyStyle(String bodyStyle) {
+               this.bodyStyle = bodyStyle;
+       }
+
+       public void setCloseIcon(Image closeIcon) {
+               this.closeIcon = closeIcon;
+       }
+
+       public void setSingleTab(boolean singleTab) {
+               this.singleTab = singleTab;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/SwtUiProvider.java
new file mode 100644 (file)
index 0000000..4988fc6
--- /dev/null
@@ -0,0 +1,10 @@
+package org.argeo.cms.swt.acr;
+
+import org.argeo.api.acr.Content;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+@FunctionalInterface
+public interface SwtUiProvider {
+       Control createUiPart(Composite parent, Content context);
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/AcrContentTreeView.java
new file mode 100644 (file)
index 0000000..fb1a79d
--- /dev/null
@@ -0,0 +1,158 @@
+package org.argeo.cms.swt.app;
+
+import static org.argeo.api.acr.NamespaceUtils.toPrefixedName;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.widgets.SwtHierarchicalPart;
+import org.argeo.cms.swt.widgets.SwtTabularPart;
+import org.argeo.cms.ux.acr.ContentHierarchicalPart;
+import org.argeo.cms.ux.widgets.Column;
+import org.argeo.cms.ux.widgets.DefaultTabularPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.widgets.Composite;
+
+public class AcrContentTreeView extends Composite {
+       private static final long serialVersionUID = -3707881216246077323L;
+
+       private Content rootContent;
+
+//     private Content selected;
+
+       public AcrContentTreeView(Composite parent, int style, Content content) {
+               super(parent, style);
+               this.rootContent = content;
+               // this.selected = rootContent;
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               SashForm split = new SashForm(this, SWT.HORIZONTAL);
+               split.setLayoutData(CmsSwtUtils.fillAll());
+
+               ContentHierarchicalPart contentPart = new ContentHierarchicalPart();
+               contentPart.setInput(rootContent);
+
+               SwtHierarchicalPart<Content> hPart = new SwtHierarchicalPart<>(split, getStyle(), contentPart);
+
+               Composite area = new Composite(split, SWT.BORDER);
+               area.setLayout(CmsSwtUtils.noSpaceGridLayout(2));
+               split.setWeights(new int[] { 30, 70 });
+
+               // attributes
+               DefaultTabularPart<Content, QName> attributesPart = new DefaultTabularPart<>() {
+
+                       @Override
+                       protected List<QName> asList(Content input) {
+                               return new ArrayList<>(input.keySet());
+                       }
+               };
+
+               attributesPart.addColumn(new Column<QName>() {
+
+                       @Override
+                       public String getText(QName model) {
+                               try {
+                                       return NamespaceUtils.toPrefixedName(model);
+                               } catch (IllegalStateException e) {
+                                       return model.toString();
+                               }
+                       }
+               });
+               attributesPart.addColumn(new Column<QName>() {
+
+                       @Override
+                       public String getText(QName model) {
+                               return attributesPart.getInput().get(model).toString();
+                       }
+
+                       @Override
+                       public int getWidth() {
+                               return 400;
+                       }
+
+               });
+               // attributesPart.setInput(selected);
+
+               SwtTabularPart<Content, QName> attributeTable = new SwtTabularPart<>(area, style, attributesPart);
+               attributeTable.setLayoutData(CmsSwtUtils.fillAll());
+
+               // types
+               DefaultTabularPart<Content, QName> typesPart = new DefaultTabularPart<>() {
+
+                       @Override
+                       protected List<QName> asList(Content input) {
+                               return input.getContentClasses();
+                       }
+               };
+               typesPart.addColumn(new Column<QName>() {
+
+                       @Override
+                       public String getText(QName model) {
+                               return toPrefixedName(model);
+                       }
+
+               });
+
+               // typesPart.setInput(selected);
+
+               SwtTabularPart<Content, QName> typesTable = new SwtTabularPart<>(area, style, typesPart);
+               typesTable.setLayoutData(CmsSwtUtils.fillAll());
+
+               // controller
+               contentPart.setInput(rootContent);
+               contentPart.onSelected((o) -> {
+                       Content c = (Content) o;
+//                     selected = c;
+                       attributesPart.setInput(c);
+                       typesPart.setInput(c);
+               });
+
+               attributesPart.refresh();
+               typesPart.refresh();
+       }
+
+//     protected void refreshTable() {
+//             for (TableItem item : table.getItems()) {
+//                     item.dispose();
+//             }
+//             for (QName key : selected.keySet()) {
+//                     TableItem item = new TableItem(table, 0);
+//                     item.setText(0, key.toString());
+//                     Object value = selected.get(key);
+//                     item.setText(1, value.toString());
+//             }
+//             table.getColumn(0).pack();
+//             table.getColumn(1).pack();
+//     }
+
+//     public static void main(String[] args) {
+//             Path basePath;
+//             if (args.length > 0) {
+//                     basePath = Paths.get(args[0]);
+//             } else {
+//                     basePath = Paths.get(System.getProperty("user.home"));
+//             }
+//
+//             final Display display = new Display();
+//             final Shell shell = new Shell(display);
+//             shell.setText(basePath.toString());
+//             shell.setLayout(new FillLayout());
+//
+//             FsContentProvider contentSession = new FsContentProvider("/", basePath);
+////           GcrContentTreeView treeView = new GcrContentTreeView(shell, 0, contentSession.get("/"));
+//
+//             shell.setSize(shell.computeSize(800, 600));
+//             shell.open();
+//             while (!shell.isDisposed()) {
+//                     if (!display.readAndDispatch())
+//                             display.sleep();
+//             }
+//             display.dispose();
+//     }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/app/CmsUserApp.java
new file mode 100644 (file)
index 0000000..add6e9e
--- /dev/null
@@ -0,0 +1,62 @@
+package org.argeo.cms.swt.app;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.AbstractCmsApp;
+import org.argeo.cms.swt.CmsSwtUi;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.auth.CmsLogin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+public class CmsUserApp extends AbstractCmsApp {
+       private ContentRepository contentRepository;
+
+       @Override
+       public Set<String> getUiNames() {
+               Set<String> uiNames = new HashSet<>();
+               uiNames.add("login");
+               uiNames.add("data");
+               return uiNames;
+       }
+
+       @Override
+       public CmsUi initUi(Object uiParent) {
+               Composite parent = (Composite) uiParent;
+               String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null;
+               CmsSwtUi cmsUi = new CmsSwtUi(parent, SWT.NONE);
+               if ("login".equals(uiName)) {
+                       CmsView cmsView = CmsSwtUtils.getCmsView(cmsUi);
+                       CmsLogin cmsLogin = new CmsLogin(cmsView, getCmsContext());
+                       cmsLogin.createUi(cmsUi);
+
+               } else if ("data".equals(uiName)) {
+                       Content rootContent = contentRepository.get().get("/");
+                       AcrContentTreeView view = new AcrContentTreeView(cmsUi, 0, rootContent);
+                       view.setLayoutData(CmsSwtUtils.fillAll());
+
+               }
+               return cmsUi;
+       }
+
+       @Override
+       public void refreshUi(CmsUi cmsUi, String state) {
+       }
+
+       @Override
+       public void setState(CmsUi cmsUi, String state) {
+               // TODO Auto-generated method stub
+
+       }
+
+       public void setContentRepository(ContentRepository contentRepository) {
+               this.contentRepository = contentRepository;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLogin.java
new file mode 100644 (file)
index 0000000..6cc410c
--- /dev/null
@@ -0,0 +1,338 @@
+package org.argeo.cms.swt.auth;
+
+import static org.argeo.cms.CmsMsg.password;
+import static org.argeo.cms.CmsMsg.username;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.LanguageCallback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.auth.RemoteAuthCallback;
+import org.argeo.cms.servlet.ServletHttpRequest;
+import org.argeo.cms.servlet.ServletHttpResponse;
+import org.argeo.cms.swt.CmsStyles;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class CmsLogin implements CmsStyles, CallbackHandler {
+       private final static CmsLog log = CmsLog.getLog(CmsLogin.class);
+
+       private Composite parent;
+       private Text usernameT, passwordT;
+       private Composite credentialsBlock;
+       private final SelectionListener loginSelectionListener;
+
+       private final Locale defaultLocale;
+       private LocaleChoice localeChoice = null;
+
+       private final CmsView cmsView;
+
+       // optional subject to be set explicitly
+       private Subject subject = null;
+
+       private CmsContext cmsContext;
+
+       public CmsLogin(CmsView cmsView, CmsContext cmsContext) {
+               this.cmsView = cmsView;
+               this.cmsContext = cmsContext;
+               if (this.cmsContext != null) {
+                       defaultLocale = this.cmsContext.getDefaultLocale();
+                       List<Locale> locales = this.cmsContext.getLocales();
+                       if (locales != null)
+                               localeChoice = new LocaleChoice(locales, defaultLocale);
+               } else {
+                       defaultLocale = Locale.getDefault();
+               }
+               loginSelectionListener = new SelectionListener() {
+                       private static final long serialVersionUID = -8832133363830973578L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               login();
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                       }
+               };
+       }
+
+       protected boolean isAnonymous() {
+               return cmsView.isAnonymous();
+       }
+
+       public final void createUi(Composite parent) {
+               this.parent = parent;
+               createContents(parent);
+       }
+
+       protected void createContents(Composite parent) {
+               defaultCreateContents(parent);
+       }
+
+       public final void defaultCreateContents(Composite parent) {
+               parent.setLayout(CmsSwtUtils.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);
+               }
+       }
+
+       public Composite getCredentialsBlock() {
+               return credentialsBlock;
+       }
+
+       protected Composite userUi(Composite parent) {
+               Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
+               credentialsBlock = new Composite(parent, SWT.NONE);
+               credentialsBlock.setLayout(new GridLayout());
+               // credentialsBlock.setLayoutData(CmsUiUtils.fillAll());
+
+               specificUserUi(credentialsBlock);
+
+               Label l = new Label(credentialsBlock, SWT.NONE);
+               CmsSwtUtils.style(l, CMS_USER_MENU_ITEM);
+               l.setText(CmsMsg.logout.lead(locale));
+               GridData lData = CmsSwtUtils.fillWidth();
+               lData.widthHint = 120;
+               l.setLayoutData(lData);
+
+               l.addMouseListener(new MouseAdapter() {
+                       private static final long serialVersionUID = 6444395812777413116L;
+
+                       public void mouseDown(MouseEvent e) {
+                               logout();
+                       }
+               });
+               return credentialsBlock;
+       }
+
+       /** To be overridden */
+       protected void specificUserUi(Composite parent) {
+
+       }
+
+       protected Composite anonymousUi(Composite parent) {
+               Locale locale = localeChoice == null ? this.defaultLocale : localeChoice.getSelectedLocale();
+               // We need a composite for the traversal
+               credentialsBlock = new Composite(parent, SWT.NONE);
+               credentialsBlock.setLayout(new GridLayout());
+               // credentialsBlock.setLayoutData(CmsUiUtils.fillAll());
+               CmsSwtUtils.style(credentialsBlock, CMS_LOGIN_DIALOG);
+
+               Integer textWidth = 120;
+               if (parent instanceof Shell)
+                       CmsSwtUtils.style(parent, CMS_USER_MENU);
+               // new Label(this, SWT.NONE).setText(CmsMsg.username.lead());
+               usernameT = new Text(credentialsBlock, SWT.BORDER);
+               usernameT.setMessage(username.lead(locale));
+               CmsSwtUtils.style(usernameT, CMS_LOGIN_DIALOG_USERNAME);
+               GridData gd = CmsSwtUtils.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));
+               CmsSwtUtils.style(passwordT, CMS_LOGIN_DIALOG_PASSWORD);
+               gd = CmsSwtUtils.fillWidth();
+               gd.widthHint = textWidth;
+               passwordT.setLayoutData(gd);
+
+               TraverseListener tl = new TraverseListener() {
+                       private static final long serialVersionUID = -1158892811534971856L;
+
+                       public void keyTraversed(TraverseEvent e) {
+                               if (e.detail == SWT.TRAVERSE_RETURN)
+                                       login();
+                       }
+               };
+               credentialsBlock.addTraverseListener(tl);
+               usernameT.addTraverseListener(tl);
+               passwordT.addTraverseListener(tl);
+               parent.setTabList(new Control[] { credentialsBlock });
+               credentialsBlock.setTabList(new Control[] { usernameT, passwordT });
+
+               // Button
+               Button loginButton = new Button(credentialsBlock, SWT.PUSH);
+               loginButton.setText(CmsMsg.login.lead(locale));
+               loginButton.setLayoutData(CmsSwtUtils.fillWidth());
+               loginButton.addSelectionListener(loginSelectionListener);
+
+               extendsCredentialsBlock(credentialsBlock, locale, loginSelectionListener);
+               if (localeChoice != null)
+                       createLocalesBlock(credentialsBlock);
+               return credentialsBlock;
+       }
+
+       /**
+        * To be overridden in order to provide custom login button and other links.
+        */
+       protected void extendsCredentialsBlock(Composite credentialsBlock, Locale selectedLocale,
+                       SelectionListener loginSelectionListener) {
+
+       }
+
+       protected void updateLocale(Locale selectedLocale) {
+               // save already entered values
+               String usernameStr = usernameT.getText();
+               char[] pwd = passwordT.getTextChars();
+
+               for (Control child : parent.getChildren())
+                       child.dispose();
+               createContents(parent);
+               if (parent.getParent() != null)
+                       parent.getParent().layout(true, true);
+               else
+                       parent.layout();
+               usernameT.setText(usernameStr);
+               passwordT.setTextChars(pwd);
+       }
+
+       protected Composite createLocalesBlock(final Composite parent) {
+               Composite c = new Composite(parent, SWT.NONE);
+               CmsSwtUtils.style(c, CMS_USER_MENU_ITEM);
+               c.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               c.setLayoutData(CmsSwtUtils.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);
+                       CmsSwtUtils.style(button, CMS_USER_MENU_ITEM);
+                       button.setData(i);
+                       button.setText(LocaleUtils.toLead(locale.getDisplayName(locale), locale) + " (" + locale + ")");
+                       // button.addListener(SWT.Selection, listener);
+                       button.addSelectionListener(selectionListener);
+                       if (i == localeChoice.getSelectedIndex())
+                               button.setSelection(true);
+               }
+               return c;
+       }
+
+       protected boolean login() {
+               // TODO use CmsVie in order to retrieve subject?
+               // Subject subject = cmsView.getLoginContext().getSubject();
+               // LoginContext loginContext = cmsView.getLoginContext();
+               try {
+                       //
+                       // LOGIN
+                       //
+                       // loginContext.logout();
+                       LoginContext loginContext;
+                       if (subject == null)
+                               loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, this);
+                       else
+                               loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, this);
+                       loginContext.login();
+                       cmsView.authChange(loginContext);
+                       return true;
+               } catch (LoginException e) {
+                       if (log.isTraceEnabled())
+                               log.warn("Login failed: " + e.getMessage(), e);
+                       else
+                               log.warn("Login failed: " + e.getMessage());
+
+                       try {
+                               Thread.sleep(3000);
+                       } catch (InterruptedException e2) {
+                               // silent
+                       }
+                       // ErrorFeedback.show("Login failed", e);
+                       return false;
+               }
+               // catch (LoginException e) {
+               // log.error("Cannot login", e);
+               // return false;
+               // }
+       }
+
+
+       protected void logout() {
+               cmsView.logout();
+               cmsView.navigateTo("~");
+       }
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+               for (Callback callback : callbacks) {
+                       if (callback instanceof NameCallback && usernameT != null)
+                               ((NameCallback) callback).setName(usernameT.getText());
+                       else if (callback instanceof PasswordCallback && passwordT != null)
+                               ((PasswordCallback) callback).setPassword(passwordT.getTextChars());
+                       else if (callback instanceof RemoteAuthCallback) {
+                               ((RemoteAuthCallback) callback).setRequest(new ServletHttpRequest(UiContext.getHttpRequest()));
+                               ((RemoteAuthCallback) callback).setResponse(new ServletHttpResponse(UiContext.getHttpResponse()));
+                       } else if (callback instanceof LanguageCallback) {
+                               Locale toUse = null;
+                               if (localeChoice != null)
+                                       toUse = localeChoice.getSelectedLocale();
+                               else if (defaultLocale != null)
+                                       toUse = defaultLocale;
+
+                               if (toUse != null) {
+                                       ((LanguageCallback) callback).setLocale(toUse);
+                                       UiContext.setLocale(toUse);
+                               }
+
+                       }
+               }
+       }
+
+       public void setSubject(Subject subject) {
+               this.subject = subject;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CmsLoginShell.java
new file mode 100644 (file)
index 0000000..39cf82a
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.cms.swt.auth;
+
+import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/** The site-related user menu */
+public class CmsLoginShell extends CmsLogin {
+       private final Shell shell;
+
+       public CmsLoginShell(CmsView cmsView, CmsContext cmsContext) {
+               super(cmsView, cmsContext);
+               shell = createShell();
+//             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() {
+               CmsSwtUtils.style(shell, CMS_USER_MENU);
+               shell.open();
+       }
+
+       @Override
+       protected boolean login() {
+               boolean success = false;
+               try {
+                       success = super.login();
+                       return success;
+               } finally {
+                       if (success)
+                               closeShell();
+                       else {
+                               for (Control child : shell.getChildren())
+                                       child.dispose();
+                               createUi(shell);
+                               shell.layout();
+                               // TODO error message
+                       }
+               }
+       }
+
+       @Override
+       protected void logout() {
+               closeShell();
+               super.logout();
+       }
+
+       protected void closeShell() {
+               if (!shell.isDisposed()) {
+                       shell.close();
+                       shell.dispose();
+               }
+       }
+
+       public Shell getShell() {
+               return shell;
+       }
+
+       public void createUi() {
+               createUi(shell);
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/CompositeCallbackHandler.java
new file mode 100644 (file)
index 0000000..495007c
--- /dev/null
@@ -0,0 +1,273 @@
+package org.argeo.cms.swt.auth;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A composite that can populate itself based on {@link Callback}s. It can be
+ * used directly as a {@link CallbackHandler} or be used by one by calling the
+ * {@link #createCallbackHandlers(Callback[])}. Supported standard
+ * {@link Callback}s are:<br>
+ * <ul>
+ * <li>{@link PasswordCallback}</li>
+ * <li>{@link NameCallback}</li>
+ * <li>{@link TextOutputCallback}</li>
+ * </ul>
+ * Supported Argeo {@link Callback}s are:<br>
+ * <ul>
+ * <li>{@link LocaleChoice}</li>
+ * </ul>
+ */
+public class CompositeCallbackHandler extends Composite implements CallbackHandler {
+       private static final long serialVersionUID = -928223893722723777L;
+
+       private boolean wasUsedAlready = false;
+       private boolean isSubmitted = false;
+       private boolean isCanceled = false;
+
+       public CompositeCallbackHandler(Composite parent, int style) {
+               super(parent, style);
+       }
+
+       @Override
+       public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+               // reset
+               if (wasUsedAlready && !isSubmitted() && !isCanceled()) {
+                       cancel();
+                       for (Control control : getChildren())
+                               control.dispose();
+                       isSubmitted = false;
+                       isCanceled = false;
+               }
+
+               for (Callback callback : callbacks)
+                       checkCallbackSupported(callback);
+               // create controls synchronously in the UI thread
+               getDisplay().syncExec(new Runnable() {
+
+                       @Override
+                       public void run() {
+                               createCallbackHandlers(callbacks);
+                       }
+               });
+
+               if (!wasUsedAlready)
+                       wasUsedAlready = true;
+
+               // while (!isSubmitted() && !isCanceled()) {
+               // try {
+               // wait(1000l);
+               // } catch (InterruptedException e) {
+               // // silent
+               // }
+               // }
+
+               // cleanCallbacksAfterCancel(callbacks);
+       }
+
+       public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException {
+               if (callback instanceof TextOutputCallback || callback instanceof NameCallback
+                               || callback instanceof PasswordCallback || callback instanceof LocaleChoice) {
+                       return;
+               } else {
+                       throw new UnsupportedCallbackException(callback);
+               }
+       }
+
+       /**
+        * Set writable callbacks to null if the handle is canceled (check is done
+        * by the method)
+        */
+       public void cleanCallbacksAfterCancel(Callback[] callbacks) {
+               if (isCanceled()) {
+                       for (Callback callback : callbacks) {
+                               if (callback instanceof NameCallback) {
+                                       ((NameCallback) callback).setName(null);
+                               } else if (callback instanceof PasswordCallback) {
+                                       PasswordCallback pCallback = (PasswordCallback) callback;
+                                       char[] arr = pCallback.getPassword();
+                                       if (arr != null) {
+                                               Arrays.fill(arr, '*');
+                                               pCallback.setPassword(null);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public void createCallbackHandlers(Callback[] callbacks) {
+               Composite composite = this;
+               for (int i = 0; i < callbacks.length; i++) {
+                       Callback callback = callbacks[i];
+                       if (callback instanceof TextOutputCallback) {
+                               createLabelTextoutputHandler(composite, (TextOutputCallback) callback);
+                       } else if (callback instanceof NameCallback) {
+                               createNameHandler(composite, (NameCallback) callback);
+                       } else if (callback instanceof PasswordCallback) {
+                               createPasswordHandler(composite, (PasswordCallback) callback);
+                       } else if (callback instanceof LocaleChoice) {
+                               createLocaleHandler(composite, (LocaleChoice) callback);
+                       }
+               }
+       }
+
+       protected Text createNameHandler(Composite composite, final NameCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getPrompt());
+               final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+               if (callback.getDefaultName() != null) {
+                       // set default value, if provided
+                       text.setText(callback.getDefaultName());
+                       callback.setName(callback.getDefaultName());
+               }
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               text.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = 7300032545287292973L;
+
+                       public void modifyText(ModifyEvent event) {
+                               callback.setName(text.getText());
+                       }
+               });
+               text.addSelectionListener(new SelectionListener() {
+                       private static final long serialVersionUID = 1820530045857665111L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                               submit();
+                       }
+               });
+
+               text.addKeyListener(new KeyListener() {
+                       private static final long serialVersionUID = -8698107785092095713L;
+
+                       @Override
+                       public void keyReleased(KeyEvent e) {
+                       }
+
+                       @Override
+                       public void keyPressed(KeyEvent e) {
+                       }
+               });
+               return text;
+       }
+
+       protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getPrompt());
+               final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER);
+               passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               passwordText.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = -7099363995047686732L;
+
+                       public void modifyText(ModifyEvent event) {
+                               callback.setPassword(passwordText.getTextChars());
+                       }
+               });
+               passwordText.addSelectionListener(new SelectionListener() {
+                       private static final long serialVersionUID = 1820530045857665111L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                               submit();
+                       }
+               });
+               return passwordText;
+       }
+
+       protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) {
+               String[] labels = callback.getSupportedLocalesLabels();
+               if (labels.length == 0)
+                       return null;
+               Label label = new Label(composite, SWT.NONE);
+               label.setText("Language");
+
+               final Combo combo = new Combo(composite, SWT.READ_ONLY);
+               combo.setItems(labels);
+               combo.select(callback.getDefaultIndex());
+               combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               combo.addSelectionListener(new SelectionListener() {
+                       private static final long serialVersionUID = 38678989091946277L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               callback.setSelectedIndex(combo.getSelectionIndex());
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                       }
+               });
+               return combo;
+       }
+
+       protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) {
+               Label label = new Label(composite, SWT.NONE);
+               label.setText(callback.getMessage());
+               GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+               data.horizontalSpan = 2;
+               label.setLayoutData(data);
+               return label;
+               // TODO: find a way to pass this information
+               // int messageType = callback.getMessageType();
+               // int dialogMessageType = IMessageProvider.NONE;
+               // switch (messageType) {
+               // case TextOutputCallback.INFORMATION:
+               // dialogMessageType = IMessageProvider.INFORMATION;
+               // break;
+               // case TextOutputCallback.WARNING:
+               // dialogMessageType = IMessageProvider.WARNING;
+               // break;
+               // case TextOutputCallback.ERROR:
+               // dialogMessageType = IMessageProvider.ERROR;
+               // break;
+               // }
+               // setMessage(callback.getMessage(), dialogMessageType);
+       }
+
+       synchronized boolean isSubmitted() {
+               return isSubmitted;
+       }
+
+       synchronized boolean isCanceled() {
+               return isCanceled;
+       }
+
+       protected synchronized void submit() {
+               isSubmitted = true;
+               notifyAll();
+       }
+
+       protected synchronized void cancel() {
+               isCanceled = true;
+               notifyAll();
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/DynamicCallbackHandler.java
new file mode 100644 (file)
index 0000000..b0c36c6
--- /dev/null
@@ -0,0 +1,34 @@
+package org.argeo.cms.swt.auth;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.argeo.eclipse.ui.dialogs.LightweightDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class DynamicCallbackHandler implements CallbackHandler {
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+               Shell activeShell = Display.getCurrent().getActiveShell();
+               LightweightDialog dialog = new LightweightDialog(activeShell) {
+
+                       @Override
+                       protected Control createDialogArea(Composite parent) {
+                               CompositeCallbackHandler cch = new CompositeCallbackHandler(parent, SWT.NONE);
+                               cch.createCallbackHandlers(callbacks);
+                               return cch;
+                       }
+               };
+               dialog.setBlockOnOpen(true);
+               dialog.open();
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/LocaleChoice.java
new file mode 100644 (file)
index 0000000..3ce5ae5
--- /dev/null
@@ -0,0 +1,86 @@
+package org.argeo.cms.swt.auth;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import javax.security.auth.callback.LanguageCallback;
+
+import org.argeo.cms.swt.CmsException;
+
+/** Choose in a list of locales. TODO: replace with {@link LanguageCallback} */
+@Deprecated
+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/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/auth/package-info.java
new file mode 100644 (file)
index 0000000..b431423
--- /dev/null
@@ -0,0 +1,2 @@
+/** Argeo CMS authentication widgets, based on SWT. */
+package org.argeo.cms.swt.auth;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/ChangePasswordDialog.java
new file mode 100644 (file)
index 0000000..06e4d0f
--- /dev/null
@@ -0,0 +1,83 @@
+package org.argeo.cms.swt.dialogs;
+
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.swt.CmsSwtUtils;
+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.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** Dialog to change a password. */
+public class ChangePasswordDialog extends CmsMessageDialog {
+       private final static CmsLog log = CmsLog.getLog(ChangePasswordDialog.class);
+
+       private CmsUserManager cmsUserManager;
+       private CmsView cmsView;
+
+       private PrivilegedAction<Integer> doIt;
+
+       public ChangePasswordDialog(Shell parentShell, String message, int kind, CmsUserManager cmsUserManager) {
+               super(parentShell, message, kind);
+               this.cmsUserManager = cmsUserManager;
+               cmsView = CmsSwtUtils.getCmsView(parentShell);
+       }
+
+       @Override
+       protected Control createInputArea(Composite userSection) {
+               addFormLabel(userSection, CmsMsg.currentPassword.lead());
+               Text previousPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               previousPassword.setLayoutData(CmsSwtUtils.fillWidth());
+               addFormLabel(userSection, CmsMsg.newPassword.lead());
+               Text newPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               newPassword.setLayoutData(CmsSwtUtils.fillWidth());
+               addFormLabel(userSection, CmsMsg.repeatNewPassword.lead());
+               Text confirmPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               confirmPassword.setLayoutData(CmsSwtUtils.fillWidth());
+
+               doIt = () -> {
+                       if (Arrays.equals(newPassword.getTextChars(), confirmPassword.getTextChars())) {
+                               try {
+                                       cmsUserManager.changeOwnPassword(previousPassword.getTextChars(), newPassword.getTextChars());
+                                       return OK;
+                               } catch (Exception e1) {
+                                       log.error("Could not change password", e1);
+                                       cancel();
+                                       CmsMessageDialog.openError(CmsMsg.invalidPassword.lead());
+                                       return CANCEL;
+                               }
+                       } else {
+                               cancel();
+                               CmsMessageDialog.openError(CmsMsg.repeatNewPassword.lead());
+                               return CANCEL;
+                       }
+               };
+
+               pack();
+               return previousPassword;
+       }
+
+       @Override
+       protected void okPressed() {
+               Integer returnCode = cmsView.doAs(doIt);
+               if (returnCode.equals(OK)) {
+                       super.okPressed();
+                       CmsMessageDialog.openInformation(CmsMsg.passwordChanged.lead());
+               }
+       }
+
+       private static Label addFormLabel(Composite parent, String label) {
+               Label lbl = new Label(parent, SWT.WRAP);
+               lbl.setText(label);
+//             CmsUiUtils.style(lbl, SuiteStyle.simpleLabel);
+               return lbl;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsFeedback.java
new file mode 100644 (file)
index 0000000..a01c919
--- /dev/null
@@ -0,0 +1,100 @@
+package org.argeo.cms.swt.dialogs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.swt.Selected;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** A dialog feedback based on a {@link LightweightDialog}. */
+public class CmsFeedback extends LightweightDialog {
+       private final static CmsLog log = CmsLog.getLog(CmsFeedback.class);
+
+       private String message;
+       private Throwable exception;
+
+       public CmsFeedback(Shell parentShell, String message, Throwable e) {
+               super(parentShell);
+               this.message = message;
+               this.exception = e;
+               log.error(message, e);
+       }
+
+       public static CmsFeedback show(String message, Throwable e) {
+               // rethrow ThreaDeath in order to make sure that RAP will properly clean
+               // up the UI thread
+               if (e instanceof ThreadDeath)
+                       throw (ThreadDeath) e;
+
+               try {
+                       CmsFeedback cmsFeedback = new CmsFeedback(null, message, e);
+                       cmsFeedback.setBlockOnOpen(false);
+                       cmsFeedback.open();
+                       return cmsFeedback;
+               } catch (Throwable e1) {
+                       log.error("Cannot open error feedback (" + e.getMessage() + "), original error below", e);
+                       return null;
+               }
+       }
+
+       public static CmsFeedback show(String message) {
+               CmsFeedback cmsFeedback = new CmsFeedback(null, message, null);
+               cmsFeedback.open();
+               return cmsFeedback;
+       }
+
+       /** Tries to find a display */
+       // private static Display getDisplay() {
+       // try {
+       // Display display = Display.getCurrent();
+       // if (display != null)
+       // return display;
+       // else
+       // return Display.getDefault();
+       // } catch (Exception e) {
+       // return Display.getCurrent();
+       // }
+       // }
+
+       protected Control createDialogArea(Composite parent) {
+               parent.setLayout(new GridLayout(2, false));
+
+               Label messageLbl = new Label(parent, SWT.WRAP);
+               if (message != null)
+                       messageLbl.setText(message);
+               else if (exception != null)
+                       messageLbl.setText(exception.getLocalizedMessage());
+
+               Button close = new Button(parent, SWT.FLAT);
+               close.setText(CmsMsg.close.lead());
+               close.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
+               close.addSelectionListener((Selected) (e) -> closeShell(OK));
+
+               // Composite composite = new Composite(dialogarea, SWT.NONE);
+               // composite.setLayout(new GridLayout(2, false));
+               // composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               if (exception != null) {
+                       Text stack = new Text(parent, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+                       stack.setEditable(false);
+                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+                       StringWriter sw = new StringWriter();
+                       exception.printStackTrace(new PrintWriter(sw));
+                       stack.setText(sw.toString());
+               }
+
+               // parent.pack();
+               return messageLbl;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/CmsMessageDialog.java
new file mode 100644 (file)
index 0000000..66e6405
--- /dev/null
@@ -0,0 +1,167 @@
+package org.argeo.cms.swt.dialogs;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/** Base class for dialogs displaying messages or small forms. */
+public class CmsMessageDialog extends LightweightDialog {
+       public final static int NONE = 0;
+       public final static int ERROR = 1;
+       public final static int INFORMATION = 2;
+       public final static int QUESTION = 3;
+       public final static int WARNING = 4;
+       public final static int CONFIRM = 5;
+       public final static int QUESTION_WITH_CANCEL = 6;
+
+       private int kind;
+       private String message;
+
+       public CmsMessageDialog(Shell parentShell, String message, int kind) {
+               super(parentShell);
+               this.kind = kind;
+               this.message = message;
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               parent.setLayout(new GridLayout());
+
+               TraverseListener traverseListener = new TraverseListener() {
+                       private static final long serialVersionUID = -1158892811534971856L;
+
+                       public void keyTraversed(TraverseEvent e) {
+                               if (e.detail == SWT.TRAVERSE_RETURN)
+                                       okPressed();
+                               else if (e.detail == SWT.TRAVERSE_ESCAPE)
+                                       cancelPressed();
+                       }
+               };
+
+               // message
+               Composite body = new Composite(parent, SWT.NONE);
+               body.addTraverseListener(traverseListener);
+               GridLayout bodyGridLayout = new GridLayout();
+               bodyGridLayout.marginHeight = 20;
+               bodyGridLayout.marginWidth = 20;
+               body.setLayout(bodyGridLayout);
+               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               if (message != null) {
+                       Label messageLbl = new Label(body, SWT.WRAP);
+                       CmsSwtUtils.markup(messageLbl);
+                       messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+                       messageLbl.setFont(EclipseUiUtils.getBoldFont(parent));
+                       messageLbl.setText(message);
+               }
+
+               // buttons
+               Composite buttons = new Composite(parent, SWT.NONE);
+               buttons.addTraverseListener(traverseListener);
+               buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+               if (kind == INFORMATION || kind == WARNING || kind == ERROR || kind == ERROR) {
+                       GridLayout layout = new GridLayout(1, true);
+                       layout.marginWidth = 0;
+                       layout.marginHeight = 0;
+                       buttons.setLayout(layout);
+
+                       Button close = new Button(buttons, SWT.FLAT);
+                       close.setText(CmsMsg.close.lead());
+                       close.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                       close.addSelectionListener((Selected) (e) -> closeShell(OK));
+                       close.setFocus();
+                       close.addTraverseListener(traverseListener);
+
+                       buttons.setTabList(new Control[] { close });
+               } else if (kind == CONFIRM || kind == QUESTION || kind == QUESTION_WITH_CANCEL) {
+                       Control input = createInputArea(body);
+                       if (input != null) {
+                               input.addTraverseListener(traverseListener);
+                               body.setTabList(new Control[] { input });
+                       }
+                       GridLayout layout = new GridLayout(2, true);
+                       layout.marginWidth = 0;
+                       layout.marginHeight = 0;
+                       buttons.setLayout(layout);
+
+                       Button cancel = new Button(buttons, SWT.FLAT);
+                       cancel.setText(CmsMsg.cancel.lead());
+                       cancel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                       cancel.addSelectionListener((Selected) (e) -> cancelPressed());
+                       cancel.addTraverseListener(traverseListener);
+
+                       Button ok = new Button(buttons, SWT.FLAT);
+                       ok.setText(CmsMsg.ok.lead());
+                       ok.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                       ok.addSelectionListener((Selected) (e) -> okPressed());
+                       ok.addTraverseListener(traverseListener);
+                       if (input == null)
+                               ok.setFocus();
+                       else
+                               input.setFocus();
+
+                       buttons.setTabList(new Control[] { ok, cancel });
+               }
+               // pack();
+               parent.setTabList(new Control[] { body, buttons });
+               return body;
+       }
+
+       protected Control createInputArea(Composite parent) {
+               return null;
+       }
+
+       protected void okPressed() {
+               closeShell(OK);
+       }
+
+       protected void cancelPressed() {
+               closeShell(CANCEL);
+       }
+
+       protected void cancel() {
+               closeShell(CANCEL);
+       }
+
+       protected Point getInitialSize() {
+               return new Point(400, 200);
+       }
+
+       public static boolean open(int kind, Shell parent, String message) {
+               CmsMessageDialog dialog = new CmsMessageDialog(parent, message, kind);
+               return dialog.open() == 0;
+       }
+
+       public static boolean openConfirm(String message) {
+               return open(CONFIRM, Display.getCurrent().getActiveShell(), message);
+       }
+
+       public static void openInformation(String message) {
+               open(INFORMATION, Display.getCurrent().getActiveShell(), message);
+       }
+
+       public static boolean openQuestion(String message) {
+               return open(QUESTION, Display.getCurrent().getActiveShell(), message);
+       }
+
+       public static void openWarning(String message) {
+               open(WARNING, Display.getCurrent().getActiveShell(), message);
+       }
+
+       public static void openError(String message) {
+               open(ERROR, Display.getCurrent().getActiveShell(), message);
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/LightweightDialog.java
new file mode 100644 (file)
index 0000000..bf6417b
--- /dev/null
@@ -0,0 +1,255 @@
+package org.argeo.cms.swt.dialogs;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+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;
+
+/** Generic lightweight dialog, not based on JFace. */
+public class LightweightDialog {
+       private final static CmsLog log = CmsLog.getLog(LightweightDialog.class);
+
+       // must be the same value as org.eclipse.jface.window.Window#OK
+       public final static int OK = 0;
+       // must be the same value as org.eclipse.jface.window.Window#CANCEL
+       public final static int CANCEL = 1;
+
+       private Shell parentShell;
+       private Shell backgroundShell;
+       private Shell foregoundShell;
+
+       private Integer returnCode = null;
+       private boolean block = true;
+
+       private String title;
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = Display.getCurrent();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       public LightweightDialog(Shell parentShell) {
+               this.parentShell = parentShell;
+       }
+
+       public int open() {
+               if (foregoundShell != null)
+                       throw new EclipseUiException("There is already a shell");
+               backgroundShell = new Shell(parentShell, SWT.ON_TOP);
+               backgroundShell.setFullScreen(true);
+               // if (parentShell != null) {
+               // backgroundShell.setBounds(parentShell.getBounds());
+               // } else
+               // backgroundShell.setMaximized(true);
+               backgroundShell.setAlpha(128);
+               backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
+               foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
+               if (title != null)
+                       setTitle(title);
+               foregoundShell.setLayout(new GridLayout());
+               foregoundShell.setSize(getInitialSize());
+               createDialogArea(foregoundShell);
+               // shell.pack();
+               // shell.layout();
+
+               Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP
+               Point dialogSize = foregoundShell.getSize();
+               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
+               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
+               foregoundShell.setLocation(x, y);
+
+               foregoundShell.addShellListener(new ShellAdapter() {
+                       private static final long serialVersionUID = -2701270481953688763L;
+
+                       @Override
+                       public void shellDeactivated(ShellEvent e) {
+                               if (hasChildShells())
+                                       return;
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
+                       }
+
+                       @Override
+                       public void shellClosed(ShellEvent e) {
+                               notifyClose();
+                       }
+
+               });
+
+               backgroundShell.open();
+               foregoundShell.open();
+               // after the foreground shell has been opened
+               backgroundShell.addFocusListener(new FocusListener() {
+                       private static final long serialVersionUID = 3137408447474661070L;
+
+                       @Override
+                       public void focusLost(FocusEvent event) {
+                       }
+
+                       @Override
+                       public void focusGained(FocusEvent event) {
+                               if (hasChildShells())
+                                       return;
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
+                       }
+               });
+
+               if (block) {
+                       block();
+               }
+               if (returnCode == null)
+                       returnCode = OK;
+               return returnCode;
+       }
+
+       public void block() {
+               try {
+                       runEventLoop(foregoundShell);
+               } catch (ThreadDeath t) {
+                       returnCode = CANCEL;
+                       if (log.isTraceEnabled())
+                               log.error("Thread death, canceling dialog", t);
+               } catch (Throwable t) {
+                       returnCode = CANCEL;
+                       log.error("Cannot open blocking lightweight dialog", t);
+               }
+       }
+
+       private boolean hasChildShells() {
+               if (foregoundShell == null)
+                       return false;
+               return foregoundShell.getShells().length != 0;
+       }
+
+       // public synchronized int openAndWait() {
+       // open();
+       // while (returnCode == null)
+       // try {
+       // wait(100);
+       // } catch (InterruptedException e) {
+       // // silent
+       // }
+       // return returnCode;
+       // }
+
+       private synchronized void notifyClose() {
+               if (returnCode == null)
+                       returnCode = CANCEL;
+               notifyAll();
+       }
+
+       protected void closeShell(int returnCode) {
+               this.returnCode = returnCode;
+               if (CANCEL == returnCode)
+                       onCancel();
+               if (foregoundShell != null && !foregoundShell.isDisposed()) {
+                       foregoundShell.close();
+                       foregoundShell.dispose();
+                       foregoundShell = null;
+               }
+
+               if (backgroundShell != null && !backgroundShell.isDisposed()) {
+                       backgroundShell.close();
+                       backgroundShell.dispose();
+               }
+       }
+
+       protected Point getInitialSize() {
+               // if (exception != null)
+               // return new Point(800, 600);
+               // else
+               return new Point(600, 400);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = new Composite(parent, SWT.NONE);
+               dialogarea.setLayout(new GridLayout());
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               return dialogarea;
+       }
+
+       protected Shell getBackgroundShell() {
+               return backgroundShell;
+       }
+
+       protected Shell getForegoundShell() {
+               return foregoundShell;
+       }
+
+       public void setBlockOnOpen(boolean shouldBlock) {
+               block = shouldBlock;
+       }
+
+       public void pack() {
+               foregoundShell.pack();
+       }
+
+       private void runEventLoop(Shell loopShell) {
+               Display display;
+               if (foregoundShell == null) {
+                       display = Display.getCurrent();
+               } else {
+                       display = loopShell.getDisplay();
+               }
+
+               while (loopShell != null && !loopShell.isDisposed()) {
+                       try {
+                               if (!display.readAndDispatch()) {
+                                       display.sleep();
+                               }
+                       } catch (UnsupportedOperationException e) {
+                               throw e;
+                       } catch (Throwable e) {
+                               handleException(e);
+                       }
+               }
+               if (!display.isDisposed())
+                       display.update();
+       }
+
+       protected void handleException(Throwable t) {
+               if (t instanceof ThreadDeath) {
+                       // Don't catch ThreadDeath as this is a normal occurrence when
+                       // the thread dies
+                       throw (ThreadDeath) t;
+               }
+               // Try to keep running.
+               t.printStackTrace();
+       }
+
+       /** @return false, if the dialog should not be closed. */
+       protected boolean onCancel() {
+               return true;
+       }
+
+       public void setTitle(String title) {
+               this.title = title;
+               if (title != null && getForegoundShell() != null)
+                       getForegoundShell().setText(title);
+       }
+
+       public Integer getReturnCode() {
+               return returnCode;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/SingleValueDialog.java
new file mode 100644 (file)
index 0000000..9404b81
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.cms.swt.dialogs;
+
+import org.eclipse.jface.window.Window;
+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.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/** A dialog asking a for a single value. */
+public class SingleValueDialog extends CmsMessageDialog {
+       private Text valueT;
+       private String value;
+       private String defaultValue;
+
+       public SingleValueDialog(Shell parentShell, String message) {
+               super(parentShell, message, QUESTION);
+       }
+
+       public SingleValueDialog(Shell parentShell, String message, String defaultValue) {
+               super(parentShell, message, QUESTION);
+               this.defaultValue = defaultValue;
+       }
+
+       @Override
+       protected Control createInputArea(Composite parent) {
+               valueT = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE);
+               valueT.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+               if (defaultValue != null)
+                       valueT.setText(defaultValue);
+               return valueT;
+       }
+
+       @Override
+       protected void okPressed() {
+               value = valueT.getText();
+               super.okPressed();
+       }
+
+       public String getString() {
+               return value;
+       }
+
+       public Long getLong() {
+               return Long.valueOf(getString());
+       }
+
+       public Double getDouble() {
+               return Double.valueOf(getString());
+       }
+
+       public static String ask(String message) {
+               return ask(message, null);
+       }
+
+       public static String ask(String message, String defaultValue) {
+               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message, defaultValue);
+               if (svd.open() == Window.OK)
+                       return svd.getString();
+               else
+                       return null;
+       }
+
+       public static Long askLong(String message) {
+               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message);
+               if (svd.open() == Window.OK)
+                       return svd.getLong();
+               else
+                       return null;
+       }
+
+       public static Double askDouble(String message) {
+               SingleValueDialog svd = new SingleValueDialog(Display.getCurrent().getActiveShell(), message);
+               if (svd.open() == Window.OK)
+                       return svd.getDouble();
+               else
+                       return null;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/dialogs/package-info.java
new file mode 100644 (file)
index 0000000..ac76dba
--- /dev/null
@@ -0,0 +1,2 @@
+/** SWT/JFace dialogs. */
+package org.argeo.cms.swt.dialogs;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleCmsSwtTheme.java
new file mode 100644 (file)
index 0000000..b3fec78
--- /dev/null
@@ -0,0 +1,111 @@
+package org.argeo.cms.swt.osgi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.cms.osgi.BundleCmsTheme;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+
+/** Centralises some generic {@link CmsSwtTheme} patterns. */
+public class BundleCmsSwtTheme extends BundleCmsTheme implements CmsSwtTheme {
+       private Map<String, ImageData> imageCache = new HashMap<>();
+
+       private Map<String, Map<Integer, String>> iconPaths = new HashMap<>();
+
+       protected Image getImage(String path) {
+               if (!imageCache.containsKey(path)) {
+                       try (InputStream in = getResourceAsStream(path)) {
+                               if (in == null)
+                                       return null;
+                               ImageData imageData = new ImageData(in);
+                               imageCache.put(path, imageData);
+                       } catch (IOException e) {
+                               throw new IllegalStateException(e);
+                       }
+               }
+               ImageData imageData = imageCache.get(path);
+               Image image = new Image(Display.getCurrent(), imageData);
+               return image;
+       }
+
+       /**
+        * And icon with this file name (without the extension), with a best effort to
+        * find the appropriate size, or <code>null</code> if not found.
+        * 
+        * @param name          An icon file name without path and extension.
+        * @param preferredSize the preferred size, if <code>null</code>,
+        *                      {@link #getSmallIconSize()} will be tried.
+        */
+       public Image getIcon(String name, Integer preferredSize) {
+               if (preferredSize == null)
+                       preferredSize = getSmallIconSize();
+               Map<Integer, String> subCache;
+               if (!iconPaths.containsKey(name))
+                       subCache = new HashMap<>();
+               else
+                       subCache = iconPaths.get(name);
+               Image image = null;
+               if (!subCache.containsKey(preferredSize)) {
+                       Image bestMatchSoFar = null;
+                       paths: for (String p : getImagesPaths()) {
+                               int lastSlash = p.lastIndexOf('/');
+                               String fileName = p;
+                               String ext = "";
+                               if (lastSlash >= 0)
+                                       fileName = p.substring(lastSlash + 1);
+                               int lastDot = fileName.lastIndexOf('.');
+                               if (lastDot >= 0) {
+                                       ext = fileName.substring(lastDot + 1);
+                                       fileName = fileName.substring(0, lastDot);
+                               }
+
+                               if ("svg".equals(ext))
+                                       continue paths;
+
+                               if (fileName.equals(name)) {// matched
+                                       Image img = getImage(p);
+                                       int width = img.getBounds().width;
+                                       if (width == preferredSize) {// perfect match
+                                               subCache.put(preferredSize, p);
+                                               image = img;
+                                               break paths;
+                                       }
+                                       if (bestMatchSoFar == null) {
+                                               bestMatchSoFar = img;
+                                       } else {
+                                               if (Math.abs(width - preferredSize) < Math
+                                                               .abs(bestMatchSoFar.getBounds().width - preferredSize))
+                                                       bestMatchSoFar = img;
+                                       }
+                               }
+                       }
+
+                       if (image == null)
+                               image = bestMatchSoFar;
+               } else {
+                       image = getImage(subCache.get(preferredSize));
+               }
+
+               if (image != null && !iconPaths.containsKey(name))
+                       iconPaths.put(name, subCache);
+
+               return image;
+       }
+
+       @Override
+       public Image getSmallIcon(CmsIcon icon) {
+               return getIcon(icon.name(), getSmallIconSize());
+       }
+
+       @Override
+       public Image getBigIcon(CmsIcon icon) {
+               return getIcon(icon.name(), getBigIconSize());
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/osgi/BundleSvgTheme.java
new file mode 100644 (file)
index 0000000..e65f226
--- /dev/null
@@ -0,0 +1,105 @@
+package org.argeo.cms.swt.osgi;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.batik.transcoder.TranscoderException;
+import org.apache.batik.transcoder.TranscoderInput;
+import org.apache.batik.transcoder.TranscoderOutput;
+import org.apache.batik.transcoder.image.ImageTranscoder;
+import org.apache.batik.transcoder.image.PNGTranscoder;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.framework.BundleContext;
+
+/** Theme which can dynamically create icons from SVG data. */
+public class BundleSvgTheme extends BundleCmsSwtTheme {
+       private final static Logger logger = System.getLogger(BundleSvgTheme.class.getName());
+
+       private Map<String, Map<Integer, Image>> imageCache = Collections.synchronizedMap(new HashMap<>());
+
+       private Map<Integer, ImageTranscoder> transcoders = Collections.synchronizedMap(new HashMap<>());
+
+       @Override
+       public Image getIcon(String name, Integer preferredSize) {
+               String path = "icons/types/svg/" + name + ".svg";
+               return createImageFromSvg(path, preferredSize);
+       }
+
+       protected Image createImageFromSvg(String path, Integer preferredSize) {
+               Image image = null;
+               if (imageCache.containsKey(path)) {
+                       image = imageCache.get(path).get(preferredSize);
+               }
+               if (image != null)
+                       return image;
+               ImageData imageData = loadFromSvg(path, preferredSize);
+               image = new Image(Display.getDefault(), imageData);
+               if (!imageCache.containsKey(path))
+                       imageCache.put(path, Collections.synchronizedMap(new HashMap<>()));
+               imageCache.get(path).put(preferredSize, image);
+               return image;
+       }
+
+       protected ImageData loadFromSvg(String path, int size) {
+               ImageTranscoder transcoder = null;
+               synchronized (this) {
+                       transcoder = transcoders.get(size);
+                       if (transcoder == null) {
+                               transcoder = new PNGTranscoder();
+                               transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) size);
+                               transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) size);
+                               transcoders.put(size, transcoder);
+                       }
+               }
+               ImageData imageData;
+               try (InputStream in = getResourceAsStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
+                       if (in == null)
+                               throw new IllegalArgumentException(path + " not found");
+                       TranscoderInput input = new TranscoderInput(in);
+                       TranscoderOutput output = new TranscoderOutput(out);
+                       transcoder.transcode(input, output);
+                       try (InputStream imageIn = new ByteArrayInputStream(out.toByteArray())) {
+                               imageData = new ImageData(imageIn);
+                       }
+                       logger.log(Level.DEBUG, () -> "Generated " + size + "x" + size + " PNG icon from " + path);
+               } catch (IOException | TranscoderException e) {
+                       throw new RuntimeException("Cannot transcode SVG " + path, e);
+               }
+
+               return imageData;
+       }
+
+       @Override
+       public void init(BundleContext bundleContext, Map<String, String> properties) {
+               super.init(bundleContext, properties);
+
+               // preload all icons
+//             paths: for (String p : getImagesPaths()) {
+//                     if (!p.endsWith(".svg"))
+//                             continue paths;
+//                     createImageFromSvg(p, getDefaultIconSize());
+//             }
+       }
+
+       @Override
+       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+               Display display = Display.getDefault();
+               if (display != null)
+                       for (String path : imageCache.keySet()) {
+                               for (Image image : imageCache.get(path).values()) {
+                                       display.syncExec(() -> image.dispose());
+                               }
+                       }
+               super.destroy(bundleContext, properties);
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/PickUpUserDialog.java
new file mode 100644 (file)
index 0000000..ed1bfd8
--- /dev/null
@@ -0,0 +1,246 @@
+package org.argeo.cms.swt.useradmin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.parts.LdifUsersTable;
+import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.naming.LdapObjs;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.TrayDialog;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+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.FillLayout;
+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.Shell;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.Group;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/** Dialog with a user (or group) list to pick up one */
+public class PickUpUserDialog extends TrayDialog {
+       private static final long serialVersionUID = -1420106871173920369L;
+
+       // Business objects
+       private final UserAdmin userAdmin;
+       private User selectedUser;
+
+       // this page widgets and UI objects
+       private String title;
+       private LdifUsersTable userTableViewerCmp;
+       private TableViewer userViewer;
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+
+       /**
+        * A dialog to pick up a group or a user, showing a table with default
+        * columns
+        */
+       public PickUpUserDialog(Shell parentShell, String title, UserAdmin userAdmin) {
+               super(parentShell);
+               this.title = title;
+               this.userAdmin = userAdmin;
+
+               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_ICON), "",
+                               24, 24));
+               columnDefs.add(new ColumnDefinition(
+                               new UserLP(UserLP.COL_DISPLAY_NAME), "Common Name", 150, 100));
+               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DOMAIN),
+                               "Domain", 100, 120));
+               columnDefs.add(new ColumnDefinition(new UserLP(UserLP.COL_DN),
+                               "Distinguished Name", 300, 100));
+       }
+
+       /** A dialog to pick up a group or a user */
+       public PickUpUserDialog(Shell parentShell, String title,
+                       UserAdmin userAdmin, List<ColumnDefinition> columnDefs) {
+               super(parentShell);
+               this.title = title;
+               this.userAdmin = userAdmin;
+               this.columnDefs = columnDefs;
+       }
+
+       @Override
+       protected void okPressed() {
+               if (getSelected() == null)
+                       MessageDialog.openError(getShell(), "No user chosen",
+                                       "Please, choose a user or press Cancel.");
+               else
+                       super.okPressed();
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogArea = (Composite) super.createDialogArea(parent);
+               dialogArea.setLayout(new FillLayout());
+
+               Composite bodyCmp = new Composite(dialogArea, SWT.NO_FOCUS);
+               bodyCmp.setLayout(new GridLayout());
+
+               // Create and configure the table
+               userTableViewerCmp = new MyUserTableViewer(bodyCmp, SWT.MULTI
+                               | SWT.H_SCROLL | SWT.V_SCROLL);
+
+               userTableViewerCmp.setColumnDefinitions(columnDefs);
+               userTableViewerCmp.populateWithStaticFilters(false, false);
+               GridData gd = EclipseUiUtils.fillAll();
+               gd.minimumHeight = 300;
+               userTableViewerCmp.setLayoutData(gd);
+               userTableViewerCmp.refresh();
+
+               // Controllers
+               userViewer = userTableViewerCmp.getTableViewer();
+               userViewer.addDoubleClickListener(new MyDoubleClickListener());
+               userViewer
+                               .addSelectionChangedListener(new MySelectionChangedListener());
+
+               parent.pack();
+               return dialogArea;
+       }
+
+       public User getSelected() {
+               if (selectedUser == null)
+                       return null;
+               else
+                       return selectedUser;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText(title);
+       }
+
+       class MyDoubleClickListener implements IDoubleClickListener {
+               public void doubleClick(DoubleClickEvent evt) {
+                       if (evt.getSelection().isEmpty())
+                               return;
+
+                       Object obj = ((IStructuredSelection) evt.getSelection())
+                                       .getFirstElement();
+                       if (obj instanceof User) {
+                               selectedUser = (User) obj;
+                               okPressed();
+                       }
+               }
+       }
+
+       class MySelectionChangedListener implements ISelectionChangedListener {
+               @Override
+               public void selectionChanged(SelectionChangedEvent event) {
+                       if (event.getSelection().isEmpty()) {
+                               selectedUser = null;
+                               return;
+                       }
+                       Object obj = ((IStructuredSelection) event.getSelection())
+                                       .getFirstElement();
+                       if (obj instanceof Group) {
+                               selectedUser = (Group) obj;
+                       }
+               }
+       }
+
+       private class MyUserTableViewer extends LdifUsersTable {
+               private static final long serialVersionUID = 8467999509931900367L;
+
+               private final String[] knownProps = { LdapAttrs.uid.name(),
+                               LdapAttrs.cn.name(), LdapAttrs.DN };
+
+               private Button showSystemRoleBtn;
+               private Button showUserBtn;
+
+               public MyUserTableViewer(Composite parent, int style) {
+                       super(parent, style);
+               }
+
+               protected void populateStaticFilters(Composite staticFilterCmp) {
+                       staticFilterCmp.setLayout(new GridLayout());
+                       showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
+                       showSystemRoleBtn.setText("Show system roles  ");
+
+                       showUserBtn = new Button(staticFilterCmp, SWT.CHECK);
+                       showUserBtn.setText("Show users  ");
+
+                       SelectionListener sl = new SelectionAdapter() {
+                               private static final long serialVersionUID = -7033424592697691676L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       refresh();
+                               }
+                       };
+
+                       showSystemRoleBtn.addSelectionListener(sl);
+                       showUserBtn.addSelectionListener(sl);
+               }
+
+               @Override
+               protected List<User> listFilteredElements(String filter) {
+                       Role[] roles;
+                       try {
+                               StringBuilder builder = new StringBuilder();
+
+                               StringBuilder filterBuilder = new StringBuilder();
+                               if (notNull(filter))
+                                       for (String prop : knownProps) {
+                                               filterBuilder.append("(");
+                                               filterBuilder.append(prop);
+                                               filterBuilder.append("=*");
+                                               filterBuilder.append(filter);
+                                               filterBuilder.append("*)");
+                                       }
+
+                               String typeStr = "(" + LdapAttrs.objectClass.name() + "="
+                                               + LdapObjs.groupOfNames.name() + ")";
+                               if ((showUserBtn.getSelection()))
+                                       typeStr = "(|(" + LdapAttrs.objectClass.name() + "="
+                                                       + LdapObjs.inetOrgPerson.name() + ")" + typeStr
+                                                       + ")";
+
+                               if (!showSystemRoleBtn.getSelection())
+                                       typeStr = "(& " + typeStr + "(!(" + LdapAttrs.DN + "=*"
+                                                       + CmsConstants.ROLES_BASEDN + ")))";
+
+                               if (filterBuilder.length() > 1) {
+                                       builder.append("(&" + typeStr);
+                                       builder.append("(|");
+                                       builder.append(filterBuilder.toString());
+                                       builder.append("))");
+                               } else {
+                                       builder.append(typeStr);
+                               }
+                               roles = userAdmin.getRoles(builder.toString());
+                       } catch (InvalidSyntaxException e) {
+                               throw new EclipseUiException(
+                                               "Unable to get roles with filter: " + filter, e);
+                       }
+                       List<User> users = new ArrayList<User>();
+                       for (Role role : roles)
+                               if (!users.contains(role))
+                                       users.add((User) role);
+                       return users;
+               }
+       }
+
+       private boolean notNull(String string) {
+               if (string == null)
+                       return false;
+               else
+                       return !"".equals(string.trim());
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UserLP.java
new file mode 100644 (file)
index 0000000..d1c90a4
--- /dev/null
@@ -0,0 +1,76 @@
+package org.argeo.cms.swt.useradmin;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.auth.UserAdminUtils;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+
+/** Centralize label providers for the group table */
+class UserLP extends ColumnLabelProvider {
+       private static final long serialVersionUID = -4645930210988368571L;
+
+       final static String COL_ICON = "colID.icon";
+       final static String COL_DN = "colID.dn";
+       final static String COL_DISPLAY_NAME = "colID.displayName";
+       final static String COL_DOMAIN = "colID.domain";
+
+       final String currType;
+
+       // private Font italic;
+       private Font bold;
+
+       UserLP(String colId) {
+               this.currType = colId;
+       }
+
+       @Override
+       public Font getFont(Object element) {
+               // Current user as bold
+               if (UserAdminUtils.isCurrentUser(((User) element))) {
+                       if (bold == null)
+                               bold = JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD)
+                                               .createFont(Display.getCurrent());
+                       return bold;
+               }
+               return null;
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               if (COL_ICON.equals(currType)) {
+                       User user = (User) element;
+                       String dn = user.getName();
+                       if (dn.endsWith(CmsConstants.ROLES_BASEDN))
+                               return UsersImages.ICON_ROLE;
+                       else if (user.getType() == Role.GROUP)
+                               return UsersImages.ICON_GROUP;
+                       else
+                               return UsersImages.ICON_USER;
+               } else
+                       return null;
+       }
+
+       @Override
+       public String getText(Object element) {
+               User user = (User) element;
+               return getText(user);
+
+       }
+
+       public String getText(User user) {
+               if (COL_DN.equals(currType))
+                       return user.getName();
+               else if (COL_DISPLAY_NAME.equals(currType))
+                       return UserAdminUtils.getCommonName(user);
+               else if (COL_DOMAIN.equals(currType))
+                       return UserAdminUtils.getDomainName(user);
+               else
+                       return "";
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/UsersImages.java
new file mode 100644 (file)
index 0000000..21fc5af
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.cms.swt.useradmin;
+
+import org.argeo.cms.ui.theme.CmsImages;
+import org.eclipse.swt.graphics.Image;
+
+/** Specific users icons. */
+public class UsersImages {
+       private final static String PREFIX = "icons/";
+
+       public final static Image ICON_USER = CmsImages.createImg(PREFIX + "person.png");
+       public final static Image ICON_GROUP = CmsImages.createImg(PREFIX + "group.png");
+       public final static Image ICON_ROLE = CmsImages.createImg(PREFIX + "role.gif");
+       public final static Image ICON_CHANGE_PASSWORD = CmsImages.createImg(PREFIX + "security.gif");
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/useradmin/package-info.java
new file mode 100644 (file)
index 0000000..3597bfc
--- /dev/null
@@ -0,0 +1,2 @@
+/** SWT/JFace users management components. */
+package org.argeo.cms.swt.useradmin;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/AbstractSwtPart.java
new file mode 100644 (file)
index 0000000..c3d11a1
--- /dev/null
@@ -0,0 +1,45 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ux.widgets.DataPart;
+import org.argeo.cms.ux.widgets.DataView;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class AbstractSwtPart<INPUT, TYPE> extends Composite implements DataView<INPUT, TYPE> {
+       private static final long serialVersionUID = -1999179054267812170L;
+
+       protected DataPart<INPUT, TYPE> dataPart;
+
+       protected final SelectionListener selectionListener;
+
+       public AbstractSwtPart(Composite parent, int style, DataPart<INPUT, TYPE> dataPart) {
+               super(parent, style);
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               this.dataPart = dataPart;
+
+               selectionListener = new SelectionListener() {
+
+                       private static final long serialVersionUID = 4334785560035009330L;
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               if (dataPart.getOnSelected() != null)
+                                       dataPart.getOnSelected().accept((TYPE) e.item.getData());
+                       }
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                               if (dataPart.getOnAction() != null)
+                                       dataPart.getOnAction().accept((TYPE) e.item.getData());
+                       }
+               };
+
+               dataPart.addView(this);
+               addDisposeListener((e) -> dataPart.removeView(this));
+       }
+
+       public abstract void refresh();
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ContextOverlay.java
new file mode 100644 (file)
index 0000000..f7b6443
--- /dev/null
@@ -0,0 +1,113 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Manages a lightweight shell which is related to a {@link Control}, typically
+ * in order to reproduce a dropdown semantic, but with more flexibility.
+ */
+public class ContextOverlay extends ScrolledPage {
+       private static final long serialVersionUID = 6702077429573324009L;
+
+//     private Shell shell;
+       private Control control;
+
+       private int maxHeight = 400;
+
+       public ContextOverlay(Control control, int style) {
+               super(createShell(control, style), SWT.NONE);
+               Shell shell = getShell();
+               setLayoutData(CmsSwtUtils.fillAll());
+               // TODO make autohide configurable?
+               //shell.addShellListener(new AutoHideShellListener());
+               this.control = control;
+               control.addDisposeListener((e) -> {
+                       dispose();
+                       shell.dispose();
+               });
+       }
+
+       private static Composite createShell(Control control, int style) {
+               if (control == null)
+                       throw new IllegalArgumentException("Control cannot be null");
+               if (control.isDisposed())
+                       throw new IllegalArgumentException("Control is disposed");
+               Shell shell = new Shell(control.getShell(), SWT.NO_TRIM);
+               shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               Composite placeholder = new Composite(shell, SWT.BORDER);
+               placeholder.setLayoutData(CmsSwtUtils.fillAll());
+               placeholder.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               return placeholder;
+       }
+
+       public void show() {
+               Point relativeControlLocation = control.getLocation();
+               Point controlLocation = control.toDisplay(relativeControlLocation.x, relativeControlLocation.y);
+
+               int controlWidth = control.getBounds().width;
+
+               Shell shell = getShell();
+
+               layout(true, true);
+               shell.pack();
+               shell.layout(true, true);
+               int targetShellWidth = shell.getSize().x < controlWidth ? controlWidth : shell.getSize().x;
+               if (shell.getSize().y > maxHeight) {
+                       shell.setSize(targetShellWidth, maxHeight);
+               } else {
+                       shell.setSize(targetShellWidth, shell.getSize().y);
+               }
+
+               int shellHeight = shell.getSize().y;
+               int controlHeight = control.getBounds().height;
+               Point shellLocation = new Point(controlLocation.x, controlLocation.y + controlHeight);
+               int displayHeight = shell.getDisplay().getBounds().height;
+               if (shellLocation.y + shellHeight > displayHeight) {// bottom of page
+                       shellLocation = new Point(controlLocation.x, controlLocation.y - shellHeight);
+               }
+               shell.setLocation(shellLocation);
+
+               if (getChildren().length != 0)
+                       shell.open();
+               if (!control.isDisposed())
+                       control.setFocus();
+       }
+
+       public void hide() {
+               getShell().setVisible(false);
+               onHide();
+       }
+
+       public boolean isShellVisible() {
+               if (isDisposed())
+                       return false;
+               return getShell().isVisible();
+       }
+
+       /** to be overridden */
+       protected void onHide() {
+               // does nothing by default.
+       }
+
+       private class AutoHideShellListener extends ShellAdapter {
+               private static final long serialVersionUID = 7743287433907938099L;
+
+               @Override
+               public void shellDeactivated(ShellEvent e) {
+                       try {
+                               Thread.sleep(1000);
+                       } catch (InterruptedException e1) {
+                               // silent
+                       }
+                       if (!control.isDisposed() && !control.isFocusControl())
+                               hide();
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/EditableText.java
new file mode 100644 (file)
index 0000000..0612e8f
--- /dev/null
@@ -0,0 +1,135 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Editable text part displaying styled text. */
+public class EditableText extends StyledControl {
+       private static final long serialVersionUID = -6372283442330912755L;
+
+       private boolean editable = true;
+       private boolean multiLine = true;
+
+       private Color highlightColor;
+       private Composite highlight;
+
+       private boolean useTextAsLabel = false;
+
+       public EditableText(Composite parent, int style) {
+               super(parent, style);
+               editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY));
+               multiLine = !(SWT.SINGLE == (style & SWT.SINGLE));
+               highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY);
+               useTextAsLabel = SWT.FLAT == (style & SWT.FLAT);
+       }
+
+       @Override
+       protected Control createControl(Composite box, String style) {
+               if (isEditing() && getEditable()) {
+                       return createText(box, style, true);
+               } else {
+                       if (useTextAsLabel) {
+                               return createTextLabel(box, style);
+                       } else {
+                               return createLabel(box, style);
+                       }
+               }
+       }
+
+       protected Label createLabel(Composite box, String style) {
+               Label lbl = new Label(box, getStyle() | SWT.WRAP);
+               lbl.setLayoutData(CmsSwtUtils.fillWidth());
+               if (style != null)
+                       CmsSwtUtils.style(lbl, style);
+               CmsSwtUtils.markup(lbl);
+               if (mouseListener != null)
+                       lbl.addMouseListener(mouseListener);
+               return lbl;
+       }
+
+       protected Text createTextLabel(Composite box, String style) {
+               Text lbl = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE));
+               lbl.setEditable(false);
+               lbl.setLayoutData(CmsSwtUtils.fillWidth());
+               if (style != null)
+                       CmsSwtUtils.style(lbl, style);
+               CmsSwtUtils.markup(lbl);
+               if (mouseListener != null)
+                       lbl.addMouseListener(mouseListener);
+               return lbl;
+       }
+
+       protected Text createText(Composite box, String style, boolean editable) {
+               highlight = new Composite(box, SWT.NONE);
+               highlight.setBackground(highlightColor);
+               GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false);
+               highlightGd.widthHint = 5;
+               highlightGd.heightHint = 3;
+               highlight.setLayoutData(highlightGd);
+
+               final Text text = new Text(box, getStyle() | (multiLine ? SWT.MULTI : SWT.SINGLE) | SWT.WRAP);
+               text.setEditable(editable);
+               GridData textLayoutData = CmsSwtUtils.fillWidth();
+               // textLayoutData.heightHint = preferredHeight;
+               text.setLayoutData(textLayoutData);
+               if (style != null)
+                       CmsSwtUtils.style(text, style);
+               text.setFocus();
+               return text;
+       }
+
+       @Override
+       protected void clear(boolean deep) {
+               if (highlight != null)
+                       highlight.dispose();
+               super.clear(deep);
+       }
+
+       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();
+       }
+
+       public String getText() {
+               Control child = getControl();
+
+               if (child instanceof Label)
+                       return ((Label) child).getText();
+               else if (child instanceof Text)
+                       return ((Text) child).getText();
+               else
+                       throw new IllegalStateException("Unsupported control " + child.getClass());
+       }
+
+       /** @deprecated Use {@link #isEditable()} instead. */
+       @Deprecated
+       public boolean getEditable() {
+               return isEditable();
+       }
+
+       public boolean isEditable() {
+               return editable;
+       }
+
+       public void setUseTextAsLabel(boolean useTextAsLabel) {
+               this.useTextAsLabel = useTextAsLabel;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/ScrolledPage.java
new file mode 100644 (file)
index 0000000..135f4c1
--- /dev/null
@@ -0,0 +1,74 @@
+package org.argeo.cms.swt.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;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * 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) {
+               this(parent, style, false);
+       }
+
+       public ScrolledPage(Composite parent, int style, boolean alwaysShowScroll) {
+               super(createScrolledComposite(parent, alwaysShowScroll), style);
+               scrolledComposite = (ScrolledComposite) getParent();
+               scrolledComposite.setContent(this);
+
+               scrolledComposite.setExpandVertical(true);
+               scrolledComposite.setExpandHorizontal(true);
+               scrolledComposite.addControlListener(new ScrollControlListener());
+       }
+
+       private static ScrolledComposite createScrolledComposite(Composite parent, boolean alwaysShowScroll) {
+               ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL);
+               scrolledComposite.setAlwaysShowScrollBars(alwaysShowScroll);
+               return scrolledComposite;
+       }
+
+       @Override
+       public void layout(boolean changed, boolean all) {
+               updateScroll();
+               super.layout(changed, all);
+       }
+
+       public void showControl(Control control) {
+               scrolledComposite.showControl(control);
+       }
+
+       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/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/StyledControl.java
new file mode 100644 (file)
index 0000000..82c04a2
--- /dev/null
@@ -0,0 +1,151 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.api.cms.ux.CmsStyle;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SwtEditablePart;
+import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils;
+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 Composite implements SwtEditablePart {
+       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;
+
+       private Composite ancestorToLayout;
+
+       public StyledControl(Composite parent, int swtStyle) {
+               super(parent, swtStyle);
+               setLayout(CmsSwtUtils.noSpaceGridLayout());
+       }
+
+       protected abstract Control createControl(Composite box, String style);
+
+       protected Composite createBox() {
+               Composite box = new Composite(container, SWT.INHERIT_DEFAULT);
+               setContainerLayoutData(box);
+               box.setLayout(CmsSwtUtils.noSpaceGridLayout(3));
+               return box;
+       }
+
+       protected Composite createContainer() {
+               Composite container = new Composite(this, SWT.INHERIT_DEFAULT);
+               setContainerLayoutData(container);
+               container.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               return container;
+       }
+
+       @Override
+       public Control getControl() {
+               return control;
+       }
+
+       protected synchronized Boolean isEditing() {
+               return editing;
+       }
+
+       @Override
+       public synchronized void startEditing() {
+               assert !isEditing();
+               editing = true;
+               // int height = control.getSize().y;
+               String style = (String) EclipseUiSpecificUtils.getStyleData(control);
+               clear(false);
+               refreshControl(style);
+
+               // add the focus listener to the newly created edition control
+               if (focusListener != null)
+                       control.addFocusListener(focusListener);
+       }
+
+       @Override
+       public synchronized void stopEditing() {
+               assert isEditing();
+               editing = false;
+               String style = (String) EclipseUiSpecificUtils.getStyleData(control);
+               clear(false);
+               refreshControl(style);
+       }
+
+       protected void refreshControl(String style) {
+               control = createControl(box, style);
+               setControlLayoutData(control);
+               if (ancestorToLayout != null)
+                       ancestorToLayout.layout(true, true);
+               else
+                       getParent().layout(true, true);
+       }
+
+       public void setStyle(CmsStyle style) {
+               setStyle(style.style());
+       }
+
+       public void setStyle(String style) {
+               Object currentStyle = null;
+               if (control != null)
+                       currentStyle = EclipseUiSpecificUtils.getStyleData(control);
+               if (currentStyle != null && currentStyle.equals(style))
+                       return;
+
+               clear(true);
+               refreshControl(style);
+
+               if (style != null) {
+                       CmsSwtUtils.style(box, style + "_box");
+                       CmsSwtUtils.style(container, style + "_container");
+               }
+       }
+
+       /** To be overridden */
+       protected void setControlLayoutData(Control control) {
+               control.setLayoutData(CmsSwtUtils.fillWidth());
+       }
+
+       /** To be overridden */
+       protected void setContainerLayoutData(Composite composite) {
+               composite.setLayoutData(CmsSwtUtils.fillWidth());
+       }
+
+       protected void clear(boolean deep) {
+               if (deep) {
+                       for (Control control : getChildren())
+                               control.dispose();
+                       container = createContainer();
+                       box = createBox();
+               } 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);
+       }
+
+       public void setAncestorToLayout(Composite ancestorToLayout) {
+               this.ancestorToLayout = ancestorToLayout;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtHierarchicalPart.java
new file mode 100644 (file)
index 0000000..07c9bac
--- /dev/null
@@ -0,0 +1,106 @@
+package org.argeo.cms.swt.widgets;
+
+import java.util.List;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ux.widgets.HierarchicalPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+/** {@link HierarchicalPart} implementation based on a {@link Tree}. */
+public class SwtHierarchicalPart<T> extends AbstractSwtPart<T, T> {
+       private static final long serialVersionUID = -6247710601465713047L;
+
+       private final Tree tree;
+
+       private HierarchicalPart<T> hierarchicalPart;
+
+       public SwtHierarchicalPart(Composite parent, int style, HierarchicalPart<T> hierarchicalPart) {
+               super(parent, style, hierarchicalPart);
+               tree = new Tree(this, SWT.BORDER);
+               tree.setLayoutData(CmsSwtUtils.fillAll());
+               this.hierarchicalPart = hierarchicalPart;
+
+               tree.addSelectionListener(selectionListener);
+       }
+
+       @Override
+       public void refresh() {
+               // TODO optimise
+               // tree.clearAll(true);
+
+               for (TreeItem rootItem : tree.getItems()) {
+                       rootItem.dispose();
+               }
+
+               List<T> rootItems = hierarchicalPart.getChildren(hierarchicalPart.getInput());
+               for (T child : rootItems) {
+                       TreeItem childItem = addTreeItem(null, child);
+//                     List<T> grandChildren = hierarchicalPart.getChildren(child);
+//                     for (T grandChild : grandChildren) {
+//                             addTreeItem(childItem, grandChild);
+//                     }
+               }
+//             tree.addListener(SWT.SetData, event -> {
+//                     TreeItem item = (TreeItem) event.item;
+//                     TreeItem parentItem = item.getParentItem();
+//                     if (parentItem == null) {
+//                             refreshRootItem(item);
+//                     } else {
+//                             refreshItem(parentItem, item);
+//                     }
+//             });
+//             tree.setItemCount(getRootItemCount());
+
+               tree.addListener(SWT.Expand, event -> {
+                       final TreeItem root = (TreeItem) event.item;
+                       TreeItem[] items = root.getItems();
+                       for (TreeItem item : items) {
+                               if (item.getData() != null) {
+//                                     List<T> grandChildren = hierarchicalPart.getChildren((T) item.getData());
+//                                     for (T grandChild : grandChildren) {
+//                                             addTreeItem(item, grandChild);
+//                                     }
+                                       return;
+                               }
+                               item.dispose();
+                       }
+
+                       List<T> children = hierarchicalPart.getChildren((T) root.getData());
+                       for (T child : children) {
+                               TreeItem childItem = addTreeItem(root, child);
+//                             List<T> grandChildren = hierarchicalPart.getChildren(child);
+//                             for (T grandChild : grandChildren) {
+//                                     addTreeItem(childItem, grandChild);
+//                             }
+                       }
+               });
+
+               CmsSwtUtils.fill(tree);
+
+       }
+
+       protected TreeItem addTreeItem(TreeItem parent, T data) {
+               TreeItem item = parent == null ? new TreeItem(tree, SWT.NONE) : new TreeItem(parent, SWT.NONE);
+               item.setData(data);
+               String txt = hierarchicalPart.getText(data);
+               if (txt != null)
+                       item.setText(hierarchicalPart.getText(data));
+               CmsIcon icon = hierarchicalPart.getIcon(data);
+               // TODO optimize
+               List<T> grandChildren = hierarchicalPart.getChildren(data);
+               if (grandChildren.size() != 0)
+                       new TreeItem(item, SWT.NONE);
+               return item;
+//if(icon!=null)
+//     item.setImage(null);
+       }
+
+       protected Tree getTree() {
+               return tree;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/widgets/SwtTabularPart.java
new file mode 100644 (file)
index 0000000..2f10cac
--- /dev/null
@@ -0,0 +1,82 @@
+package org.argeo.cms.swt.widgets;
+
+import org.argeo.api.cms.ux.CmsIcon;
+import org.argeo.cms.swt.CmsSwtTheme;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.ux.widgets.Column;
+import org.argeo.cms.ux.widgets.TabularPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+/** {@link TabularPart} implementation based on a {@link Table}. */
+public class SwtTabularPart<INPUT, T> extends AbstractSwtPart<INPUT, T> {
+       private static final long serialVersionUID = -1114155772446357750L;
+       private final Table table;
+       private TabularPart<INPUT, T> tabularPart;
+
+       private CmsSwtTheme theme;
+
+       public SwtTabularPart(Composite parent, int style, TabularPart<INPUT, T> tabularPart) {
+               super(parent, style, tabularPart);
+               theme = CmsSwtUtils.getCmsTheme(parent);
+
+               table = new Table(this, SWT.VIRTUAL | SWT.BORDER);
+               table.setLinesVisible(true);
+               table.setLayoutData(CmsSwtUtils.fillAll());
+
+               this.tabularPart = tabularPart;
+       }
+
+       @Override
+       public void refresh() {
+               // TODO optimise
+               table.clearAll();
+               table.addListener(SWT.SetData, event -> {
+                       TableItem item = (TableItem) event.item;
+                       refreshItem(item);
+               });
+               table.setItemCount(tabularPart.getItemCount());
+               for (int i = 0; i < tabularPart.getColumnCount(); i++) {
+                       TableColumn swtColumn = new TableColumn(table, SWT.NONE);
+                       swtColumn.setWidth(tabularPart.getColumn(i).getWidth());
+               }
+               CmsSwtUtils.fill(table);
+
+               table.addSelectionListener(selectionListener);
+
+       }
+
+       protected Object getDataFromEvent(SelectionEvent e) {
+               Object data = e.item.getData();
+               if (data == null)
+                       data = tabularPart.getData(getTable().indexOf((TableItem) e.item));
+               return data;
+       }
+
+       protected void refreshItem(TableItem item) {
+               int row = getTable().indexOf(item);
+               for (int i = 0; i < tabularPart.getColumnCount(); i++) {
+                       Column<T> column = tabularPart.getColumn(i);
+                       T data = tabularPart.getData(row);
+                       item.setData(data);
+                       String text = data != null ? column.getText(data) : "";
+                       if (text != null)
+                               item.setText(i, text);
+                       CmsIcon icon = column.getIcon(data);
+                       if (icon != null) {
+                               Image image = theme.getSmallIcon(icon);
+                               item.setImage(i, image);
+                       }
+               }
+       }
+
+       protected Table getTable() {
+               return table;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/CmsImages.java
new file mode 100644 (file)
index 0000000..1c4d79e
--- /dev/null
@@ -0,0 +1,49 @@
+package org.argeo.cms.ui.theme;
+
+import java.net.URL;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+public class CmsImages {
+       private static BundleContext themeBc = FrameworkUtil.getBundle(CmsImages.class).getBundleContext();
+
+       final public static String ICONS_BASE = "icons/";
+       final public static String TYPES_BASE = ICONS_BASE + "types/";
+       final public static String ACTIONS_BASE = ICONS_BASE + "actions/";
+
+       public static Image createIcon(String name) {
+               return createImg(CmsImages.ICONS_BASE + name);
+       }
+
+       public static Image createAction(String name) {
+               return createImg(CmsImages.ACTIONS_BASE + name);
+       }
+
+       public static Image createType(String name) {
+               return createImg(CmsImages.TYPES_BASE + name);
+       }
+
+       public static Image createImg(String name) {
+               return CmsImages.createDesc(name).createImage(Display.getDefault());
+       }
+
+       public static ImageDescriptor createDesc(String name) {
+               return createDesc(themeBc, name);
+       }
+
+       public static ImageDescriptor createDesc(BundleContext bc, String name) {
+               URL url = bc.getBundle().getResource(name);
+               if (url == null)
+                       return ImageDescriptor.getMissingImageDescriptor();
+               return ImageDescriptor.createFromURL(url);
+       }
+
+       public static Image createImg(BundleContext bc, String name) {
+               return createDesc(bc, name).createImage(Display.getDefault());
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/ui/theme/package-info.java
new file mode 100644 (file)
index 0000000..7d3a260
--- /dev/null
@@ -0,0 +1,2 @@
+/** Argeo CMS core theme images. */
+package org.argeo.cms.ui.theme;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/AbstractTreeContentProvider.java
new file mode 100644 (file)
index 0000000..64ea2db
--- /dev/null
@@ -0,0 +1,43 @@
+package org.argeo.eclipse.ui;
+
+import org.argeo.cms.ux.widgets.TreeParent;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Tree content provider dealing with tree objects and providing reasonable
+ * defaults.
+ */
+public abstract class AbstractTreeContentProvider implements
+               ITreeContentProvider {
+       private static final long serialVersionUID = 8246126401957763868L;
+
+       /** Does nothing */
+       public void dispose() {
+       }
+
+       /** Does nothing */
+       public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       }
+
+       public Object[] getChildren(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).getChildren();
+               }
+               return new Object[0];
+       }
+
+       public Object getParent(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).getParent();
+               }
+               return null;
+       }
+
+       public boolean hasChildren(Object element) {
+               if (element instanceof TreeParent) {
+                       return ((TreeParent) element).hasChildren();
+               }
+               return false;
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnDefinition.java
new file mode 100644 (file)
index 0000000..a38552c
--- /dev/null
@@ -0,0 +1,68 @@
+package org.argeo.eclipse.ui;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+/**
+ * Wraps the definition of a column to be used in the various JFace viewers
+ * (typically tree and table). It enables definition of generic viewers which
+ * column can be then defined externally. Also used to generate export.
+ */
+public class ColumnDefinition {
+       private ColumnLabelProvider labelProvider;
+       private String label;
+       private int weight = 0;
+       private int minWidth = 120;
+
+       public ColumnDefinition(ColumnLabelProvider labelProvider, String label) {
+               this.labelProvider = labelProvider;
+               this.label = label;
+       }
+
+       public ColumnDefinition(ColumnLabelProvider labelProvider, String label,
+                       int weight) {
+               this.labelProvider = labelProvider;
+               this.label = label;
+               this.weight = weight;
+               this.minWidth = weight;
+       }
+
+       public ColumnDefinition(ColumnLabelProvider labelProvider, String label,
+                       int weight, int minimumWidth) {
+               this.labelProvider = labelProvider;
+               this.label = label;
+               this.weight = weight;
+               this.minWidth = minimumWidth;
+       }
+
+       public ColumnLabelProvider getLabelProvider() {
+               return labelProvider;
+       }
+
+       public void setLabelProvider(ColumnLabelProvider labelProvider) {
+               this.labelProvider = labelProvider;
+       }
+
+       public String getLabel() {
+               return label;
+       }
+
+       public void setLabel(String label) {
+               this.label = label;
+       }
+
+       public int getWeight() {
+               return weight;
+       }
+
+       public void setWeight(int weight) {
+               this.weight = weight;
+       }
+
+       public int getMinWidth() {
+               return minWidth;
+       }
+
+       public void setMinWidth(int minWidth) {
+               this.minWidth = minWidth;
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/ColumnViewerComparator.java
new file mode 100644 (file)
index 0000000..9430a20
--- /dev/null
@@ -0,0 +1,81 @@
+package org.argeo.eclipse.ui;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+/** Generic column viewer sorter */
+public class ColumnViewerComparator extends ViewerComparator {
+       private static final long serialVersionUID = -2266218906355859909L;
+
+       public static final int ASC = 1;
+
+       public static final int NONE = 0;
+
+       public static final int DESC = -1;
+
+       private int direction = 0;
+
+       private TableViewerColumn column;
+
+       private ColumnViewer viewer;
+
+       public ColumnViewerComparator(TableViewerColumn column) {
+               super(null);
+               this.column = column;
+               this.viewer = column.getViewer();
+               this.column.getColumn().addSelectionListener(new SelectionAdapter() {
+                       private static final long serialVersionUID = 7586796298965472189L;
+
+                       public void widgetSelected(SelectionEvent e) {
+                               if (ColumnViewerComparator.this.viewer.getComparator() != null) {
+                                       if (ColumnViewerComparator.this.viewer.getComparator() == ColumnViewerComparator.this) {
+                                               int tdirection = ColumnViewerComparator.this.direction;
+
+                                               if (tdirection == ASC) {
+                                                       setSortDirection(DESC);
+                                               } else if (tdirection == DESC) {
+                                                       setSortDirection(NONE);
+                                               }
+                                       } else {
+                                               setSortDirection(ASC);
+                                       }
+                               } else {
+                                       setSortDirection(ASC);
+                               }
+                       }
+               });
+       }
+
+       private void setSortDirection(int direction) {
+               if (direction == NONE) {
+                       column.getColumn().getParent().setSortColumn(null);
+                       column.getColumn().getParent().setSortDirection(SWT.NONE);
+                       viewer.setComparator(null);
+               } else {
+                       column.getColumn().getParent().setSortColumn(column.getColumn());
+                       this.direction = direction;
+
+                       if (direction == ASC) {
+                               column.getColumn().getParent().setSortDirection(SWT.DOWN);
+                       } else {
+                               column.getColumn().getParent().setSortDirection(SWT.UP);
+                       }
+
+                       if (viewer.getComparator() == this) {
+                               viewer.refresh();
+                       } else {
+                               viewer.setComparator(this);
+                       }
+
+               }
+       }
+
+       public int compare(Viewer viewer, Object e1, Object e2) {
+               return direction * super.compare(viewer, e1, e2);
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiException.java
new file mode 100644 (file)
index 0000000..37a36e8
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.eclipse.ui;
+
+/** CMS specific exceptions. */
+public class EclipseUiException extends RuntimeException {
+       private static final long serialVersionUID = -5341764743356771313L;
+
+       public EclipseUiException(String message) {
+               super(message);
+       }
+
+       public EclipseUiException(String message, Throwable e) {
+               super(message, e);
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/EclipseUiUtils.java
new file mode 100644 (file)
index 0000000..95b45fe
--- /dev/null
@@ -0,0 +1,195 @@
+package org.argeo.eclipse.ui;
+
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/** Utilities to simplify UI development. */
+public class EclipseUiUtils {
+
+       /** Dispose all children of a Composite */
+       public static void clear(Composite composite) {
+               for (Control child : composite.getChildren())
+                       child.dispose();
+       }
+
+       /**
+        * Enables efficient call to the layout method of a composite, refreshing only
+        * some of the children controls.
+        */
+       public static void layout(Composite parent, Control... toUpdateControls) {
+               parent.layout(toUpdateControls);
+       }
+
+       //
+       // FONTS
+       //
+       /** Shortcut to retrieve default italic font from display */
+       public static Font getItalicFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.ITALIC)
+                               .createFont(parent.getDisplay());
+       }
+
+       /** Shortcut to retrieve default bold font from display */
+       public static Font getBoldFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD)
+                               .createFont(parent.getDisplay());
+       }
+
+       /** Shortcut to retrieve default bold italic font from display */
+       public static Font getBoldItalicFont(Composite parent) {
+               return JFaceResources.getFontRegistry().defaultFontDescriptor().setStyle(SWT.BOLD | SWT.ITALIC)
+                               .createFont(parent.getDisplay());
+       }
+
+       //
+       // Simplify grid layouts management
+       //
+       public static GridLayout noSpaceGridLayout() {
+               return noSpaceGridLayout(new GridLayout());
+       }
+
+       public static GridLayout noSpaceGridLayout(int columns) {
+               return noSpaceGridLayout(new GridLayout(columns, false));
+       }
+
+       public static GridLayout noSpaceGridLayout(GridLayout layout) {
+               layout.horizontalSpacing = 0;
+               layout.verticalSpacing = 0;
+               layout.marginWidth = 0;
+               layout.marginHeight = 0;
+               return layout;
+       }
+
+       public static GridData fillWidth() {
+               return grabWidth(SWT.FILL, SWT.FILL);
+       }
+
+       public static GridData fillWidth(int colSpan) {
+               GridData gd = grabWidth(SWT.FILL, SWT.FILL);
+               gd.horizontalSpan = colSpan;
+               return gd;
+       }
+
+       public static GridData fillAll() {
+               return new GridData(SWT.FILL, SWT.FILL, true, true);
+       }
+
+       public static GridData fillAll(int colSpan, int rowSpan) {
+               return new GridData(SWT.FILL, SWT.FILL, true, true, colSpan, rowSpan);
+       }
+
+       public static GridData grabWidth(int horizontalAlignment, int verticalAlignment) {
+               return new GridData(horizontalAlignment, horizontalAlignment, true, false);
+       }
+
+       //
+       // Simplify Form layout management
+       //
+
+       /**
+        * Creates a basic form data that is attached to the 4 corners of the parent
+        * composite
+        */
+       public static FormData fillFormData() {
+               FormData formData = new FormData();
+               formData.top = new FormAttachment(0, 0);
+               formData.left = new FormAttachment(0, 0);
+               formData.right = new FormAttachment(100, 0);
+               formData.bottom = new FormAttachment(100, 0);
+               return formData;
+       }
+
+       /**
+        * Create a label and a text field for a grid layout, the text field grabbing
+        * excess horizontal
+        * 
+        * @param parent
+        *            the parent composite
+        * @param label
+        *            the label to display
+        * @param modifyListener
+        *            a {@link ModifyListener} to listen on events on the text, can be
+        *            null
+        * @return the created text
+        * 
+        */
+       // FIXME why was this deprecated.
+       // * @ deprecated use { @ link #createGridLT(Composite, String)} instead
+       // @ Deprecated
+       public static Text createGridLT(Composite parent, String label, ModifyListener modifyListener) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER);
+               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               if (modifyListener != null)
+                       txt.addModifyListener(modifyListener);
+               return txt;
+       }
+
+       /**
+        * Create a label and a text field for a grid layout, the text field grabbing
+        * excess horizontal
+        */
+       public static Text createGridLT(Composite parent, String label) {
+               return createGridLT(parent, label, null);
+       }
+
+       /**
+        * Creates one label and a text field not editable with background colour of the
+        * parent (like a label but with selectable text)
+        */
+       public static Text createGridLL(Composite parent, String label, String text) {
+               Text txt = createGridLT(parent, label);
+               txt.setText(text);
+               txt.setEditable(false);
+               txt.setBackground(parent.getBackground());
+               return txt;
+       }
+
+       /**
+        * Create a label and a text field with password display for a grid layout, the
+        * text field grabbing excess horizontal
+        */
+       public static Text createGridLP(Composite parent, String label) {
+               return createGridLP(parent, label, null);
+       }
+
+       /**
+        * Create a label and a text field with password display for a grid layout, the
+        * text field grabbing excess horizontal. The given modify listener will be
+        * added to the newly created text field if not null.
+        */
+       public static Text createGridLP(Composite parent, String label, ModifyListener modifyListener) {
+               Label lbl = new Label(parent, SWT.LEAD);
+               lbl.setText(label);
+               lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+               Text txt = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.PASSWORD);
+               txt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+               if (modifyListener != null)
+                       txt.addModifyListener(modifyListener);
+               return txt;
+       }
+
+       // MISCELLANEOUS
+
+       /** Simply checks if a string is not null nor empty */
+       public static boolean notEmpty(String stringToTest) {
+               return !(stringToTest == null || "".equals(stringToTest.trim()));
+       }
+
+       /** Simply checks if a string is null or empty */
+       public static boolean isEmpty(String stringToTest) {
+               return stringToTest == null || "".equals(stringToTest.trim());
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/FileProvider.java
new file mode 100644 (file)
index 0000000..e82505d
--- /dev/null
@@ -0,0 +1,16 @@
+package org.argeo.eclipse.ui;
+
+import java.io.InputStream;
+
+/**
+ * Used for file download : subclasses must implement model specific methods to
+ * get a byte array representing a file given is ID.
+ */
+@Deprecated
+public interface FileProvider {
+
+       public byte[] getByteArrayFileFromId(String fileId);
+
+       public InputStream getInputStreamFromFileId(String fileId);
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/GenericTableComparator.java
new file mode 100644 (file)
index 0000000..e1d8b05
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.eclipse.ui;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+public abstract class GenericTableComparator extends ViewerComparator {
+       private static final long serialVersionUID = -1175894935075325810L;
+       protected int propertyIndex;
+       public static final int ASCENDING = 0, DESCENDING = 1;
+       protected int direction = DESCENDING;
+
+       /**
+        * Creates an instance of a sorter for TableViewer.
+        * 
+        * @param defaultColumnIndex
+        *            the default sorter column
+        */
+
+       public GenericTableComparator(int defaultColumnIndex, int direction) {
+               propertyIndex = defaultColumnIndex;
+               this.direction = direction;
+       }
+
+       public void setColumn(int column) {
+               if (column == this.propertyIndex) {
+                       // Same column as last sort; toggle the direction
+                       direction = 1 - direction;
+               } else {
+                       // New column; do a descending sort
+                       this.propertyIndex = column;
+                       direction = DESCENDING;
+               }
+       }
+
+       /**
+        * Must be Overriden in each view.
+        */
+       public abstract int compare(Viewer viewer, Object e1, Object e2);
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/IListProvider.java
new file mode 100644 (file)
index 0000000..ac7b2d8
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.eclipse.ui;
+
+import java.util.List;
+
+/**
+ * Views and editors can implement this interface so that one of the list that
+ * is displayed in the part (For instance in a Table or a Tree Viewer) can be
+ * rebuilt externally. Typically to generate csv or calc extract.
+ */
+public interface IListProvider {
+       /**
+        * Returns an array of current and relevant elements
+        */
+       public Object[] getElements(String extractId);
+
+       /**
+        * Returns the column definition for passed ID
+        */
+       public List<ColumnDefinition> getColumnDefinition(String extractId);
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/ErrorFeedback.java
new file mode 100644 (file)
index 0000000..a388e74
--- /dev/null
@@ -0,0 +1,106 @@
+package org.argeo.eclipse.ui.dialogs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.argeo.api.cms.CmsLog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Generic error dialog to be used in try/catch blocks.
+ * 
+ * @deprecated Use CMS dialogs instead.
+ */
+@Deprecated
+public class ErrorFeedback extends TitleAreaDialog {
+       private static final long serialVersionUID = -8918084784628179044L;
+
+       private final static CmsLog log = CmsLog.getLog(ErrorFeedback.class);
+
+       private final String message;
+       private final Throwable exception;
+
+       public static void show(String message, Throwable e) {
+               // rethrow ThreaDeath in order to make sure that RAP will properly clean
+               // up the UI thread
+               if (e instanceof ThreadDeath)
+                       throw (ThreadDeath) e;
+
+               new ErrorFeedback(newShell(), message, e).open();
+       }
+
+       public static void show(String message) {
+               new ErrorFeedback(newShell(), message, null).open();
+       }
+
+       private static Shell newShell() {
+               return new Shell(getDisplay(), SWT.NO_TRIM);
+       }
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = Display.getCurrent();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       public ErrorFeedback(Shell parentShell, String message, Throwable e) {
+               super(parentShell);
+               setShellStyle(SWT.NO_TRIM);
+               this.message = message;
+               this.exception = e;
+               log.error(message, e);
+       }
+
+       protected Point getInitialSize() {
+               if (exception != null)
+                       return new Point(800, 600);
+               else
+                       return new Point(400, 300);
+       }
+
+       @Override
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               setMessage(message != null ? message + (exception != null ? ": " + exception.getMessage() : "")
+                               : exception != null ? exception.getMessage() : "Unkown Error", IMessageProvider.ERROR);
+
+               if (exception != null) {
+                       Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+                       stack.setEditable(false);
+                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       StringWriter sw = new StringWriter();
+                       exception.printStackTrace(new PrintWriter(sw));
+                       stack.setText(sw.toString());
+               }
+
+               parent.pack();
+               return composite;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText("Error");
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java
new file mode 100644 (file)
index 0000000..f2715bc
--- /dev/null
@@ -0,0 +1,141 @@
+package org.argeo.eclipse.ui.dialogs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+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.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Generic lightweight dialog, not based on JFace.
+ * 
+ * @deprecated Use CMS dialogs instead.
+ */
+@Deprecated
+public class FeedbackDialog extends LightweightDialog {
+       private final static CmsLog log = CmsLog.getLog(FeedbackDialog.class);
+
+       private String message;
+       private Throwable exception;
+
+//     private Shell parentShell;
+       private Shell shell;
+
+       public static void show(String message, Throwable e) {
+               // rethrow ThreaDeath in order to make sure that RAP will properly clean
+               // up the UI thread
+               if (e instanceof ThreadDeath)
+                       throw (ThreadDeath) e;
+
+               new FeedbackDialog(getDisplay().getActiveShell(), message, e).open();
+       }
+
+       public static void show(String message) {
+               new FeedbackDialog(getDisplay().getActiveShell(), message, null).open();
+       }
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = Display.getCurrent();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       public FeedbackDialog(Shell parentShell, String message, Throwable e) {
+               super(parentShell);
+               this.message = message;
+               this.exception = e;
+               log.error(message, e);
+       }
+
+       public int open() {
+               if (shell != null)
+                       throw new EclipseUiException("There is already a shell");
+               shell = new Shell(getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
+               shell.setLayout(new GridLayout());
+               // shell.setText("Error");
+               shell.setSize(getInitialSize());
+               createDialogArea(shell);
+               // shell.pack();
+               // shell.layout();
+
+               Rectangle shellBounds = Display.getCurrent().getBounds();// RAP
+               Point dialogSize = shell.getSize();
+               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
+               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
+               shell.setLocation(x, y);
+
+               shell.addShellListener(new ShellAdapter() {
+                       private static final long serialVersionUID = -2701270481953688763L;
+
+                       @Override
+                       public void shellDeactivated(ShellEvent e) {
+                               closeShell();
+                       }
+               });
+
+               shell.open();
+               return OK;
+       }
+
+       protected void closeShell() {
+               shell.close();
+               shell.dispose();
+               shell = null;
+       }
+
+       protected Point getInitialSize() {
+               // if (exception != null)
+               // return new Point(800, 600);
+               // else
+               return new Point(400, 300);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = new Composite(parent, SWT.NONE);
+               dialogarea.setLayout(new GridLayout());
+               // Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               Label messageLbl = new Label(dialogarea, SWT.NONE);
+               if (message != null)
+                       messageLbl.setText(message);
+               else if (exception != null)
+                       messageLbl.setText(exception.getLocalizedMessage());
+
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayout(new GridLayout(2, false));
+               composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               if (exception != null) {
+                       Text stack = new Text(composite, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+                       stack.setEditable(false);
+                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+                       StringWriter sw = new StringWriter();
+                       exception.printStackTrace(new PrintWriter(sw));
+                       stack.setText(sw.toString());
+               }
+
+               // parent.pack();
+               return composite;
+       }
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java
new file mode 100644 (file)
index 0000000..615e141
--- /dev/null
@@ -0,0 +1,256 @@
+package org.argeo.eclipse.ui.dialogs;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+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;
+
+/** Generic lightweight dialog, not based on JFace. */
+@Deprecated
+public class LightweightDialog {
+       private final static CmsLog log = CmsLog.getLog(LightweightDialog.class);
+
+       // must be the same value as org.eclipse.jface.window.Window#OK
+       public final static int OK = 0;
+       // must be the same value as org.eclipse.jface.window.Window#CANCEL
+       public final static int CANCEL = 1;
+
+       private Shell parentShell;
+       private Shell backgroundShell;
+       private Shell foregoundShell;
+
+       private Integer returnCode = null;
+       private boolean block = true;
+
+       private String title;
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = Display.getCurrent();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       public LightweightDialog(Shell parentShell) {
+               this.parentShell = parentShell;
+       }
+
+       public int open() {
+               if (foregoundShell != null)
+                       throw new EclipseUiException("There is already a shell");
+               backgroundShell = new Shell(parentShell, SWT.ON_TOP);
+               backgroundShell.setFullScreen(true);
+               // if (parentShell != null) {
+               // backgroundShell.setBounds(parentShell.getBounds());
+               // } else
+               // backgroundShell.setMaximized(true);
+               backgroundShell.setAlpha(128);
+               backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
+               foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
+               if (title != null)
+                       setTitle(title);
+               foregoundShell.setLayout(new GridLayout());
+               foregoundShell.setSize(getInitialSize());
+               createDialogArea(foregoundShell);
+               // shell.pack();
+               // shell.layout();
+
+               Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP
+               Point dialogSize = foregoundShell.getSize();
+               int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
+               int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
+               foregoundShell.setLocation(x, y);
+
+               foregoundShell.addShellListener(new ShellAdapter() {
+                       private static final long serialVersionUID = -2701270481953688763L;
+
+                       @Override
+                       public void shellDeactivated(ShellEvent e) {
+                               if (hasChildShells())
+                                       return;
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
+                       }
+
+                       @Override
+                       public void shellClosed(ShellEvent e) {
+                               notifyClose();
+                       }
+
+               });
+
+               backgroundShell.open();
+               foregoundShell.open();
+               // after the foreground shell has been opened
+               backgroundShell.addFocusListener(new FocusListener() {
+                       private static final long serialVersionUID = 3137408447474661070L;
+
+                       @Override
+                       public void focusLost(FocusEvent event) {
+                       }
+
+                       @Override
+                       public void focusGained(FocusEvent event) {
+                               if (hasChildShells())
+                                       return;
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
+                       }
+               });
+
+               if (block) {
+                       block();
+               }
+               if (returnCode == null)
+                       returnCode = OK;
+               return returnCode;
+       }
+
+       public void block() {
+               try {
+                       runEventLoop(foregoundShell);
+               } catch (ThreadDeath t) {
+                       returnCode = CANCEL;
+                       if (log.isTraceEnabled())
+                               log.error("Thread death, canceling dialog", t);
+               } catch (Throwable t) {
+                       returnCode = CANCEL;
+                       log.error("Cannot open blocking lightweight dialog", t);
+               }
+       }
+
+       private boolean hasChildShells() {
+               if (foregoundShell == null)
+                       return false;
+               return foregoundShell.getShells().length != 0;
+       }
+
+       // public synchronized int openAndWait() {
+       // open();
+       // while (returnCode == null)
+       // try {
+       // wait(100);
+       // } catch (InterruptedException e) {
+       // // silent
+       // }
+       // return returnCode;
+       // }
+
+       private synchronized void notifyClose() {
+               if (returnCode == null)
+                       returnCode = CANCEL;
+               notifyAll();
+       }
+
+       protected void closeShell(int returnCode) {
+               this.returnCode = returnCode;
+               if (CANCEL == returnCode)
+                       onCancel();
+               if (foregoundShell != null && !foregoundShell.isDisposed()) {
+                       foregoundShell.close();
+                       foregoundShell.dispose();
+                       foregoundShell = null;
+               }
+
+               if (backgroundShell != null && !backgroundShell.isDisposed()) {
+                       backgroundShell.close();
+                       backgroundShell.dispose();
+               }
+       }
+
+       protected Point getInitialSize() {
+               // if (exception != null)
+               // return new Point(800, 600);
+               // else
+               return new Point(600, 400);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = new Composite(parent, SWT.NONE);
+               dialogarea.setLayout(new GridLayout());
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               return dialogarea;
+       }
+
+       protected Shell getBackgroundShell() {
+               return backgroundShell;
+       }
+
+       protected Shell getForegoundShell() {
+               return foregoundShell;
+       }
+
+       public void setBlockOnOpen(boolean shouldBlock) {
+               block = shouldBlock;
+       }
+
+       public void pack() {
+               foregoundShell.pack();
+       }
+
+       private void runEventLoop(Shell loopShell) {
+               Display display;
+               if (foregoundShell == null) {
+                       display = Display.getCurrent();
+               } else {
+                       display = loopShell.getDisplay();
+               }
+
+               while (loopShell != null && !loopShell.isDisposed()) {
+                       try {
+                               if (!display.readAndDispatch()) {
+                                       display.sleep();
+                               }
+                       } catch (UnsupportedOperationException e) {
+                               throw e;
+                       } catch (Throwable e) {
+                               handleException(e);
+                       }
+               }
+               if (!display.isDisposed())
+                       display.update();
+       }
+
+       protected void handleException(Throwable t) {
+               if (t instanceof ThreadDeath) {
+                       // Don't catch ThreadDeath as this is a normal occurrence when
+                       // the thread dies
+                       throw (ThreadDeath) t;
+               }
+               // Try to keep running.
+               t.printStackTrace();
+       }
+
+       /** @return false, if the dialog should not be closed. */
+       protected boolean onCancel() {
+               return true;
+       }
+
+       public void setTitle(String title) {
+               this.title = title;
+               if (title != null && getForegoundShell() != null)
+                       getForegoundShell().setText(title);
+       }
+
+       public Integer getReturnCode() {
+               return returnCode;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/SingleValue.java
new file mode 100644 (file)
index 0000000..8ce9b44
--- /dev/null
@@ -0,0 +1,130 @@
+package org.argeo.eclipse.ui.dialogs;
+
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.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;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Dialog to retrieve a single value.
+ * 
+ * @deprecated Use CMS dialogs instead.
+ */
+@Deprecated
+public class SingleValue extends TitleAreaDialog {
+       private static final long serialVersionUID = 2843538207460082349L;
+
+       private Text valueT;
+       private String value;
+       private final String title, message, label;
+       private final Boolean multiline;
+
+       public static String ask(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Window.OK)
+                       return svd.getString();
+               else
+                       return null;
+       }
+
+       public static Long askLong(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Window.OK)
+                       return svd.getLong();
+               else
+                       return null;
+       }
+
+       public static Double askDouble(String label, String message) {
+               SingleValue svd = new SingleValue(label, message);
+               if (svd.open() == Window.OK)
+                       return svd.getDouble();
+               else
+                       return null;
+       }
+
+       public SingleValue(String label, String message) {
+               this(Display.getDefault().getActiveShell(), label, message, label, false);
+       }
+
+       public SingleValue(Shell parentShell, String title, String message, String label, Boolean multiline) {
+               super(parentShell);
+               this.title = title;
+               this.message = message;
+               this.label = label;
+               this.multiline = multiline;
+       }
+
+       protected Point getInitialSize() {
+               if (multiline)
+                       return new Point(450, 350);
+
+               else
+                       return new Point(400, 270);
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               Composite dialogarea = (Composite) super.createDialogArea(parent);
+               dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               Composite composite = new Composite(dialogarea, SWT.NONE);
+               composite.setLayoutData(EclipseUiUtils.fillAll());
+               GridLayout layout = new GridLayout(2, false);
+               layout.marginWidth = layout.marginHeight = 20;
+               composite.setLayout(layout);
+
+               valueT = createLT(composite, label);
+
+               setMessage(message, IMessageProvider.NONE);
+
+               parent.pack();
+               valueT.setFocus();
+               return composite;
+       }
+
+       @Override
+       protected void okPressed() {
+               value = valueT.getText();
+               super.okPressed();
+       }
+
+       /** Creates label and text. */
+       protected Text createLT(Composite parent, String label) {
+               new Label(parent, SWT.NONE).setText(label);
+               Text text;
+               if (multiline) {
+                       text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.MULTI);
+                       text.setLayoutData(EclipseUiUtils.fillAll());
+               } else {
+                       text = new Text(parent, SWT.LEAD | SWT.BORDER | SWT.SINGLE);
+                       text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+               }
+               return text;
+       }
+
+       protected void configureShell(Shell shell) {
+               super.configureShell(shell);
+               shell.setText(title);
+       }
+
+       public String getString() {
+               return value;
+       }
+
+       public Long getLong() {
+               return Long.valueOf(getString());
+       }
+
+       public Double getDouble() {
+               return Double.valueOf(getString());
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/dialogs/package-info.java
new file mode 100644 (file)
index 0000000..d6ab148
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic SWT/JFace dialogs. */
+package org.argeo.eclipse.ui.dialogs;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java
new file mode 100644 (file)
index 0000000..c01b2d7
--- /dev/null
@@ -0,0 +1,450 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+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.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+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.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.Text;
+
+/** Simple UI provider that populates a composite parent given a NIO path */
+public class AdvancedFsBrowser {
+       private final static CmsLog log = CmsLog.getLog(AdvancedFsBrowser.class);
+
+       // Some local constants to experiment. should be cleaned
+       // private final static int THUMBNAIL_WIDTH = 400;
+       // private Point imageWidth = new Point(250, 0);
+       private final static int COLUMN_WIDTH = 160;
+
+       private Path initialPath;
+       private Path currEdited;
+       // Filter
+       private Composite displayBoxCmp;
+       private Text parentPathTxt;
+       private Text filterTxt;
+       // Browser columns
+       private ScrolledComposite scrolledCmp;
+       // Keep a cache of the opened directories
+       private LinkedHashMap<Path, FilterEntitiesVirtualTable> browserCols = new LinkedHashMap<>();
+       private Composite scrolledCmpBody;
+
+       public Control createUi(Composite parent, Path basePath) {
+               if (basePath == null)
+                       throw new IllegalArgumentException("Context cannot be null");
+               parent.setLayout(new GridLayout());
+
+               // top filter
+               Composite filterCmp = new Composite(parent, SWT.NO_FOCUS);
+               filterCmp.setLayoutData(EclipseUiUtils.fillWidth());
+               addFilterPanel(filterCmp);
+
+               // Bottom part a sash with browser on the left
+               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
+               // form.setLayout(new FillLayout());
+               form.setLayoutData(EclipseUiUtils.fillAll());
+               Composite leftCmp = new Composite(form, SWT.NO_FOCUS);
+               displayBoxCmp = new Composite(form, SWT.NONE);
+               form.setWeights(new int[] { 3, 1 });
+
+               createBrowserPart(leftCmp, basePath);
+               // leftCmp.addControlListener(new ControlAdapter() {
+               // @Override
+               // public void controlResized(ControlEvent e) {
+               // Rectangle r = leftCmp.getClientArea();
+               // log.warn("Browser resized: " + r.toString());
+               // scrolledCmp.setMinSize(browserCols.size() * (COLUMN_WIDTH + 2),
+               // SWT.DEFAULT);
+               // // scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT,
+               // // r.height));
+               // }
+               // });
+
+               populateCurrEditedDisplay(displayBoxCmp, basePath);
+
+               // INIT
+               setEdited(basePath);
+               initialPath = basePath;
+               // form.layout(true, true);
+               return parent;
+       }
+
+       private void createBrowserPart(Composite parent, Path context) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               // scrolled composite
+               scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS);
+               scrolledCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               scrolledCmp.setExpandVertical(true);
+               scrolledCmp.setExpandHorizontal(true);
+               scrolledCmp.setShowFocusedControl(true);
+
+               scrolledCmpBody = new Composite(scrolledCmp, SWT.NO_FOCUS);
+               scrolledCmp.setContent(scrolledCmpBody);
+               scrolledCmpBody.addControlListener(new ControlAdapter() {
+                       private static final long serialVersionUID = 183238447102854553L;
+
+                       @Override
+                       public void controlResized(ControlEvent e) {
+                               Rectangle r = scrolledCmp.getClientArea();
+                               scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, r.height));
+                       }
+               });
+               initExplorer(scrolledCmpBody, context);
+               scrolledCmpBody.layout(true, true);
+               scrolledCmp.layout();
+
+       }
+
+       private Control initExplorer(Composite parent, Path context) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               return createBrowserColumn(parent, context);
+       }
+
+       private Control createBrowserColumn(Composite parent, Path context) {
+               // 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, table);
+               parent.layout(true, true);
+               return table;
+       }
+
+       public void addFilterPanel(Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)));
+
+               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(EclipseUiUtils.fillWidth());
+               filterTxt.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = 1L;
+
+                       public void modifyText(ModifyEvent event) {
+                               modifyFilter(false);
+                       }
+               });
+               filterTxt.addKeyListener(new KeyListener() {
+                       private static final long serialVersionUID = 2533535233583035527L;
+
+                       @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(currEdited);
+                                       if (table != null && !table.isDisposed())
+                                               currTable = table;
+                               }
+
+                               if (e.keyCode == SWT.ARROW_DOWN)
+                                       currTable.setFocus();
+                               else if (e.keyCode == SWT.BS) {
+                                       if (filterTxt.getText().equals("")
+                                                       && !(currEdited.getNameCount() == 1 || currEdited.equals(initialPath))) {
+                                               Path oldEdited = currEdited;
+                                               Path parentPath = currEdited.getParent();
+                                               setEdited(parentPath);
+                                               if (browserCols.containsKey(parentPath))
+                                                       browserCols.get(parentPath).setSelected(oldEdited);
+                                               filterTxt.setFocus();
+                                               e.doit = false;
+                                       }
+                               } else if (e.keyCode == SWT.TAB && !shiftPressed) {
+                                       Path uniqueChild = getOnlyChild(currEdited, filterTxt.getText());
+                                       if (uniqueChild != null) {
+                                               // Highlight the unique chosen child
+                                               currTable.setSelected(uniqueChild);
+                                               setEdited(uniqueChild);
+                                       }
+                                       filterTxt.setFocus();
+                                       e.doit = false;
+                               }
+                       }
+               });
+       }
+
+       private Path getOnlyChild(Path parent, String filter) {
+               try (DirectoryStream<Path> stream = Files.newDirectoryStream(currEdited, filter + "*")) {
+                       Path uniqueChild = null;
+                       boolean moreThanOne = false;
+                       loop: for (Path entry : stream) {
+                               if (uniqueChild == null) {
+                                       uniqueChild = entry;
+                               } else {
+                                       moreThanOne = true;
+                                       break loop;
+                               }
+                       }
+                       if (!moreThanOne)
+                               return uniqueChild;
+                       return null;
+               } catch (IOException ioe) {
+                       throw new FsUiException(
+                                       "Unable to determine unique child existence and get it under " + parent + " with filter " + filter,
+                                       ioe);
+               }
+       }
+
+       private void setEdited(Path path) {
+               currEdited = path;
+               EclipseUiUtils.clear(displayBoxCmp);
+               populateCurrEditedDisplay(displayBoxCmp, currEdited);
+               refreshFilters(path);
+               refreshBrowser(path);
+       }
+
+       private void refreshFilters(Path path) {
+               parentPathTxt.setText(path.toUri().toString());
+               filterTxt.setText("");
+               filterTxt.getParent().layout();
+       }
+
+       private void refreshBrowser(Path currPath) {
+               Path currParPath = currPath.getParent();
+               Object[][] colMatrix = new Object[browserCols.size()][2];
+
+               int i = 0, currPathIndex = -1, lastLeftOpenedIndex = -1;
+               for (Path path : browserCols.keySet()) {
+                       colMatrix[i][0] = path;
+                       colMatrix[i][1] = browserCols.get(path);
+                       if (currPathIndex >= 0 && lastLeftOpenedIndex < 0 && currParPath != null) {
+                               boolean leaveOpened = path.startsWith(currPath);
+                               if (!leaveOpened)
+                                       lastLeftOpenedIndex = i;
+                       }
+                       if (currParPath.equals(path))
+                               currPathIndex = i;
+                       i++;
+               }
+
+               if (currPathIndex >= 0 && lastLeftOpenedIndex >= 0) {
+                       // dispose and remove useless cols
+                       for (int l = i - 1; l >= lastLeftOpenedIndex; l--) {
+                               ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose();
+                               browserCols.remove(colMatrix[l][0]);
+                       }
+               }
+
+               if (browserCols.containsKey(currPath)) {
+                       FilterEntitiesVirtualTable currCol = browserCols.get(currPath);
+                       if (currCol.isDisposed()) {
+                               // Does it still happen ?
+                               log.warn(currPath + " browser column was disposed and still listed");
+                               browserCols.remove(currPath);
+                       }
+               }
+
+               if (!browserCols.containsKey(currPath) && Files.isDirectory(currPath))
+                       createBrowserColumn(scrolledCmpBody, currPath);
+
+               scrolledCmpBody.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false)));
+               scrolledCmpBody.layout(true, true);
+               // also resize the scrolled composite
+               scrolledCmp.layout();
+       }
+
+       private void modifyFilter(boolean fromOutside) {
+               if (!fromOutside)
+                       if (currEdited != null) {
+                               String filter = filterTxt.getText() + "*";
+                               FilterEntitiesVirtualTable table = browserCols.get(currEdited);
+                               if (table != null && !table.isDisposed())
+                                       table.filterList(filter);
+                       }
+       }
+
+       /**
+        * Recreates the content of the box that displays information about the current
+        * selected node.
+        */
+       private void populateCurrEditedDisplay(Composite parent, Path context) {
+               parent.setLayout(new GridLayout());
+
+               // if (isImg(context)) {
+               // EditableImage image = new Img(parent, RIGHT, context, imageWidth);
+               // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false,
+               // 2, 1));
+               // }
+
+               try {
+                       Label contextL = new Label(parent, SWT.NONE);
+                       contextL.setText(context.getFileName().toString());
+                       contextL.setFont(EclipseUiUtils.getBoldFont(parent));
+                       addProperty(parent, "Last modified", Files.getLastModifiedTime(context).toString());
+                       addProperty(parent, "Owner", Files.getOwner(context).getName());
+                       if (Files.isDirectory(context)) {
+                               addProperty(parent, "Type", "Folder");
+                       } else {
+                               String mimeType = Files.probeContentType(context);
+                               if (EclipseUiUtils.isEmpty(mimeType))
+                                       mimeType = "<i>Unknown</i>";
+                               addProperty(parent, "Type", mimeType);
+                               addProperty(parent, "Size", FsUiUtils.humanReadableByteCount(Files.size(context), false));
+                       }
+                       parent.layout(true, true);
+               } catch (IOException e) {
+                       throw new FsUiException("Cannot display details for " + context, e);
+               }
+       }
+
+       private void addProperty(Composite parent, String propName, String value) {
+               Label contextL = new Label(parent, SWT.NONE);
+               contextL.setText(propName + ": " + value);
+       }
+
+       /**
+        * Almost canonical implementation of a table that displays the content of a
+        * directory
+        */
+       private class FilterEntitiesVirtualTable extends Composite {
+               private static final long serialVersionUID = 2223410043691844875L;
+
+               // Context
+               private Path context;
+               private Path currSelected = null;
+
+               // UI Objects
+               private FsTableViewer viewer;
+
+               @Override
+               public boolean setFocus() {
+                       if (viewer.getTable().isDisposed())
+                               return false;
+                       if (currSelected != null)
+                               viewer.setSelection(new StructuredSelection(currSelected), true);
+                       else if (viewer.getSelection().isEmpty()) {
+                               Object first = viewer.getElementAt(0);
+                               if (first != null)
+                                       viewer.setSelection(new StructuredSelection(first), true);
+                       }
+                       return viewer.getTable().setFocus();
+               }
+
+               /**
+                * Enable highlighting the correct element in the table when externally browsing
+                * (typically via the command-line-like Text field)
+                */
+               void setSelected(Path selected) {
+                       // to prevent change selection event to be thrown
+                       currSelected = selected;
+                       viewer.setSelection(new StructuredSelection(currSelected), true);
+               }
+
+               void filterList(String filter) {
+                       viewer.setInput(context, filter);
+               }
+
+               public FilterEntitiesVirtualTable(Composite parent, int style, Path context) {
+                       super(parent, SWT.NO_FOCUS);
+                       this.context = context;
+                       createTableViewer(this);
+               }
+
+               private void createTableViewer(final Composite parent) {
+                       parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+                       // 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(EclipseUiUtils.noSpaceGridLayout());
+                       // viewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.MULTI |
+                       // SWT.V_SCROLL);
+                       // Table table = viewer.getTable();
+                       // table.setLayoutData(EclipseUiUtils.fillAll());
+
+                       viewer = new FsTableViewer(parent, SWT.MULTI);
+                       Table table = viewer.configureDefaultSingleColumnTable(COLUMN_WIDTH);
+
+                       viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+
+                               @Override
+                               public void selectionChanged(SelectionChangedEvent event) {
+                                       IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+                                       if (selection.isEmpty())
+                                               return;
+                                       Object obj = selection.getFirstElement();
+                                       Path newSelected;
+                                       if (obj instanceof Path)
+                                               newSelected = (Path) obj;
+                                       else if (obj instanceof ParentDir)
+                                               newSelected = ((ParentDir) obj).getPath();
+                                       else
+                                               return;
+                                       if (newSelected.equals(currSelected))
+                                               return;
+                                       currSelected = newSelected;
+                                       setEdited(newSelected);
+
+                               }
+                       });
+
+                       table.addKeyListener(new KeyListener() {
+                               private static final long serialVersionUID = -8083424284436715709L;
+
+                               @Override
+                               public void keyReleased(KeyEvent e) {
+                               }
+
+                               @Override
+                               public void keyPressed(KeyEvent e) {
+                                       IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+                                       Path selected = null;
+                                       if (!selection.isEmpty())
+                                               selected = ((Path) selection.getFirstElement());
+                                       if (e.keyCode == SWT.ARROW_RIGHT) {
+                                               if (!Files.isDirectory(selected))
+                                                       return;
+                                               if (selected != null) {
+                                                       setEdited(selected);
+                                                       browserCols.get(selected).setFocus();
+                                               }
+                                       } else if (e.keyCode == SWT.ARROW_LEFT) {
+                                               if (context.equals(initialPath))
+                                                       return;
+                                               Path parent = context.getParent();
+                                               if (parent == null)
+                                                       return;
+
+                                               setEdited(parent);
+                                               browserCols.get(parent).setFocus();
+                                       }
+                               }
+                       });
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java
new file mode 100644 (file)
index 0000000..d3fc1c9
--- /dev/null
@@ -0,0 +1,84 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+/** Basic label provider with icon for NIO file viewers */
+public class FileIconNameLabelProvider extends ColumnLabelProvider {
+       private static final long serialVersionUID = 8187902187946523148L;
+
+       private Image folderIcon;
+       private Image fileIcon;
+
+       public FileIconNameLabelProvider() {
+               // if (!PlatformUI.isWorkbenchRunning()) {
+               folderIcon = ImageDescriptor.createFromFile(getClass(), "folder.png").createImage();
+               fileIcon = ImageDescriptor.createFromFile(getClass(), "file.png").createImage();
+               // }
+       }
+
+       @Override
+       public void dispose() {
+               if (folderIcon != null)
+                       folderIcon.dispose();
+               if (fileIcon != null)
+                       fileIcon.dispose();
+               super.dispose();
+       }
+
+       @Override
+       public String getText(Object element) {
+               if (element instanceof Path) {
+                       Path curr = ((Path) element);
+                       Path name = curr.getFileName();
+                       if (name == null)
+                               return "[No name]";
+                       else
+                               return name.toString();
+               } else if (element instanceof ParentDir) {
+                       return "..";
+               }
+               return null;
+       }
+
+       @Override
+       public Image getImage(Object element) {
+               if (element instanceof Path) {
+                       Path curr = ((Path) element);
+                       if (Files.isDirectory(curr))
+                               // if (folderIcon != null)
+                               return folderIcon;
+                       // else
+                       // return
+                       // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
+                       // else if (fileIcon != null)
+                       return fileIcon;
+                       // else
+                       // return
+                       // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);
+               } else if (element instanceof ParentDir) {
+                       return folderIcon;
+               }
+               return null;
+       }
+
+       @Override
+       public String getToolTipText(Object element) {
+               if (element instanceof Path) {
+                       Path curr = ((Path) element);
+                       Path name = curr.getFileName();
+                       if (name == null)
+                               return "[No name]";
+                       else
+                               return name.toAbsolutePath().toString();
+               } else if (element instanceof ParentDir) {
+                       return ((ParentDir) element).getPath().toAbsolutePath().toString();
+               }
+               return null;
+       }
+
+}
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTableViewer.java
new file mode 100644 (file)
index 0000000..3b126e9
--- /dev/null
@@ -0,0 +1,141 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.nio.file.Path;
+import java.util.List;
+
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ILazyContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * Canonical implementation of a JFace table viewer to display the content of a
+ * file folder
+ */
+public class FsTableViewer extends TableViewer {
+       private static final long serialVersionUID = -5632407542678477234L;
+
+       private boolean showHiddenItems = false;
+       private boolean folderFirst = true;
+       private boolean reverseOrder = false;
+       private String orderProperty = FsUiConstants.PROPERTY_NAME;
+
+       private Path initialPath = null;
+
+       public FsTableViewer(Composite parent, int style) {
+               super(parent, style | SWT.VIRTUAL);
+       }
+
+       public Table configureDefaultSingleColumnTable(int tableWidthHint) {
+
+               return configureDefaultSingleColumnTable(tableWidthHint, new FileIconNameLabelProvider());
+       }
+
+       public Table configureDefaultSingleColumnTable(int tableWidthHint, CellLabelProvider labelProvider) {
+               Table table = this.getTable();
+               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+               table.setLinesVisible(false);
+               table.setHeaderVisible(false);
+               // CmsUtils.markup(table);
+               // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
+
+               TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
+               TableColumn tcol = column.getColumn();
+               tcol.setWidth(tableWidthHint);
+               column.setLabelProvider(labelProvider);
+               this.setContentProvider(new MyLazyCP());
+               return table;
+       }
+
+       public Table configureDefaultTable(List<ColumnDefinition> columns) {
+               this.setContentProvider(new MyLazyCP());
+               Table table = this.getTable();
+               table.setLinesVisible(true);
+               table.setHeaderVisible(true);
+               // CmsUtils.markup(table);
+               // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN);
+               for (ColumnDefinition colDef : columns) {
+                       TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
+                       column.setLabelProvider(colDef.getLabelProvider());
+                       TableColumn tcol = column.getColumn();
+                       tcol.setResizable(true);
+                       tcol.setText(colDef.getLabel());
+                       tcol.setWidth(colDef.getMinWidth());
+               }
+               return table;
+       }
+
+       public void setInput(Path dir, String filter) {
+               Path[] rows = FsUiUtils.getChildren(dir, filter, showHiddenItems, folderFirst, orderProperty, reverseOrder);
+               if (rows == null) {
+                       this.setInput(null);
+                       this.setItemCount(0);
+                       return;
+               }
+               boolean isRoot;
+               try {
+                       isRoot = dir.getRoot().equals(dir);
+               } catch (Exception e) {
+                       // FIXME Workaround for JCR root node access
+                       isRoot = dir.toString().equals("/");
+               }
+               final Object[] res;
+               if (isRoot)
+                       res = rows;
+               else if (initialPath != null && initialPath.equals(dir))
+                       res = rows;
+               else {
+                       res = new Object[rows.length + 1];
+                       res[0] = new ParentDir(dir.getParent());
+                       for (int i = 1; i < res.length; i++) {
+                               res[i] = rows[i - 1];
+                       }
+               }
+               this.setInput(res);
+               int length = res.length;
+               this.setItemCount(length);
+               this.refresh();
+       }
+
+       /** Directly displays bookmarks **/
+       public void setPathsInput(Path... paths) {
+               this.setInput((Object[]) paths);
+               this.setItemCount(paths.length);
+               this.refresh();
+       }
+
+       /**
+        * A path which is to be considered as root (and thus provide no link to a
+        * parent directory)
+        */
+       public void setInitialPath(Path initialPath) {
+               this.initialPath = initialPath;
+       }
+
+       private class MyLazyCP implements ILazyContentProvider {
+               private static final long serialVersionUID = 9096550041395433128L;
+               private Object[] elements;
+
+               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) {
+                       if (index < elements.length)
+                               FsTableViewer.this.replace(elements[index], index);
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java
new file mode 100644 (file)
index 0000000..f55ead7
--- /dev/null
@@ -0,0 +1,144 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * Canonical implementation of a JFace TreeViewer to display the content of a
+ * repository
+ */
+public class FsTreeViewer extends TreeViewer {
+       private static final long serialVersionUID = -5632407542678477234L;
+
+       private boolean showHiddenItems = false;
+       private boolean showDirectoryFirst = true;
+       private String orderingProperty = FsUiConstants.PROPERTY_NAME;
+
+       public FsTreeViewer(Composite parent, int style) {
+               super(parent, style | SWT.VIRTUAL);
+       }
+
+       public Tree configureDefaultSingleColumnTable(int tableWidthHint) {
+               Tree tree = this.getTree();
+               tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+               tree.setLinesVisible(true);
+               tree.setHeaderVisible(false);
+//             CmsUtils.markup(tree);
+
+               TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
+               TreeColumn tcol = column.getColumn();
+               tcol.setWidth(tableWidthHint);
+               column.setLabelProvider(new FileIconNameLabelProvider());
+
+               this.setContentProvider(new MyCP());
+               return tree;
+       }
+
+       public Tree configureDefaultTable(List<ColumnDefinition> columns) {
+               this.setContentProvider(new MyCP());
+               Tree tree = this.getTree();
+               tree.setLinesVisible(true);
+               tree.setHeaderVisible(true);
+//             CmsUtils.markup(tree);
+//             CmsUtils.style(tree, MaintenanceStyles.BROWSER_COLUMN);
+               for (ColumnDefinition colDef : columns) {
+                       TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
+                       column.setLabelProvider(colDef.getLabelProvider());
+                       TreeColumn tcol = column.getColumn();
+                       tcol.setResizable(true);
+                       tcol.setText(colDef.getLabel());
+                       tcol.setWidth(colDef.getMinWidth());
+               }
+               return tree;
+       }
+
+       public void setInput(Path dir, String filter) {
+               try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
+                       // TODO make this lazy
+                       List<Path> paths = new ArrayList<>();
+                       for (Path entry : stream) {
+                               paths.add(entry);
+                       }
+                       Object[] rows = paths.toArray(new Object[0]);
+                       this.setInput(rows);
+                       // this.setItemCount(rows.length);
+                       this.refresh();
+               } catch (IOException | DirectoryIteratorException e) {
+                       throw new FsUiException("Unable to filter " + dir + " children with filter " + filter, e);
+               }
+       }
+
+       /** Directly displays bookmarks **/
+       public void setPathsInput(Path... paths) {
+               this.setInput((Object[]) paths);
+               // this.setItemCount(paths.length);
+               this.refresh();
+       }
+
+       private class MyCP implements ITreeContentProvider {
+               private static final long serialVersionUID = 9096550041395433128L;
+               private Object[] elements;
+
+               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;
+               }
+
+               @Override
+               public Object[] getElements(Object inputElement) {
+                       return elements;
+               }
+
+               @Override
+               public Object[] getChildren(Object parentElement) {
+                       Path path = (Path) parentElement;
+                       if (!Files.isDirectory(path))
+                               return null;
+                       else
+                               return FsUiUtils.getChildren(path, "*", showHiddenItems, showDirectoryFirst, orderingProperty, false);
+               }
+
+               @Override
+               public Object getParent(Object element) {
+                       Path path = (Path) element;
+                       return path.getParent();
+               }
+
+               @Override
+               public boolean hasChildren(Object element) {
+                       Path path = (Path) element;
+                       try {
+                               if (!Files.isDirectory(path))
+                                       return false;
+                               else
+                                       try (DirectoryStream<Path> children = Files.newDirectoryStream(path, "*")) {
+                                               return children.iterator().hasNext();
+                                       }
+                       } catch (IOException e) {
+                               throw new FsUiException("Unable to check child existence on " + path, e);
+                       }
+               }
+
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiConstants.java
new file mode 100644 (file)
index 0000000..2b51e71
--- /dev/null
@@ -0,0 +1,11 @@
+package org.argeo.eclipse.ui.fs;
+
+/** Centralize constants used by the Nio FS UI parts */
+public interface FsUiConstants {
+
+       // TODO use standard properties
+       String PROPERTY_NAME = "name";
+       String PROPERTY_SIZE = "size";
+       String PROPERTY_LAST_MODIFIED = "last-modified";
+       String PROPERTY_TYPE = "type";
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiException.java
new file mode 100644 (file)
index 0000000..422b0e1
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.eclipse.ui.fs;
+
+/** Files specific exception */
+public class FsUiException extends RuntimeException {
+       private static final long serialVersionUID = 1L;
+
+       public FsUiException(String message) {
+               super(message);
+       }
+
+       public FsUiException(String message, Throwable e) {
+               super(message, e);
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/FsUiUtils.java
new file mode 100644 (file)
index 0000000..956d96b
--- /dev/null
@@ -0,0 +1,132 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Centralise additional utilitary methods to manage Java7 NIO files */
+public class FsUiUtils {
+
+       /**
+        * thanks to
+        * http://programming.guide/java/formatting-byte-size-to-human-readable-format.html
+        */
+       public static String humanReadableByteCount(long bytes, boolean si) {
+               int unit = si ? 1000 : 1024;
+               if (bytes < unit)
+                       return bytes + " B";
+               int exp = (int) (Math.log(bytes) / Math.log(unit));
+               String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
+               return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+       }
+
+       public static Path[] getChildren(Path parent, String filter, boolean showHiddenItems, boolean folderFirst,
+                       String orderProperty, boolean reverseOrder) {
+               if (!Files.isDirectory(parent))
+                       return null;
+               List<Pair> pairs = new ArrayList<>();
+               try (DirectoryStream<Path> stream = Files.newDirectoryStream(parent, filter)) {
+                       loop: for (Path entry : stream) {
+                               if (!showHiddenItems)
+                                       if (Files.isHidden(entry))
+                                               continue loop;
+                               switch (orderProperty) {
+                               case FsUiConstants.PROPERTY_SIZE:
+                                       if (folderFirst)
+                                               pairs.add(new LPair(entry, Files.size(entry), Files.isDirectory(entry)));
+                                       else
+                                               pairs.add(new LPair(entry, Files.size(entry)));
+                                       break;
+                               case FsUiConstants.PROPERTY_LAST_MODIFIED:
+                                       if (folderFirst)
+                                               pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis(),
+                                                               Files.isDirectory(entry)));
+                                       else
+                                               pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis()));
+                                       break;
+                               case FsUiConstants.PROPERTY_NAME:
+                                       if (folderFirst)
+                                               pairs.add(new SPair(entry, entry.getFileName().toString(), Files.isDirectory(entry)));
+                                       else
+                                               pairs.add(new SPair(entry, entry.getFileName().toString()));
+                                       break;
+                               default:
+                                       throw new FsUiException("Unable to prepare sort for property " + orderProperty);
+                               }
+                       }
+                       Pair[] rows = pairs.toArray(new Pair[0]);
+                       Arrays.sort(rows);
+                       Path[] results = new Path[rows.length];
+                       if (reverseOrder) {
+                               int j = rows.length - 1;
+                               for (int i = 0; i < rows.length; i++)
+                                       results[i] = rows[j - i].p;
+                       } else
+                               for (int i = 0; i < rows.length; i++)
+                                       results[i] = rows[i].p;
+                       return results;
+               } catch (IOException | DirectoryIteratorException e) {
+                       throw new FsUiException("Unable to filter " + parent + " children with filter " + filter, e);
+               }
+       }
+
+       static abstract class Pair implements Comparable<Object> {
+               Path p;
+               Boolean i;
+       };
+
+       static class LPair extends Pair {
+               long v;
+
+               public LPair(Path path, long propValue) {
+                       p = path;
+                       v = propValue;
+               }
+
+               public LPair(Path path, long propValue, boolean isDir) {
+                       p = path;
+                       v = propValue;
+                       i = isDir;
+               }
+
+               public int compareTo(Object o) {
+                       if (i != null) {
+                               Boolean j = ((LPair) o).i;
+                               if (i.booleanValue() != j.booleanValue())
+                                       return i.booleanValue() ? -1 : 1;
+                       }
+                       long u = ((LPair) o).v;
+                       return v < u ? -1 : v == u ? 0 : 1;
+               }
+       };
+
+       static class SPair extends Pair {
+               String v;
+
+               public SPair(Path path, String propValue) {
+                       p = path;
+                       v = propValue;
+               }
+
+               public SPair(Path path, String propValue, boolean isDir) {
+                       p = path;
+                       v = propValue;
+                       i = isDir;
+               }
+
+               public int compareTo(Object o) {
+                       if (i != null) {
+                               Boolean j = ((SPair) o).i;
+                               if (i.booleanValue() != j.booleanValue())
+                                       return i.booleanValue() ? -1 : 1;
+                       }
+                       String u = ((SPair) o).v;
+                       return v.compareTo(u);
+               }
+       };
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java
new file mode 100644 (file)
index 0000000..2bb65ee
--- /dev/null
@@ -0,0 +1,78 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+/** Expect a {@link Path} as input element */
+public class NioFileLabelProvider extends ColumnLabelProvider {
+       private final static FileTime EPOCH = FileTime.fromMillis(0);
+       private static final long serialVersionUID = 2160026425187796930L;
+       private final String propName;
+       private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm");
+
+       // TODO use new formatting
+       // DateTimeFormatter formatter =
+       // DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
+       // .withLocale( Locale.UK )
+       // .withZone( ZoneId.systemDefault() );
+       public NioFileLabelProvider(String propName) {
+               this.propName = propName;
+       }
+
+       @Override
+       public String getText(Object element) {
+               try {
+                       Path path;
+                       if (element instanceof ParentDir) {
+//                             switch (propName) {
+//                             case FsUiConstants.PROPERTY_SIZE:
+//                                     return "-";
+//                             case FsUiConstants.PROPERTY_LAST_MODIFIED:
+//                                     return "-";
+//                             // return Files.getLastModifiedTime(((ParentDir) element).getPath()).toString();
+//                             case FsUiConstants.PROPERTY_TYPE:
+//                                     return "Folder";
+//                             }
+                               path = ((ParentDir) element).getPath();
+                       } else
+                               path = (Path) element;
+                       switch (propName) {
+                       case FsUiConstants.PROPERTY_SIZE:
+                               if (Files.isDirectory(path))
+                                       return "-";
+                               else
+                                       return FsUiUtils.humanReadableByteCount(Files.size(path), false);
+                       case FsUiConstants.PROPERTY_LAST_MODIFIED:
+                               if (Files.isDirectory(path))
+                                       return "-";
+                               FileTime time = Files.getLastModifiedTime(path);
+                               if (time.equals(EPOCH))
+                                       return "-";
+                               else
+                                       return dateFormat.format(new Date(time.toMillis()));
+                       case FsUiConstants.PROPERTY_TYPE:
+                               if (Files.isDirectory(path))
+                                       return "Folder";
+                               else {
+                                       String mimeType = Files.probeContentType(path);
+                                       if (EclipseUiUtils.isEmpty(mimeType))
+                                               return "Unknown";
+                                       else
+                                               return mimeType;
+                               }
+                       default:
+                               throw new IllegalArgumentException("Unsupported property " + propName);
+                       }
+               } catch (IOException ioe) {
+                       throw new FsUiException("Cannot get property " + propName + " on " + element);
+               }
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/ParentDir.java
new file mode 100644 (file)
index 0000000..6f09c29
--- /dev/null
@@ -0,0 +1,28 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.nio.file.Path;
+
+/** A parent directory (..) reference. */
+public class ParentDir {
+       Path path;
+
+       public ParentDir(Path path) {
+               super();
+               this.path = path;
+       }
+
+       public Path getPath() {
+               return path;
+       }
+
+       @Override
+       public int hashCode() {
+               return path.hashCode();
+       }
+
+       @Override
+       public String toString() {
+               return "Parent dir " + path;
+       }
+
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java
new file mode 100644 (file)
index 0000000..2e3d6b4
--- /dev/null
@@ -0,0 +1,211 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+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.eclipse.swt.widgets.Table;
+
+/**
+ * Experimental UI upon Java 7 nio files api: SashForm layout with bookmarks on
+ * the left hand side and a simple table on the right hand side.
+ */
+public class SimpleFsBrowser extends Composite {
+       private final static CmsLog log = CmsLog.getLog(SimpleFsBrowser.class);
+       private static final long serialVersionUID = -40347919096946585L;
+
+       private Path currSelected;
+       private FsTableViewer bookmarksViewer;
+       private FsTableViewer directoryDisplayViewer;
+
+       public SimpleFsBrowser(Composite parent, int style) {
+               super(parent, style);
+               createContent(this);
+               // parent.layout(true, true);
+       }
+
+       public Viewer getViewer() {
+               return directoryDisplayViewer;
+       }
+
+       private void createContent(Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+
+               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
+               Composite leftCmp = new Composite(form, SWT.NONE);
+               populateBookmarks(leftCmp);
+
+               Composite rightCmp = new Composite(form, SWT.BORDER);
+               populateDisplay(rightCmp);
+               form.setLayoutData(EclipseUiUtils.fillAll());
+               form.setWeights(new int[] { 1, 3 });
+       }
+
+       public void setInput(Path... paths) {
+               bookmarksViewer.setPathsInput(paths);
+               bookmarksViewer.getTable().getParent().layout(true, true);
+       }
+
+       private void populateBookmarks(final Composite parent) {
+               // GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
+               // layout.verticalSpacing = 5;
+               parent.setLayout(new GridLayout());
+
+               ISelectionChangedListener selList = new MySelectionChangedListener();
+
+               appendTitle(parent, "My bookmarks");
+               bookmarksViewer = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL);
+               Table table = bookmarksViewer.configureDefaultSingleColumnTable(500);
+               GridData gd = EclipseUiUtils.fillWidth();
+               gd.horizontalIndent = 10;
+               table.setLayoutData(gd);
+               bookmarksViewer.addSelectionChangedListener(selList);
+
+               appendTitle(parent, "Jcr + File");
+
+               FsTableViewer jcrFilesViewers = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL);
+               table = jcrFilesViewers.configureDefaultSingleColumnTable(500);
+               gd = EclipseUiUtils.fillWidth();
+               gd.horizontalIndent = 10;
+               table.setLayoutData(gd);
+               jcrFilesViewers.addSelectionChangedListener(selList);
+
+               // FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider();
+               // try {
+               // Path testPath = fsProvider.getPath(new URI("jcr+memory:/"));
+               // jcrFilesViewers.setPathsInput(testPath);
+               // } catch (URISyntaxException e) {
+               // // TODO Auto-generated catch block
+               // e.printStackTrace();
+               // }
+       }
+
+       private Label appendTitle(Composite parent, String value) {
+               Label titleLbl = new Label(parent, SWT.NONE);
+               titleLbl.setText(value);
+               titleLbl.setFont(EclipseUiUtils.getBoldFont(parent));
+               GridData gd = EclipseUiUtils.fillWidth();
+               gd.horizontalIndent = 5;
+               gd.verticalIndent = 5;
+               titleLbl.setLayoutData(gd);
+               return titleLbl;
+       }
+
+       private class MySelectionChangedListener implements ISelectionChangedListener {
+               @Override
+               public void selectionChanged(SelectionChangedEvent event) {
+                       IStructuredSelection selection = (IStructuredSelection) bookmarksViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+                       else {
+                               Path newSelected = (Path) selection.getFirstElement();
+                               if (newSelected.equals(currSelected))
+                                       return;
+                               currSelected = newSelected;
+                               directoryDisplayViewer.setInput(currSelected, "*");
+                       }
+               }
+       }
+
+       private void populateDisplay(final Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
+               List<ColumnDefinition> colDefs = new ArrayList<>();
+               colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 250));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
+                               "Last modified", 200));
+               Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
+               table.setLayoutData(EclipseUiUtils.fillAll());
+
+               table.addKeyListener(new KeyListener() {
+                       private static final long serialVersionUID = -8083424284436715709L;
+
+                       @Override
+                       public void keyReleased(KeyEvent e) {
+                       }
+
+                       @Override
+                       public void keyPressed(KeyEvent e) {
+                               log.debug("Key event received: " + e.keyCode);
+                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+                               Path selected = null;
+                               if (!selection.isEmpty())
+                                       selected = ((Path) selection.getFirstElement());
+                               if (e.keyCode == SWT.CR) {
+                                       if (!Files.isDirectory(selected))
+                                               return;
+                                       if (selected != null) {
+                                               currSelected = selected;
+                                               directoryDisplayViewer.setInput(currSelected, "*");
+                                       }
+                               } else if (e.keyCode == SWT.BS) {
+                                       currSelected = currSelected.getParent();
+                                       directoryDisplayViewer.setInput(currSelected, "*");
+                                       directoryDisplayViewer.getTable().setFocus();
+                               }
+                       }
+               });
+
+//             directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
+//                     @Override
+//                     public void doubleClick(DoubleClickEvent event) {
+//                             IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+//                             Path selected = null;
+//                             if (!selection.isEmpty()) {
+//                                     Object obj = selection.getFirstElement();
+//                                     if (obj instanceof Path)
+//                                             selected = (Path) obj;
+//                                     else if (obj instanceof ParentDir)
+//                                             selected = ((ParentDir) obj).getPath();
+//                             }
+//                             if (selected != null) {
+//                                     if (!Files.isDirectory(selected))
+//                                             return;
+//                                     currSelected = selected;
+//                                     directoryDisplayViewer.setInput(currSelected, "*");
+//                             }
+//                     }
+//             });
+
+               directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() {
+                       @Override
+                       public void doubleClick(DoubleClickEvent event) {
+                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+                               Path selected = null;
+                               if (!selection.isEmpty()) {
+                                       Object obj = selection.getFirstElement();
+                                       if (obj instanceof Path)
+                                               selected = (Path) obj;
+                                       else if (obj instanceof ParentDir)
+                                               selected = ((ParentDir) obj).getPath();
+                               }
+                               if (selected != null) {
+                                       if (!Files.isDirectory(selected))
+                                               return;
+                                       currSelected = selected;
+                                       directoryDisplayViewer.setInput(currSelected, "*");
+                               }
+                       }
+               });
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java
new file mode 100644 (file)
index 0000000..401e5cb
--- /dev/null
@@ -0,0 +1,128 @@
+package org.argeo.eclipse.ui.fs;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Tree;
+
+/** A simple Java 7 nio files browser with a tree */
+public class SimpleFsTreeBrowser extends Composite {
+       private final static CmsLog log = CmsLog.getLog(SimpleFsTreeBrowser.class);
+       private static final long serialVersionUID = -40347919096946585L;
+
+       private Path currSelected;
+       private FsTreeViewer treeViewer;
+       private FsTableViewer directoryDisplayViewer;
+
+       public SimpleFsTreeBrowser(Composite parent, int style) {
+               super(parent, style);
+               createContent(this);
+               // parent.layout(true, true);
+       }
+
+       private void createContent(Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               SashForm form = new SashForm(parent, SWT.HORIZONTAL);
+               Composite child1 = new Composite(form, SWT.NONE);
+               populateTree(child1);
+               Composite child2 = new Composite(form, SWT.BORDER);
+               populateDisplay(child2);
+               form.setLayoutData(EclipseUiUtils.fillAll());
+               form.setWeights(new int[] { 1, 3 });
+       }
+
+       public void setInput(Path... paths) {
+               treeViewer.setPathsInput(paths);
+               treeViewer.getControl().getParent().layout(true, true);
+       }
+
+       private void populateTree(final Composite parent) {
+               // GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
+               // layout.verticalSpacing = 5;
+               parent.setLayout(new GridLayout());
+
+               ISelectionChangedListener selList = new MySelectionChangedListener();
+
+               treeViewer = new FsTreeViewer(parent, SWT.MULTI);
+               Tree tree = treeViewer.configureDefaultSingleColumnTable(500);
+               GridData gd = EclipseUiUtils.fillAll();
+               // gd.horizontalIndent = 10;
+               tree.setLayoutData(gd);
+               treeViewer.addSelectionChangedListener(selList);
+       }
+
+       private class MySelectionChangedListener implements ISelectionChangedListener {
+               @Override
+               public void selectionChanged(SelectionChangedEvent event) {
+                       IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
+                       if (selection.isEmpty())
+                               return;
+                       else {
+                               Path newSelected = (Path) selection.getFirstElement();
+                               if (newSelected.equals(currSelected))
+                                       return;
+                               currSelected = newSelected;
+                               if (Files.isDirectory(currSelected))
+                                       directoryDisplayViewer.setInput(currSelected, "*");
+                       }
+               }
+       }
+
+       private void populateDisplay(final Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI);
+               List<ColumnDefinition> colDefs = new ArrayList<>();
+               colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200, 200));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100, 100));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 300, 300));
+               colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED),
+                               "Last modified", 100, 100));
+               Table table = directoryDisplayViewer.configureDefaultTable(colDefs);
+               table.setLayoutData(EclipseUiUtils.fillAll());
+
+               table.addKeyListener(new KeyListener() {
+                       private static final long serialVersionUID = -8083424284436715709L;
+
+                       @Override
+                       public void keyReleased(KeyEvent e) {
+                       }
+
+                       @Override
+                       public void keyPressed(KeyEvent e) {
+                               log.debug("Key event received: " + e.keyCode);
+                               IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection();
+                               Path selected = null;
+                               if (!selection.isEmpty())
+                                       selected = ((Path) selection.getFirstElement());
+                               if (e.keyCode == SWT.CR) {
+                                       if (!Files.isDirectory(selected))
+                                               return;
+                                       if (selected != null) {
+                                               currSelected = selected;
+                                               directoryDisplayViewer.setInput(currSelected, "*");
+                                       }
+                               } else if (e.keyCode == SWT.BS) {
+                                       currSelected = currSelected.getParent();
+                                       directoryDisplayViewer.setInput(currSelected, "*");
+                                       directoryDisplayViewer.getTable().setFocus();
+                               }
+                       }
+               });
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png
new file mode 100644 (file)
index 0000000..ce2f2a8
Binary files /dev/null and b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/file.png differ
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png
new file mode 100644 (file)
index 0000000..c31f37e
Binary files /dev/null and b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/folder.png differ
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/fs/package-info.java
new file mode 100644 (file)
index 0000000..d7f83c3
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic SWT/JFace file system utilities. */
+package org.argeo.eclipse.ui.fs;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/package-info.java
new file mode 100644 (file)
index 0000000..0d245db
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic SWT/JFace utilities. */
+package org.argeo.eclipse.ui;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/LdifUsersTable.java
new file mode 100644 (file)
index 0000000..5713905
--- /dev/null
@@ -0,0 +1,402 @@
+package org.argeo.eclipse.ui.parts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.eclipse.ui.ColumnDefinition;
+import org.argeo.eclipse.ui.EclipseUiException;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.util.ViewerUtils;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Generic composite that display a filter and a table viewer to display users
+ * (can also be groups)
+ * 
+ * Warning: this class does not extends <code>TableViewer</code>. Use the
+ * getTableViewer method to access it.
+ * 
+ */
+public abstract class LdifUsersTable extends Composite {
+       private static final long serialVersionUID = -7385959046279360420L;
+
+       // Context
+       // private UserAdmin userAdmin;
+
+       // Configuration
+       private List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
+       private boolean hasFilter;
+       private boolean preventTableLayout = false;
+       private boolean hasSelectionColumn;
+       private int tableStyle;
+
+       // Local UI Objects
+       private TableViewer usersViewer;
+       private Text filterTxt;
+
+       /* EXPOSED METHODS */
+
+       /**
+        * @param parent
+        * @param style
+        */
+       public LdifUsersTable(Composite parent, int style) {
+               super(parent, SWT.NO_FOCUS);
+               this.tableStyle = style;
+       }
+
+       // TODO workaround the bug of the table layout in the Form
+       public LdifUsersTable(Composite parent, int style, boolean preventTableLayout) {
+               super(parent, SWT.NO_FOCUS);
+               this.tableStyle = style;
+               this.preventTableLayout = preventTableLayout;
+       }
+
+       /** This must be called before the call to populate method */
+       public void setColumnDefinitions(List<ColumnDefinition> columnDefinitions) {
+               this.columnDefs = columnDefinitions;
+       }
+
+       /**
+        * 
+        * @param addFilter
+        *            choose to add a field to filter results or not
+        * @param addSelection
+        *            choose to add a column to select some of the displayed results or
+        *            not
+        */
+       public void populate(boolean addFilter, boolean addSelection) {
+               // initialization
+               Composite parent = this;
+               hasFilter = addFilter;
+               hasSelectionColumn = addSelection;
+
+               // Main Layout
+               GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
+               layout.verticalSpacing = 5;
+               this.setLayout(layout);
+               if (hasFilter)
+                       createFilterPart(parent);
+
+               Composite tableComp = new Composite(parent, SWT.NO_FOCUS);
+               tableComp.setLayoutData(EclipseUiUtils.fillAll());
+               usersViewer = createTableViewer(tableComp);
+               usersViewer.setContentProvider(new UsersContentProvider());
+       }
+
+       /**
+        * 
+        * @param showMore
+        *            display static filters on creation
+        * @param addSelection
+        *            choose to add a column to select some of the displayed results or
+        *            not
+        */
+       public void populateWithStaticFilters(boolean showMore, boolean addSelection) {
+               // initialization
+               Composite parent = this;
+               hasFilter = true;
+               hasSelectionColumn = addSelection;
+
+               // Main Layout
+               GridLayout layout = EclipseUiUtils.noSpaceGridLayout();
+               layout.verticalSpacing = 5;
+               this.setLayout(layout);
+               createStaticFilterPart(parent, showMore);
+
+               Composite tableComp = new Composite(parent, SWT.NO_FOCUS);
+               tableComp.setLayoutData(EclipseUiUtils.fillAll());
+               usersViewer = createTableViewer(tableComp);
+               usersViewer.setContentProvider(new UsersContentProvider());
+       }
+
+       /** Enable access to the selected users or groups */
+       public List<User> getSelectedUsers() {
+               if (hasSelectionColumn) {
+                       Object[] elements = ((CheckboxTableViewer) usersViewer).getCheckedElements();
+
+                       List<User> result = new ArrayList<User>();
+                       for (Object obj : elements) {
+                               result.add((User) obj);
+                       }
+                       return result;
+               } else
+                       throw new EclipseUiException(
+                                       "Unvalid request: no selection column " + "has been created for the current table");
+       }
+
+       /** Returns the User table viewer, typically to add doubleclick listener */
+       public TableViewer getTableViewer() {
+               return usersViewer;
+       }
+
+       /**
+        * Force the refresh of the underlying table using the current filter string if
+        * relevant
+        */
+       public void refresh() {
+               String filter = hasFilter ? filterTxt.getText().trim() : null;
+               if ("".equals(filter))
+                       filter = null;
+               refreshFilteredList(filter);
+       }
+
+       /** Effective repository request: caller must implement this method */
+       abstract protected List<User> listFilteredElements(String filter);
+
+       // protected List<User> listFilteredElements(String filter) {
+       // List<User> users = new ArrayList<User>();
+       // try {
+       // Role[] roles = userAdmin.getRoles(filter);
+       // // Display all users and groups
+       // for (Role role : roles)
+       // users.add((User) role);
+       // } catch (InvalidSyntaxException e) {
+       // throw new EclipseUiException("Unable to get roles with filter: "
+       // + filter, e);
+       // }
+       // return users;
+       // }
+
+       /* GENERIC COMPOSITE METHODS */
+       @Override
+       public boolean setFocus() {
+               if (hasFilter)
+                       return filterTxt.setFocus();
+               else
+                       return usersViewer.getTable().setFocus();
+       }
+
+       @Override
+       public void dispose() {
+               super.dispose();
+       }
+
+       /* LOCAL CLASSES AND METHODS */
+       // Will be usefull to rather use a virtual table viewer
+       private void refreshFilteredList(String filter) {
+               List<User> users = listFilteredElements(filter);
+               usersViewer.setInput(users.toArray());
+       }
+
+       private class UsersContentProvider implements IStructuredContentProvider {
+               private static final long serialVersionUID = 1L;
+
+               public Object[] getElements(Object inputElement) {
+                       return (Object[]) inputElement;
+               }
+
+               public void dispose() {
+               }
+
+               public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+               }
+       }
+
+       /* MANAGE FILTER */
+       private void createFilterPart(Composite parent) {
+               // Text Area for the filter
+               filterTxt = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
+               filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+               filterTxt.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = 1L;
+
+                       public void modifyText(ModifyEvent event) {
+                               refreshFilteredList(filterTxt.getText());
+                       }
+               });
+       }
+
+       private void createStaticFilterPart(Composite parent, boolean showMore) {
+               Composite filterComp = new Composite(parent, SWT.NO_FOCUS);
+               filterComp.setLayout(new GridLayout(2, false));
+               filterComp.setLayoutData(EclipseUiUtils.fillWidth());
+               // generic search
+               filterTxt = new Text(filterComp, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL);
+               filterTxt.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+               // filterTxt.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL |
+               // GridData.HORIZONTAL_ALIGN_FILL));
+               filterTxt.addModifyListener(new ModifyListener() {
+                       private static final long serialVersionUID = 1L;
+
+                       public void modifyText(ModifyEvent event) {
+                               refreshFilteredList(filterTxt.getText());
+                       }
+               });
+
+               // add static filter abilities
+               Link moreLk = new Link(filterComp, SWT.NONE);
+               Composite staticFilterCmp = new Composite(filterComp, SWT.NO_FOCUS);
+               staticFilterCmp.setLayoutData(EclipseUiUtils.fillWidth(2));
+               populateStaticFilters(staticFilterCmp);
+
+               MoreLinkListener listener = new MoreLinkListener(moreLk, staticFilterCmp, showMore);
+               // initialise the layout
+               listener.refresh();
+               moreLk.addSelectionListener(listener);
+       }
+
+       /** Overwrite to add static filters */
+       protected void populateStaticFilters(Composite staticFilterCmp) {
+       }
+
+       // private void addMoreSL(final Link more) {
+       // more.addSelectionListener( }
+
+       private class MoreLinkListener extends SelectionAdapter {
+               private static final long serialVersionUID = -524987616510893463L;
+               private boolean isShown;
+               private final Composite staticFilterCmp;
+               private final Link moreLk;
+
+               public MoreLinkListener(Link moreLk, Composite staticFilterCmp, boolean isShown) {
+                       this.moreLk = moreLk;
+                       this.staticFilterCmp = staticFilterCmp;
+                       this.isShown = isShown;
+               }
+
+               @Override
+               public void widgetSelected(SelectionEvent e) {
+                       isShown = !isShown;
+                       refresh();
+               }
+
+               public void refresh() {
+                       GridData gd = (GridData) staticFilterCmp.getLayoutData();
+                       if (isShown) {
+                               moreLk.setText("<a> Less... </a>");
+                               gd.heightHint = SWT.DEFAULT;
+                       } else {
+                               moreLk.setText("<a> More... </a>");
+                               gd.heightHint = 0;
+                       }
+                       forceLayout();
+               }
+       }
+
+       private void forceLayout() {
+               LdifUsersTable.this.getParent().layout(true, true);
+       }
+
+       private TableViewer createTableViewer(final Composite parent) {
+
+               int style = tableStyle | SWT.H_SCROLL | SWT.V_SCROLL;
+               if (hasSelectionColumn)
+                       style = style | SWT.CHECK;
+               Table table = new Table(parent, style);
+               TableColumnLayout layout = new TableColumnLayout();
+
+               // TODO the table layout does not works with the scrolled form
+
+               if (preventTableLayout) {
+                       parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+                       table.setLayoutData(EclipseUiUtils.fillAll());
+               } else
+                       parent.setLayout(layout);
+
+               TableViewer viewer;
+               if (hasSelectionColumn)
+                       viewer = new CheckboxTableViewer(table);
+               else
+                       viewer = new TableViewer(table);
+               table.setLinesVisible(true);
+               table.setHeaderVisible(true);
+
+               TableViewerColumn column;
+               // int offset = 0;
+               if (hasSelectionColumn) {
+                       // offset = 1;
+                       column = ViewerUtils.createTableViewerColumn(viewer, "", SWT.NONE, 25);
+                       column.setLabelProvider(new ColumnLabelProvider() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public String getText(Object element) {
+                                       return null;
+                               }
+                       });
+                       layout.setColumnData(column.getColumn(), new ColumnWeightData(25, 25, false));
+
+                       SelectionAdapter selectionAdapter = new SelectionAdapter() {
+                               private static final long serialVersionUID = 1L;
+
+                               boolean allSelected = false;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       allSelected = !allSelected;
+                                       ((CheckboxTableViewer) usersViewer).setAllChecked(allSelected);
+                               }
+                       };
+                       column.getColumn().addSelectionListener(selectionAdapter);
+               }
+
+               // NodeViewerComparator comparator = new NodeViewerComparator();
+               // TODO enable the sort by click on the header
+               // int i = offset;
+               for (ColumnDefinition colDef : columnDefs)
+                       createTableColumn(viewer, layout, colDef);
+
+               // column = ViewerUtils.createTableViewerColumn(viewer,
+               // colDef.getHeaderLabel(), SWT.NONE, colDef.getColumnSize());
+               // column.setLabelProvider(new CLProvider(colDef.getPropertyName()));
+               // column.getColumn().addSelectionListener(
+               // JcrUiUtils.getNodeSelectionAdapter(i,
+               // colDef.getPropertyType(), colDef.getPropertyName(),
+               // comparator, viewer));
+               // i++;
+               // }
+
+               // IMPORTANT: initialize comparator before setting it
+               // JcrColumnDefinition firstCol = colDefs.get(0);
+               // comparator.setColumn(firstCol.getPropertyType(),
+               // firstCol.getPropertyName());
+               // viewer.setComparator(comparator);
+
+               return viewer;
+       }
+
+       /** Default creation of a column for a user table */
+       private TableViewerColumn createTableColumn(TableViewer tableViewer, TableColumnLayout layout,
+                       ColumnDefinition columnDef) {
+
+               boolean resizable = true;
+               TableViewerColumn tvc = new TableViewerColumn(tableViewer, SWT.NONE);
+               TableColumn column = tvc.getColumn();
+
+               column.setText(columnDef.getLabel());
+               column.setWidth(columnDef.getMinWidth());
+               column.setResizable(resizable);
+
+               ColumnLabelProvider lp = columnDef.getLabelProvider();
+               // add a reference to the display to enable font management
+               // if (lp instanceof UserAdminAbstractLP)
+               // ((UserAdminAbstractLP) lp).setDisplay(tableViewer.getTable()
+               // .getDisplay());
+               tvc.setLabelProvider(lp);
+
+               layout.setColumnData(column, new ColumnWeightData(columnDef.getWeight(), columnDef.getMinWidth(), resizable));
+
+               return tvc;
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/parts/package-info.java
new file mode 100644 (file)
index 0000000..9e93b11
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic SWT/JFace composites. */
+package org.argeo.eclipse.ui.parts;
\ No newline at end of file
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/ViewerUtils.java
new file mode 100644 (file)
index 0000000..8f4df17
--- /dev/null
@@ -0,0 +1,58 @@
+package org.argeo.eclipse.ui.util;
+
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * Centralise useful methods to manage JFace Table, Tree and TreeColumn viewers.
+ */
+public class ViewerUtils {
+
+       /**
+        * Creates a basic column for the given table. For the time being, we do not
+        * support movable columns.
+        */
+       public static TableColumn createColumn(Table parent, String name, int style, int width) {
+               TableColumn result = new TableColumn(parent, style);
+               result.setText(name);
+               result.setWidth(width);
+               result.setResizable(true);
+               return result;
+       }
+
+       /**
+        * Creates a TableViewerColumn for the given viewer. For the time being, we do
+        * not support movable columns.
+        */
+       public static TableViewerColumn createTableViewerColumn(TableViewer parent, String name, int style, int width) {
+               TableViewerColumn tvc = new TableViewerColumn(parent, style);
+               TableColumn column = tvc.getColumn();
+               column.setText(name);
+               column.setWidth(width);
+               column.setResizable(true);
+               return tvc;
+       }
+
+       // public static TableViewerColumn createTableViewerColumn(TableViewer parent,
+       // Localized name, int style, int width) {
+       // return createTableViewerColumn(parent, name.lead(), style, width);
+       // }
+
+       /**
+        * Creates a TreeViewerColumn for the given viewer. For the time being, we do
+        * not support movable columns.
+        */
+       public static TreeViewerColumn createTreeViewerColumn(TreeViewer parent, String name, int style, int width) {
+               TreeViewerColumn tvc = new TreeViewerColumn(parent, style);
+               TreeColumn column = tvc.getColumn();
+               column.setText(name);
+               column.setWidth(width);
+               column.setResizable(true);
+               return tvc;
+       }
+}
diff --git a/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java b/swt/org.argeo.cms.swt/src/org/argeo/eclipse/ui/util/package-info.java
new file mode 100644 (file)
index 0000000..798d174
--- /dev/null
@@ -0,0 +1,2 @@
+/** Generic SWT/JFace JCR helpers. */
+package org.argeo.eclipse.ui.util;
\ No newline at end of file
diff --git a/swt/rap/org.argeo.cms.e4.rap/.classpath b/swt/rap/org.argeo.cms.e4.rap/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /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-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/rap/org.argeo.cms.e4.rap/.project b/swt/rap/org.argeo.cms.e4.rap/.project
new file mode 100644 (file)
index 0000000..40c9e01
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.e4.rap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore b/swt/rap/org.argeo.cms.e4.rap/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml b/swt/rap/org.argeo.cms.e4.rap/OSGI-INF/cms-admin-rap.xml
new file mode 100644 (file)
index 0000000..1f688ba
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" configuration-policy="optional" name="CMS Admin RAP">
+   <implementation class="org.argeo.cms.e4.rap.CmsE4AdminApp"/>
+   <service>
+      <provide interface="org.eclipse.rap.rwt.application.ApplicationConfiguration"/>
+      <property name="contextName" type="String" value="cms"/>
+   </service>
+</scr:component>
diff --git a/swt/rap/org.argeo.cms.e4.rap/bnd.bnd b/swt/rap/org.argeo.cms.e4.rap/bnd.bnd
new file mode 100644 (file)
index 0000000..5bbe4bc
--- /dev/null
@@ -0,0 +1,9 @@
+Bundle-ActivationPolicy: lazy
+Service-Component: OSGI-INF/cms-admin-rap.xml
+
+Import-Package: org.eclipse.swt,\
+org.eclipse.swt.graphics,\
+org.eclipse.e4.ui.workbench,\
+org.eclipse.rap.rwt.client,\
+org.eclipse.nebula.widgets.richtext;resolution:=optional,\
+*
diff --git a/swt/rap/org.argeo.cms.e4.rap/build.properties b/swt/rap/org.argeo.cms.e4.rap/build.properties
new file mode 100644 (file)
index 0000000..c58ea21
--- /dev/null
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               OSGI-INF/
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java
new file mode 100644 (file)
index 0000000..5fe22ae
--- /dev/null
@@ -0,0 +1,139 @@
+package org.argeo.cms.e4.rap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.eclipse.rap.e4.E4ApplicationConfig;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.application.ExceptionHandler;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.osgi.framework.BundleContext;
+
+/** Base class for CMS RAP applications. */
+public abstract class AbstractRapE4App implements ApplicationConfiguration {
+       private String e4Xmi;
+       private String path;
+       private String lifeCycleUri = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle";
+
+       private Map<String, String> baseProperties = new HashMap<String, String>();
+
+       private BundleContext bundleContext;
+       public final static String CONTEXT_NAME_PROPERTY = "contextName";
+       private String contextName;
+
+       /**
+        * To be overridden in order to add multiple entry points, directly or using
+        * {@link #addE4EntryPoint(Application, String, String, Map)}.
+        */
+       protected void addEntryPoints(Application application) {
+       }
+
+       public void configure(Application application) {
+               application.setExceptionHandler(new ExceptionHandler() {
+
+                       @Override
+                       public void handleException(Throwable throwable) {
+                               CmsFeedback.show("Unexpected RWT exception", throwable);
+                       }
+               });
+
+               if (e4Xmi != null) {// backward compatibility
+                       addE4EntryPoint(application, path, e4Xmi, getBaseProperties());
+               } else {
+                       addEntryPoints(application);
+               }
+       }
+
+       protected Map<String, String> getBaseProperties() {
+               return baseProperties;
+       }
+
+//     protected void addEntryPoint(Application application, E4ApplicationConfig config, Map<String, String> properties) {
+//             CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config);
+//             application.addEntryPoint(path, entryPointFactory, properties);
+//             application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+//     }
+
+       protected void addE4EntryPoint(Application application, String path, String e4Xmi, Map<String, String> properties) {
+               E4ApplicationConfig config = createE4ApplicationConfig(e4Xmi);
+               CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(config);
+               application.addEntryPoint(path, entryPointFactory, properties);
+               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+       }
+
+       /**
+        * To be overridden for further configuration.
+        * 
+        * @see E4ApplicationConfig
+        */
+       protected E4ApplicationConfig createE4ApplicationConfig(String e4Xmi) {
+               return new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true);
+       }
+
+       @Deprecated
+       public void setPageTitle(String pageTitle) {
+               if (pageTitle != null)
+                       baseProperties.put(WebClient.PAGE_TITLE, pageTitle);
+       }
+
+       /** Returns a new map used to customise and entry point. */
+       public Map<String, String> customise(String pageTitle) {
+               Map<String, String> custom = new HashMap<>(getBaseProperties());
+               if (pageTitle != null)
+                       custom.put(WebClient.PAGE_TITLE, pageTitle);
+               return custom;
+       }
+
+       @Deprecated
+       public void setE4Xmi(String e4Xmi) {
+               this.e4Xmi = e4Xmi;
+       }
+
+       @Deprecated
+       public void setPath(String path) {
+               this.path = path;
+       }
+
+       public void setLifeCycleUri(String lifeCycleUri) {
+               this.lifeCycleUri = lifeCycleUri;
+       }
+
+       protected BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       protected void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       public String getContextName() {
+               return contextName;
+       }
+
+       public void setContextName(String contextName) {
+               this.contextName = contextName;
+       }
+
+       public void init(BundleContext bundleContext, Map<String, Object> properties) {
+               this.bundleContext = bundleContext;
+               for (String key : properties.keySet()) {
+                       Object value = properties.get(key);
+                       if (value != null)
+                               baseProperties.put(key, value.toString());
+               }
+
+               if (properties.containsKey(CONTEXT_NAME_PROPERTY)) {
+                       assert properties.get(CONTEXT_NAME_PROPERTY) != null;
+                       contextName = properties.get(CONTEXT_NAME_PROPERTY).toString();
+               } else {
+                       contextName = "<unknown context>";
+               }
+       }
+
+       public void destroy(Map<String, Object> properties) {
+
+       }
+}
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4AdminApp.java
new file mode 100644 (file)
index 0000000..66be1e8
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.cms.e4.rap;
+
+import org.eclipse.rap.rwt.application.Application;
+
+/**
+ * Access to canonical views of the core CMS concepts, useful for devleopers and
+ * operators.
+ */
+public class CmsE4AdminApp extends AbstractRapE4App {
+       @Override
+       protected void addEntryPoints(Application application) {
+               addE4EntryPoint(application, "/devops", "org.argeo.cms.e4/e4xmi/cms-devops.e4xmi",
+                               customise("Argeo CMS DevOps"));
+               addE4EntryPoint(application, "/ego", "org.argeo.cms.e4/e4xmi/cms-ego.e4xmi", customise("Argeo CMS Ego"));
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java
new file mode 100644 (file)
index 0000000..a5a3234
--- /dev/null
@@ -0,0 +1,76 @@
+package org.argeo.cms.e4.rap;
+
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.rap.e4.E4ApplicationConfig;
+import org.eclipse.rap.e4.E4EntryPointFactory;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+
+public class CmsE4EntryPointFactory extends E4EntryPointFactory {
+       public final static String DEFAULT_LIFECYCLE_URI = "bundleclass://org.argeo.cms.e4.rap/org.argeo.cms.e4.rap.CmsLoginLifecycle";
+
+       public CmsE4EntryPointFactory(E4ApplicationConfig config) {
+               super(config);
+       }
+
+       public CmsE4EntryPointFactory(String e4Xmi, String lifeCycleUri) {
+               super(defaultConfig(e4Xmi, lifeCycleUri));
+       }
+
+       public CmsE4EntryPointFactory(String e4Xmi) {
+               this(e4Xmi, DEFAULT_LIFECYCLE_URI);
+       }
+
+       public static E4ApplicationConfig defaultConfig(String e4Xmi, String lifeCycleUri) {
+               E4ApplicationConfig config = new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, null, false, true, true);
+               return config;
+       }
+
+       @Override
+       public EntryPoint create() {
+               EntryPoint ep = createEntryPoint();
+               EntryPoint authEp = new EntryPoint() {
+
+                       @Override
+                       public int createUI() {
+                               Subject subject = new Subject();
+                               return Subject.doAs(subject, new PrivilegedAction<Integer>() {
+
+                                       @Override
+                                       public Integer run() {
+                                               // SPNEGO
+                                               // HttpServletRequest request = RWT.getRequest();
+                                               // String authorization = request.getHeader(HEADER_AUTHORIZATION);
+                                               // if (authorization == null || !authorization.startsWith("Negotiate")) {
+                                               // HttpServletResponse response = RWT.getResponse();
+                                               // response.setStatus(401);
+                                               // response.setHeader(HEADER_WWW_AUTHENTICATE, "Negotiate");
+                                               // response.setDateHeader("Date", System.currentTimeMillis());
+                                               // response.setDateHeader("Expires", System.currentTimeMillis() + (24 * 60 * 60
+                                               // * 1000));
+                                               // response.setHeader("Accept-Ranges", "bytes");
+                                               // response.setHeader("Connection", "Keep-Alive");
+                                               // response.setHeader("Keep-Alive", "timeout=5, max=97");
+                                               // // response.setContentType("text/html; charset=UTF-8");
+                                               // }
+
+                                               JavaScriptExecutor jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
+                                               Integer exitCode = ep.createUI();
+                                               jsExecutor.execute("location.reload()");
+                                               return exitCode;
+                                       }
+
+                               });
+                       }
+               };
+               return authEp;
+       }
+
+       protected EntryPoint createEntryPoint() {
+               return super.create();
+       }
+}
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java
new file mode 100644 (file)
index 0000000..471cdec
--- /dev/null
@@ -0,0 +1,183 @@
+package org.argeo.cms.e4.rap;
+
+import java.security.AccessController;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SimpleSwtUxContext;
+import org.argeo.cms.swt.auth.CmsLoginShell;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.ui.util.SimpleImageManager;
+import org.eclipse.e4.core.services.events.IEventBroker;
+import org.eclipse.e4.ui.workbench.UIEvents;
+import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
+import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.service.BrowserNavigation;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+@SuppressWarnings("restriction")
+public class CmsLoginLifecycle implements CmsView {
+       private final static CmsLog log = CmsLog.getLog(CmsLoginLifecycle.class);
+
+       private UxContext uxContext;
+       private CmsImageManager imageManager;
+
+       private LoginContext loginContext;
+       private BrowserNavigation browserNavigation;
+
+       private String state = null;
+       private String uid;
+
+       @PostContextCreate
+       boolean login(final IEventBroker eventBroker) {
+               uid = UUID.randomUUID().toString();
+               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
+               if (browserNavigation != null)
+                       browserNavigation.addBrowserNavigationListener(new BrowserNavigationListener() {
+                               private static final long serialVersionUID = -3668136623771902865L;
+
+                               @Override
+                               public void navigated(BrowserNavigationEvent event) {
+                                       state = event.getState();
+                                       if (uxContext != null)// is logged in
+                                               stateChanged();
+                               }
+                       });
+
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               Display display = Display.getCurrent();
+//             UiContext.setData(CmsView.KEY, this);
+               // FIXME get CMS context
+               CmsLoginShell loginShell = new CmsLoginShell(this, null);
+               CmsSwtUtils.registerCmsView(loginShell.getShell(), this);
+               loginShell.setSubject(subject);
+               try {
+                       // try pre-auth
+                       loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, subject, loginShell);
+                       loginContext.login();
+               } catch (LoginException e) {
+                       loginShell.createUi();
+                       loginShell.open();
+
+                       while (!loginShell.getShell().isDisposed()) {
+                               if (!display.readAndDispatch())
+                                       display.sleep();
+                       }
+               }
+               if (CurrentUser.getUsername(getSubject()) == null)
+                       return false;
+               uxContext = new SimpleSwtUxContext();
+               imageManager = new SimpleImageManager();
+
+               eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new EventHandler() {
+                       @Override
+                       public void handleEvent(Event event) {
+                               startupComplete();
+                               eventBroker.unsubscribe(this);
+                       }
+               });
+
+               // lcs.changeApplicationLocale(Locale.FRENCH);
+               return true;
+       }
+
+       @PreSave
+       void destroy() {
+               // logout();
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+
+       @Override
+       public void navigateTo(String state) {
+               browserNavigation.pushState(state, state);
+       }
+
+       @Override
+       public void authChange(LoginContext loginContext) {
+               if (loginContext == null)
+                       throw new IllegalArgumentException("Login context cannot be null");
+               // logout previous login context
+               // if (this.loginContext != null)
+               // try {
+               // this.loginContext.logout();
+               // } catch (LoginException e1) {
+               // System.err.println("Could not log out: " + e1);
+               // }
+               this.loginContext = loginContext;
+       }
+
+       @Override
+       public void logout() {
+               if (loginContext == null)
+                       throw new IllegalStateException("Login context should not be null");
+               try {
+                       CurrentUser.logoutCmsSession(loginContext.getSubject());
+                       loginContext.logout();
+               } catch (LoginException e) {
+                       throw new IllegalStateException("Cannot log out", e);
+               }
+       }
+
+       @Override
+       public void exception(Throwable e) {
+               String msg = "Unexpected exception in Eclipse 4 RAP";
+               log.error(msg, e);
+               CmsFeedback.show(msg, e);
+       }
+
+       @Override
+       public CmsImageManager getImageManager() {
+               return imageManager;
+       }
+
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return CurrentUser.isAnonymous(getSubject());
+       }
+
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+       // CALLBACKS
+       protected void startupComplete() {
+       }
+
+       protected void stateChanged() {
+
+       }
+
+       // GETTERS
+       protected BrowserNavigation getBrowserNavigation() {
+               return browserNavigation;
+       }
+
+       protected String getState() {
+               return state;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/SimpleRapE4App.java
new file mode 100644 (file)
index 0000000..1bca333
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.cms.e4.rap;
+
+import java.util.Enumeration;
+
+import org.apache.commons.io.FilenameUtils;
+import org.argeo.api.cms.CmsLog;
+import org.eclipse.rap.rwt.application.Application;
+import org.osgi.framework.Bundle;
+
+/** Simple RAP app which loads all e4xmi files. */
+public class SimpleRapE4App extends AbstractRapE4App {
+       private final static CmsLog log = CmsLog.getLog(SimpleRapE4App.class);
+
+       private String baseE4xmi = "/e4xmi";
+
+       @Override
+       protected void addEntryPoints(Application application) {
+               Bundle bundle = getBundleContext().getBundle();
+               Enumeration<String> paths = bundle.getEntryPaths(baseE4xmi);
+               while (paths.hasMoreElements()) {
+                       String p = paths.nextElement();
+                       if (p.endsWith(".e4xmi")) {
+                               String e4xmiPath = bundle.getSymbolicName() + '/' + p;
+                               String name = '/' + FilenameUtils.removeExtension(FilenameUtils.getName(p));
+                               addE4EntryPoint(application, name, e4xmiPath, getBaseProperties());
+                               if (log.isDebugEnabled())
+                                       log.debug("Registered " + e4xmiPath + " as " + getContextName() + name);
+                       }
+               }
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/package-info.java
new file mode 100644 (file)
index 0000000..1122f19
--- /dev/null
@@ -0,0 +1,2 @@
+/** Eclipse 4 RAP specific extensions. */
+package org.argeo.cms.e4.rap;
\ No newline at end of file
diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/.classpath b/swt/rap/org.argeo.cms.swt.rap.cli/.classpath
new file mode 100644 (file)
index 0000000..81fe078
--- /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-17"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/.project b/swt/rap/org.argeo.cms.swt.rap.cli/.project
new file mode 100644 (file)
index 0000000..7b3af9d
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.swt.rap.cli</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/swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd b/swt/rap/org.argeo.cms.swt.rap.cli/bnd.bnd
new file mode 100644 (file)
index 0000000..4dce79b
--- /dev/null
@@ -0,0 +1,8 @@
+Import-Package: \
+org.eclipse.jetty.util.component;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.http;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.io;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.security;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.server.handler;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\
+*
\ No newline at end of file
diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/build.properties b/swt/rap/org.argeo.cms.swt.rap.cli/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/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java
new file mode 100644 (file)
index 0000000..8d0fff4
--- /dev/null
@@ -0,0 +1,123 @@
+package org.argeo.cms.swt.rap.cli;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.cli.CommandsCli;
+import org.argeo.api.cli.DescribedCommand;
+import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsContext;
+import org.argeo.cms.runtime.StaticCms;
+import org.argeo.cms.swt.app.CmsUserApp;
+import org.argeo.cms.web.CmsWebApp;
+import org.argeo.util.register.Component;
+import org.argeo.util.register.ComponentRegister;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+
+public class CmsRapCli extends CommandsCli {
+
+       public CmsRapCli(String commandName) {
+               super(commandName);
+               addCommand("user", new Launch());
+       }
+
+       @Override
+       public String getDescription() {
+               return "Argeo CMS utilities.";
+       }
+
+       public static void main(String[] args) {
+               mainImpl(new CmsRapCli("web"), args);
+       }
+
+       static class Launch implements DescribedCommand<String> {
+               private Option dataOption;
+               private Option uiOption;
+
+               @Override
+               public Options getOptions() {
+                       Options options = new Options();
+                       dataOption = Option.builder().longOpt("data").hasArg().required()
+                                       .desc("path to the writable data area (mandatory)").build();
+                       uiOption = Option.builder().longOpt("ui").desc("open a user interface").build();
+                       options.addOption(dataOption);
+                       options.addOption(uiOption);
+                       return options;
+               }
+
+               @Override
+               public String apply(List<String> args) {
+                       CommandLine cl = toCommandLine(args);
+                       String dataPath = cl.getOptionValue(dataOption);
+                       boolean ui = cl.hasOption(uiOption);
+
+                       Path instancePath = Paths.get(dataPath);
+                       System.setProperty("osgi.instance.area", instancePath.toUri().toString());
+
+                       StaticCms staticCms = new StaticCms() {
+                               @Override
+                               protected void addComponents(ComponentRegister register) {
+                                       if (ui) {
+                                               CmsUserApp cmsApp = new CmsUserApp();
+                                               Component<CmsUserApp> cmsAppC = new Component.Builder<>(cmsApp) //
+                                                               .addType(CmsApp.class) //
+                                                               .addType(CmsUserApp.class) //
+                                                               .addDependency(register.getSingleton(CmsContext.class), cmsApp::setCmsContext, null) //
+                                                               .addDependency(register.getSingleton(ProvidedRepository.class),
+                                                                               cmsApp::setContentRepository, null) //
+                                                               .build(register);
+
+                                               CmsWebApp cmsWebApp = new CmsWebApp();
+                                               Component<CmsWebApp> cmsWebAppC = new Component.Builder<>(cmsWebApp) //
+                                                               .addType(ApplicationConfiguration.class) //
+                                                               .addDependency(cmsAppC.getType(CmsApp.class), cmsWebApp::setCmsApp, null) //
+                                                               .build(register);
+
+                                               RwtRunner rwtRunner = new RwtRunner();
+                                               Component<RwtRunner> rwtRunnerC = new Component.Builder<>(rwtRunner) //
+                                                               .addActivation(rwtRunner::init) //
+                                                               .addDeactivation(rwtRunner::destroy) //
+                                                               .addType(RwtRunner.class) //
+                                                               .addDependency(cmsWebAppC.getType(ApplicationConfiguration.class),
+                                                                               rwtRunner::setApplicationConfiguration, null) //
+                                                               .build(register);
+                                       }
+                               }
+
+                       };
+                       Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown"));
+                       staticCms.start();
+
+                       long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+                       System.out.println("Static CMS available in " + jvmUptime + " ms.");
+
+                       if (ui) {
+                               try {
+                                       // open browser in app mode
+                                       Thread.sleep(2000);// wait for RWT to be ready
+                                       Runtime.getRuntime().exec("google-chrome --app=http://localhost:"
+                                                       + staticCms.getComponentRegister().getObject(RwtRunner.class).getEffectivePort() + "/data");
+                               } catch (InterruptedException | IOException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+
+                       staticCms.waitForStop();
+
+                       return null;
+               }
+
+               @Override
+               public String getDescription() {
+                       return "Launch a static CMS.";
+               }
+
+       }
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RwtRunner.java
new file mode 100644 (file)
index 0000000..6b678a8
--- /dev/null
@@ -0,0 +1,142 @@
+package org.argeo.cms.swt.rap.cli;
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.rap.rwt.application.AbstractEntryPoint;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.application.ApplicationRunner;
+import org.eclipse.rap.rwt.engine.RWTServlet;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+/** A minimal RWT runner based on embedded Jetty. */
+public class RwtRunner {
+
+       private final Server server;
+       private final ServerConnector serverConnector;
+       private Path tempDir;
+
+       private ApplicationConfiguration applicationConfiguration;
+
+       public RwtRunner() {
+               server = new Server(new QueuedThreadPool(10, 1));
+               serverConnector = new ServerConnector(server);
+               serverConnector.setPort(0);
+               server.setConnectors(new Connector[] { serverConnector });
+       }
+
+       protected Control createUi(Composite parent, Object context) {
+               return new Label(parent, 0);
+       }
+
+       public void init() {
+               Objects.requireNonNull(applicationConfiguration);
+
+               ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+               context.setContextPath("/");
+               server.setHandler(context);
+
+               String entryPoint = "data";
+
+               // rwt-resources requires a file system
+               try {
+                       tempDir = Files.createTempDirectory("argeo-rwtRunner");
+                       context.setBaseResource(Resource.newResource(tempDir.resolve("www").toString()));
+               } catch (IOException e) {
+                       throw new IllegalStateException("Cannot create temporary directory", e);
+               }
+               context.addEventListener(new ServletContextListener() {
+                       ApplicationRunner applicationRunner;
+
+                       @Override
+                       public void contextInitialized(ServletContextEvent sce) {
+                               applicationRunner = new ApplicationRunner(applicationConfiguration, sce.getServletContext());
+                               applicationRunner.start();
+                       }
+
+                       @Override
+                       public void contextDestroyed(ServletContextEvent sce) {
+                               applicationRunner.stop();
+                       }
+               });
+
+               context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint);
+
+               // Required to serve rwt-resources. It is important that this is last.
+               ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
+               context.addServlet(holderPwd, "/");
+
+               try {
+                       server.start();
+               } catch (Exception e) {
+                       throw new IllegalStateException("Cannot start Jetty server", e);
+               }
+               Runtime.getRuntime().addShutdownHook(new Thread(() -> destroy(), "Jetty shutdown"));
+
+               long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+               System.out.println("RWT App available in " + jvmUptime + " ms, on port " + getEffectivePort());
+       }
+
+       public void destroy() {
+               try {
+                       serverConnector.close();
+                       server.stop();
+                       // TODO delete temp dir
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+       public Integer getEffectivePort() {
+               return serverConnector.getLocalPort();
+       }
+
+       public void waitFor() throws InterruptedException {
+               server.join();
+       }
+
+       public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) {
+               this.applicationConfiguration = applicationConfiguration;
+       }
+
+       public static void main(String[] args) throws Exception {
+               RwtRunner rwtRunner = new RwtRunner();
+
+               String entryPoint = "app";
+               ApplicationConfiguration applicationConfiguration = (application) -> application.addEntryPoint("/" + entryPoint,
+                               () -> new AbstractEntryPoint() {
+                                       private static final long serialVersionUID = 5678385921969090733L;
+
+                                       @Override
+                                       protected void createContents(Composite parent) {
+                                               Label label = new Label(parent, 0);
+                                               label.setText("Hello world!");
+                                       }
+                               }, null);
+
+               rwtRunner.setApplicationConfiguration(applicationConfiguration);
+               rwtRunner.init();
+
+               // open browser in app mode
+               Thread.sleep(2000);// wait for RWT to be ready
+               Runtime.getRuntime().exec("google-chrome --app=http://localhost:" + rwtRunner.getEffectivePort() + "/app");
+
+               rwtRunner.waitFor();
+       }
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/.classpath b/swt/rap/org.argeo.cms.swt.rap/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /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-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/rap/org.argeo.cms.swt.rap/.project b/swt/rap/org.argeo.cms.swt.rap/.project
new file mode 100644 (file)
index 0000000..6301604
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.ui.rap</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore b/swt/rap/org.argeo.cms.swt.rap/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml b/swt/rap/org.argeo.cms.swt.rap/OSGI-INF/cmsWebAppFactory.xml
new file mode 100644 (file)
index 0000000..aa7e0ad
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="CMS Web App Factory">
+   <implementation class="org.argeo.cms.web.osgi.CmsWebAppFactory"/>
+   <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
+   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
+</scr:component>
diff --git a/swt/rap/org.argeo.cms.swt.rap/bnd.bnd b/swt/rap/org.argeo.cms.swt.rap/bnd.bnd
new file mode 100644 (file)
index 0000000..db2ac0b
--- /dev/null
@@ -0,0 +1,12 @@
+Import-Package:\
+org.argeo.api.acr,\
+org.eclipse.swt,\
+org.argeo.eclipse.ui,\
+javax.jcr.nodetype,\
+javax.jcr.security,\
+org.eclipse.swt.graphics,\
+javax.servlet.*;version="[3,5)",\
+*
+
+Service-Component: OSGI-INF/cmsWebAppFactory.xml
+
diff --git a/swt/rap/org.argeo.cms.swt.rap/build.properties b/swt/rap/org.argeo.cms.swt.rap/build.properties
new file mode 100644 (file)
index 0000000..2416e52
--- /dev/null
@@ -0,0 +1,7 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               OSGI-INF/cmsWebAppFactory.xml
+source.. = src/
+additional.bundles = org.argeo.ext.slf4j,\
+                     org.slf4j.api
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java
new file mode 100644 (file)
index 0000000..01ebb23
--- /dev/null
@@ -0,0 +1,268 @@
+package org.argeo.cms.ui.script;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.script.Invocable;
+import javax.script.ScriptException;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.Selected;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsPane;
+import org.argeo.cms.web.SimpleErgonomics;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.application.EntryPointFactory;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.osgi.framework.BundleContext;
+
+public class AppUi implements CmsUiProvider, Branding {
+       private final CmsScriptApp app;
+
+       private CmsUiProvider ui;
+       private String createUi;
+       private Object impl;
+       private String script;
+       // private Branding branding = new Branding();
+
+       private EntryPointFactory factory;
+
+       // Branding
+       private String themeId;
+       private String additionalHeaders;
+       private String bodyHtml;
+       private String pageTitle;
+       private String pageOverflow;
+       private String favicon;
+
+       public AppUi(CmsScriptApp app) {
+               this.app = app;
+       }
+
+       public AppUi(CmsScriptApp app, String scriptPath) {
+               this.app = app;
+               this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC),
+                               app.getScriptEngine(), scriptPath);
+       }
+
+       public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) {
+               this.app = app;
+               this.ui = uiProvider;
+       }
+
+       public AppUi(CmsScriptApp app, EntryPointFactory factory) {
+               this.app = app;
+               this.factory = factory;
+       }
+
+       public void apply(Repository repository, Application application, Branding appBranding, String path) {
+               Map<String, String> factoryProperties = new HashMap<>();
+               if (appBranding != null)
+                       appBranding.applyBranding(factoryProperties);
+               applyBranding(factoryProperties);
+               if (factory != null) {
+                       application.addEntryPoint("/" + path, factory, factoryProperties);
+               } else {
+                       EntryPointFactory entryPointFactory = new EntryPointFactory() {
+                               @Override
+                               public EntryPoint create() {
+                                       SimpleErgonomics ergonomics = new SimpleErgonomics(repository, CmsConstants.SYS_WORKSPACE,
+                                                       "/home/root/argeo:keyring", AppUi.this, factoryProperties);
+//                                     CmsUiProvider header = app.getHeader();
+//                                     if (header != null)
+//                                             ergonomics.setHeader(header);
+                                       app.applySides(ergonomics);
+                                       Integer headerHeight = app.getHeaderHeight();
+                                       if (headerHeight != null)
+                                               ergonomics.setHeaderHeight(headerHeight);
+                                       return ergonomics;
+                               }
+                       };
+                       application.addEntryPoint("/" + path, entryPointFactory, factoryProperties);
+               }
+       }
+
+       public void setUi(CmsUiProvider uiProvider) {
+               this.ui = uiProvider;
+       }
+
+       public void applyBranding(Map<String, String> properties) {
+               if (themeId != null)
+                       properties.put(WebClient.THEME_ID, themeId);
+               if (additionalHeaders != null)
+                       properties.put(WebClient.HEAD_HTML, additionalHeaders);
+               if (bodyHtml != null)
+                       properties.put(WebClient.BODY_HTML, bodyHtml);
+               if (pageTitle != null)
+                       properties.put(WebClient.PAGE_TITLE, pageTitle);
+               if (pageOverflow != null)
+                       properties.put(WebClient.PAGE_OVERFLOW, pageOverflow);
+               if (favicon != null)
+                       properties.put(WebClient.FAVICON, favicon);
+       }
+
+       // public Branding getBranding() {
+       // return branding;
+       // }
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               CmsPane cmsPane = new CmsPane(parent, SWT.NONE);
+
+               if (false) {
+                       // QA
+                       CmsSwtUtils.style(cmsPane.getQaArea(), "qa");
+                       Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT);
+                       CmsSwtUtils.style(reload, "qa");
+                       reload.setText("Reload");
+                       reload.addSelectionListener(new Selected() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       new Thread() {
+                                               @Override
+                                               public void run() {
+                                                       app.reload();
+                                               }
+                                       }.start();
+                                       RWT.getClient().getService(JavaScriptExecutor.class)
+                                                       .execute("setTimeout('location.reload()',1000)");
+                               }
+                       });
+
+                       // Support
+                       CmsSwtUtils.style(cmsPane.getSupportArea(), "support");
+                       Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE);
+                       CmsSwtUtils.style(msg, "support");
+                       msg.setText("UNSUPPORTED DEVELOPMENT VERSION");
+               }
+
+               if (ui != null) {
+                       ui.createUi(cmsPane.getMainArea(), context);
+               }
+               if (createUi != null) {
+                       Invocable invocable = (Invocable) app.getScriptEngine();
+                       try {
+                               invocable.invokeFunction(createUi, cmsPane.getMainArea(), context);
+
+                       } catch (NoSuchMethodException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       } catch (ScriptException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+               }
+               if (impl != null) {
+                       Invocable invocable = (Invocable) app.getScriptEngine();
+                       try {
+                               invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context);
+
+                       } catch (NoSuchMethodException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       } catch (ScriptException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+               }
+
+               // Invocable invocable = (Invocable) app.getScriptEngine();
+               // try {
+               // invocable.invokeMethod(AppUi.this, "initUi", parent, context);
+               //
+               // } catch (NoSuchMethodException e) {
+               // // TODO Auto-generated catch block
+               // e.printStackTrace();
+               // } catch (ScriptException e) {
+               // // TODO Auto-generated catch block
+               // e.printStackTrace();
+               // }
+
+               return null;
+       }
+
+       public void setCreateUi(String createUi) {
+               this.createUi = createUi;
+       }
+
+       public void setImpl(Object impl) {
+               this.impl = impl;
+       }
+
+       public Object getImpl() {
+               return impl;
+       }
+
+       public String getScript() {
+               return script;
+       }
+
+       public void setScript(String script) {
+               this.script = script;
+       }
+
+       // Branding
+       public String getThemeId() {
+               return themeId;
+       }
+
+       public void setThemeId(String themeId) {
+               this.themeId = themeId;
+       }
+
+       public String getAdditionalHeaders() {
+               return additionalHeaders;
+       }
+
+       public void setAdditionalHeaders(String additionalHeaders) {
+               this.additionalHeaders = additionalHeaders;
+       }
+
+       public String getBodyHtml() {
+               return bodyHtml;
+       }
+
+       public void setBodyHtml(String bodyHtml) {
+               this.bodyHtml = bodyHtml;
+       }
+
+       public String getPageTitle() {
+               return pageTitle;
+       }
+
+       public void setPageTitle(String pageTitle) {
+               this.pageTitle = pageTitle;
+       }
+
+       public String getPageOverflow() {
+               return pageOverflow;
+       }
+
+       public void setPageOverflow(String pageOverflow) {
+               this.pageOverflow = pageOverflow;
+       }
+
+       public String getFavicon() {
+               return favicon;
+       }
+
+       public void setFavicon(String favicon) {
+               this.favicon = favicon;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java
new file mode 100644 (file)
index 0000000..f72338e
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.cms.ui.script;
+
+import java.util.Map;
+
+public interface Branding {
+       public void applyBranding(Map<String, String> properties);
+
+       public String getThemeId();
+
+       public String getAdditionalHeaders();
+
+       public String getBodyHtml();
+
+       public String getPageTitle();
+
+       public String getPageOverflow();
+
+       public String getFavicon();
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java
new file mode 100644 (file)
index 0000000..6b3a670
--- /dev/null
@@ -0,0 +1,421 @@
+package org.argeo.cms.ui.script;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.script.ScriptEngine;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.ui.CmsUiConstants;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.web.BundleResourceLoader;
+import org.argeo.cms.web.SimpleErgonomics;
+import org.argeo.cms.web.WebThemeUtils;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.application.ExceptionHandler;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+
+public class CmsScriptApp implements Branding {
+       public final static String CONTEXT_NAME = "contextName";
+
+       ServiceRegistration<ApplicationConfiguration> appConfigReg;
+
+       private ScriptEngine scriptEngine;
+
+       private final static CmsLog log = CmsLog.getLog(CmsScriptApp.class);
+
+       private String webPath;
+       private String repo = "(cn=node)";
+
+       // private Branding branding = new Branding();
+       private CmsTheme theme;
+
+       private List<String> resources = new ArrayList<>();
+
+       private Map<String, AppUi> ui = new HashMap<>();
+
+       private CmsUiProvider header;
+       private Integer headerHeight = null;
+       private CmsUiProvider lead;
+       private CmsUiProvider end;
+       private CmsUiProvider footer;
+
+       // Branding
+       private String themeId;
+       private String additionalHeaders;
+       private String bodyHtml;
+       private String pageTitle;
+       private String pageOverflow;
+       private String favicon;
+
+       public CmsScriptApp(ScriptEngine scriptEngine) {
+               super();
+               this.scriptEngine = scriptEngine;
+       }
+
+       public void apply(BundleContext bundleContext, Repository repository, Application application) {
+               BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
+
+               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+               // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
+
+               application.setExceptionHandler(new CmsExceptionHandler());
+
+               // loading animated gif
+               application.addResource(CmsUiConstants.LOADING_IMAGE, createResourceLoader(CmsUiConstants.LOADING_IMAGE));
+               // empty image
+               application.addResource(CmsUiConstants.NO_IMAGE, createResourceLoader(CmsUiConstants.NO_IMAGE));
+
+               for (String resource : resources) {
+                       application.addResource(resource, bundleRL);
+                       if (log.isTraceEnabled())
+                               log.trace("Resource " + resource);
+               }
+
+               if (theme != null) {
+                       WebThemeUtils.apply(application, theme);
+                       String themeHeaders = theme.getHtmlHeaders();
+                       if (themeHeaders != null) {
+                               if (additionalHeaders == null)
+                                       additionalHeaders = themeHeaders;
+                               else
+                                       additionalHeaders = themeHeaders + "\n" + additionalHeaders;
+                       }
+                       themeId = theme.getThemeId();
+               }
+
+               // client JavaScript
+               Bundle appBundle = bundleRL.getBundle();
+               BundleContext bc = appBundle.getBundleContext();
+               HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class));
+               HttpContext httpContext = new BundleHttpContext(bc);
+               Enumeration<URL> themeResources = appBundle.findEntries("/js/", "*", true);
+               if (themeResources != null)
+                       bundleResources: while (themeResources.hasMoreElements()) {
+                               try {
+                                       String name = themeResources.nextElement().getPath();
+                                       if (name.endsWith("/"))
+                                               continue bundleResources;
+                                       String alias = "/" + getWebPath() + name;
+
+                                       httpService.registerResources(alias, name, httpContext);
+                                       if (log.isDebugEnabled())
+                                               log.debug("Mapped " + name + " to alias " + alias);
+
+                               } catch (NamespaceException e) {
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               }
+                       }
+
+               // App UIs
+               for (String appUiName : ui.keySet()) {
+                       AppUi appUi = ui.get(appUiName);
+                       appUi.apply(repository, application, this, appUiName);
+
+               }
+
+       }
+
+       public void applySides(SimpleErgonomics simpleErgonomics) {
+               simpleErgonomics.setHeader(header);
+               simpleErgonomics.setLead(lead);
+               simpleErgonomics.setEnd(end);
+               simpleErgonomics.setFooter(footer);
+       }
+
+       public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) {
+               Hashtable<String, String> props = new Hashtable<>();
+               props.put(CONTEXT_NAME, webPath);
+               appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props);
+       }
+
+       public void reload() {
+               BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext();
+               ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference());
+               appConfigReg.unregister();
+               register(bundleContext, appConfig);
+
+               // BundleContext bundleContext = (BundleContext)
+               // getScriptEngine().get("bundleContext");
+               // try {
+               // Bundle bundle = bundleContext.getBundle();
+               // bundle.stop();
+               // bundle.start();
+               // } catch (BundleException e) {
+               // // TODO Auto-generated catch block
+               // e.printStackTrace();
+               // }
+       }
+
+       private static ResourceLoader createResourceLoader(final String resourceName) {
+               return new ResourceLoader() {
+                       public InputStream getResourceAsStream(String resourceName) throws IOException {
+                               return getClass().getClassLoader().getResourceAsStream(resourceName);
+                       }
+               };
+       }
+
+       public List<String> getResources() {
+               return resources;
+       }
+
+       public AppUi newUi(String name) {
+               if (ui.containsKey(name))
+                       throw new IllegalArgumentException("There is already an UI named " + name);
+               AppUi appUi = new AppUi(this);
+               // appUi.setApp(this);
+               ui.put(name, appUi);
+               return appUi;
+       }
+
+       public void addUi(String name, AppUi appUi) {
+               if (ui.containsKey(name))
+                       throw new IllegalArgumentException("There is already an UI named " + name);
+               // appUi.setApp(this);
+               ui.put(name, appUi);
+       }
+
+       public void applyBranding(Map<String, String> properties) {
+               if (themeId != null)
+                       properties.put(WebClient.THEME_ID, themeId);
+               if (additionalHeaders != null)
+                       properties.put(WebClient.HEAD_HTML, additionalHeaders);
+               if (bodyHtml != null)
+                       properties.put(WebClient.BODY_HTML, bodyHtml);
+               if (pageTitle != null)
+                       properties.put(WebClient.PAGE_TITLE, pageTitle);
+               if (pageOverflow != null)
+                       properties.put(WebClient.PAGE_OVERFLOW, pageOverflow);
+               if (favicon != null)
+                       properties.put(WebClient.FAVICON, favicon);
+       }
+
+       class CmsExceptionHandler implements ExceptionHandler {
+
+               @Override
+               public void handleException(Throwable throwable) {
+                       // TODO be smarter
+                       CmsUiUtils.getCmsView().exception(throwable);
+               }
+
+       }
+
+       // public Branding getBranding() {
+       // return branding;
+       // }
+
+       ScriptEngine getScriptEngine() {
+               return scriptEngine;
+       }
+
+       public static String toJson(Node node) {
+               try {
+                       StringBuilder sb = new StringBuilder();
+                       sb.append('{');
+                       PropertyIterator pit = node.getProperties();
+                       int count = 0;
+                       while (pit.hasNext()) {
+                               Property p = pit.nextProperty();
+                               int type = p.getType();
+                               if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) {
+                                       Node ref = p.getNode();
+                                       if (count != 0)
+                                               sb.append(',');
+                                       // TODO limit depth?
+                                       sb.append(toJson(ref));
+                                       count++;
+                               } else if (!p.isMultiple()) {
+                                       if (count != 0)
+                                               sb.append(',');
+                                       sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"');
+                                       count++;
+                               }
+                       }
+                       sb.append('}');
+                       return sb.toString();
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot convert " + node + " to JSON", e);
+               }
+       }
+
+       public void fromJson(Node node, String json) {
+               // TODO
+       }
+
+       public CmsTheme getTheme() {
+               return theme;
+       }
+
+       public void setTheme(CmsTheme theme) {
+               this.theme = theme;
+       }
+
+       public String getWebPath() {
+               return webPath;
+       }
+
+       public void setWebPath(String context) {
+               this.webPath = context;
+       }
+
+       public String getRepo() {
+               return repo;
+       }
+
+       public void setRepo(String repo) {
+               this.repo = repo;
+       }
+
+       public Map<String, AppUi> getUi() {
+               return ui;
+       }
+
+       public void setUi(Map<String, AppUi> ui) {
+               this.ui = ui;
+       }
+
+       // Branding
+       public String getThemeId() {
+               return themeId;
+       }
+
+       public void setThemeId(String themeId) {
+               this.themeId = themeId;
+       }
+
+       public String getAdditionalHeaders() {
+               return additionalHeaders;
+       }
+
+       public void setAdditionalHeaders(String additionalHeaders) {
+               this.additionalHeaders = additionalHeaders;
+       }
+
+       public String getBodyHtml() {
+               return bodyHtml;
+       }
+
+       public void setBodyHtml(String bodyHtml) {
+               this.bodyHtml = bodyHtml;
+       }
+
+       public String getPageTitle() {
+               return pageTitle;
+       }
+
+       public void setPageTitle(String pageTitle) {
+               this.pageTitle = pageTitle;
+       }
+
+       public String getPageOverflow() {
+               return pageOverflow;
+       }
+
+       public void setPageOverflow(String pageOverflow) {
+               this.pageOverflow = pageOverflow;
+       }
+
+       public String getFavicon() {
+               return favicon;
+       }
+
+       public void setFavicon(String favicon) {
+               this.favicon = favicon;
+       }
+
+       public CmsUiProvider getHeader() {
+               return header;
+       }
+
+       public void setHeader(CmsUiProvider header) {
+               this.header = header;
+       }
+
+       public Integer getHeaderHeight() {
+               return headerHeight;
+       }
+
+       public void setHeaderHeight(Integer headerHeight) {
+               this.headerHeight = headerHeight;
+       }
+
+       public CmsUiProvider getLead() {
+               return lead;
+       }
+
+       public void setLead(CmsUiProvider lead) {
+               this.lead = lead;
+       }
+
+       public CmsUiProvider getEnd() {
+               return end;
+       }
+
+       public void setEnd(CmsUiProvider end) {
+               this.end = end;
+       }
+
+       public CmsUiProvider getFooter() {
+               return footer;
+       }
+
+       public void setFooter(CmsUiProvider footer) {
+               this.footer = footer;
+       }
+
+       static class BundleHttpContext implements HttpContext {
+               private BundleContext bundleContext;
+
+               public BundleHttpContext(BundleContext bundleContext) {
+                       super();
+                       this.bundleContext = bundleContext;
+               }
+
+               @Override
+               public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                       // TODO Auto-generated method stub
+                       return true;
+               }
+
+               @Override
+               public URL getResource(String name) {
+
+                       return bundleContext.getBundle().getEntry(name);
+               }
+
+               @Override
+               public String getMimeType(String name) {
+                       return null;
+               }
+
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java
new file mode 100644 (file)
index 0000000..9879fb0
--- /dev/null
@@ -0,0 +1,120 @@
+package org.argeo.cms.ui.script;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+import javax.jcr.Repository;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.swt.CmsException;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class CmsScriptRwtApplication implements ApplicationConfiguration {
+       public final static String APP = "APP";
+       public final static String BC = "BC";
+
+       private final CmsLog log = CmsLog.getLog(CmsScriptRwtApplication.class);
+
+       BundleContext bundleContext;
+       Repository repository;
+
+       ScriptEngine engine;
+
+       public void init(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+               ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+               ClassLoader originalCcl = Thread.currentThread().getContextClassLoader();
+               try {
+//                     Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager
+//                     ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl);
+//                     engine = scriptEngineManager.getEngineByName("JavaScript");
+//                     if (engine == null) {// Nashorn
+//                             Thread.currentThread().setContextClassLoader(originalCcl);
+//                             scriptEngineManager = new ScriptEngineManager();
+//                             Thread.currentThread().setContextClassLoader(bundleCl);
+//                             engine = scriptEngineManager.getEngineByName("JavaScript");
+//                     }
+                       engine = loadScriptEngine(originalCcl, bundleCl);
+
+                       // Load script
+                       URL appUrl = bundleContext.getBundle().getEntry("cms/app.js");
+                       // System.out.println("Loading " + appUrl);
+                       // System.out.println("Loading " + appUrl.getHost());
+                       // System.out.println("Loading " + appUrl.getPath());
+
+                       CmsScriptApp app = new CmsScriptApp(engine);
+                       engine.put(APP, app);
+                       engine.put(BC, bundleContext);
+                       try (Reader reader = new InputStreamReader(appUrl.openStream())) {
+                               engine.eval(reader);
+                       } catch (IOException | ScriptException e) {
+                               throw new CmsException("Cannot execute " + appUrl, e);
+                       }
+
+                       if (log.isDebugEnabled())
+                               log.debug("CMS script app initialized from " + appUrl);
+
+               } catch (Exception e) {
+                       e.printStackTrace();
+               } finally {
+                       Thread.currentThread().setContextClassLoader(originalCcl);
+               }
+       }
+
+       public void destroy(BundleContext bundleContext) {
+               engine = null;
+       }
+
+       @Override
+       public void configure(Application application) {
+               load(application);
+       }
+
+       void load(Application application) {
+               CmsScriptApp app = getApp();
+               app.apply(bundleContext, repository, application);
+               if (log.isDebugEnabled())
+                       log.debug("CMS script app loaded to " + app.getWebPath());
+       }
+
+       CmsScriptApp getApp() {
+               if (engine == null)
+                       throw new IllegalStateException("CMS script app is not initialized");
+               return (CmsScriptApp) engine.get(APP);
+       }
+
+       void update() {
+
+               try {
+                       bundleContext.getBundle().update();
+               } catch (BundleException e) {
+                       e.printStackTrace();
+               }
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) {
+               Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager
+               ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl);
+               ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript");
+               if (engine == null) {// Nashorn
+                       Thread.currentThread().setContextClassLoader(originalCcl);
+                       scriptEngineManager = new ScriptEngineManager();
+                       Thread.currentThread().setContextClassLoader(bundleCl);
+                       engine = scriptEngineManager.getEngineByName("JavaScript");
+               }
+               return engine;
+       }
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java
new file mode 100644 (file)
index 0000000..a550953
--- /dev/null
@@ -0,0 +1,45 @@
+package org.argeo.cms.ui.script;
+
+import javax.jcr.Repository;
+
+import org.argeo.api.cms.CmsLog;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class ScriptAppActivator implements BundleActivator {
+       private final static CmsLog log = CmsLog.getLog(ScriptAppActivator.class);
+
+       @Override
+       public void start(BundleContext context) throws Exception {
+               try {
+                       CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication();
+                       appConfig.init(context);
+                       CmsScriptApp app = appConfig.getApp();
+                       ServiceTracker<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
+                                       FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
+
+                               @Override
+                               public Repository addingService(ServiceReference<Repository> reference) {
+                                       Repository repository = super.addingService(reference);
+                                       appConfig.setRepository(repository);
+                                       CmsScriptApp app = appConfig.getApp();
+                                       app.register(context, appConfig);
+                                       return repository;
+                               }
+
+                       };
+                       repoSt.open();
+               } catch (Exception e) {
+                       log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e);
+                       throw e;
+               }
+       }
+
+       @Override
+       public void stop(BundleContext context) throws Exception {
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java
new file mode 100644 (file)
index 0000000..0c870e1
--- /dev/null
@@ -0,0 +1,115 @@
+package org.argeo.cms.ui.script;
+
+import java.net.URL;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.framework.BundleContext;
+
+class ScriptUi implements CmsUiProvider {
+       private final static CmsLog log = CmsLog.getLog(ScriptUi.class);
+
+       private boolean development = true;
+       private ScriptEngine scriptEngine;
+
+       private URL appUrl;
+       // private BundleContext bundleContext;
+       // private String path;
+
+       // private Bindings bindings;
+       // private String script;
+
+       public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) {
+               this.scriptEngine = scriptEngine;
+////           ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+//             ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+//             ClassLoader originalCcl = Thread.currentThread().getContextClassLoader();
+//             try {
+////                   Thread.currentThread().setContextClassLoader(bundleCl);
+////                   scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
+////                   scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext);
+//                     scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl);
+//
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//             } finally {
+//                     Thread.currentThread().setContextClassLoader(originalCcl);
+//             }
+               this.appUrl = bundleContext.getBundle().getEntry(path);
+               load();
+       }
+
+       private void load() {
+//             try (Reader reader = new InputStreamReader(appUrl.openStream())) {
+//                     scriptEngine.eval(reader);
+//             } catch (IOException | ScriptException e) {
+//                     log.warn("Cannot execute " + appUrl, e);
+//             }
+
+               try {
+                       scriptEngine.eval("load('" + appUrl + "')");
+               } catch (ScriptException e) {
+                       log.warn("Cannot execute " + appUrl, e);
+               }
+
+       }
+
+       // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws
+       // ScriptException {
+       // super();
+       // this.scriptEngine = scriptEngine;
+       // this.script = script;
+       // bindings = scriptEngine.createBindings();
+       // scriptEngine.eval(script, bindings);
+       // }
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               long begin = System.currentTimeMillis();
+               // if (bindings == null) {
+               // bindings = scriptEngine.createBindings();
+               // try {
+               // scriptEngine.eval(script, bindings);
+               // } catch (ScriptException e) {
+               // log.warn("Cannot evaluate script", e);
+               // }
+               // }
+               // Bindings bindings = scriptEngine.createBindings();
+               // bindings.put("parent", parent);
+               // bindings.put("context", context);
+               // URL appUrl = bundleContext.getBundle().getEntry(path);
+               // try (Reader reader = new InputStreamReader(appUrl.openStream())) {
+               // scriptEngine.eval(reader,bindings);
+               // } catch (IOException | ScriptException e) {
+               // log.warn("Cannot execute " + appUrl, e);
+               // }
+
+               if (development)
+                       load();
+
+               Invocable invocable = (Invocable) scriptEngine;
+               try {
+                       invocable.invokeFunction("createUi", parent, context);
+               } catch (NoSuchMethodException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               } catch (ScriptException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+
+               long duration = System.currentTimeMillis() - begin;
+               if (log.isTraceEnabled())
+                       log.trace(appUrl + " UI in " + duration + " ms");
+               return null;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js
new file mode 100644 (file)
index 0000000..be9618d
--- /dev/null
@@ -0,0 +1,90 @@
+// CMS
+var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage');
+
+var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp');
+var AppUi = Java.type('org.argeo.cms.ui.script.AppUi');
+var Theme = Java.type('org.argeo.cms.ui.script.Theme');
+var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi');
+var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils');
+var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader');
+var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink');
+var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink');
+var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink');
+
+// SWT
+var SWT = Java.type('org.eclipse.swt.SWT');
+var Composite = Java.type('org.eclipse.swt.widgets.Composite');
+var Label = Java.type('org.eclipse.swt.widgets.Label');
+var Button = Java.type('org.eclipse.swt.widgets.Button');
+var Text = Java.type('org.eclipse.swt.widgets.Text');
+var Browser = Java.type('org.eclipse.swt.browser.Browser');
+
+var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout');
+var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout');
+var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout');
+var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout');
+var GridData = Java.type('org.eclipse.swt.layout.GridData');
+
+function loadNode(node) {
+       var json = CmsScriptApp.toJson(node)
+       var fromJson = JSON.parse(json)
+       return fromJson
+}
+
+function newArea(parent, style, layout) {
+       var control = new Composite(parent, SWT.NONE)
+       control.setLayout(layout)
+       CmsUtils.style(control, style)
+       return control
+}
+
+function newLabel(parent, style, text) {
+       var control = new Label(parent, SWT.WRAP)
+       control.setText(text)
+       CmsUtils.style(control, style)
+       CmsUtils.markup(control)
+       return control
+}
+
+function newButton(parent, style, text) {
+       var control = new Button(parent, SWT.FLAT)
+       control.setText(text)
+       CmsUtils.style(control, style)
+       CmsUtils.markup(control)
+       return control
+}
+
+function newFormLabel(parent, style, text) {
+       return newLabel(parent, style, '<b>' + text + '</b>')
+}
+
+function newText(parent, style, msg) {
+       var control = new Text(parent, SWT.NONE)
+       control.setMessage(msg)
+       CmsUtils.style(control, style)
+       return control
+}
+
+function newScrolledPage(parent) {
+       var scrolled = new ScrolledPage(parent, SWT.NONE)
+       scrolled.setLayoutData(CmsUtils.fillAll())
+       scrolled.setLayout(CmsUtils.noSpaceGridLayout())
+       var page = new Composite(scrolled, SWT.NONE)
+       page.setLayout(CmsUtils.noSpaceGridLayout())
+       page.setBackgroundMode(SWT.INHERIT_NONE)
+       return page
+}
+
+function gridData(control) {
+       var gridData = new GridData()
+       control.setLayoutData(gridData)
+       return gridData
+}
+
+function gridData(control, hAlign, vAlign) {
+       var gridData = new GridData(hAlign, vAlign, false, false)
+       control.setLayoutData(gridData)
+       return gridData
+}
+
+// print(__FILE__, __LINE__, __DIR__)
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java
new file mode 100644 (file)
index 0000000..7440596
--- /dev/null
@@ -0,0 +1,2 @@
+/** Argeo CMS user interface scripting. */
+package org.argeo.cms.ui.script;
\ No newline at end of file
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java
new file mode 100644 (file)
index 0000000..f3269f2
--- /dev/null
@@ -0,0 +1,398 @@
+package org.argeo.cms.web;
+
+import static org.argeo.util.directory.ldap.SharedSecret.X_SHARED_SECRET;
+
+import java.io.IOException;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.RemoteAuthCallback;
+import org.argeo.cms.auth.RemoteAuthCallbackHandler;
+import org.argeo.cms.servlet.ServletHttpRequest;
+import org.argeo.cms.servlet.ServletHttpResponse;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.CmsStyles;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.util.directory.ldap.AuthPassword;
+import org.argeo.util.directory.ldap.SharedSecret;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.AbstractEntryPoint;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.rap.rwt.client.service.BrowserNavigation;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/** Manages history and navigation */
+@Deprecated
+public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements CmsView {
+       private static final long serialVersionUID = 906558779562569784L;
+
+       private final CmsLog log = CmsLog.getLog(AbstractCmsEntryPoint.class);
+
+       // private final Subject subject;
+       private LoginContext loginContext;
+
+       private final Repository repository;
+       private final String workspace;
+       private final String defaultPath;
+       private final Map<String, String> factoryProperties;
+
+       // Current state
+       private Session session;
+       private Node node;
+       private String nodePath;// useful when changing auth
+       private String state;
+       private Throwable exception;
+
+       // Client services
+       private final JavaScriptExecutor jsExecutor;
+       private final BrowserNavigation browserNavigation;
+
+       public AbstractCmsEntryPoint(Repository repository, String workspace, String defaultPath,
+                       Map<String, String> factoryProperties) {
+               this.repository = repository;
+               this.workspace = workspace;
+               this.defaultPath = defaultPath;
+               this.factoryProperties = new HashMap<String, String>(factoryProperties);
+               // subject = new Subject();
+
+               // Initial login
+               LoginContext lc;
+               try {
+                       lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
+                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
+                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
+                       lc.login();
+               } catch (LoginException e) {
+                       try {
+                               lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS);
+                               lc.login();
+                       } catch (LoginException e1) {
+                               throw new CmsException("Cannot log in as anonymous", e1);
+                       }
+               }
+               authChange(lc);
+
+               jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
+               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
+               if (browserNavigation != null)
+                       browserNavigation.addBrowserNavigationListener(new CmsNavigationListener());
+       }
+
+       @Override
+       protected Shell createShell(Display display) {
+               Shell shell = super.createShell(display);
+               shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL);
+               display.disposeExec(new Runnable() {
+
+                       @Override
+                       public void run() {
+                               if (log.isTraceEnabled())
+                                       log.trace("Logging out " + session);
+                               JcrUtils.logoutQuietly(session);
+                       }
+               });
+               return shell;
+       }
+
+       @Override
+       protected final void createContents(final Composite parent) {
+               // UiContext.setData(CmsView.KEY, this);
+               CmsSwtUtils.registerCmsView(parent.getShell(), this);
+               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
+                       @Override
+                       public Void run() {
+                               try {
+                                       initUi(parent);
+                               } catch (Exception e) {
+                                       throw new CmsException("Cannot create entrypoint contents", e);
+                               }
+                               return null;
+                       }
+               });
+       }
+
+       /** Create UI */
+       protected abstract void initUi(Composite parent);
+
+       /** Recreate UI after navigation or auth change */
+       protected abstract void refresh();
+
+       /**
+        * The node to return when no node was found (for authenticated users and
+        * anonymous)
+        */
+//     private Node getDefaultNode(Session session) throws RepositoryException {
+//             if (!session.hasPermission(defaultPath, "read")) {
+//                     String userId = session.getUserID();
+//                     if (userId.equals(NodeConstants.ROLE_ANONYMOUS))
+//                             // TODO throw a special exception
+//                             throw new CmsException("Login required");
+//                     else
+//                             throw new CmsException("Unauthorized");
+//             }
+//             return session.getNode(defaultPath);
+//     }
+
+       protected String getBaseTitle() {
+               return factoryProperties.get(WebClient.PAGE_TITLE);
+       }
+
+       public void navigateTo(String state) {
+               exception = null;
+               String title = setState(state);
+               doRefresh();
+               if (browserNavigation != null)
+                       browserNavigation.pushState(state, title);
+       }
+
+       // @Override
+       // public synchronized Subject getSubject() {
+       // return subject;
+       // }
+
+       // @Override
+       // public LoginContext getLoginContext() {
+       // return loginContext;
+       // }
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return CurrentUser.isAnonymous(getSubject());
+       }
+
+       @Override
+       public synchronized void logout() {
+               if (loginContext == null)
+                       throw new CmsException("Login context should not be null");
+               try {
+                       CurrentUser.logoutCmsSession(loginContext.getSubject());
+                       loginContext.logout();
+                       LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS);
+                       anonymousLc.login();
+                       authChange(anonymousLc);
+               } catch (LoginException e) {
+                       log.error("Cannot logout", e);
+               }
+       }
+
+       @Override
+       public synchronized void authChange(LoginContext lc) {
+               if (lc == null)
+                       throw new CmsException("Login context cannot be null");
+               // logout previous login context
+               if (this.loginContext != null)
+                       try {
+                               this.loginContext.logout();
+                       } catch (LoginException e1) {
+                               log.warn("Could not log out: " + e1);
+                       }
+               this.loginContext = lc;
+               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
+
+                       @Override
+                       public Void run() {
+                               try {
+                                       JcrUtils.logoutQuietly(session);
+                                       session = repository.login(workspace);
+                                       if (nodePath != null)
+                                               try {
+                                                       node = session.getNode(nodePath);
+                                               } catch (PathNotFoundException e) {
+                                                       navigateTo("~");
+                                               }
+
+                                       // refresh UI
+                                       doRefresh();
+                               } catch (RepositoryException e) {
+                                       throw new CmsException("Cannot perform auth change", e);
+                               }
+                               return null;
+                       }
+
+               });
+       }
+
+       @Override
+       public void exception(final Throwable e) {
+               AbstractCmsEntryPoint.this.exception = e;
+               log.error("Unexpected exception in CMS", e);
+               doRefresh();
+       }
+
+       protected synchronized void doRefresh() {
+               Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
+                       @Override
+                       public Void run() {
+                               refresh();
+                               return null;
+                       }
+               });
+       }
+
+       /** Sets the state of the entry point and retrieve the related JCR node. */
+       protected synchronized String setState(String newState) {
+               String previousState = this.state;
+
+               String newNodePath = null;
+               String prefix = null;
+               this.state = newState;
+               if (newState.equals("~"))
+                       this.state = "";
+
+               try {
+                       int firstSlash = state.indexOf('/');
+                       if (firstSlash == 0) {
+                               newNodePath = state;
+                               prefix = "";
+                       } else if (firstSlash > 0) {
+                               prefix = state.substring(0, firstSlash);
+                               newNodePath = state.substring(firstSlash);
+                       } else {
+                               newNodePath = defaultPath;
+                               prefix = state;
+
+                       }
+
+                       // auth
+                       int colonIndex = prefix.indexOf('$');
+                       if (colonIndex > 0) {
+                               SharedSecret token = new SharedSecret(new AuthPassword(X_SHARED_SECRET + '$' + prefix)) {
+
+                                       @Override
+                                       public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                                               super.handle(callbacks);
+                                               // handle HTTP context
+                                               for (Callback callback : callbacks) {
+                                                       if (callback instanceof RemoteAuthCallback) {
+                                                               ((RemoteAuthCallback) callback)
+                                                                               .setRequest(new ServletHttpRequest(UiContext.getHttpRequest()));
+                                                               ((RemoteAuthCallback) callback)
+                                                                               .setResponse(new ServletHttpResponse(UiContext.getHttpResponse()));
+                                                       }
+                                               }
+                                       }
+                               };
+                               LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, token);
+                               lc.login();
+                               authChange(lc);// sets the node as well
+                               // } else {
+                               // // TODO check consistency
+                               // }
+                       } else {
+                               Node newNode = null;
+                               if (session.nodeExists(newNodePath))
+                                       newNode = session.getNode(newNodePath);
+                               else {
+//                                     throw new CmsException("Data " + newNodePath + " does not exist");
+                                       newNode = null;
+                               }
+                               setNode(newNode);
+                       }
+                       String title = publishMetaData(getNode());
+
+                       if (log.isTraceEnabled())
+                               log.trace("node=" + newNodePath + ", state=" + state + " (prefix=" + prefix + ")");
+
+                       return title;
+               } catch (Exception e) {
+                       log.error("Cannot set state '" + state + "'", e);
+                       if (state.equals("") || newState.equals("~") || newState.equals(previousState))
+                               return "Unrecoverable exception : " + e.getClass().getSimpleName();
+                       if (previousState.equals(""))
+                               previousState = "~";
+                       navigateTo(previousState);
+                       throw new CmsException("Unexpected issue when accessing #" + newState, e);
+               }
+       }
+
+       private String publishMetaData(Node node) throws RepositoryException {
+               // Title
+               String title;
+               if (node != null && node.isNodeType(NodeType.MIX_TITLE) && node.hasProperty(Property.JCR_TITLE))
+                       title = node.getProperty(Property.JCR_TITLE).getString() + " - " + getBaseTitle();
+               else
+                       title = getBaseTitle();
+
+               HttpServletRequest request = UiContext.getHttpRequest();
+               if (request == null)
+                       return null;
+
+               StringBuilder js = new StringBuilder();
+               if (title == null)
+                       title = "";
+               title = title.replace("'", "\\'");// sanitize
+               js.append("document.title = '" + title + "';");
+               jsExecutor.execute(js.toString());
+               return title;
+       }
+
+       // Simply remove some illegal character
+       // private String clean(String stringToClean) {
+       // return stringToClean.replaceAll("'", "").replaceAll("\\n", "")
+       // .replaceAll("\\t", "");
+       // }
+
+       protected synchronized Node getNode() {
+               return node;
+       }
+
+       private synchronized void setNode(Node node) throws RepositoryException {
+               this.node = node;
+               this.nodePath = node == null ? null : node.getPath();
+       }
+
+       protected String getState() {
+               return state;
+       }
+
+       protected Throwable getException() {
+               return exception;
+       }
+
+       protected void resetException() {
+               exception = null;
+       }
+
+       protected Session getSession() {
+               return session;
+       }
+
+       private class CmsNavigationListener implements BrowserNavigationListener {
+               private static final long serialVersionUID = -3591018803430389270L;
+
+               @Override
+               public void navigated(BrowserNavigationEvent event) {
+                       setState(event.getState());
+                       doRefresh();
+               }
+       }
+}
\ No newline at end of file
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/BundleResourceLoader.java
new file mode 100644 (file)
index 0000000..ca93e62
--- /dev/null
@@ -0,0 +1,34 @@
+package org.argeo.cms.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.osgi.framework.Bundle;
+
+/** {@link ResourceLoader} implementation wrapping an {@link Bundle}. */
+public class BundleResourceLoader implements ResourceLoader {
+       private final Bundle bundle;
+
+       public BundleResourceLoader(Bundle bundle) {
+               this.bundle = bundle;
+       }
+
+       @Override
+       public InputStream getResourceAsStream(String resourceName) throws IOException {
+               URL res = bundle.getEntry(resourceName);
+               if (res == null) {
+                       res = bundle.getResource(resourceName);
+                       if (res == null)
+                               throw new IllegalArgumentException(
+                                               "Resource " + resourceName + " not found in bundle " + bundle.getSymbolicName());
+               }
+               return res.openStream();
+       }
+
+       public Bundle getBundle() {
+               return bundle;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsThemeResourceLoader.java
new file mode 100644 (file)
index 0000000..102a4e1
--- /dev/null
@@ -0,0 +1,23 @@
+package org.argeo.cms.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.argeo.api.cms.ux.CmsTheme;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+
+/** A RAP {@link ResourceLoader} based on a {@link CmsTheme}. */
+public class CmsThemeResourceLoader implements ResourceLoader {
+       private final CmsTheme theme;
+
+       public CmsThemeResourceLoader(CmsTheme theme) {
+               super();
+               this.theme = theme;
+       }
+
+       @Override
+       public InputStream getResourceAsStream(String resourceName) throws IOException {
+               return theme.getResourceAsStream(resourceName);
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java
new file mode 100644 (file)
index 0000000..0270933
--- /dev/null
@@ -0,0 +1,167 @@
+package org.argeo.cms.web;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsAppListener;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.util.LangUtils;
+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.ExceptionHandler;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.EventAdmin;
+
+/** An RWT web app integrating with a {@link CmsApp}. */
+public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, CmsAppListener {
+       private final static CmsLog log = CmsLog.getLog(CmsWebApp.class);
+
+       private BundleContext bundleContext;
+       private CmsApp cmsApp;
+//     private String cmsAppId;
+       private EventAdmin eventAdmin;
+
+       private ServiceRegistration<ApplicationConfiguration> rwtAppReg;
+
+       private final static String CONTEXT_NAME = "contextName";
+       private String contextName;
+
+       private final static String FAVICON_PNG = "favicon.png";
+
+       public void init(BundleContext bundleContext, Map<String, String> properties) {
+               this.bundleContext = bundleContext;
+               contextName = properties.get(CONTEXT_NAME);
+               if (cmsApp != null) {
+                       if (cmsApp.allThemesAvailable())
+                               publishWebApp();
+               }
+       }
+
+       public void destroy(BundleContext bundleContext, Map<String, String> properties) {
+               if (cmsApp != null) {
+                       cmsApp.removeCmsAppListener(this);
+                       cmsApp = null;
+               }
+       }
+
+       @Override
+       public void configure(Application application) {
+               // TODO make it configurable?
+               // SWT compatibility is required for:
+               // - Browser.execute()
+               // - blocking dialogs
+               application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+               for (String uiName : cmsApp.getUiNames()) {
+                       CmsTheme theme = cmsApp.getTheme(uiName);
+                       if (theme != null)
+                               WebThemeUtils.apply(application, theme);
+               }
+
+               Map<String, String> properties = new HashMap<>();
+               addEntryPoints(application, properties);
+               application.setExceptionHandler(this);
+       }
+
+       @Override
+       public void handleException(Throwable throwable) {
+               Display display = Display.getCurrent();
+               if (display != null && !display.isDisposed()) {
+                       CmsView cmsView = CmsSwtUtils.getCmsView(display.getActiveShell());
+                       cmsView.exception(throwable);
+               } else {
+                       log.error("Unexpected exception outside an UI thread", throwable);
+               }
+
+       }
+
+       protected void addEntryPoints(Application application, Map<String, String> commonProperties) {
+               for (String uiName : cmsApp.getUiNames()) {
+                       Map<String, String> properties = new HashMap<>(commonProperties);
+                       CmsTheme theme = cmsApp.getTheme(uiName);
+                       if (theme != null) {
+                               properties.put(WebClient.THEME_ID, theme.getThemeId());
+                               properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders());
+                               properties.put(WebClient.BODY_HTML, theme.getBodyHtml());
+                               Set<String> imagePaths = theme.getImagesPaths();
+                               if (imagePaths.contains(FAVICON_PNG)) {
+                                       properties.put(WebClient.FAVICON, FAVICON_PNG);
+                               }
+                       } else {
+                               properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID);
+                       }
+                       String entryPointName = !uiName.equals("") ? "/" + uiName : "/";
+                       application.addEntryPoint(entryPointName, () -> {
+                               CmsWebEntryPoint entryPoint = new CmsWebEntryPoint(this, uiName);
+                               entryPoint.setEventAdmin(eventAdmin);
+                               return entryPoint;
+                       }, properties);
+                       if (log.isDebugEnabled())
+                               log.info("Added web entry point " + (contextName != null ? "/" + contextName : "") + entryPointName);
+               }
+//             if (log.isDebugEnabled())
+//                     log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
+       }
+
+       CmsApp getCmsApp() {
+               return cmsApp;
+       }
+
+       BundleContext getBundleContext() {
+               return bundleContext;
+       }
+
+       public void setCmsApp(CmsApp cmsApp) {
+               this.cmsApp = cmsApp;
+//             this.cmsAppId = properties.get(Constants.SERVICE_PID);
+               this.cmsApp.addCmsAppListener(this);
+       }
+
+       public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               if (!contextName.equals(this.contextName))
+                       return;
+               if (this.cmsApp != null) {
+                       this.cmsApp.removeCmsAppListener(this);
+               }
+               if (rwtAppReg != null)
+                       rwtAppReg.unregister();
+               this.cmsApp = null;
+       }
+
+       @Override
+       public void themingUpdated() {
+               if (cmsApp != null && cmsApp.allThemesAvailable())
+                       publishWebApp();
+       }
+
+       protected void publishWebApp() {
+               Dictionary<String, Object> regProps = LangUtils.dict(CONTEXT_NAME, contextName);
+               if (rwtAppReg != null)
+                       rwtAppReg.unregister();
+               if (bundleContext != null) {
+                       rwtAppReg = bundleContext.registerService(ApplicationConfiguration.class, this, regProps);
+                       if (log.isDebugEnabled())
+                               log.debug("Publishing CMS web app /" + (contextName != null ? contextName : "") + " ...");
+               }
+       }
+
+       public void setEventAdmin(EventAdmin eventAdmin) {
+               this.eventAdmin = eventAdmin;
+       }
+
+       public void setContextName(String contextName) {
+               this.contextName = contextName;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java
new file mode 100644 (file)
index 0000000..1597197
--- /dev/null
@@ -0,0 +1,368 @@
+package org.argeo.cms.web;
+
+import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicationContext;
+
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.LocaleUtils;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.auth.RemoteAuthCallbackHandler;
+import org.argeo.cms.servlet.ServletHttpRequest;
+import org.argeo.cms.servlet.ServletHttpResponse;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SimpleSwtUxContext;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.ui.util.DefaultImageManager;
+import org.argeo.eclipse.ui.specific.UiContext;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.client.service.BrowserNavigation;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
+import org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/** The {@link CmsView} for a {@link CmsWebApp}. */
+@SuppressWarnings("restriction")
+public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationListener {
+       private static final long serialVersionUID = 7733510691684570402L;
+       private final static CmsLog log = CmsLog.getLog(CmsWebEntryPoint.class);
+
+       private EventAdmin eventAdmin;
+
+       private final CmsWebApp cmsWebApp;
+       private final String uiName;
+
+       private LoginContext loginContext;
+       private String state;
+       private Throwable exception;
+       private UxContext uxContext;
+       private CmsImageManager imageManager;
+
+       private Display display;
+       private CmsUi ui;
+
+       private String uid;
+
+       // Client services
+       // private final JavaScriptExecutor jsExecutor;
+       private final BrowserNavigation browserNavigation;
+
+       /** Experimental OS-like multi windows. */
+       private boolean multipleShells = false;
+
+       public CmsWebEntryPoint(CmsWebApp cmsWebApp, String uiName) {
+               assert cmsWebApp != null;
+               assert uiName != null;
+               this.cmsWebApp = cmsWebApp;
+               this.uiName = uiName;
+               uid = UUID.randomUUID().toString();
+
+               // Initial login
+               LoginContext lc;
+               try {
+                       lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER,
+                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
+                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
+                       lc.login();
+               } catch (LoginException e) {
+                       try {
+                               lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
+                                               new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
+                                                               new ServletHttpResponse(UiContext.getHttpResponse())));
+                               lc.login();
+                       } catch (LoginException e1) {
+                               throw new IllegalStateException("Cannot log in as anonymous", e1);
+                       }
+               }
+               authChange(lc);
+
+               // jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
+               browserNavigation = RWT.getClient().getService(BrowserNavigation.class);
+               if (browserNavigation != null)
+                       browserNavigation.addBrowserNavigationListener(this);
+       }
+
+       protected void createContents(Composite parent) {
+               Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
+                       @Override
+                       public Void run() {
+                               try {
+                                       uxContext = new SimpleSwtUxContext();
+                                       imageManager = new DefaultImageManager();
+                                       CmsSession cmsSession = getCmsSession();
+                                       if (cmsSession != null) {
+                                               UiContext.setLocale(cmsSession.getLocale());
+                                               LocaleUtils.setThreadLocale(cmsSession.getLocale());
+                                       } else {
+                                               Locale rwtLocale = RWT.getUISession().getLocale();
+                                               LocaleUtils.setThreadLocale(rwtLocale);
+                                       }
+                                       parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
+                                       display = parent.getDisplay();
+                                       ui = cmsWebApp.getCmsApp().initUi(parent);
+                                       if (ui instanceof Composite)
+                                               ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
+                                       // we need ui to be set before refresh so that CmsView can store UI context data
+                                       // in it.
+                                       cmsWebApp.getCmsApp().refreshUi(ui, null);
+                               } catch (Exception e) {
+                                       throw new IllegalStateException("Cannot create entrypoint contents", e);
+                               }
+                               return null;
+                       }
+               });
+       }
+
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       public <T> T doAs(PrivilegedAction<T> action) {
+               return Subject.doAs(getSubject(), action);
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return CurrentUser.isAnonymous(getSubject());
+       }
+
+       @Override
+       public synchronized void logout() {
+               if (loginContext == null)
+                       throw new IllegalArgumentException("Login context should not be null");
+               try {
+                       CurrentUser.logoutCmsSession(loginContext.getSubject());
+                       loginContext.logout();
+                       LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS,
+                                       new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()),
+                                                       new ServletHttpResponse(UiContext.getHttpResponse())));
+                       anonymousLc.login();
+                       authChange(anonymousLc);
+               } catch (LoginException e) {
+                       log.error("Cannot logout", e);
+               }
+       }
+
+       @Override
+       public synchronized void authChange(LoginContext lc) {
+               if (lc == null)
+                       throw new IllegalArgumentException("Login context cannot be null");
+               // logout previous login context
+               if (this.loginContext != null)
+                       try {
+                               this.loginContext.logout();
+                       } catch (LoginException e1) {
+                               log.warn("Could not log out: " + e1);
+                       }
+               this.loginContext = lc;
+               doRefresh();
+       }
+
+       @Override
+       public void exception(final Throwable e) {
+               if (e instanceof SWTError) {
+                       SWTError swtError = (SWTError) e;
+                       if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED)
+                               return;
+               }
+               display.syncExec(() -> {
+//                     CmsFeedback.show("Unexpected exception in CMS", e);
+                       exception = e;
+                       log.error("Unexpected exception in CMS", e);
+                       doRefresh();
+               });
+       }
+
+       protected synchronized void doRefresh() {
+               if (ui != null)
+                       Subject.doAs(getSubject(), new PrivilegedAction<Void>() {
+                               @Override
+                               public Void run() {
+                                       if (exception != null) {
+                                               // TODO internationalise
+                                               CmsFeedback.show("Unexpected exception", exception);
+                                               exception = null;
+                                               // TODO report
+                                       }
+                                       cmsWebApp.getCmsApp().refreshUi(ui, state);
+                                       return null;
+                               }
+                       });
+       }
+
+       /** Sets the state of the entry point and retrieve the related JCR node. */
+       protected String setState(String newState) {
+               cmsWebApp.getCmsApp().setState(ui, newState);
+               state = newState;
+               return null;
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+       @Override
+       public void navigateTo(String state) {
+               exception = null;
+               String title = setState(state);
+               if (title != null)
+                       doRefresh();
+               if (browserNavigation != null)
+                       browserNavigation.pushState(state, title);
+       }
+
+       public CmsImageManager getImageManager() {
+               return imageManager;
+       }
+
+       @Override
+       public void navigated(BrowserNavigationEvent event) {
+               setState(event.getState());
+               // doRefresh();
+       }
+
+       @Override
+       public void sendEvent(String topic, Map<String, Object> properties) {
+               if (properties == null)
+                       properties = new HashMap<>();
+               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
+                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
+                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
+               properties.put(CMS_VIEW_UID_PROPERTY, uid);
+               eventAdmin.sendEvent(new Event(topic, properties));
+       }
+
+       @Override
+       public void stateChanged(String state, String title) {
+               browserNavigation.pushState(state, title);
+       }
+
+       @Override
+       public CmsSession getCmsSession() {
+               CmsSession cmsSession = cmsWebApp.getCmsApp().getCmsContext().getCmsSession(getSubject());
+               if (cmsSession == null)
+                       throw new IllegalStateException("No CMS session available for " + getSubject());
+               return cmsSession;
+       }
+
+       @Override
+       public Object getData(String key) {
+               if (ui != null) {
+                       return ui.getData(key);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+       @Override
+       public void setData(String key, Object value) {
+               if (ui != null) {
+                       ui.setData(key, value);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+       /*
+        * EntryPoint IMPLEMENTATION
+        */
+
+       @Override
+       public int createUI() {
+               Display display = new Display();
+               Shell shell = createShell(display);
+               shell.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               CmsSwtUtils.registerCmsView(shell, this);
+               createContents(shell);
+               shell.layout();
+//             if (shell.getMaximized()) {
+//                     shell.layout();
+//             } else {
+////                   shell.pack();
+//             }
+               shell.open();
+               if (getApplicationContext().getLifeCycleFactory().getLifeCycle() instanceof RWTLifeCycle) {
+                       eventLoop: while (!shell.isDisposed()) {
+                               try {
+                                       Subject.doAs(loginContext.getSubject(), new PrivilegedAction<Void>() {
+                                               @Override
+                                               public Void run() {
+                                                       if (!display.readAndDispatch()) {
+                                                               display.sleep();
+                                                       }
+                                                       return null;
+                                               }
+                                       });
+                               } catch (Throwable e) {
+                                       if (e instanceof SWTError) {
+                                               SWTError swtError = (SWTError) e;
+                                               if (swtError.code == SWT.ERROR_FUNCTION_DISPOSED) {
+                                                       log.error("Unexpected SWT error in event loop, ignoring it. " + e.getMessage());
+                                                       continue eventLoop;
+                                               } else {
+                                                       log.error("Unexpected SWT error in event loop, shutting down...", e);
+                                                       break eventLoop;
+                                               }
+                                       } else if (e instanceof ThreadDeath) {
+                                               throw (ThreadDeath) e;
+                                       } else if (e instanceof Error) {
+                                               log.error("Unexpected error in event loop, shutting down...", e);
+                                               break eventLoop;
+                                       } else {
+                                               log.error("Unexpected exception in event loop, ignoring it. " + e.getMessage());
+                                               continue eventLoop;
+                                       }
+                               }
+                       }
+                       if (!display.isDisposed())
+                               display.dispose();
+               }
+               return 0;
+       }
+
+       protected Shell createShell(Display display) {
+               Shell shell;
+               if (!multipleShells) {
+                       shell = new Shell(display, SWT.NO_TRIM);
+                       shell.setMaximized(true);
+               } else {
+                       shell = new Shell(display, SWT.SHELL_TRIM);
+                       shell.setSize(800, 600);
+               }
+               return shell;
+       }
+
+       public void setEventAdmin(EventAdmin eventAdmin) {
+               this.eventAdmin = eventAdmin;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/MinimalWebApp.java
new file mode 100644 (file)
index 0000000..2eff71e
--- /dev/null
@@ -0,0 +1,56 @@
+package org.argeo.cms.web;
+
+import static org.argeo.cms.osgi.BundleCmsTheme.CMS_THEME_BUNDLE_PROPERTY;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.cms.osgi.BundleCmsTheme;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.osgi.framework.BundleContext;
+
+/** Lightweight web app using only RWT and not the whole Eclipse platform. */
+public class MinimalWebApp implements ApplicationConfiguration {
+
+       private BundleCmsTheme theme;
+
+       public void init(BundleContext bundleContext, Map<String, Object> properties) {
+               if (properties.containsKey(CMS_THEME_BUNDLE_PROPERTY)) {
+                       String cmsThemeBundle = properties.get(CMS_THEME_BUNDLE_PROPERTY).toString();
+                       theme = new BundleCmsTheme(bundleContext, cmsThemeBundle);
+               }
+       }
+
+       public void destroy() {
+
+       }
+
+       /** To be overridden. Does nothing by default. */
+       protected void addEntryPoints(Application application, Map<String, String> properties) {
+
+       }
+
+       @Override
+       public void configure(Application application) {
+               if (theme != null)
+                       WebThemeUtils.apply(application, theme);
+
+               Map<String, String> properties = new HashMap<>();
+               if (theme != null) {
+                       properties.put(WebClient.THEME_ID, theme.getThemeId());
+                       properties.put(WebClient.HEAD_HTML, theme.getHtmlHeaders());
+               } else {
+                       properties.put(WebClient.THEME_ID, RWT.DEFAULT_THEME_ID);
+               }
+               addEntryPoints(application, properties);
+
+       }
+
+       public void setTheme(BundleCmsTheme theme) {
+               this.theme = theme;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java
new file mode 100644 (file)
index 0000000..38a9b44
--- /dev/null
@@ -0,0 +1,414 @@
+package org.argeo.cms.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionManager;
+
+import org.argeo.api.cms.CmsConstants;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.jcr.CmsJcrUtils;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.ui.CmsUiConstants;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.LifeCycleUiProvider;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.util.StyleSheetResourceLoader;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.application.Application.OperationMode;
+import org.eclipse.rap.rwt.application.ApplicationConfiguration;
+import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.application.EntryPointFactory;
+import org.eclipse.rap.rwt.application.ExceptionHandler;
+import org.eclipse.rap.rwt.client.WebClient;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/** A basic generic app based on {@link SimpleErgonomics}. */
+@Deprecated
+public class SimpleApp implements CmsUiConstants, ApplicationConfiguration {
+       private final static CmsLog log = CmsLog.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(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER);
+       private List<String> rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER);
+
+       private CmsUiProvider header;
+       private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
+
+       private Integer headerHeight = 40;
+
+       private ServiceRegistration<ApplicationConfiguration> appReg;
+
+       public void configure(Application application) {
+               try {
+                       BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle());
+
+                       application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
+                       // application.setOperationMode(OperationMode.JEE_COMPATIBILITY);
+
+                       application.setExceptionHandler(new CmsExceptionHandler());
+
+                       // loading animated gif
+                       application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE));
+                       // empty image
+                       application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE));
+
+                       for (String resource : resources) {
+                               application.addResource(resource, bundleRL);
+                               if (log.isTraceEnabled())
+                                       log.trace("Resource " + resource);
+                       }
+
+                       Map<String, String> defaultBranding = null;
+                       if (branding.containsKey("*"))
+                               defaultBranding = branding.get("*");
+                       // String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
+
+                       // entry points
+                       for (String page : pages.keySet()) {
+                               Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
+                                               : new HashMap<String, String>();
+                               if (branding.containsKey(page)) {
+                                       properties.putAll(branding.get(page));
+                               }
+                               // favicon
+                               if (properties.containsKey(WebClient.FAVICON)) {
+                                       String themeId = defaultBranding.get(WebClient.THEME_ID);
+                                       Bundle themeBundle = findThemeBundle(bundleContext, themeId);
+                                       String faviconRelPath = properties.get(WebClient.FAVICON);
+                                       application.addResource(faviconRelPath,
+                                                       new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle()));
+                                       if (log.isTraceEnabled())
+                                               log.trace("Favicon " + faviconRelPath);
+
+                               }
+
+                               // page title
+                               if (!properties.containsKey(WebClient.PAGE_TITLE)) {
+                                       if (page.length() > 0)
+                                               properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1));
+                               }
+
+                               // default body HTML
+                               if (!properties.containsKey(WebClient.BODY_HTML))
+                                       properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY);
+
+                               //
+                               // ADD ENTRY POINT
+                               //
+                               application.addEntryPoint("/" + page,
+                                               new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties);
+                               log.info("Page /" + page);
+                       }
+
+                       // stylesheets and themes
+                       Set<Bundle> themeBundles = new HashSet<>();
+                       for (String themeId : styleSheets.keySet()) {
+                               Bundle themeBundle = findThemeBundle(bundleContext, themeId);
+                               StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader(
+                                               themeBundle != null ? themeBundle : bundleContext.getBundle());
+                               if (themeBundle != null)
+                                       themeBundles.add(themeBundle);
+                               List<String> cssLst = styleSheets.get(themeId);
+                               if (log.isDebugEnabled())
+                                       log.debug("Theme " + themeId);
+                               for (String css : cssLst) {
+                                       application.addStyleSheet(themeId, css, styleSheetRL);
+                                       if (log.isDebugEnabled())
+                                               log.debug(" CSS " + css);
+                               }
+
+                       }
+                       for (Bundle themeBundle : themeBundles) {
+                               BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle);
+                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.png");
+                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.gif");
+                               SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.jpg");
+                       }
+               } catch (RuntimeException e) {
+                       // Easier access to initialisation errors
+                       log.error("Unexpected exception when configuring RWT application.", e);
+                       throw e;
+               }
+       }
+
+       public void init() throws RepositoryException {
+               Session session = null;
+               try {
+                       session = CmsJcrUtils.openDataAdminSession(repository, workspace);
+                       // session = JcrUtils.loginOrCreateWorkspace(repository, workspace);
+                       VersionManager vm = session.getWorkspace().getVersionManager();
+                       JcrUtils.mkdirs(session, jcrBasePath);
+                       session.save();
+                       if (!vm.isCheckedOut(jcrBasePath))
+                               vm.checkout(jcrBasePath);
+                       for (String principal : rwPrincipals)
+                               JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE);
+                       for (String principal : roPrincipals)
+                               JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ);
+
+                       for (String pageName : pages.keySet()) {
+                               try {
+                                       initPage(session, pages.get(pageName));
+                                       session.save();
+                               } catch (Exception e) {
+                                       throw new CmsException("Cannot initialize page " + pageName, e);
+                               }
+                       }
+
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+
+               // publish to OSGi
+               register();
+       }
+
+       protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException {
+               if (page instanceof LifeCycleUiProvider)
+                       ((LifeCycleUiProvider) page).init(adminSession);
+       }
+
+       public void destroy() {
+               for (String pageName : pages.keySet()) {
+                       try {
+                               CmsUiProvider page = pages.get(pageName);
+                               if (page instanceof LifeCycleUiProvider)
+                                       ((LifeCycleUiProvider) page).destroy();
+                       } catch (Exception e) {
+                               log.error("Cannot destroy page " + pageName, e);
+                       }
+               }
+       }
+
+       protected void register() {
+               Hashtable<String, String> props = new Hashtable<String, String>();
+               if (contextName != null)
+                       props.put("contextName", contextName);
+               appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props);
+               if (log.isDebugEnabled())
+                       log.debug("Registered " + (contextName == null ? "/" : contextName));
+       }
+
+       protected void unregister() {
+               appReg.unregister();
+               if (log.isDebugEnabled())
+                       log.debug("Unregistered " + (contextName == null ? "/" : contextName));
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setWorkspace(String workspace) {
+               this.workspace = workspace;
+       }
+
+       public void setHeader(CmsUiProvider header) {
+               this.header = header;
+       }
+
+       public void setPages(Map<String, CmsUiProvider> pages) {
+               this.pages = pages;
+       }
+
+       public void setJcrBasePath(String basePath) {
+               this.jcrBasePath = basePath;
+       }
+
+       public void setRoPrincipals(List<String> roPrincipals) {
+               this.roPrincipals = roPrincipals;
+       }
+
+       public void setRwPrincipals(List<String> rwPrincipals) {
+               this.rwPrincipals = rwPrincipals;
+       }
+
+       public void setHeaderHeight(Integer headerHeight) {
+               this.headerHeight = headerHeight;
+       }
+
+       public void setBranding(Map<String, Map<String, String>> branding) {
+               this.branding = branding;
+       }
+
+       public void setStyleSheets(Map<String, List<String>> styleSheets) {
+               this.styleSheets = styleSheets;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+       public void setResources(List<String> resources) {
+               this.resources = resources;
+       }
+
+       public void setContextName(String contextName) {
+               this.contextName = contextName;
+       }
+
+       private static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL,
+                       String pattern) {
+               Enumeration<URL> themeResources = themeBundle.findEntries("/", pattern, true);
+               if (themeResources == null)
+                       return;
+               while (themeResources.hasMoreElements()) {
+                       String resource = themeResources.nextElement().getPath();
+                       // remove first '/' so that RWT registers it
+                       resource = resource.substring(1);
+                       if (!resource.endsWith("/")) {
+                               application.addResource(resource, themeBRL);
+                               if (log.isTraceEnabled())
+                                       log.trace("Registered " + resource + " from theme " + themeBundle);
+                       }
+
+               }
+
+       }
+
+       private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) {
+               if (themeId == null)
+                       return null;
+               // TODO optimize
+               // TODO deal with multiple versions
+               Bundle themeBundle = null;
+               if (themeId != null) {
+                       for (Bundle bundle : bundleContext.getBundles())
+                               if (themeId.equals(bundle.getSymbolicName())) {
+                                       themeBundle = bundle;
+                                       break;
+                               }
+               }
+               return themeBundle;
+       }
+
+       class CmsExceptionHandler implements ExceptionHandler {
+
+               @Override
+               public void handleException(Throwable throwable) {
+                       // TODO be smarter
+                       CmsUiUtils.getCmsView().exception(throwable);
+               }
+
+       }
+
+       private class CmsEntryPointFactory implements EntryPointFactory {
+               private final CmsUiProvider page;
+               private final Repository repository;
+               private final String workspace;
+               private final Map<String, String> properties;
+
+               public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
+                               Map<String, String> properties) {
+                       this.page = page;
+                       this.repository = repository;
+                       this.workspace = workspace;
+                       this.properties = properties;
+               }
+
+               @Override
+               public EntryPoint create() {
+                       SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) {
+                               private static final long serialVersionUID = -637940404865527290L;
+
+                               @Override
+                               protected void createAdminArea(Composite parent) {
+                                       Composite adminArea = new Composite(parent, SWT.NONE);
+                                       adminArea.setLayout(new FillLayout());
+                                       Button refresh = new Button(adminArea, SWT.PUSH);
+                                       refresh.setText("Reload App");
+                                       refresh.addSelectionListener(new SelectionAdapter() {
+                                               private static final long serialVersionUID = -7671999525536351366L;
+
+                                               @Override
+                                               public void widgetSelected(SelectionEvent e) {
+                                                       long timeBeforeReload = 1000;
+                                                       RWT.getClient().getService(JavaScriptExecutor.class).execute(
+                                                                       "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");");
+                                                       reloadApp();
+                                               }
+                                       });
+                               }
+                       };
+                       // entryPoint.setState("");
+                       entryPoint.setHeader(header);
+                       entryPoint.setHeaderHeight(headerHeight);
+                       // CmsSession.current.set(entryPoint);
+                       return entryPoint;
+               }
+
+               private void reloadApp() {
+                       new Thread("Refresh app") {
+                               @Override
+                               public void run() {
+                                       unregister();
+                                       register();
+                               }
+                       }.start();
+               }
+       }
+
+       private static ResourceLoader createResourceLoader(final String resourceName) {
+               return new ResourceLoader() {
+                       public InputStream getResourceAsStream(String resourceName) throws IOException {
+                               return getClass().getClassLoader().getResourceAsStream(resourceName);
+                       }
+               };
+       }
+
+       // private static ResourceLoader createUrlResourceLoader(final URL url) {
+       // return new ResourceLoader() {
+       // public InputStream getResourceAsStream(String resourceName)
+       // throws IOException {
+       // return url.openStream();
+       // }
+       // };
+       // }
+
+       /*
+        * TEXTS
+        */
+       private static String DEFAULT_LOADING_BODY = "<div"
+                       + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
+                       + "<img src=\"./rwt-resources/" + LOADING_IMAGE
+                       + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java
new file mode 100644 (file)
index 0000000..783f6eb
--- /dev/null
@@ -0,0 +1,238 @@
+package org.argeo.cms.web;
+
+import java.util.Map;
+import java.util.UUID;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.CmsStyles;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SimpleSwtUxContext;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.DefaultImageManager;
+import org.argeo.cms.ui.util.SystemNotifications;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Simple header/body ergonomics. */
+@Deprecated
+public class SimpleErgonomics extends AbstractCmsEntryPoint {
+       private static final long serialVersionUID = 8743413921359548523L;
+
+       private final static CmsLog log = CmsLog.getLog(SimpleErgonomics.class);
+
+       private boolean uiInitialized = false;
+       private Composite headerArea;
+       private Composite leftArea;
+       private Composite rightArea;
+       private Composite footerArea;
+       private Composite bodyArea;
+       private final CmsUiProvider uiProvider;
+
+       private CmsUiProvider header;
+       private Integer headerHeight = 0;
+       private Integer footerHeight = 0;
+       private CmsUiProvider lead;
+       private CmsUiProvider end;
+       private CmsUiProvider footer;
+
+       private CmsImageManager imageManager = new DefaultImageManager();
+       private UxContext uxContext = null;
+       private String uid;
+
+       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) {
+               uid = UUID.randomUUID().toString();
+               parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false)));
+
+               uxContext = new SimpleSwtUxContext();
+               if (!getUxContext().isMasterData())
+                       createAdminArea(parent);
+               headerArea = new Composite(parent, SWT.NONE);
+               headerArea.setLayout(new FillLayout());
+               GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1);
+               headerData.heightHint = headerHeight;
+               headerArea.setLayoutData(headerData);
+
+               // TODO: bi-directional
+               leftArea = new Composite(parent, SWT.NONE);
+               leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false));
+               leftArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               bodyArea = new Composite(parent, SWT.NONE);
+               bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY);
+               bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               // TODO: bi-directional
+               rightArea = new Composite(parent, SWT.NONE);
+               rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
+               rightArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               footerArea = new Composite(parent, SWT.NONE);
+               // footerArea.setLayout(new FillLayout());
+               GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1);
+               footerData.heightHint = footerHeight;
+               footerArea.setLayoutData(footerData);
+
+               uiInitialized = true;
+               refresh();
+       }
+
+       @Override
+       protected void refresh() {
+               if (!uiInitialized)
+                       return;
+               if (getState() == null)
+                       setState("");
+               refreshSides();
+               refreshBody();
+               if (log.isTraceEnabled())
+                       log.trace("UI refreshed " + getNode());
+       }
+
+       protected void createAdminArea(Composite parent) {
+       }
+
+       @Deprecated
+       protected void refreshHeader() {
+               if (header == null)
+                       return;
+
+               for (Control child : headerArea.getChildren())
+                       child.dispose();
+               try {
+                       header.createUi(headerArea, getNode());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot refresh header", e);
+               }
+               headerArea.layout(true, true);
+       }
+
+       protected void refreshSides() {
+               refresh(headerArea, header, CmsStyles.CMS_HEADER);
+               refresh(leftArea, lead, CmsStyles.CMS_LEAD);
+               refresh(rightArea, end, CmsStyles.CMS_END);
+               refresh(footerArea, footer, CmsStyles.CMS_FOOTER);
+       }
+
+       private void refresh(Composite area, CmsUiProvider uiProvider, String style) {
+               if (uiProvider == null)
+                       return;
+
+               for (Control child : area.getChildren())
+                       child.dispose();
+               CmsSwtUtils.style(area, style);
+               try {
+                       uiProvider.createUi(area, getNode());
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot refresh header", e);
+               }
+               area.layout(true, true);
+       }
+
+       protected void refreshBody() {
+               // Exception
+               Throwable exception = getException();
+               if (exception != null) {
+                       SystemNotifications systemNotifications = new SystemNotifications(bodyArea);
+                       systemNotifications.notifyException(exception);
+                       resetException();
+                       return;
+                       // TODO report
+               }
+
+               // clear
+               for (Control child : bodyArea.getChildren())
+                       child.dispose();
+               bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout());
+
+               try {
+                       Node node = getNode();
+//                     if (node == null)
+//                             log.error("Context cannot be null");
+//                     else
+                       uiProvider.createUi(bodyArea, node);
+               } catch (RepositoryException e) {
+                       throw new CmsException("Cannot refresh body", e);
+               }
+
+               bodyArea.layout(true, true);
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+       public CmsImageManager getImageManager() {
+               return imageManager;
+       }
+
+       public void setHeader(CmsUiProvider header) {
+               this.header = header;
+       }
+
+       public void setHeaderHeight(Integer headerHeight) {
+               this.headerHeight = headerHeight;
+       }
+
+       public void setImageManager(CmsImageManager imageManager) {
+               this.imageManager = imageManager;
+       }
+
+       public CmsUiProvider getLead() {
+               return lead;
+       }
+
+       public void setLead(CmsUiProvider lead) {
+               this.lead = lead;
+       }
+
+       public CmsUiProvider getEnd() {
+               return end;
+       }
+
+       public void setEnd(CmsUiProvider end) {
+               this.end = end;
+       }
+
+       public CmsUiProvider getFooter() {
+               return footer;
+       }
+
+       public void setFooter(CmsUiProvider footer) {
+               this.footer = footer;
+       }
+
+       public CmsUiProvider getHeader() {
+               return header;
+       }
+
+       public void setFooterHeight(Integer footerHeight) {
+               this.footerHeight = footerHeight;
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/WebThemeUtils.java
new file mode 100644 (file)
index 0000000..e51644b
--- /dev/null
@@ -0,0 +1,28 @@
+package org.argeo.cms.web;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.eclipse.rap.rwt.application.Application;
+import org.eclipse.rap.rwt.service.ResourceLoader;
+
+/** Web specific utilities around theming. */
+public class WebThemeUtils {
+       private final static CmsLog log = CmsLog.getLog(WebThemeUtils.class);
+
+       public static void apply(Application application, CmsTheme theme) {
+               ResourceLoader resourceLoader = new CmsThemeResourceLoader(theme);
+               resources: for (String path : theme.getImagesPaths()) {
+                       if (path.startsWith("target/"))
+                               continue resources; // skip maven output
+                       application.addResource(path, resourceLoader);
+                       if (log.isTraceEnabled())
+                               log.trace("Theme " + theme.getThemeId() + ": added resource " + path);
+               }
+               for (String path : theme.getRapCssPaths()) {
+                       application.addStyleSheet(theme.getThemeId(), path, resourceLoader);
+                       if (log.isDebugEnabled())
+                               log.debug("Theme " + theme.getThemeId() + ": added RAP CSS " + path);
+               }
+       }
+
+}
diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/osgi/CmsWebAppFactory.java
new file mode 100644 (file)
index 0000000..19b9fe8
--- /dev/null
@@ -0,0 +1,54 @@
+package org.argeo.cms.web.osgi;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.web.CmsWebApp;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.event.EventAdmin;
+
+/** Publish a CmsApp as a RAP application. */
+public class CmsWebAppFactory {
+       private BundleContext bundleContext = FrameworkUtil.getBundle(CmsWebAppFactory.class).getBundleContext();
+       private final static String CONTEXT_NAME = "contextName";
+
+       private EventAdmin eventAdmin;
+
+       private Map<String, CmsWebApp> registrations = Collections.synchronizedMap(new HashMap<>());
+
+       public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               if (contextName != null) {
+                       CmsWebApp cmsWebApp = new CmsWebApp();
+                       cmsWebApp.setEventAdmin(eventAdmin);
+                       cmsWebApp.setCmsApp(cmsApp);
+                       Hashtable<String, String> serviceProperties = new Hashtable<>();
+                       if (!contextName.equals(""))
+                               serviceProperties.put(CONTEXT_NAME, contextName);
+                       cmsWebApp.init(bundleContext, serviceProperties);
+                       registrations.put(contextName, cmsWebApp);
+               }
+       }
+
+       public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               if (contextName != null) {
+                       CmsWebApp cmsWebApp = registrations.get(contextName);
+                       if (cmsWebApp != null) {
+                               cmsWebApp.destroy(bundleContext, new HashMap<>());
+                               cmsWebApp.unsetCmsApp(cmsApp, properties);
+                       } else {
+                               // TODO log warning
+                       }
+               }
+       }
+
+       public void setEventAdmin(EventAdmin eventAdmin) {
+               this.eventAdmin = eventAdmin;
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/.classpath b/swt/rap/org.argeo.swt.specific.rap/.classpath
new file mode 100644 (file)
index 0000000..e03d341
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/swt/rap/org.argeo.swt.specific.rap/.project b/swt/rap/org.argeo.swt.specific.rap/.project
new file mode 100644 (file)
index 0000000..53d7976
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.swt.specific.rap</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/swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore b/swt/rap/org.argeo.swt.specific.rap/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rap/org.argeo.swt.specific.rap/bnd.bnd b/swt/rap/org.argeo.swt.specific.rap/bnd.bnd
new file mode 100644 (file)
index 0000000..bcd9b19
--- /dev/null
@@ -0,0 +1,5 @@
+Import-Package: org.eclipse.swt,\
+org.eclipse.jface.dialogs,\
+org.eclipse.swt.events,\
+javax.servlet.http;version="[3,5)",\
+*
diff --git a/swt/rap/org.argeo.swt.specific.rap/build.properties b/swt/rap/org.argeo.swt.specific.rap/build.properties
new file mode 100644 (file)
index 0000000..fd806ca
--- /dev/null
@@ -0,0 +1,2 @@
+source.. = src/
+output.. = bin/
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java
new file mode 100644 (file)
index 0000000..ac4e0df
--- /dev/null
@@ -0,0 +1,20 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.awt.image.BufferedImage;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+public class BufferedImageDisplay extends Composite {
+       private static final long serialVersionUID = 4541163690514461514L;
+       private BufferedImage image;
+
+       public BufferedImageDisplay(Composite parent, int style) {
+               super(parent, SWT.NO_BACKGROUND);
+       }
+
+       public void setImage(BufferedImage image) {
+               this.image = image;
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java
new file mode 100644 (file)
index 0000000..6100c1a
--- /dev/null
@@ -0,0 +1,17 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class CmsFileDialog extends FileDialog {
+       private static final long serialVersionUID = -7540791204102318801L;
+
+       public CmsFileDialog(Shell parent, int style) {
+               super(parent, style);
+       }
+
+       public CmsFileDialog(Shell parent) {
+               super(parent);
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java
new file mode 100644 (file)
index 0000000..3f30bde
--- /dev/null
@@ -0,0 +1,34 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+
+public class CmsFileUpload extends FileUpload {
+       private static final long serialVersionUID = 8027963992680980549L;
+
+       public CmsFileUpload(Composite parent, int style) {
+               super(parent, style);
+       }
+
+       @Override
+       public void setText(String text) {
+               super.setText(text);
+       }
+
+       @Override
+       public String getFileName() {
+               return super.getFileName();
+       }
+
+       @Override
+       public String[] getFileNames() {
+               return super.getFileNames();
+       }
+
+       @Override
+       public void addSelectionListener(SelectionListener listener) {
+               super.addSelectionListener(listener);
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
new file mode 100644 (file)
index 0000000..a89b921
--- /dev/null
@@ -0,0 +1,39 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.widgets.Widget;
+
+/** Static utilities to bridge differences between RCP and RAP */
+public class EclipseUiSpecificUtils {
+
+       public static void setStyleData(Widget widget, Object data) {
+               widget.setData(RWT.CUSTOM_VARIANT, data);
+       }
+
+       public static Object getStyleData(Widget widget) {
+               return widget.getData(RWT.CUSTOM_VARIANT);
+       }
+
+       public static void setMarkupData(Widget widget) {
+               widget.setData(RWT.MARKUP_ENABLED, true);
+       }
+
+       public static void setMarkupValidationDisabledData(Widget widget) {
+               widget.setData("org.eclipse.rap.rwt.markupValidationDisabled", Boolean.TRUE);
+       }
+
+       /**
+        * TootlTip support is supported only for {@link AbstractTableViewer} in RAP
+        */
+       public static void enableToolTipSupport(Viewer viewer) {
+               if (viewer instanceof ColumnViewer)
+                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
+       }
+
+       private EclipseUiSpecificUtils() {
+       }
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java
new file mode 100644 (file)
index 0000000..f9ca816
--- /dev/null
@@ -0,0 +1,73 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.rap.fileupload.FileDetails;
+import org.eclipse.rap.fileupload.FileUploadHandler;
+import org.eclipse.rap.fileupload.FileUploadReceiver;
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.client.ClientFile;
+import org.eclipse.rap.rwt.client.service.ClientFileUploader;
+import org.eclipse.rap.rwt.dnd.ClientFileTransfer;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Control;
+
+/** Configures a {@link Control} to receive files drop from the client OS. */
+public class FileDropAdapter {
+
+       public void prepareDropTarget(Control control, DropTarget dropTarget) {
+               dropTarget.setTransfer(new Transfer[] { ClientFileTransfer.getInstance() });
+               dropTarget.addDropListener(new DropTargetAdapter() {
+                       private static final long serialVersionUID = 5361645765549463168L;
+
+                       @Override
+                       public void dropAccept(DropTargetEvent event) {
+                               if (!ClientFileTransfer.getInstance().isSupportedType(event.currentDataType)) {
+                                       event.detail = DND.DROP_NONE;
+                               }
+                       }
+
+                       @Override
+                       public void drop(DropTargetEvent event) {
+                               handleFileDrop(control, event);
+                       }
+               });
+       }
+
+       public void handleFileDrop(Control control, DropTargetEvent event) {
+               ClientFile[] clientFiles = (ClientFile[]) event.data;
+               ClientFileUploader service = RWT.getClient().getService(ClientFileUploader.class);
+//             DiskFileUploadReceiver receiver = new DiskFileUploadReceiver();
+               FileUploadReceiver receiver = new FileUploadReceiver() {
+
+                       @Override
+                       public void receive(InputStream stream, FileDetails details) throws IOException {
+                               control.getDisplay().syncExec(() -> {
+                                       try {
+                                               processUpload(stream, details.getFileName(), details.getContentType());
+                                       } catch (IOException e) {
+                                               throw new IllegalStateException("Cannot process upload of " + details.getFileName(), e);
+                                       }
+                               });
+                       }
+               };
+               FileUploadHandler handler = new FileUploadHandler(receiver);
+//                 handler.setMaxFileSize( sizeLimit );
+//                 handler.setUploadTimeLimit( timeLimit );
+               service.submit(handler.getUploadUrl(), clientFiles);
+//             for (File file : receiver.getTargetFiles()) {
+//                     paths.add(file.toPath());
+//             }
+       }
+
+       /** Executed in UI thread */
+       protected void processUpload(InputStream in, String fileName, String contentType) throws IOException {
+
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/UiContext.java
new file mode 100644 (file)
index 0000000..72e17a2
--- /dev/null
@@ -0,0 +1,59 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.swt.widgets.Display;
+
+/** Singleton class providing single sources infos about the UI context. */
+public class UiContext {
+       /** Can be null, thus indicating that we are not in a web context. */
+       public static HttpServletRequest getHttpRequest() {
+               return RWT.getRequest();
+       }
+
+       public static HttpServletResponse getHttpResponse() {
+               return RWT.getResponse();
+       }
+
+       public static Locale getLocale() {
+               if (Display.getCurrent() != null)
+                       return RWT.getUISession().getLocale();
+               else
+                       return Locale.getDefault();
+       }
+
+       public static void setLocale(Locale locale) {
+               if (Display.getCurrent() != null)
+                       RWT.getUISession().setLocale(locale);
+               else
+                       Locale.setDefault(locale);
+       }
+
+       /** Can always be null */
+       @SuppressWarnings("unchecked")
+       public static <T> T getData(String key) {
+               Display display = getDisplay();
+               if (display == null)
+                       return null;
+               return (T) display.getData(key);
+       }
+
+       public static void setData(String key, Object value) {
+               Display display = getDisplay();
+               if (display == null)
+                       throw new IllegalStateException("Not display available");
+               display.setData(key, value);
+       }
+
+       private static Display getDisplay() {
+               return Display.getCurrent();
+       }
+
+       private UiContext() {
+       }
+
+}
diff --git a/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java b/swt/rap/org.argeo.swt.specific.rap/src/org/argeo/eclipse/ui/specific/package-info.java
new file mode 100644 (file)
index 0000000..4ec451f
--- /dev/null
@@ -0,0 +1,2 @@
+/** Eclipse RAP-specific SWT/JFace utilities, to simplify single-sourcing. */
+package org.argeo.eclipse.ui.specific;
\ No newline at end of file
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.classpath b/swt/rcp/org.argeo.cms.e4.rcp/.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/swt/rcp/org.argeo.cms.e4.rcp/.gitignore b/swt/rcp/org.argeo.cms.e4.rcp/.gitignore
new file mode 100644 (file)
index 0000000..710cd68
--- /dev/null
@@ -0,0 +1,3 @@
+/bin/
+/target/
+/exec
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.project b/swt/rcp/org.argeo.cms.e4.rcp/.project
new file mode 100644 (file)
index 0000000..64d5619
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.e4.rcp</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/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..0c68a61
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs b/swt/rcp/org.argeo.cms.e4.rcp/.settings/org.eclipse.pde.core.prefs
new file mode 100644 (file)
index 0000000..f29e940
--- /dev/null
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.cms.e4.rcp/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.e4xmi
new file mode 100644 (file)
index 0000000..5b250ee
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="ASCII"?>
+<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:advanced="http://www.eclipse.org/ui/2010/UIModel/application/ui/advanced" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmi:id="_c4iAgCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.application">
+  <children xsi:type="basic:TrimmedWindow" xmi:id="_hSGBwCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.trimmedwindow.argeocompanion" label="Argeo Companion">
+    <children xsi:type="advanced:PerspectiveStack" xmi:id="_nxzQICnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.perspectivestack.0">
+      <children xsi:type="advanced:Perspective" xmi:id="_oI_oICnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.perspective.cmsadmin" label="CMS Admin">
+        <children xsi:type="basic:PartSashContainer" xmi:id="_qc16ECnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.partsashcontainer.0" horizontal="true">
+          <children xsi:type="basic:PartStack" xmi:id="_RE87kDsXEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.rcp.partstack.1">
+            <children xsi:type="basic:Part" xmi:id="_V1WvgDsXEeiUntFYWh-hFg" elementId="org.argeo.cms.e4.rcp.part.files" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.files.NodeFsBrowserView" label="Files"/>
+            <children xsi:type="basic:Part" xmi:id="_vOqDQCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.part.jcr" containerData="4000" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrBrowserView" label="JCR"/>
+          </children>
+          <children xsi:type="basic:PartStack" xmi:id="_0eRiwCnCEei1F8sdBz8Mpw" elementId="org.argeo.cms.e4.rcp.partstack.0" containerData="6000">
+            <tags>editorArea</tags>
+          </children>
+        </children>
+      </children>
+    </children>
+  </children>
+  <descriptors xmi:id="__9SDsC8JEeq0koquN4xGyg" elementId="org.argeo.cms.e4.partdescriptor.nodeEditor" allowMultiple="true" category="editorArea" closeable="true" contributionURI="bundleclass://org.argeo.cms.e4/org.argeo.cms.e4.jcr.JcrNodeEditor"/>
+  <addons xmi:id="_c4iAgSnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
+  <addons xmi:id="_c4iAginCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
+  <addons xmi:id="_c4iAgynCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
+  <addons xmi:id="_c4iAhCnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
+  <addons xmi:id="_c4iAhSnCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
+  <addons xmi:id="_c4iAhinCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
+  <addons xmi:id="_c4iAhynCEei1F8sdBz8Mpw" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
+</application:Application>
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties b/swt/rcp/org.argeo.cms.e4.rcp/argeo-companion.properties
new file mode 100644 (file)
index 0000000..0a0da75
--- /dev/null
@@ -0,0 +1,23 @@
+argeo.osgi.start.2.node=\
+org.eclipse.equinox.http.servlet,\
+org.eclipse.equinox.metatype,\
+org.eclipse.equinox.cm,\
+org.argeo.init
+
+argeo.osgi.start.3.node=\
+org.argeo.cms,\
+org.argeo.cms.jcr,\
+
+applicationXMI=org.argeo.cms.e4.rcp/argeo-companion.e4xmi
+lifeCycleURI=bundleclass://org.argeo.cms.e4.rcp/org.argeo.cms.e4.rcp.CmsRcpLifeCycle
+clearPersistedState=true
+#argeo.cms.desktop.inTray=true
+
+# Remote node:
+#argeo.node.repo.labeledUri=http://root:demo@localhost:7070/jcr/node
+
+# Logging
+log.org.argeo=DEBUG
+
+argeo.node.useradmin.uris=os:///
+eclipse.application=org.argeo.cms.e4.rcp.CmsE4Application
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd b/swt/rcp/org.argeo.cms.e4.rcp/bnd.bnd
new file mode 100644 (file)
index 0000000..ff79c80
--- /dev/null
@@ -0,0 +1,7 @@
+Bundle-SymbolicName: org.argeo.cms.e4.rcp;singleton=true
+
+Require-Bundle: org.eclipse.core.runtime
+
+Import-Package: !org.eclipse.core.runtime,\
+org.eclipse.swt,\
+*
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/build.properties b/swt/rcp/org.argeo.cms.e4.rcp/build.properties
new file mode 100644 (file)
index 0000000..355413e
--- /dev/null
@@ -0,0 +1,5 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               argeo-companion.e4xmi
+source.. = src/
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/log4j.properties b/swt/rcp/org.argeo.cms.e4.rcp/log4j.properties
new file mode 100644 (file)
index 0000000..13f949f
--- /dev/null
@@ -0,0 +1,32 @@
+log4j.rootLogger=WARN, development
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+log4j.logger.org.argeo.jackrabbit.remote.ExtendedDispatcherServlet=WARN
+log4j.logger.org.argeo.server.webextender.TomcatDeployer=INFO
+
+#log4j.logger.org.springframework.security=DEBUG
+#log4j.logger.org.apache.commons.exec=DEBUG
+#log4j.logger.org.apache.jackrabbit.webdav=DEBUG
+#log4j.logger.org.apache.jackrabbit.remote=DEBUG
+#log4j.logger.org.apache.jackrabbit.core.observation=DEBUG
+
+log4j.logger.org.apache.catalina=INFO
+log4j.logger.org.apache.coyote=INFO
+
+log4j.logger.org.apache.directory=INFO
+log4j.logger.org.apache.directory.server=ERROR
+log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR
+
+## Appenders
+# console is set to be a ConsoleAppender.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+
+# console uses PatternLayout.
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n
+
+# development appender (slow!)
+log4j.appender.development=org.apache.log4j.ConsoleAppender
+log4j.appender.development.layout=org.apache.log4j.PatternLayout
+log4j.appender.development.layout.ConversionPattern=%d{HH:mm:ss} [%16.16t] %5p %m (%F:%L) %c%n
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/plugin.xml b/swt/rcp/org.argeo.cms.e4.rcp/plugin.xml
new file mode 100644 (file)
index 0000000..3e6896b
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         id="CmsE4Application"
+         name="CMS E4 Application"
+         point="org.eclipse.core.runtime.applications">
+      <application
+            cardinality="singleton-global"
+            thread="main"
+            visible="true">
+         <run class="org.argeo.cms.e4.rcp.CmsE4Application"></run>
+      </application>
+   </extension>
+</plugin>
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsE4Application.java
new file mode 100644 (file)
index 0000000..b37a765
--- /dev/null
@@ -0,0 +1,207 @@
+package org.argeo.cms.e4.rcp;
+
+import java.security.PrivilegedExceptionAction;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.swt.CmsException;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SimpleSwtUxContext;
+import org.argeo.cms.swt.auth.CmsLoginShell;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+import org.eclipse.swt.widgets.Display;
+
+public class CmsE4Application implements IApplication, CmsView {
+       private LoginContext loginContext;
+       private IApplication e4Application;
+       private UxContext uxContext;
+       private String uid;
+
+       @Override
+       public Object start(IApplicationContext context) throws Exception {
+               // TODO wait for CMS to be ready
+               Thread.sleep(5000);
+
+               uid = UUID.randomUUID().toString();
+               Subject subject = new Subject();
+               Display display = createDisplay();
+               CmsLoginShell loginShell = new CmsLoginShell(this, null);
+               // TODO customize CmsLoginShell to be smaller and centered
+               loginShell.setSubject(subject);
+               try {
+                       // try pre-auth
+                       loginContext = new LoginContext(CmsAuth.LOGIN_CONTEXT_SINGLE_USER, subject, loginShell);
+                       loginContext.login();
+               } catch (LoginException e) {
+                       e.printStackTrace();
+                       loginShell.createUi();
+                       loginShell.open();
+
+                       while (!loginShell.getShell().isDisposed()) {
+                               if (!display.readAndDispatch())
+                                       display.sleep();
+                       }
+               }
+               if (CurrentUser.getUsername(getSubject()) == null)
+                       throw new IllegalStateException("Cannot log in");
+
+               // try {
+               // CallbackHandler callbackHandler = new DefaultLoginDialog(
+               // display.getActiveShell());
+               // loginContext = new LoginContext(
+               // NodeConstants.LOGIN_CONTEXT_SINGLE_USER, subject,
+               // callbackHandler);
+               // } catch (LoginException e1) {
+               // throw new CmsException("Cannot initialize login context", e1);
+               // }
+               //
+               // // login
+               // try {
+               // loginContext.login();
+               // subject = loginContext.getSubject();
+               // } catch (LoginException e) {
+               // e.printStackTrace();
+               // display.dispose();
+               // try {
+               // Thread.sleep(2000);
+               // } catch (InterruptedException e1) {
+               // // silent
+               // }
+               // return null;
+               // }
+
+               uxContext = new SimpleSwtUxContext();
+               // UiContext.setData(CmsView.KEY, this);
+               CmsSwtUtils.registerCmsView(loginShell.getShell(), this);
+               e4Application = getApplication(null);
+               Object res = Subject.doAs(subject, new PrivilegedExceptionAction<Object>() {
+
+                       @Override
+                       public Object run() throws Exception {
+                               return e4Application.start(context);
+                       }
+
+               });
+               return res;
+       }
+
+       @Override
+       public void stop() {
+               if (e4Application != null)
+                       e4Application.stop();
+       }
+
+       static IApplication getApplication(String[] args) {
+               IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME,
+                               Platform.PT_APPLICATIONS, "org.eclipse.e4.ui.workbench.swt.E4Application");
+               try {
+                       IConfigurationElement[] elements = extension.getConfigurationElements();
+                       if (elements.length > 0) {
+                               IConfigurationElement[] runs = elements[0].getChildren("run");
+                               if (runs.length > 0) {
+                                       Object runnable;
+                                       runnable = runs[0].createExecutableExtension("class");
+                                       if (runnable instanceof IApplication)
+                                               return (IApplication) runnable;
+                               }
+                       }
+               } catch (Exception e) {
+                       throw new IllegalStateException("Cannot find e4 application", e);
+               }
+               throw new IllegalStateException("Cannot find e4 application");
+       }
+
+       public static Display createDisplay() {
+               Display.setAppName("Argeo CMS RCP");
+
+               // create the display
+               Display newDisplay = Display.getCurrent();
+               if (newDisplay == null) {
+                       newDisplay = new Display();
+               }
+               // Set the priority higher than normal so as to be higher
+               // than the JobManager.
+               Thread.currentThread().setPriority(Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1));
+               return newDisplay;
+       }
+
+       //
+       // CMS VIEW
+       //
+
+       @Override
+       public UxContext getUxContext() {
+               return uxContext;
+       }
+
+       @Override
+       public void navigateTo(String state) {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public void authChange(LoginContext loginContext) {
+               if (loginContext == null)
+                       throw new CmsException("Login context cannot be null");
+               // logout previous login context
+               // if (this.loginContext != null)
+               // try {
+               // this.loginContext.logout();
+               // } catch (LoginException e1) {
+               // System.err.println("Could not log out: " + e1);
+               // }
+               this.loginContext = loginContext;
+       }
+
+       @Override
+       public void logout() {
+               if (loginContext == null)
+                       throw new CmsException("Login context should not bet null");
+               try {
+                       CurrentUser.logoutCmsSession(loginContext.getSubject());
+                       loginContext.logout();
+               } catch (LoginException e) {
+                       throw new CmsException("Cannot log out", e);
+               }
+       }
+
+       @Override
+       public void exception(Throwable e) {
+               // TODO Auto-generated method stub
+
+       }
+
+       @Override
+       public CmsImageManager getImageManager() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return CurrentUser.isAnonymous(getSubject());
+       }
+
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+}
diff --git a/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java b/swt/rcp/org.argeo.cms.e4.rcp/src/org/argeo/cms/e4/rcp/CmsRcpLifeCycle.java
new file mode 100644 (file)
index 0000000..1d38fe7
--- /dev/null
@@ -0,0 +1,27 @@
+package org.argeo.cms.e4.rcp;
+
+import org.eclipse.e4.core.contexts.IEclipseContext;
+import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
+import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
+import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions;
+import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals;
+
+@SuppressWarnings("restriction")
+public class CmsRcpLifeCycle {
+
+       @PostContextCreate
+       void postContextCreate(IEclipseContext workbenchContext) {
+       }
+
+       @PreSave
+       void preSave(IEclipseContext workbenchContext) {
+       }
+
+       @ProcessAdditions
+       void processAdditions(IEclipseContext workbenchContext) {
+       }
+
+       @ProcessRemovals
+       void processRemovals(IEclipseContext workbenchContext) {
+       }
+}
diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath b/swt/rcp/org.argeo.cms.swt.rcp.cli/.classpath
new file mode 100644 (file)
index 0000000..81fe078
--- /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-17"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/.project b/swt/rcp/org.argeo.cms.swt.rcp.cli/.project
new file mode 100644 (file)
index 0000000..07c8c5d
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.swt.rcp.cli</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/swt/rcp/org.argeo.cms.swt.rcp.cli/bnd.bnd b/swt/rcp/org.argeo.cms.swt.rcp.cli/bnd.bnd
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/swt/rcp/org.argeo.cms.swt.rcp.cli/build.properties b/swt/rcp/org.argeo.cms.swt.rcp.cli/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/swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java b/swt/rcp/org.argeo.cms.swt.rcp.cli/src/org/argeo/cms/swt/rcp/cli/CmsCli.java
new file mode 100644 (file)
index 0000000..187f5b0
--- /dev/null
@@ -0,0 +1,110 @@
+package org.argeo.cms.swt.rcp.cli;
+
+import java.lang.management.ManagementFactory;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.concurrent.ForkJoinPool;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.cli.CommandsCli;
+import org.argeo.api.cli.DescribedCommand;
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.runtime.StaticCms;
+import org.argeo.cms.swt.app.CmsUserApp;
+import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
+import org.argeo.util.register.Component;
+import org.argeo.util.register.ComponentRegister;
+
+public class CmsCli extends CommandsCli {
+
+       public CmsCli(String commandName) {
+               super(commandName);
+               addCommand("static", new Launch());
+       }
+
+       @Override
+       public String getDescription() {
+               return "Argeo CMS utilities.";
+       }
+
+       static class Launch implements DescribedCommand<String> {
+               private Option dataOption;
+               private Option uiOption;
+
+               @Override
+               public Options getOptions() {
+                       Options options = new Options();
+                       dataOption = Option.builder().longOpt("data").hasArg().required()
+                                       .desc("path to the writable data area (mandatory)").build();
+                       uiOption = Option.builder().longOpt("ui").desc("open a user interface").build();
+                       options.addOption(dataOption);
+                       options.addOption(uiOption);
+                       return options;
+               }
+
+               @Override
+               public String apply(List<String> args) {
+                       CommandLine cl = toCommandLine(args);
+                       String dataPath = cl.getOptionValue(dataOption);
+                       boolean ui = cl.hasOption(uiOption);
+
+                       Path instancePath = Paths.get(dataPath);
+                       System.setProperty("osgi.instance.area", instancePath.toUri().toString());
+
+                       StaticCms staticCms = new StaticCms() {
+                               @Override
+                               protected void addComponents(ComponentRegister register) {
+                                       if (ui) {
+                                               Component<? extends ProvidedRepository> contentRepositoryC = register
+                                                               .find(ProvidedRepository.class, null).first();
+                                               CmsUserApp cmsApp = new CmsUserApp();
+                                               Component<CmsUserApp> cmsAppC = new Component.Builder<>(cmsApp) //
+                                                               .addType(CmsApp.class) //
+                                                               .addType(CmsUserApp.class) //
+                                                               .addDependency(contentRepositoryC.getType(ProvidedRepository.class),
+                                                                               cmsApp::setContentRepository, null) //
+                                                               .build(register);
+
+                                               CmsRcpDisplayFactory displayFactory = new CmsRcpDisplayFactory();
+                                               Component<CmsRcpDisplayFactory> displayFactoryC = new Component.Builder<>(displayFactory) //
+                                                               .addActivation(displayFactory::init) //
+                                                               .addDeactivation(displayFactory::destroy) //
+                                                               .build(register);
+
+                                       }
+                               }
+
+                               @Override
+                               protected void postActivation(ComponentRegister register) {
+                                       if (ui) {
+                                               Component<? extends CmsUserApp> cmsAppC = register.find(CmsUserApp.class, null).first();
+                                               CmsRcpDisplayFactory.openCmsApp(null, cmsAppC.get(), "data", (e) -> {
+                                                       // asynchronous in order to avoid deadlock in UI thread
+                                                       ForkJoinPool.commonPool().execute(() -> stop());
+                                               });
+                                       }
+                               }
+
+                       };
+                       Runtime.getRuntime().addShutdownHook(new Thread(() -> staticCms.stop(), "Static CMS Shutdown"));
+                       staticCms.start();
+
+                       long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+                       System.out.println("Static CMS available in " + jvmUptime + " ms.");
+
+                       staticCms.waitForStop();
+
+                       return null;
+               }
+
+               @Override
+               public String getDescription() {
+                       return "Launch a static CMS.";
+               }
+
+       }
+}
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.classpath b/swt/rcp/org.argeo.cms.swt.rcp/.classpath
new file mode 100644 (file)
index 0000000..e801ebf
--- /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-11"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.gitignore b/swt/rcp/org.argeo.cms.swt.rcp/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/.project b/swt/rcp/org.argeo.cms.swt.rcp/.project
new file mode 100644 (file)
index 0000000..c9c2a44
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.ui.rcp</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.cms.swt.rcp/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpDisplayFactory.xml
new file mode 100644 (file)
index 0000000..a0c0f0f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="CMS RCP Display Factory">
+   <implementation class="org.argeo.cms.ui.rcp.CmsRcpDisplayFactory"/>
+</scr:component>
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml b/swt/rcp/org.argeo.cms.swt.rcp/OSGI-INF/cmsRcpServletFactory.xml
new file mode 100644 (file)
index 0000000..a1f0b3a
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="CMS RCP Servlet Factory">
+   <implementation class="org.argeo.cms.ui.rcp.servlet.CmsRcpServletFactory"/>
+   <reference bind="addCmsApp" cardinality="0..n" interface="org.argeo.api.cms.CmsApp" name="CmsApp" policy="dynamic" unbind="removeCmsApp"/>
+   <reference bind="setEventAdmin" cardinality="1..1" interface="org.osgi.service.event.EventAdmin" name="EventAdmin" policy="static"/>
+   <reference bind="setHttpService" cardinality="1..1" interface="org.osgi.service.http.HttpService" name="HttpService" policy="static"/>
+</scr:component>
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd b/swt/rcp/org.argeo.cms.swt.rcp/bnd.bnd
new file mode 100644 (file)
index 0000000..91f0a8a
--- /dev/null
@@ -0,0 +1,12 @@
+
+Import-Package:\
+org.argeo.cms.auth,\
+org.eclipse.swt,\
+org.eclipse.swt.graphics,\
+org.w3c.css.sac,\
+*
+
+Service-Component:\
+OSGI-INF/cmsRcpDisplayFactory.xml,\
+OSGI-INF/cmsRcpServletFactory.xml
+
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/build.properties b/swt/rcp/org.argeo.cms.swt.rcp/build.properties
new file mode 100644 (file)
index 0000000..5eef705
--- /dev/null
@@ -0,0 +1,6 @@
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               OSGI-INF/,\
+               OSGI-INF/cmsRcpServletFactory.xml
+source.. = src/
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpApp.java
new file mode 100644 (file)
index 0000000..e25a9f7
--- /dev/null
@@ -0,0 +1,230 @@
+package org.argeo.cms.ui.rcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.api.cms.CmsAuth;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsSession;
+import org.argeo.api.cms.ux.CmsImageManager;
+import org.argeo.api.cms.ux.CmsTheme;
+import org.argeo.api.cms.ux.CmsUi;
+import org.argeo.api.cms.ux.CmsView;
+import org.argeo.api.cms.ux.UxContext;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.eclipse.e4.ui.css.core.engine.CSSEngine;
+import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler;
+import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/** Runs a {@link CmsApp} as an SWT desktop application. */
+@SuppressWarnings("restriction")
+public class CmsRcpApp implements CmsView {
+       private final static CmsLog log = CmsLog.getLog(CmsRcpApp.class);
+
+       // private BundleContext bundleContext =
+       // FrameworkUtil.getBundle(CmsRcpApp.class).getBundleContext();
+
+       private Shell shell;
+       private CmsApp cmsApp;
+
+       // CMS View
+       private String uid;
+       private LoginContext loginContext;
+
+       private EventAdmin eventAdmin;
+
+       private CSSEngine cssEngine;
+
+       private CmsUi ui;
+       // TODO make it configurable
+       private String uiName = "desktop";
+
+       public CmsRcpApp(String uiName) {
+               uid = UUID.randomUUID().toString();
+               this.uiName = uiName;
+       }
+
+       public void initRcpApp() {
+               Display display = Display.getCurrent();
+               shell = new Shell(display);
+               shell.setText("Argeo CMS");
+               Composite parent = shell;
+               parent.setLayout(CmsSwtUtils.noSpaceGridLayout());
+               CmsSwtUtils.registerCmsView(shell, CmsRcpApp.this);
+
+               try {
+                       loginContext = new LoginContext(CmsAuth.SINGLE_USER.getLoginContextName());
+                       loginContext.login();
+               } catch (LoginException e) {
+                       throw new IllegalStateException("Could not log in.", e);
+               }
+               if (log.isDebugEnabled())
+                       log.debug("Logged in to desktop: " + loginContext.getSubject());
+
+               Subject.doAs(loginContext.getSubject(), (PrivilegedAction<Void>) () -> {
+
+                       // TODO factorise with web app
+                       parent.setData(CmsApp.UI_NAME_PROPERTY, uiName);
+                       ui = cmsApp.initUi(parent);
+                       if (ui instanceof Composite)
+                               ((Composite) ui).setLayoutData(CmsSwtUtils.fillAll());
+                       // we need ui to be set before refresh so that CmsView can store UI context data
+                       // in it.
+                       cmsApp.refreshUi(ui, null);
+
+                       // Styling
+                       CmsTheme theme = CmsSwtUtils.getCmsTheme(parent);
+                       if (theme != null) {
+                               cssEngine = new CSSSWTEngineImpl(display);
+                               for (String path : theme.getSwtCssPaths()) {
+                                       try (InputStream in = theme.loadPath(path)) {
+                                               cssEngine.parseStyleSheet(in);
+                                       } catch (IOException e) {
+                                               throw new IllegalStateException("Cannot load stylesheet " + path, e);
+                                       }
+                               }
+                               cssEngine.setErrorHandler(new CSSErrorHandler() {
+                                       public void error(Exception e) {
+                                               log.error("SWT styling error: ", e);
+                                       }
+                               });
+                               applyStyles(shell);
+                       }
+                       shell.layout(true, true);
+
+                       shell.open();
+                       return null;
+               });
+       }
+
+       /*
+        * CMS VIEW
+        */
+
+       @Override
+       public String getUid() {
+               return uid;
+       }
+
+       @Override
+       public UxContext getUxContext() {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public void navigateTo(String state) {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public void authChange(LoginContext loginContext) {
+       }
+
+       @Override
+       public void logout() {
+               if (loginContext != null)
+                       try {
+                               loginContext.logout();
+                       } catch (LoginException e) {
+                               log.error("Cannot log out", e);
+                       }
+       }
+
+       @Override
+       public void exception(Throwable e) {
+               log.error("Unexpected exception in CMS RCP", e);
+       }
+
+       @Override
+       public CmsImageManager getImageManager() {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public CmsSession getCmsSession() {
+               CmsSession cmsSession = cmsApp.getCmsContext().getCmsSession(getSubject());
+               return cmsSession;
+       }
+
+       @Override
+       public Object getData(String key) {
+               if (ui != null) {
+                       return ui.getData(key);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+       @Override
+       public void setData(String key, Object value) {
+               if (ui != null) {
+                       ui.setData(key, value);
+               } else {
+                       throw new IllegalStateException("UI is not initialized");
+               }
+       }
+
+       @Override
+       public boolean isAnonymous() {
+               return false;
+       }
+
+       @Override
+       public void applyStyles(Object node) {
+               if (cssEngine != null)
+                       cssEngine.applyStyles(node, true);
+       }
+
+       @Override
+       public void sendEvent(String topic, Map<String, Object> properties) {
+               if (properties == null)
+                       properties = new HashMap<>();
+               if (properties.containsKey(CMS_VIEW_UID_PROPERTY) && !properties.get(CMS_VIEW_UID_PROPERTY).equals(uid))
+                       throw new IllegalArgumentException("Property " + CMS_VIEW_UID_PROPERTY + " is set to another CMS view uid ("
+                                       + properties.get(CMS_VIEW_UID_PROPERTY) + ") then " + uid);
+               properties.put(CMS_VIEW_UID_PROPERTY, uid);
+               eventAdmin.sendEvent(new Event(topic, properties));
+       }
+
+       public <T> T doAs(PrivilegedAction<T> action) {
+               return Subject.doAs(getSubject(), action);
+       }
+
+       protected Subject getSubject() {
+               return loginContext.getSubject();
+       }
+
+       public Shell getShell() {
+               return shell;
+       }
+
+       /*
+        * DEPENDENCY INJECTION
+        */
+       public void setCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               this.cmsApp = cmsApp;
+       }
+
+       public void unsetCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               this.cmsApp = null;
+       }
+
+       public void setEventAdmin(EventAdmin eventAdmin) {
+               this.eventAdmin = eventAdmin;
+       }
+
+}
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/CmsRcpDisplayFactory.java
new file mode 100644 (file)
index 0000000..ec471c0
--- /dev/null
@@ -0,0 +1,90 @@
+package org.argeo.cms.ui.rcp;
+
+import java.nio.file.Path;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.util.OS;
+import org.eclipse.swt.widgets.Display;
+import org.osgi.service.event.EventAdmin;
+import org.eclipse.swt.events.DisposeListener;
+
+/** Creates the SWT {@link Display} in a dedicated thread. */
+public class CmsRcpDisplayFactory {
+       /** File name in a run directory */
+       private final static String ARGEO_RCP_URL = "argeo.rcp.url";
+
+       /** There is only one display in RCP mode */
+       private static Display display;
+
+       private CmsUiThread uiThread;
+
+       private boolean shutdown = false;
+
+       public void init() {
+               uiThread = new CmsUiThread();
+               uiThread.start();
+               while (display == null)
+                       try {
+                               Thread.sleep(100);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+       }
+
+       public void destroy() {
+               shutdown = true;
+               display.wake();
+               try {
+                       uiThread.join();
+               } catch (InterruptedException e) {
+                       // silent
+               } finally {
+                       uiThread = null;
+               }
+       }
+
+       class CmsUiThread extends Thread {
+
+               public CmsUiThread() {
+                       super("CMS UI");
+               }
+
+               @Override
+               public void run() {
+                       display = Display.getDefault();
+                       display.setRuntimeExceptionHandler((e) -> e.printStackTrace());
+                       display.setErrorHandler((e) -> e.printStackTrace());
+
+//                     for (String contextName : cmsApps.keySet()) {
+//                             openCmsApp(contextName);
+//                     }
+
+                       while (!shutdown) {
+                               if (!display.readAndDispatch())
+                                       display.sleep();
+                       }
+                       display.dispose();
+                       display = null;
+               }
+       }
+
+       public static Display getDisplay() {
+               return display;
+       }
+
+       public static void openCmsApp(EventAdmin eventAdmin, CmsApp cmsApp, String uiName,
+                       DisposeListener disposeListener) {
+               CmsRcpDisplayFactory.getDisplay().syncExec(() -> {
+                       CmsRcpApp cmsRcpApp = new CmsRcpApp(uiName);
+                       cmsRcpApp.setEventAdmin(eventAdmin);
+                       cmsRcpApp.setCmsApp(cmsApp, null);
+                       cmsRcpApp.initRcpApp();
+                       if (disposeListener != null)
+                               cmsRcpApp.getShell().addDisposeListener(disposeListener);
+               });
+       }
+
+       public static Path getUrlRunFile() {
+               return OS.getRunDir().resolve(CmsRcpDisplayFactory.ARGEO_RCP_URL);
+       }
+}
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServlet.java
new file mode 100644 (file)
index 0000000..f8aecd3
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.cms.ui.rcp.servlet;
+
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.Objects;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
+import org.osgi.service.event.EventAdmin;
+
+/** Open the related app when called. */
+public class CmsRcpServlet extends HttpServlet {
+       private static final long serialVersionUID = -3944472431354848923L;
+       private final static Logger logger = System.getLogger(CmsRcpServlet.class.getName());
+
+       private CmsApp cmsApp;
+       private EventAdmin eventAdmin;
+
+       public CmsRcpServlet(EventAdmin eventAdmin, CmsApp cmsApp) {
+               Objects.requireNonNull(eventAdmin);
+               Objects.requireNonNull(cmsApp);
+               this.cmsApp = cmsApp;
+               this.eventAdmin = eventAdmin;
+       }
+
+       @Override
+       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+               String path = req.getPathInfo();
+               String uiName = path != null ? path.substring(path.lastIndexOf('/') + 1) : "";
+               CmsRcpDisplayFactory.openCmsApp(eventAdmin, cmsApp, uiName, null);
+               logger.log(Level.DEBUG, "Opened RCP UI  " + uiName + " of  CMS App " + req.getServletPath());
+       }
+
+}
diff --git a/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java b/swt/rcp/org.argeo.cms.swt.rcp/src/org/argeo/cms/ui/rcp/servlet/CmsRcpServletFactory.java
new file mode 100644 (file)
index 0000000..7c24f87
--- /dev/null
@@ -0,0 +1,131 @@
+package org.argeo.cms.ui.rcp.servlet;
+
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.net.DatagramSocket;
+import java.net.ServerSocket;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import javax.servlet.Servlet;
+
+import org.argeo.api.cms.CmsApp;
+import org.argeo.cms.ui.rcp.CmsRcpDisplayFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.http.HttpService;
+
+/** Publishes one {@link CmsRcpServlet} per {@link CmsApp}. */
+public class CmsRcpServletFactory {
+       private final static Logger logger = System.getLogger(CmsRcpServletFactory.class.getName());
+
+       private BundleContext bundleContext = FrameworkUtil.getBundle(CmsRcpServletFactory.class).getBundleContext();
+
+       private CompletableFuture<EventAdmin> eventAdmin = new CompletableFuture<>();
+
+       private Map<String, ServiceRegistration<Servlet>> registrations = Collections.synchronizedMap(new HashMap<>());
+
+       public void init() {
+
+       }
+
+       public void destroy() {
+               Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
+               try {
+                       if (Files.exists(runFile)) {
+                               Files.delete(runFile);
+                       }
+               } catch (IOException e) {
+                       logger.log(Level.ERROR, "Cannot delete " + runFile, e);
+               }
+       }
+
+       public void addCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               if (contextName != null) {
+                       eventAdmin.thenAccept((eventAdmin) -> {
+                               CmsRcpServlet servlet = new CmsRcpServlet(eventAdmin, cmsApp);
+                               Hashtable<String, String> serviceProperties = new Hashtable<>();
+                               serviceProperties.put("osgi.http.whiteboard.servlet.pattern", "/" + contextName + "/*");
+                               ServiceRegistration<Servlet> sr = bundleContext.registerService(Servlet.class, servlet,
+                                               serviceProperties);
+                               registrations.put(contextName, sr);
+                       });
+               }
+       }
+
+       public void removeCmsApp(CmsApp cmsApp, Map<String, String> properties) {
+               String contextName = properties.get(CmsApp.CONTEXT_NAME_PROPERTY);
+               if (contextName != null) {
+                       ServiceRegistration<Servlet> sr = registrations.get(contextName);
+                       sr.unregister();
+               }
+       }
+
+       public void setEventAdmin(EventAdmin eventAdmin) {
+               this.eventAdmin.complete(eventAdmin);
+       }
+
+       public void setHttpService(HttpService httpService, Map<String, Object> properties) {
+               Integer httpPort = Integer.parseInt(properties.get("http.port").toString());
+               String baseUrl = "http://localhost:" + httpPort + "/";
+               Path runFile = CmsRcpDisplayFactory.getUrlRunFile();
+               try {
+                       if (!Files.exists(runFile)) {
+                               Files.createDirectories(runFile.getParent());
+                               // TODO give read permission only to the owner
+                               Files.createFile(runFile);
+                       } else {
+                               URI uri = URI.create(Files.readString(runFile));
+                               if (!httpPort.equals(uri.getPort()))
+                                       if (!isPortAvailable(uri.getPort())) {
+                                               throw new IllegalStateException("Another CMS is running on " + runFile);
+                                       } else {
+                                               logger.log(Level.WARNING,
+                                                               "Run file " + runFile + " found but port of " + uri + " is available. Overwriting...");
+                                       }
+                       }
+                       Files.writeString(runFile, baseUrl, StandardCharsets.UTF_8);
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot write run file to " + runFile, e);
+               }
+               logger.log(Level.DEBUG, "RCP available under " + baseUrl + ", written to " + runFile);
+       }
+
+       protected boolean isPortAvailable(int port) {
+               ServerSocket ss = null;
+               DatagramSocket ds = null;
+               try {
+                       ss = new ServerSocket(port);
+                       ss.setReuseAddress(true);
+                       ds = new DatagramSocket(port);
+                       ds.setReuseAddress(true);
+                       return true;
+               } catch (IOException e) {
+               } finally {
+                       if (ds != null) {
+                               ds.close();
+                       }
+
+                       if (ss != null) {
+                               try {
+                                       ss.close();
+                               } catch (IOException e) {
+                                       /* should not be thrown */
+                               }
+                       }
+               }
+
+               return false;
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.classpath b/swt/rcp/org.argeo.swt.specific.rcp/.classpath
new file mode 100644 (file)
index 0000000..457b115
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8" />
+       <classpathentry kind="output" path="bin" />
+</classpath>
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.gitignore b/swt/rcp/org.argeo.swt.specific.rcp/.gitignore
new file mode 100644 (file)
index 0000000..5e77890
--- /dev/null
@@ -0,0 +1,3 @@
+/target/
+/bin/
+*.log
\ No newline at end of file
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/.project b/swt/rcp/org.argeo.swt.specific.rcp/.project
new file mode 100644 (file)
index 0000000..c79ee3f
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.swt.specific.rcp</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/swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore b/swt/rcp/org.argeo.swt.specific.rcp/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd b/swt/rcp/org.argeo.swt.specific.rcp/bnd.bnd
new file mode 100644 (file)
index 0000000..bb88efd
--- /dev/null
@@ -0,0 +1,20 @@
+Import-Package: \
+!java.*,\
+org.apache.commons.io,\
+org.eclipse.core.commands,\
+!org.eclipse.core.runtime,\
+!org.eclipse.ui.plugin,\
+org.eclipse.swt,\
+javax.servlet.http;version="[3,5)",\
+javax.servlet;version="[3,5)",\
+*
+
+Export-Package: org.argeo.*,\
+org.eclipse.rap.fileupload.*;version="3.10",\
+org.eclipse.rap.rwt.*;version="3.10"
+
+# Was !org.eclipse.core.commands,\ why ?
+
+#Bundle-Activator: org.argeo.eclipse.ui.ArgeoUiPlugin
+#Bundle-ActivationPolicy: lazy
+#Ignore-Package: org.eclipse.core.commands
\ No newline at end of file
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/build.properties b/swt/rcp/org.argeo.swt.specific.rcp/build.properties
new file mode 100644 (file)
index 0000000..c6b651a
--- /dev/null
@@ -0,0 +1,3 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpClient.java
new file mode 100644 (file)
index 0000000..0d9ce48
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.eclipse.ui.rcp.internal.rwt;
+
+import org.eclipse.rap.rwt.client.Client;
+import org.eclipse.rap.rwt.client.service.BrowserNavigation;
+import org.eclipse.rap.rwt.client.service.BrowserNavigationListener;
+import org.eclipse.rap.rwt.client.service.ClientService;
+import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
+
+public class RcpClient implements Client {
+
+       @Override
+       public <T extends ClientService> T getService(Class<T> type) {
+               if (type.isAssignableFrom(JavaScriptExecutor.class))
+                       return (T) javaScriptExecutor;
+               else if (type.isAssignableFrom(BrowserNavigation.class))
+                       return (T) browserNavigation;
+               else
+                       return null;
+       }
+
+       private JavaScriptExecutor javaScriptExecutor = new JavaScriptExecutor() {
+
+               @Override
+               public void execute(String code) {
+                       // TODO Auto-generated method stub
+
+               }
+       };
+       private BrowserNavigation browserNavigation = new BrowserNavigation() {
+
+               @Override
+               public void pushState(String state, String title) {
+                       // TODO Auto-generated method stub
+
+               }
+
+               @Override
+               public void addBrowserNavigationListener(
+                               BrowserNavigationListener listener) {
+                       // TODO Auto-generated method stub
+
+               }
+       };
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/rcp/internal/rwt/RcpResourceManager.java
new file mode 100644 (file)
index 0000000..91109a9
--- /dev/null
@@ -0,0 +1,46 @@
+package org.argeo.eclipse.ui.rcp.internal.rwt;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.io.IOUtils;
+import org.eclipse.rap.rwt.service.ResourceManager;
+
+public class RcpResourceManager implements ResourceManager {
+       private Map<String, byte[]> register = Collections
+                       .synchronizedMap(new TreeMap<String, byte[]>());
+
+       @Override
+       public void register(String name, InputStream in) {
+               try {
+                       register.put(name, IOUtils.toByteArray(in));
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot register " + name, e);
+               }
+       }
+
+       @Override
+       public boolean unregister(String name) {
+               return register.remove(name) != null;
+       }
+
+       @Override
+       public InputStream getRegisteredContent(String name) {
+               return new ByteArrayInputStream(register.get(name));
+       }
+
+       @Override
+       public String getLocation(String name) {
+               return name;
+       }
+
+       @Override
+       public boolean isRegistered(String name) {
+               return register.containsKey(name);
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/BufferedImageDisplay.java
new file mode 100644 (file)
index 0000000..7fd2db1
--- /dev/null
@@ -0,0 +1,38 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.awt.BorderLayout;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+
+import javax.swing.JPanel;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.widgets.Composite;
+
+public class BufferedImageDisplay extends Composite {
+       private BufferedImage image;
+
+       public BufferedImageDisplay(Composite parent, int style) {
+               super(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND);
+               Frame frame = SWT_AWT.new_Frame(this);
+               frame.setLayout(new BorderLayout());
+               frame.add(new JPanel() {
+                       private static final long serialVersionUID = 8924410573598922364L;
+
+                       public void paintComponent(Graphics g) {
+                               super.paintComponent(g);
+                               if (image != null)
+                                       g.drawImage(image, 0, 0, this);
+                       }
+
+               }, BorderLayout.CENTER);
+               frame.setVisible(true);
+       }
+
+       public void setImage(BufferedImage image) {
+               this.image = image;
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileDialog.java
new file mode 100644 (file)
index 0000000..0c5d346
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class CmsFileDialog extends FileDialog {
+       public CmsFileDialog(Shell parent, int style) {
+               super(parent, style);
+       }
+
+       public CmsFileDialog(Shell parent) {
+               super(parent);
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/CmsFileUpload.java
new file mode 100644 (file)
index 0000000..638859a
--- /dev/null
@@ -0,0 +1,32 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.rap.rwt.widgets.FileUpload;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Composite;
+
+public class CmsFileUpload extends FileUpload {
+       public CmsFileUpload(Composite parent, int style) {
+               super(parent, style);
+       }
+
+       @Override
+       public void setText(String text) {
+               super.setText(text);
+       }
+
+       @Override
+       public String getFileName() {
+               return super.getFileName();
+       }
+
+       @Override
+       public String[] getFileNames() {
+               return super.getFileNames();
+       }
+
+       @Override
+       public void addSelectionListener(SelectionListener listener) {
+               super.addSelectionListener(listener);
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/DefaultNLS.java
new file mode 100644 (file)
index 0000000..fbb4fbf
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.eclipse.ui.specific;
+
+/** RCP specific {@link NLS} to be extended */
+public class DefaultNLS {// extends NLS {
+//     public final static String DEFAULT_BUNDLE_LOCATION = "/properties/plugin";
+//
+//     public DefaultNLS() {
+//             this(DEFAULT_BUNDLE_LOCATION);
+//     }
+//
+//     public DefaultNLS(String bundleName) {
+//             NLS.initializeMessages(bundleName, getClass());
+//     }
+}
\ No newline at end of file
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiConstants.java
new file mode 100644 (file)
index 0000000..ac862d7
--- /dev/null
@@ -0,0 +1,7 @@
+package org.argeo.eclipse.ui.specific;
+
+/** Constants which are specific to RWT.*/
+public interface EclipseUiConstants {
+       final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName";
+       final static String MARKUP_SUPPORT = null;
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/EclipseUiSpecificUtils.java
new file mode 100644 (file)
index 0000000..d1acbcf
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.eclipse.ui.specific;
+
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Widget;
+
+/** Static utilities to bridge differences between RCP and RAP */
+public class EclipseUiSpecificUtils {
+       private final static String CSS_CLASS = "org.eclipse.e4.ui.css.CssClassName";
+
+       public static void setStyleData(Widget widget, Object data) {
+               widget.setData(CSS_CLASS, data);
+       }
+
+       public static Object getStyleData(Widget widget) {
+               return widget.getData(CSS_CLASS);
+       }
+
+       public static void setMarkupData(Widget widget) {
+               // does nothing
+       }
+
+       public static void setMarkupValidationDisabledData(Widget widget) {
+               // does nothing
+       }
+
+       /**
+        * TootlTip support is supported for {@link ColumnViewer} in RCP
+        * 
+        * @see ColumnViewerToolTipSupport#enableFor(Viewer)
+        */
+       public static void enableToolTipSupport(Viewer viewer) {
+               if (viewer instanceof ColumnViewer)
+                       ColumnViewerToolTipSupport.enableFor((ColumnViewer) viewer);
+       }
+
+       private EclipseUiSpecificUtils() {
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/FileDropAdapter.java
new file mode 100644 (file)
index 0000000..524447e
--- /dev/null
@@ -0,0 +1,48 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DropTarget;
+import org.eclipse.swt.dnd.DropTargetAdapter;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.FileTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Control;
+
+public class FileDropAdapter {
+
+       public void prepareDropTarget(Control control, DropTarget dropTarget) {
+               dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
+               dropTarget.addDropListener(new DropTargetAdapter() {
+                       @Override
+                       public void dropAccept(DropTargetEvent event) {
+                               if (!FileTransfer.getInstance().isSupportedType(event.currentDataType)) {
+                                       event.detail = DND.DROP_NONE;
+                               }
+                       }
+
+                       @Override
+                       public void drop(DropTargetEvent event) {
+                               handleFileDrop(control, event);
+                       }
+               });
+       }
+
+       public void handleFileDrop(Control control, DropTargetEvent event) {
+               String fileList[] = null;
+               FileTransfer ft = FileTransfer.getInstance();
+               if (ft.isSupportedType(event.currentDataType)) {
+                       fileList = (String[]) event.data;
+               }
+               System.out.println(Arrays.toString(fileList));
+       }
+
+       /** Executed in UI thread */
+       protected void processUpload(InputStream in, String fileName, String contentType) throws IOException {
+
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/argeo/eclipse/ui/specific/UiContext.java
new file mode 100644 (file)
index 0000000..20163cf
--- /dev/null
@@ -0,0 +1,52 @@
+package org.argeo.eclipse.ui.specific;
+
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.swt.widgets.Display;
+
+/** Singleton class providing single sources infos about the UI context. */
+public class UiContext {
+
+       public static HttpServletRequest getHttpRequest() {
+               return null;
+       }
+
+       public static HttpServletResponse getHttpResponse() {
+               return null;
+       }
+
+       public static Locale getLocale() {
+               return Locale.getDefault();
+       }
+
+       public static void setLocale(Locale locale) {
+               Locale.setDefault(locale);
+       }
+
+       /** Can always be null */
+       @SuppressWarnings("unchecked")
+       public static <T> T getData(String key) {
+               Display display = getDisplay();
+               if (display == null)
+                       return null;
+               return (T) display.getData(key);
+       }
+
+       public static void setData(String key, Object value) {
+               Display display = getDisplay();
+               if (display == null)
+                       throw new IllegalStateException("Not display available");
+               display.setData(key, value);
+       }
+
+       private static Display getDisplay() {
+               return Display.getCurrent();
+       }
+
+       private UiContext() {
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileDetails.java
new file mode 100644 (file)
index 0000000..fbb36dd
--- /dev/null
@@ -0,0 +1,9 @@
+package org.eclipse.rap.fileupload;
+
+public interface FileDetails {
+       String getContentType();
+
+       long getContentLength();
+
+       String getFileName();
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadEvent.java
new file mode 100644 (file)
index 0000000..a745280
--- /dev/null
@@ -0,0 +1,21 @@
+package org.eclipse.rap.fileupload;
+
+import java.util.EventObject;
+
+public abstract class FileUploadEvent extends EventObject {
+
+       private static final long serialVersionUID = 1L;
+
+       protected FileUploadEvent(FileUploadHandler source) {
+               super(source);
+       }
+
+       public abstract FileDetails[] getFileDetails();
+
+       public abstract long getContentLength();
+
+       public abstract long getBytesRead();
+
+       public abstract Exception getException();
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadHandler.java
new file mode 100644 (file)
index 0000000..7d89300
--- /dev/null
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rap.fileupload;
+
+/**
+ * A file upload handler is used to accept file uploads from a client. After
+ * creating a file upload handler, the server will accept file uploads to the
+ * URL returned by <code>getUploadUrl()</code>. Upload listeners can be attached
+ * to react on progress. When the upload has finished, a FileUploadHandler has
+ * to be disposed of by calling its <code>dispose()</code> method.
+ *
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class FileUploadHandler {
+
+       public FileUploadHandler(FileUploadReceiver fileUploadReceiver) {
+       }
+
+       public void dispose() {
+
+       }
+
+       public void addUploadListener(FileUploadListener listener) {
+
+       }
+
+       public String getUploadUrl() {
+               return null;
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadListener.java
new file mode 100644 (file)
index 0000000..b59fd39
--- /dev/null
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2012 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rap.fileupload;
+
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * Listener to react on progress and completion of a file upload.
+ * <p>
+ * <strong>Note:</strong> This listener will be called from a different thread than the UI thread.
+ * Implementations must use {@link Display#asyncExec(Runnable)} to access the UI.
+ * </p>
+ *
+ * @see FileUploadEvent
+ */
+public interface FileUploadListener {
+
+  /**
+   * Called when new information about an in-progress upload is available.
+   *
+   * @param event event object that contains information about the uploaded file
+   * @see FileUploadEvent#getBytesRead()
+   */
+  void uploadProgress( FileUploadEvent event );
+
+  /**
+   * Called when a file upload has finished successfully.
+   *
+   * @param event event object that contains information about the uploaded file
+   * @see FileUploadEvent
+   */
+  void uploadFinished( FileUploadEvent event );
+
+  /**
+   * Called when a file upload failed.
+   *
+   * @param event event object that contains information about the uploaded file
+   * @see FileUploadEvent#getErrorMessage()
+   */
+  void uploadFailed( FileUploadEvent event );
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/fileupload/FileUploadReceiver.java
new file mode 100644 (file)
index 0000000..3f4cf47
--- /dev/null
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2013 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rap.fileupload;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Instances of this interface are responsible for reading and processing the data from a file
+ * upload.
+ */
+public abstract class FileUploadReceiver {
+
+  /**
+   * Reads and processes all data from the provided input stream.
+   *
+   * @param stream the stream to read from
+   * @param details the details of the uploaded file like file name, content-type and size
+   * @throws IOException if an input / output error occurs
+   */
+  public abstract void receive( InputStream stream, FileDetails details ) throws IOException;
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/RWT.java
new file mode 100644 (file)
index 0000000..1688594
--- /dev/null
@@ -0,0 +1,45 @@
+package org.eclipse.rap.rwt;
+
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.argeo.eclipse.ui.rcp.internal.rwt.RcpClient;
+import org.argeo.eclipse.ui.rcp.internal.rwt.RcpResourceManager;
+import org.eclipse.rap.rwt.client.Client;
+import org.eclipse.rap.rwt.service.ResourceManager;
+
+public class RWT {
+       public final static String CUSTOM_VARIANT = "argeo-rcp:CUSTOM_VARIANT";
+       public final static String MARKUP_ENABLED = "argeo-rcp:MARKUP_ENABLED";
+       public static final String TOOLTIP_MARKUP_ENABLED = "argeo-rcp:TOOLTIP_MARKUP_ENABLED";
+       public final static String CUSTOM_ITEM_HEIGHT = "argeo-rcp:CUSTOM_ITEM_HEIGHT";
+       public final static String ACTIVE_KEYS = "argeo-rcp:ACTIVE_KEYS";
+       public final static String CANCEL_KEYS = "argeo-rcp:CANCEL_KEYS";
+       public final static String DEFAULT_THEME_ID  = "argeo-rcp:DEFAULT_THEME_ID";
+
+       public final static int HYPERLINK = 0;
+
+       private static Locale locale = Locale.getDefault();
+       private static RcpClient client = new RcpClient();
+       private static ResourceManager resourceManager = new RcpResourceManager();
+       static {
+
+       }
+
+       public static Locale getLocale() {
+               return locale;
+       }
+
+       public static HttpServletRequest getRequest() {
+               return null;
+       }
+
+       public static ResourceManager getResourceManager() {
+               return resourceManager;
+       }
+
+       public static Client getClient() {
+               return client;
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/SingletonUtil.java
new file mode 100644 (file)
index 0000000..6e30aa6
--- /dev/null
@@ -0,0 +1,7 @@
+package org.eclipse.rap.rwt;
+
+public class SingletonUtil {
+       public static <T> T getSessionInstance(Class<T> clss) {
+               return null;
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/AbstractEntryPoint.java
new file mode 100644 (file)
index 0000000..980a818
--- /dev/null
@@ -0,0 +1,43 @@
+package org.eclipse.rap.rwt.application;
+
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public abstract class AbstractEntryPoint implements EntryPoint {
+       private Display display;
+       private Shell shell;
+
+       protected Shell createShell(Display display) {
+               return new Shell(display);
+       }
+
+       protected void createContents(Composite parent) {
+
+       }
+
+       public int createUI() {
+               display = new Display();
+               shell = createShell(display);
+               shell.setLayout(new GridLayout(1, false));
+               createContents(shell);
+               if (shell.getMaximized()) {
+                       shell.layout();
+               } else {
+                       shell.pack();
+               }
+               shell.open();
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch()) {
+                               display.sleep();
+                       }
+               }
+               display.dispose();
+               return 0;
+       }
+
+       protected Shell getShell() {
+               return shell;
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/Application.java
new file mode 100644 (file)
index 0000000..6cb5f29
--- /dev/null
@@ -0,0 +1,27 @@
+package org.eclipse.rap.rwt.application;
+
+import java.util.Map;
+
+import org.eclipse.rap.rwt.service.ResourceLoader;
+
+public interface Application {
+       public static enum OperationMode {
+               JEE_COMPATIBILITY, SWT_COMPATIBILITY,
+       }
+
+       void setOperationMode(OperationMode operationMode);
+
+       void addResource(String name, ResourceLoader resourceLoader);
+
+       void setExceptionHandler(ExceptionHandler exceptionHandler);
+
+       void addEntryPoint(String path, EntryPointFactory entryPointFactory,
+                       Map<String, String> properties);
+
+       void addEntryPoint(String path, Class<? extends EntryPoint> entryPoint,
+                       Map<String, String> properties);
+
+       void addStyleSheet(String themeId, String styleSheetLocation,
+                       ResourceLoader resourceLoader);
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ApplicationConfiguration.java
new file mode 100644 (file)
index 0000000..961ad70
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.application;
+
+public interface ApplicationConfiguration {
+       void configure(Application application);
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPoint.java
new file mode 100644 (file)
index 0000000..c0d559a
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.application;
+
+public interface EntryPoint {
+       int createUI();
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/EntryPointFactory.java
new file mode 100644 (file)
index 0000000..d5b24d8
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.application;
+
+public interface EntryPointFactory {
+       public EntryPoint create();
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/application/ExceptionHandler.java
new file mode 100644 (file)
index 0000000..13daf21
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.application;
+
+public interface ExceptionHandler {
+       public void handleException(Throwable throwable);
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/Client.java
new file mode 100644 (file)
index 0000000..934feae
--- /dev/null
@@ -0,0 +1,18 @@
+package org.eclipse.rap.rwt.client;
+
+import java.io.Serializable;
+
+import org.eclipse.rap.rwt.client.service.ClientService;
+
+public interface Client extends Serializable {
+
+  /**
+   * Returns this client's implementation of a given service, if available.
+   *
+   * @param type the type of the requested service, must be a subtype of ClientService
+   * @return the requested service if provided by this client, otherwise <code>null</code>
+   * @see ClientService
+   */
+  <T extends ClientService> T getService( Class<T> type );
+
+}
\ No newline at end of file
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/WebClient.java
new file mode 100644 (file)
index 0000000..1f19bdd
--- /dev/null
@@ -0,0 +1,10 @@
+package org.eclipse.rap.rwt.client;
+
+public interface WebClient {
+       public final static String FAVICON = "rcp:FAVICON";
+       public final static String PAGE_TITLE = "rcp:PAGE_TITLE";
+       public final static String BODY_HTML = "rcp:BODY_HTML";
+       public final static String THEME_ID = "rcp:THEME_ID";
+       public final static String HEAD_HTML = "rcp:HEAD_HTML";
+       public final static String PAGE_OVERFLOW = "rcp:PAGE_OVERFLOW";
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigation.java
new file mode 100644 (file)
index 0000000..ffba4e4
--- /dev/null
@@ -0,0 +1,7 @@
+package org.eclipse.rap.rwt.client.service;
+
+public interface BrowserNavigation extends ClientService {
+       void pushState(String state, String title);
+
+       void addBrowserNavigationListener(BrowserNavigationListener listener);
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationEvent.java
new file mode 100644 (file)
index 0000000..3e1b3eb
--- /dev/null
@@ -0,0 +1,10 @@
+package org.eclipse.rap.rwt.client.service;
+
+public class BrowserNavigationEvent {
+       private String state;
+
+       public String getState() {
+               return state;
+       }
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/BrowserNavigationListener.java
new file mode 100644 (file)
index 0000000..8319c03
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.client.service;
+
+public interface BrowserNavigationListener {
+       public void navigated(BrowserNavigationEvent event);
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/ClientService.java
new file mode 100644 (file)
index 0000000..9f479d1
--- /dev/null
@@ -0,0 +1,6 @@
+package org.eclipse.rap.rwt.client.service;
+
+import java.io.Serializable;
+
+public interface ClientService extends Serializable {
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/JavaScriptExecutor.java
new file mode 100644 (file)
index 0000000..6c44c72
--- /dev/null
@@ -0,0 +1,5 @@
+package org.eclipse.rap.rwt.client.service;
+
+public interface JavaScriptExecutor extends ClientService {
+       public void execute( String code );
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/client/service/UrlLauncher.java
new file mode 100644 (file)
index 0000000..9dae811
--- /dev/null
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2012 EclipseSource and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rap.rwt.client.service;
+
+/**
+ * The UrlLauncher service allows loading an URL in an external window, application or save dialog.
+ *
+ * @since 2.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface UrlLauncher extends ClientService {
+
+  /**
+   * Opens the given URL.
+   *
+   * Any HTTP URL or relative URL will be opened in a new window.
+   * Modern browser may block any attempt to open new windows, but will usually prompt the user to
+   * accept or ignore. Even if accepted, the decision may be applied to only this attempt, or only
+   * to future attempts. It could also trigger a document reload, causing a session restart.
+   *
+   * Non-HTTP URLs like "mailto" will not create a new browser window, but require the client
+   * to have a matching protocol handler registered.
+   *
+   * @param url the URL to open
+   */
+  void openURL( String url );
+
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceLoader.java
new file mode 100644 (file)
index 0000000..7e7116c
--- /dev/null
@@ -0,0 +1,9 @@
+package org.eclipse.rap.rwt.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface ResourceLoader {
+       public InputStream getResourceAsStream(String resourceName)
+                       throws IOException;
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ResourceManager.java
new file mode 100644 (file)
index 0000000..c3379ea
--- /dev/null
@@ -0,0 +1,15 @@
+package org.eclipse.rap.rwt.service;
+
+import java.io.InputStream;
+
+public interface ResourceManager {
+       public void register(String name, InputStream in);
+
+       boolean unregister(String name);
+
+       public InputStream getRegisteredContent(String name);
+
+       public String getLocation(String name);
+
+       public boolean isRegistered(String name);
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/service/ServerPushSession.java
new file mode 100644 (file)
index 0000000..bed194f
--- /dev/null
@@ -0,0 +1,12 @@
+package org.eclipse.rap.rwt.service;
+
+/** Mock, does nothing as this is irrelevant for RCP. */
+public class ServerPushSession {
+       public void start() {
+
+       }
+
+       public void stop() {
+
+       }
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/DropDown.java
new file mode 100644 (file)
index 0000000..b2a2005
--- /dev/null
@@ -0,0 +1,33 @@
+package org.eclipse.rap.rwt.widgets;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Widget;
+
+public class DropDown {
+       private boolean visible=false;
+
+       public DropDown(Widget parent, int style) {
+               // FIXME implement a shell
+       }
+
+       public DropDown(Widget parent) {
+               this(parent, SWT.NONE);
+       }
+
+       public void setVisible(boolean visible) {
+               this.visible = visible;
+       }
+
+       public boolean isVisible() {
+               return visible;
+       }
+       
+       public void setItems( String[] items ) {
+               
+       }
+       
+       public void setSelectionIndex( int selection ) {
+               
+       }
+       
+}
diff --git a/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java b/swt/rcp/org.argeo.swt.specific.rcp/src/org/eclipse/rap/rwt/widgets/FileUpload.java
new file mode 100644 (file)
index 0000000..cbf1449
--- /dev/null
@@ -0,0 +1,37 @@
+package org.eclipse.rap.rwt.widgets;
+
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+
+public class FileUpload extends Composite {
+
+       public FileUpload(Composite parent, int style) {
+               super(parent, style);
+       }
+
+       public void addSelectionListener(SelectionListener listener) {
+
+       }
+
+       public void submit(String url) {
+
+       }
+
+       public void setImage(Image image) {
+
+       }
+
+       public void setText(String text) {
+
+       }
+
+       public String getFileName() {
+               return null;
+       }
+
+       public String[] getFileNames() {
+               return null;
+       }
+
+}