Improve CMS dialogs and localisation.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 24 Dec 2020 10:24:34 +0000 (11:24 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 24 Dec 2020 10:24:34 +0000 (11:24 +0100)
13 files changed:
org.argeo.cms.ui.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java
org.argeo.cms.ui/bnd.bnd
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/ChangePasswordDialog.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsFeedback.java
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsMessageDialog.java
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/LightweightDialog.java [new file with mode: 0644]
org.argeo.cms/OSGI-INF/l10n/bundle.properties
org.argeo.cms/OSGI-INF/l10n/bundle_fr.properties
org.argeo.cms/src/org/argeo/cms/CmsMsg.java
org.argeo.cms/src/org/argeo/cms/LocaleUtils.java
org.argeo.cms/src/org/argeo/cms/Localized.java
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java

index 9a023d6bc780f999ee3afa281f46209be413ae7c..4f2521fd4fa718f0a99cc3a73cd56f5f2b4561ad 100644 (file)
@@ -4,6 +4,7 @@ import static org.eclipse.rap.rwt.internal.service.ContextProvider.getApplicatio
 
 import java.security.PrivilegedAction;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.UUID;
 
@@ -14,6 +15,7 @@ import javax.security.auth.login.LoginException;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.api.NodeConstants;
+import org.argeo.cms.LocaleUtils;
 import org.argeo.cms.auth.CmsSession;
 import org.argeo.cms.auth.CurrentUser;
 import org.argeo.cms.auth.HttpRequestCallbackHandler;
@@ -104,8 +106,13 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL
                                        uxContext = new SimpleUxContext();
                                        imageManager = new DefaultImageManager();
                                        CmsSession cmsSession = getCmsSession();
-                                       if (cmsSession != null)
-                                               RWT.setLocale(cmsSession.getLocale());
+                                       if (cmsSession != null) {
+                                               UiContext.setLocale(cmsSession.getLocale());
+                                               LocaleUtils.setThreadLocale(cmsSession.getLocale());
+                                       } else {
+                                               Locale rwtLocale = RWT.getUISession().getLocale();
+                                               LocaleUtils.setThreadLocale(rwtLocale);
+                                       }
                                        ui = cmsWebApp.getCmsApp().initUi(parent);
                                        ui.setData(CmsApp.UI_NAME_PROPERTY, uiName);
                                        ui.setLayoutData(CmsUiUtils.fillAll());
index 50ebd7ceb3db15f7e7718e819da0474330f6a2ae..ed842413badc9337106009bfe9f0c7ff7c87491c 100644 (file)
@@ -5,7 +5,6 @@ Import-Package: org.eclipse.swt,\
 org.eclipse.jface.window,\
 org.eclipse.core.commands,\
 javax.jcr.security,\
-org.argeo.eclipse.ui.dialogs,\
 org.eclipse.rap.fileupload;version="[2.1,4)",\
 org.eclipse.rap.rwt;version="[2.1,4)",\
 org.eclipse.rap.rwt.application;version="[2.1,4)",\
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/ChangePasswordDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/ChangePasswordDialog.java
new file mode 100644 (file)
index 0000000..984f283
--- /dev/null
@@ -0,0 +1,79 @@
+package org.argeo.cms.ui.dialogs;
+
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+
+import org.argeo.cms.CmsMsg;
+import org.argeo.cms.CmsUserManager;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.util.CmsUiUtils;
+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 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 = CmsView.getCmsView(parentShell);
+       }
+
+       @Override
+       protected Control createInputArea(Composite userSection) {
+               addFormLabel(userSection, CmsMsg.currentPassword.lead());
+               Text previousPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               previousPassword.setLayoutData(CmsUiUtils.fillWidth());
+               addFormLabel(userSection, CmsMsg.newPassword.lead());
+               Text newPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               newPassword.setLayoutData(CmsUiUtils.fillWidth());
+               addFormLabel(userSection, CmsMsg.repeatNewPassword.lead());
+               Text confirmPassword = new Text(userSection, SWT.BORDER | SWT.PASSWORD);
+               confirmPassword.setLayoutData(CmsUiUtils.fillWidth());
+
+               doIt = () -> {
+                       if (Arrays.equals(newPassword.getTextChars(), confirmPassword.getTextChars())) {
+                               try {
+                                       cmsUserManager.changeOwnPassword(previousPassword.getTextChars(), newPassword.getTextChars());
+                                       return OK;
+                               } catch (Exception 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;
+       }
+
+}
index 91dc7fb768ac62373814c50a66aa2136ddf6564d..30e2ad34ff4e3d90edf653ffd4d7ec779e21de11 100644 (file)
@@ -7,7 +7,6 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.CmsMsg;
 import org.argeo.eclipse.ui.Selected;
-import org.argeo.eclipse.ui.dialogs.LightweightDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
index e076a11086d5270657ccca1a1615316bc8f8102c..eb881c6bd03cf60fb7e9b7181465f57055b24228 100644 (file)
@@ -4,7 +4,6 @@ import org.argeo.cms.CmsMsg;
 import org.argeo.cms.ui.util.CmsUiUtils;
 import org.argeo.eclipse.ui.EclipseUiUtils;
 import org.argeo.eclipse.ui.Selected;
-import org.argeo.eclipse.ui.dialogs.LightweightDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.TraverseEvent;
 import org.eclipse.swt.events.TraverseListener;
@@ -60,12 +59,13 @@ public class CmsMessageDialog extends LightweightDialog {
                body.setLayout(bodyGridLayout);
                body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
 
-               Label messageLbl = new Label(body, SWT.WRAP);
-               CmsUiUtils.markup(messageLbl);
-               messageLbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               messageLbl.setFont(EclipseUiUtils.getBoldFont(parent));
-               if (message != null)
+               if (message != null) {
+                       Label messageLbl = new Label(body, SWT.WRAP);
+                       CmsUiUtils.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);
@@ -131,6 +131,10 @@ public class CmsMessageDialog extends LightweightDialog {
                closeShell(CANCEL);
        }
 
+       protected void cancel() {
+               closeShell(CANCEL);
+       }
+
        protected Point getInitialSize() {
                return new Point(400, 200);
        }
index 93125be23478f406f023edb800b9d2ac68abd606..e4fe35d3426c72880e9d20afb968a5f45c90860c 100644 (file)
@@ -6,7 +6,6 @@ import org.argeo.cms.CmsMsg;
 import org.argeo.cms.ui.util.CmsUiUtils;
 import org.argeo.eclipse.ui.EclipseUiUtils;
 import org.argeo.eclipse.ui.Selected;
-import org.argeo.eclipse.ui.dialogs.LightweightDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.wizard.IWizard;
 import org.eclipse.jface.wizard.IWizardContainer2;
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/LightweightDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/LightweightDialog.java
new file mode 100644 (file)
index 0000000..8f63479
--- /dev/null
@@ -0,0 +1,256 @@
+package org.argeo.cms.ui.dialogs;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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 Log log = LogFactory.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
index 20d5b10e7fe818a98b7621096b3764af6c88d8c4..2e4944be7b31ea53e4174567da3dee686b0fd70b 100644 (file)
@@ -9,6 +9,7 @@ currentPassword=Current password
 newPassword=New password
 repeatNewPassword=Repeat new password
 passwordChanged=Password changed
+invalidPassword=Invalid password
 
 close=Close
 cancel=Cancel
index 085ea7444e29908bc2f2fb190125c84388df797e..638c927938af4dd0dca32a6361df775958493be7 100644 (file)
@@ -9,6 +9,7 @@ currentPassword=mot de passe actuel
 newPassword=nouveau mot de passe
 repeatNewPassword=répéter le nouveau mot de passe
 passwordChanged=mot de passe changé
+invalidPassword=mot de passe invalide
 
 close=Fermer
 cancel=Annuler
index a69b419c516fab6f7c0603789844ca9f193225e6..7ae73961dd74398254b27d3d9589cccefa1083a8 100644 (file)
@@ -3,7 +3,7 @@ package org.argeo.cms;
 public enum CmsMsg implements Localized {
        username, password, login, logout, register,
        // password
-       changePassword, currentPassword, newPassword, repeatNewPassword, passwordChanged,
+       changePassword, currentPassword, newPassword, repeatNewPassword, passwordChanged, invalidPassword,
        // dialog
        close, cancel, ok,
        // wizard
index 24ca3558c7dcc57ae657cd445b2de509669de6ce..46b70d75677f974f0bdff353a74d8db20f44eb7d 100644 (file)
@@ -8,10 +8,20 @@ import java.util.ResourceBundle;
 
 import javax.security.auth.Subject;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.auth.CurrentUser;
 
 /** Utilities simplifying the development of localization enums. */
 public class LocaleUtils {
+       private final static Log log = LogFactory.getLog(LocaleUtils.class);
+
+       private final static ThreadLocal<Locale> threadLocale = new ThreadLocal<>();
+
+       public static void setThreadLocale(Locale locale) {
+               threadLocale.set(locale);
+       }
+
        public static String local(Enum<?> en) {
                return local(en, getCurrentLocale(), "/OSGI-INF/l10n/bundle");
        }
@@ -64,10 +74,18 @@ public class LocaleUtils {
        }
 
        static Locale getCurrentLocale() {
+               Locale currentLocale = null;
                if (Subject.getSubject(AccessController.getContext()) != null)
-                       return CurrentUser.locale();
-               else
-                       return Locale.getDefault();
+                       currentLocale = CurrentUser.locale();
+               else if (threadLocale.get() != null) {
+                       currentLocale = threadLocale.get();
+               }
+               if (log.isDebugEnabled())
+                       log.debug("Thread #" + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " locale: "
+                                       + currentLocale);
+               if (currentLocale == null)
+                       throw new IllegalStateException("No locale found");
+               return currentLocale;
                // return UiContext.getLocale();
                // FIXME look into Subject or settings
                // return Locale.getDefault();
index bc37608ed013fd423007412c01a5abbcf71352aa..d4558cac6092c3e4c9e07bc4442165e272cc0024 100644 (file)
@@ -5,6 +5,7 @@ import java.util.Locale;
 
 /** Localized object. */
 public interface Localized {
+
        /** Default assumes that this is an {@link Enum} */
        default Object local(Locale locale) {
                return LocaleUtils.local((Enum<?>) this, locale);
index 606030e38079452eee9ae3239cff55c6a453d633..d00365bf2c7e8ad1aa6547ffae2a326a6d8ecfd8 100644 (file)
@@ -18,6 +18,7 @@ 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 Log log = LogFactory.getLog(LightweightDialog.class);