org.argeo.slc.jcr \
lib/linux/org.argeo.slc.systemd \
swt/org.argeo.tool.swt \
+swt/org.argeo.cms.e4 \
swt/org.argeo.tool.devops.e4 \
+swt/rap/org.argeo.cms.e4.rap \
swt/rap/org.argeo.tool.rap.cli \
swt/rap/org.argeo.tool.server \
A2_CATEGORY = org.argeo.slc
BUNDLES = \
+swt/rcp/org.argeo.cms.e4.rcp \
swt/rcp/org.argeo.tool.rcp.cli \
swt/rcp/org.argeo.tool.desktop \
DEP_CATEGORIES = \
org.argeo.tp \
osgi/api/org.argeo.tp.osgi \
+osgi/equinox/org.argeo.tp.eclipse \
swt/rcp/org.argeo.tp.swt \
lib/linux/x86_64/swt/rcp/org.argeo.tp.swt \
swt/rcp/org.argeo.tp.swt.workbench \
org.argeo.cms \
swt/org.argeo.cms \
swt/rcp/org.argeo.cms \
+$(A2_CATEGORY) \
+swt/$(A2_CATEGORY) \
+swt/rcp/$(A2_CATEGORY) \
clean:
rm -rf $(BUILD_BASE)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.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>
--- /dev/null
+<?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>
--- /dev/null
+Service-Component: 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;resolution:=optional,\
+org.eclipse.e4.ui.model.application;resolution:=optional,\
+org.argeo.cms,\
+org.eclipse.core.commands.common,\
+org.eclipse.jface.window,\
+org.eclipse.jface.dialogs,\
+org.argeo.cms.swt.auth,\
+org.argeo.cms.ux.widgets,\
+javax.servlet.*;version="[3,5)",\
+org.eclipse.*;resolution:=optional,\
+javax.*;resolution:=optional,\
+*
+
--- /dev/null
+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/
--- /dev/null
+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() {
+ }
+
+}
--- /dev/null
+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]);
+ }
+ }
+
+}
--- /dev/null
+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);
+}
--- /dev/null
+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.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);
+ }
+
+ // FIXME make it more generic
+ HttpServletRequest request = org.argeo.eclipse.ui.specific.UiContext.getHttpRequest();
+ if (request != null)
+ request.getSession().setMaxInactiveInterval(0);
+ }
+
+}
--- /dev/null
+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));
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/** Eclipse 4 addons to integrate with Argeo CMS. */
+package org.argeo.cms.e4.addons;
\ No newline at end of file
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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.api.cms.keyring.CryptoKeyring;
+import org.argeo.api.cms.transaction.WorkTransaction;
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+import org.argeo.cms.swt.dialogs.CmsMessageDialog;
+import org.argeo.cms.ux.widgets.CmsDialog;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.core.di.annotations.Optional;
+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() == CmsDialog.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 IllegalArgumentException("Invalid user dn " + name, e);
+ }
+ User user = (User) userAdmin.getRole(dn.toString());
+ if (!user.hasCredential(null, oldPassword))
+ throw new IllegalArgumentException("Invalid password");
+ if (Arrays.equals(newPassword, new char[0]))
+ throw new IllegalArgumentException("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 IllegalStateException("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 IllegalArgumentException("New passwords are different");
+ changePassword(oldPassword.getTextChars(), newPassword1.getTextChars());
+ closeShell(CmsDialog.OK);
+ } catch (Exception e) {
+ CmsFeedback.error("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;
+ }
+
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+package org.argeo.cms.e4.handlers;
+
+import javax.security.auth.Subject;
+
+import org.argeo.cms.CurrentUser;
+import org.argeo.cms.util.CurrentSubject;
+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 = CurrentSubject.current();
+ try {
+ CurrentUser.logoutCmsSession(subject);
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot log out", e);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.e4.handlers;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+
+public class DoNothing {
+ @Execute
+ public void execute() {
+
+ }
+}
--- /dev/null
+
+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
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/** Generic Eclipse 4 handlers. */
+package org.argeo.cms.e4.handlers;
\ No newline at end of file
--- /dev/null
+/** Eclipse 4 user interfaces. */
+package org.argeo.cms.e4;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.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>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+Import-Package: \
+org.argeo.api.acr, \
+org.eclipse.swt,\
+org.eclipse.swt.graphics,\
+org.eclipse.e4.ui.workbench,\
+org.eclipse.rap.rwt.client,\
+org.eclipse.nebula.widgets.richtext;resolution:=optional,\
+org.eclipse.*;resolution:=optional,\
+*
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ OSGI-INF/
--- /dev/null
+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.error("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) {
+
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+package org.argeo.cms.e4.rap;
+
+import java.security.AccessController;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+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.CmsContext;
+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.CurrentUser;
+import org.argeo.cms.swt.CmsSwtUtils;
+import org.argeo.cms.swt.SimpleSwtUxContext;
+import org.argeo.cms.swt.acr.AcrSwtImageManager;
+import org.argeo.cms.swt.auth.CmsLoginShell;
+import org.argeo.cms.swt.dialogs.CmsFeedback;
+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);
+
+ @Inject
+ private CmsContext cmsContext;
+
+ 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);
+ CmsLoginShell loginShell = new CmsLoginShell(this, cmsContext);
+ 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 = (CmsImageManager) new AcrSwtImageManager();
+
+ 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.error(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;
+ }
+
+ @Override
+ public <T> T doAs(Callable<T> action) {
+ throw new UnsupportedOperationException();
+ }
+
+}
--- /dev/null
+package org.argeo.cms.e4.rap;
+
+import java.util.Enumeration;
+
+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;
+ // FIXME deal with base name
+ String name=null;// = '/' + FilenameUtils.removeExtension(FilenameUtils.getName(p));
+ addE4EntryPoint(application, name, e4xmiPath, getBaseProperties());
+ if (log.isDebugEnabled())
+ log.debug("Registered " + e4xmiPath + " as " + getContextName() + name);
+ }
+ }
+ }
+
+}
--- /dev/null
+/** Eclipse 4 RAP specific extensions. */
+package org.argeo.cms.e4.rap;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+/bin/
+/target/
+/exec
--- /dev/null
+<?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>
--- /dev/null
+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
--- /dev/null
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+<?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.jcr.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.jcr.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.jcr.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>
--- /dev/null
+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
--- /dev/null
+Bundle-SymbolicName: org.argeo.cms.e4.rcp;singleton=true
+
+Require-Bundle: org.eclipse.core.runtime
+
+Import-Package: !org.eclipse.core.runtime,\
+org.eclipse.swt,\
+org.eclipse.*;resolution:=optional,\
+*
--- /dev/null
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ argeo-companion.e4xmi
+source.. = src/
--- /dev/null
+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
--- /dev/null
+<?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>
--- /dev/null
+package org.argeo.cms.e4.rcp;
+
+import java.security.PrivilegedExceptionAction;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+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.CurrentUser;
+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 IllegalStateException("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 bet null");
+ try {
+ CurrentUser.logoutCmsSession(loginContext.getSubject());
+ loginContext.logout();
+ } catch (LoginException e) {
+ throw new IllegalStateException("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;
+ }
+
+ @Override
+ public <T> T doAs(Callable<T> action) {
+ throw new UnsupportedOperationException();
+ }
+
+}
--- /dev/null
+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) {
+ }
+}