OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.sql.ui;
15
 
73 ilm 16
import static org.openconcerto.sql.TM.getTM;
17 ilm 17
import org.openconcerto.sql.Configuration;
177 ilm 18
import org.openconcerto.sql.PropsConfiguration;
73 ilm 19
import org.openconcerto.sql.TM;
17 ilm 20
import org.openconcerto.sql.model.SQLRow;
21
import org.openconcerto.sql.model.SQLTable;
22
import org.openconcerto.sql.preferences.UserProps;
63 ilm 23
import org.openconcerto.sql.request.ComboSQLRequest;
17 ilm 24
import org.openconcerto.sql.sqlobject.IComboModel;
25
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
26
import org.openconcerto.sql.sqlobject.SQLRequestComboBox;
73 ilm 27
import org.openconcerto.sql.users.UserManager;
28
import org.openconcerto.sql.users.rights.UserRightsManager;
17 ilm 29
import org.openconcerto.ui.DefaultGridBagConstraints;
30
import org.openconcerto.ui.ReloadPanel;
31
import org.openconcerto.ui.valuewrapper.EmptyValueWrapper;
32
import org.openconcerto.ui.valuewrapper.ValueWrapperFactory;
33
import org.openconcerto.utils.CollectionUtils;
34
import org.openconcerto.utils.ExceptionUtils;
35
import org.openconcerto.utils.JImage;
36
import org.openconcerto.utils.Tuple2;
37
import org.openconcerto.utils.cc.ITransformer;
38
import org.openconcerto.utils.checks.EmptyListener;
39
import org.openconcerto.utils.checks.EmptyObj;
67 ilm 40
import org.openconcerto.utils.i18n.TranslationManager;
17 ilm 41
import org.openconcerto.utils.text.SimpleDocumentListener;
42
 
43
import java.awt.GridBagConstraints;
44
import java.awt.GridBagLayout;
45
import java.awt.Insets;
46
import java.awt.event.ActionEvent;
47
import java.awt.event.ActionListener;
48
import java.beans.PropertyChangeEvent;
49
import java.beans.PropertyChangeListener;
67 ilm 50
import java.util.ArrayList;
17 ilm 51
import java.util.Arrays;
52
import java.util.List;
67 ilm 53
import java.util.Locale;
54
import java.util.ResourceBundle;
17 ilm 55
 
80 ilm 56
import javax.swing.BorderFactory;
17 ilm 57
import javax.swing.JButton;
58
import javax.swing.JCheckBox;
67 ilm 59
import javax.swing.JCheckBoxMenuItem;
177 ilm 60
import javax.swing.JComponent;
17 ilm 61
import javax.swing.JLabel;
62
import javax.swing.JOptionPane;
63
import javax.swing.JPanel;
64
import javax.swing.JPasswordField;
67 ilm 65
import javax.swing.JPopupMenu;
17 ilm 66
import javax.swing.JSeparator;
67
import javax.swing.JTextField;
68
import javax.swing.SwingConstants;
69
import javax.swing.SwingUtilities;
70
import javax.swing.event.DocumentEvent;
71
 
72
/**
73
 * Panel de connexion de l'utilisateur au démarrage avec choix de la société
74
 */
75
public class ConnexionPanel extends JPanel implements ActionListener {
76
 
77
    public static final String QUICK_LOGIN = "org.openconcerto.quickLogin";
78
 
79
    static private boolean quickLogin(final Runnable r, final boolean societeSelector) {
80
        final String lastLogin = UserProps.getInstance().getLastLoginName();
81
        final String pass = UserProps.getInstance().getStoredPassword();
82
        if (Boolean.getBoolean(QUICK_LOGIN) && lastLogin.length() > 0 && pass != null && (!societeSelector || UserProps.getInstance().getLastSocieteID() >= SQLRow.MIN_VALID_ID)) {
83
            final Tuple2<String, String> res = new Login(Configuration.getInstance().getRoot()).connectEnc(lastLogin, pass);
84
            if (res.get0() == null) {
63 ilm 85
                // ConnexionPanel normally executes r outside of the EDT, do the same
86
                execute(r);
17 ilm 87
                // no need to display a panel
88
                return true;
89
            }
90
        }
91
        return false;
92
    }
93
 
94
    /**
95
     * Create a panel to log with a user/pass if quick login fails. For quick login to succeed the
96
     * system property {@value #QUICK_LOGIN} must be <code>true</code> and the user must have stored
97
     * his credential.
98
     *
99
     * @param r what to do once authenticated, *not* run in the EDT.
100
     * @param imageLogo the picture put above the text fields.
101
     * @return a panel to log in, <code>null</code> if quick logged.
102
     */
103
    static public ConnexionPanel create(final Runnable r, final JImage imageLogo) {
104
        return create(r, imageLogo, false);
105
 
106
    }
107
 
108
    static public ConnexionPanel create(final Runnable r, final JImage imageLogo, final boolean societeSelector) {
109
        if (quickLogin(r, societeSelector)) {
110
            return null;
111
        } else {
112
            return new ConnexionPanel(r, imageLogo, societeSelector);
113
        }
114
    }
115
 
116
    private final Login login;
67 ilm 117
 
17 ilm 118
    private final JPasswordField textPassWord;
119
    private final EmptyValueWrapper<String> textLogin;
120
    private SQLRequestComboBox comboSociete;
121
    private String encryptedPassword;
122
    protected String clearPassword;
123
    private final Runnable r;
124
    private final boolean societeSelector;
125
    private final ReloadPanel reloadPanel;
126
    private boolean isConnecting = false;
127
    private String connectionAllowed;
73 ilm 128
    private final JCheckBox saveCheckBox = new JCheckBox(getTM().translate("loginPanel.storePass"));
129
    private final JButton buttonConnect = new JButton(getTM().translate("loginPanel.loginAction"));
130
    private String adminLogin = getTM().translate("loginPanel.adminLogin");
131
    private final JLabel loginLabel = new JLabel(getTM().translate("loginPanel.loginLabel"));
132
    private final JLabel passwordLabel = new JLabel(getTM().translate("loginPanel.passLabel"));
133
    private final JLabel companyLabel = new JLabel(getTM().translate("loginPanel.companyLabel"));
67 ilm 134
    private String localeBaseName = null;
135
    private final List<Locale> localesToDisplay = new ArrayList<Locale>();
136
    private final JButton langButton = new JButton(Locale.ROOT.getLanguage());
17 ilm 137
 
138
    /**
139
     * Create a panel to log with a user/pass.
140
     *
141
     * @param r what to do once authenticated, *not* run in the EDT.
142
     * @param imageLogo the picture put above the text fields.
143
     */
144
    public ConnexionPanel(final Runnable r, final JImage imageLogo) {
145
        this(r, imageLogo, false);
146
    }
147
 
148
    public ConnexionPanel(final Runnable r, final JImage imageLogo, final boolean societeSelector) {
149
        this(r, imageLogo, societeSelector, true);
150
    }
151
 
152
    public ConnexionPanel(final Runnable r, final JImage imageLogo, final boolean societeSelector, final boolean allowStoredPass) {
153
        super();
142 ilm 154
        if (r == null) {
155
            throw new IllegalArgumentException("null runnable");
156
        }
17 ilm 157
        this.login = new Login(Configuration.getInstance().getRoot());
158
 
159
        this.societeSelector = societeSelector;
160
        this.r = r;
161
        String lastLoginName = UserProps.getInstance().getLastLoginName();
162
        if (lastLoginName == null || lastLoginName.trim().length() == 0) {
67 ilm 163
            lastLoginName = this.adminLogin;
17 ilm 164
        }
165
        final String storedPassword = allowStoredPass ? UserProps.getInstance().getStoredPassword() : null;
166
        this.encryptedPassword = storedPassword;
167
        this.connectionAllowed = null;
168
 
169
        this.setLayout(new GridBagLayout());
170
 
171
        final GridBagConstraints c = new GridBagConstraints();
172
        c.gridheight = 1;
173
        c.gridwidth = 2;
174
        c.gridx = 0;
175
        c.gridy = 0;
176
        c.weightx = 1;
177
        c.weighty = 0;
178
        c.insets = new Insets(0, 0, 0, 0);
179
        c.fill = GridBagConstraints.HORIZONTAL;
180
 
181
        // Logo
182
        imageLogo.check();
183
        this.add(imageLogo, c);
184
        c.gridy++;
185
        c.gridwidth = GridBagConstraints.REMAINDER;
186
        this.add(new JSeparator(SwingConstants.HORIZONTAL), c);
187
 
188
        // Login
189
        c.insets = new Insets(2, 2, 1, 2);
190
        c.gridy++;
191
        c.gridwidth = 1;
192
        c.weightx = 0;
67 ilm 193
        this.loginLabel.setHorizontalAlignment(SwingConstants.RIGHT);
194
        this.add(this.loginLabel, c);
17 ilm 195
 
196
        this.textLogin = new EmptyValueWrapper<String>(ValueWrapperFactory.create(new JTextField(), String.class));
197
        this.textLogin.setValue(lastLoginName);
198
        c.gridx++;
199
        c.weightx = 1;
200
        this.add(this.textLogin.getComp(), c);
201
        ((JTextField) this.textLogin.getComp()).addActionListener(this);
202
        this.textLogin.addEmptyListener(new EmptyListener() {
67 ilm 203
            @Override
17 ilm 204
            public void emptyChange(final EmptyObj src, final boolean newValue) {
205
                checkValidity();
206
            }
207
        });
208
 
209
        // Password
210
        c.gridy++;
211
        c.gridx = 0;
212
        c.weightx = 0;
213
 
67 ilm 214
        this.passwordLabel.setHorizontalAlignment(SwingConstants.RIGHT);
215
        this.add(this.passwordLabel, c);
216
 
17 ilm 217
        this.textPassWord = new JPasswordField();
218
        // to show the user its password has been retrieved
219
        if (storedPassword != null) {
220
            final char[] s = new char[8];
221
            Arrays.fill(s, ' ');
222
            this.textPassWord.setText(new String(s));
223
            this.clearPassword = null;
224
        } else
225
            this.clearPassword = "";
226
        c.gridx++;
227
        c.weightx = 1;
228
        this.add(this.textPassWord, c);
229
        this.textPassWord.getDocument().addDocumentListener(new SimpleDocumentListener() {
67 ilm 230
            @Override
17 ilm 231
            public void update(final DocumentEvent e) {
232
                ConnexionPanel.this.clearPassword = String.valueOf(ConnexionPanel.this.textPassWord.getPassword());
233
                checkValidity();
234
            }
235
        });
236
        this.textPassWord.addActionListener(this);
237
 
238
        if (societeSelector) {
239
            // Societe
240
 
241
            c.gridy++;
242
            c.gridx = 0;
243
            c.weightx = 0;
244
 
67 ilm 245
            this.companyLabel.setHorizontalAlignment(SwingConstants.RIGHT);
246
            this.add(this.companyLabel, c);
247
 
17 ilm 248
            final SQLTable tableSociete = this.login.getUserTable().getDBRoot().findTable("SOCIETE_COMMON");
249
 
250
            if (tableSociete == null) {
67 ilm 251
                throw ExceptionUtils.createExn(IllegalStateException.class, "Missing table : SOCIETE_COMMON", null);
17 ilm 252
            }
253
 
254
            this.comboSociete = new SQLRequestComboBox(false, 25);
63 ilm 255
            final ComboSQLRequest req = Configuration.getInstance().getDirectory().getElement(tableSociete).getComboRequest(true);
256
            // keep rows for checkValidity()
257
            req.keepRows(true);
258
            final IComboModel model = new IComboModel(req);
17 ilm 259
            final int lastSociete = UserProps.getInstance().getLastSocieteID();
260
            if (lastSociete >= SQLRow.MIN_VALID_ID) {
261
                model.setValue(lastSociete);
262
            } else {
263
                model.setFirstFillSelection(new ITransformer<List<IComboSelectionItem>, IComboSelectionItem>() {
264
                    @Override
265
                    public IComboSelectionItem transformChecked(List<IComboSelectionItem> input) {
266
                        // Guillaume 25/06/2010
267
                        return CollectionUtils.getFirst(input);
268
                    }
269
                });
270
            }
271
            this.comboSociete.uiInit(model);
272
 
273
            this.comboSociete.addEmptyListener(new EmptyListener() {
67 ilm 274
                @Override
17 ilm 275
                public void emptyChange(final EmptyObj src, final boolean newValue) {
276
                    checkValidity();
277
                }
278
            });
279
            this.comboSociete.addValueListener(new PropertyChangeListener() {
67 ilm 280
                @Override
17 ilm 281
                public void propertyChange(final PropertyChangeEvent evt) {
282
                    checkValidity();
283
                }
284
            });
285
            c.gridx++;
286
            this.add(this.comboSociete, c);
287
        }
288
 
289
        // Button
290
        final JPanel panelButton = new JPanel();
291
        panelButton.setOpaque(false);
292
        panelButton.setLayout(new GridBagLayout());
293
        final GridBagConstraints c2 = new DefaultGridBagConstraints();
294
        c2.weightx = 1;
295
        if (allowStoredPass) {
296
            this.saveCheckBox.setOpaque(false);
297
            panelButton.add(this.saveCheckBox, c2);
298
            c2.weightx = 0;
299
            if (storedPassword != null && storedPassword.length() > 0) {
300
                this.saveCheckBox.setSelected(true);
301
            }
302
        }
303
        c2.gridx++;
304
        this.reloadPanel = new ReloadPanel();
305
        this.reloadPanel.setOpaque(false);
306
        /*
307
         * reloadPanel.setPreferredSize(new Dimension(20, 20)); reloadPanel.setMinimumSize(new
308
         * Dimension(20, 20)); reloadPanel.setMaximumSize(new Dimension(20, 20));
309
         */
310
        this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
311
        panelButton.add(this.reloadPanel, c2);
312
        c2.gridx++;
313
        c2.weightx = 0;
67 ilm 314
 
315
        this.langButton.addActionListener(new ActionListener() {
316
 
317
            @Override
318
            public void actionPerformed(ActionEvent e) {
319
                final JPopupMenu menu = new JPopupMenu();
320
                final Locale locale = UserProps.getInstance().getLocale();
321
                for (final Locale l : ConnexionPanel.this.localesToDisplay) {
322
                    final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(l.getDisplayName(l));
323
                    if (l.equals(locale)) {
324
                        menuItem.setSelected(true);
325
                    }
326
                    menu.add(menuItem);
327
                    menuItem.addActionListener(new ActionListener() {
328
                        @Override
329
                        public void actionPerformed(ActionEvent e) {
330
                            setUILanguage(l);
331
                        }
332
                    });
333
                }
334
 
335
                menu.show(ConnexionPanel.this.langButton, 0, 0);
336
 
337
            }
338
        });
339
        this.langButton.setOpaque(false);
340
        this.langButton.setBorderPainted(false);
341
        this.langButton.setContentAreaFilled(false);
80 ilm 342
        this.langButton.setBorder(BorderFactory.createEmptyBorder());
67 ilm 343
        this.langButton.setFocusable(false);
344
        this.langButton.setVisible(false);
345
        panelButton.add(this.langButton, c2);
346
        c2.gridx++;
63 ilm 347
        this.buttonConnect.setOpaque(false);
17 ilm 348
        panelButton.add(this.buttonConnect, c2);
349
 
350
        c.gridy++;
351
        c.gridx = 0;
352
        c.weightx = 0;
353
        c.gridwidth = GridBagConstraints.REMAINDER;
354
        c.weighty = 1;
355
        this.add(panelButton, c);
356
 
357
        this.buttonConnect.addActionListener(this);
358
 
359
        checkValidity();
360
    }
361
 
67 ilm 362
    public final void initLocalization(final String baseName, final List<Locale> toDisplay) {
363
        if (baseName == null)
364
            throw new NullPointerException("Null baseName");
365
        if (this.localeBaseName != null)
366
            throw new IllegalStateException("Already inited to " + this.localeBaseName);
367
        this.localeBaseName = baseName;
368
        this.localesToDisplay.addAll(toDisplay);
369
        this.setUILanguage(UserProps.getInstance().getLocale());
73 ilm 370
        TM.getInstance();
67 ilm 371
    }
372
 
17 ilm 373
    private void checkValidity() {
374
        this.buttonConnect.setEnabled(this.connectionAllowed == null && this.areFieldsValidated());
375
        this.buttonConnect.setToolTipText(this.connectionAllowed);
376
    }
377
 
378
    private boolean areFieldsValidated() {
379
        if (this.societeSelector) {
142 ilm 380
            if (this.comboSociete.isEmpty()) {
17 ilm 381
                return false;
382
            }
383
        }
384
        if (this.textLogin == null || this.textLogin.isEmpty() || this.textPassWord == null) {
385
            return false;
386
        } else {
21 ilm 387
            return this.textLogin.getValidState().isValid();
17 ilm 388
        }
389
    }
390
 
67 ilm 391
    @Override
17 ilm 392
    public void actionPerformed(final ActionEvent e) {
393
        if (isConnecting())
394
            return;
395
 
396
        if (this.textLogin.getValue().length() < 1) {
397
            return;
398
        }
399
 
400
        setConnecting(true);
401
 
63 ilm 402
        execute(new Runnable() {
67 ilm 403
            @Override
17 ilm 404
            public void run() {
405
                connect();
406
            }
407
        });
63 ilm 408
    }
409
 
410
    static private void execute(Runnable runnable) {
411
        final Thread t = new Thread(runnable, "ConnexionPanel Login");
17 ilm 412
        t.setPriority(Thread.MIN_PRIORITY);
413
        t.start();
414
    }
415
 
416
    public void setConnectionAllowed(String connectionAllowed) {
417
        this.connectionAllowed = connectionAllowed;
418
        this.checkValidity();
419
    }
420
 
421
    private synchronized boolean isConnecting() {
422
        return this.isConnecting;
423
    }
424
 
425
    private synchronized void setConnecting(final boolean b) {
426
 
427
        if (b) {
428
            this.reloadPanel.setMode(ReloadPanel.MODE_ROTATE);
429
            this.buttonConnect.setEnabled(false);
430
            this.saveCheckBox.setEnabled(false);
431
            if (this.comboSociete != null) {
432
                this.comboSociete.setEnabled(false);
433
            }
434
            ((JTextField) this.textLogin.getComp()).setEditable(false);
435
            this.textPassWord.setEditable(false);
436
        } else {
437
            this.buttonConnect.setEnabled(true);
438
            this.saveCheckBox.setEnabled(true);
439
            if (this.comboSociete != null) {
440
                this.comboSociete.setEnabled(true);
441
            }
442
            ((JTextField) this.textLogin.getComp()).setEditable(true);
443
            this.textPassWord.setEditable(true);
444
            this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
445
 
446
        }
447
        this.isConnecting = b;
448
    }
449
 
450
    private void connect() {
73 ilm 451
        final String userName = this.textLogin.getValue();
17 ilm 452
        final Tuple2<String, String> loginRes;
453
        // if the user has not typed anything and there was a stored pass
454
        if (this.clearPassword == null)
73 ilm 455
            loginRes = this.login.connectEnc(userName, this.encryptedPassword);
17 ilm 456
        else
457
            // handle legacy passwords
73 ilm 458
            loginRes = this.login.connectClear(userName, this.clearPassword, "\"" + this.clearPassword + "\"");
17 ilm 459
 
460
        if (loginRes.get0() == null) {
461
            // --->Connexion
73 ilm 462
            UserProps.getInstance().setLastLoginName(userName);
17 ilm 463
            if (this.societeSelector) {
464
                UserProps.getInstance().setLastSocieteID(this.comboSociete.getSelectedId());
465
            }
466
            if (this.saveCheckBox.isSelected()) {
467
                UserProps.getInstance().setEncryptedStoredPassword(loginRes.get1());
468
            } else
469
                UserProps.getInstance().setEncryptedStoredPassword(null);
470
            UserProps.getInstance().store();
471
 
73 ilm 472
            // Preload right to avoid request in AWT later
473
            if (UserRightsManager.getInstance() != null)
474
                UserRightsManager.getInstance().preloadRightsForUserId(UserManager.getUserID());
475
 
17 ilm 476
            // Fermeture des frames et execution du Runnable
477
            this.r.run();
478
            // only dispose the panel after r has run so that there's always something on screen for
479
            // the user to see
480
            SwingUtilities.getWindowAncestor(this).dispose();
481
        } else {
73 ilm 482
            unlockUIOnError(loginRes.get0(), userName);
17 ilm 483
        }
484
    }
485
 
73 ilm 486
    private void unlockUIOnError(final String error, final String userName) {
17 ilm 487
        SwingUtilities.invokeLater(new Runnable() {
488
            @Override
489
            public void run() {
142 ilm 490
                try {
491
                    ConnexionPanel.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
492
                    JOptionPane.showMessageDialog(ConnexionPanel.this, TM.getTM().translate("loginPanel." + error, userName));
493
                    // Guillaume wants this for the Nego
494
                    if (Login.UNKNOWN_USER.equals(error))
495
                        ConnexionPanel.this.textLogin.setValue(ConnexionPanel.this.adminLogin);
496
                    setConnecting(false);
497
                } catch (Exception e) {
498
                    e.printStackTrace();
499
                }
17 ilm 500
            }
501
        });
502
    }
503
 
504
    public int getSelectedSociete() {
505
        return this.comboSociete.getSelectedId();
506
    }
67 ilm 507
 
508
    protected void setUILanguage(Locale locale) {
73 ilm 509
        final ResourceBundle bundle = ResourceBundle.getBundle(this.localeBaseName, locale, TranslationManager.getControl());
67 ilm 510
        this.adminLogin = bundle.getString("adminLogin");
511
        this.loginLabel.setText(bundle.getString("loginLabel"));
512
        this.passwordLabel.setText(bundle.getString("passwordLabel"));
513
        this.companyLabel.setText(bundle.getString("companyLabel"));
514
        this.saveCheckBox.setText(bundle.getString("saveCheckBox"));
515
        this.buttonConnect.setText(bundle.getString("buttonConnect"));
516
        this.langButton.setText(locale.getLanguage());
517
        this.langButton.setVisible(true);
177 ilm 518
        // for next launch
67 ilm 519
        UserProps.getInstance().setLocale(locale);
177 ilm 520
        // for current run
521
        ((PropsConfiguration) Configuration.getInstance()).setLocale(locale);
522
        // for code that will never need more than one Locale concurrently
523
        // (e.g. used by TM singletons in various packages)
524
        Locale.setDefault(locale);
525
        // as explained in Locale.setDefault() javadoc : "be prepared to reinitialize
526
        // locale-sensitive code"
527
        JComponent.setDefaultLocale(locale);
528
        // throw RuntimeException like ResourceBundle.getBundle() at the beginning of this
529
        // method
530
        TranslationManager.createDefaultInstance();
67 ilm 531
    }
17 ilm 532
}