001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.ui; 018 019import static org.opends.messages.AdminToolMessages.*; 020 021import java.awt.CardLayout; 022import java.awt.Component; 023import java.awt.GridBagConstraints; 024import java.awt.Insets; 025import java.awt.event.ActionEvent; 026import java.awt.event.ActionListener; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.List; 030 031import javax.swing.JButton; 032import javax.swing.JPanel; 033import javax.swing.SwingUtilities; 034import javax.swing.border.Border; 035import javax.swing.border.EmptyBorder; 036import javax.swing.tree.TreePath; 037 038import org.forgerock.i18n.LocalizableMessage; 039import org.opends.guitools.controlpanel.browser.BasicNodeError; 040import org.opends.guitools.controlpanel.browser.BrowserController; 041import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 042import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 043import org.opends.guitools.controlpanel.datamodel.ServerDescriptor; 044import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; 045import org.opends.guitools.controlpanel.event.EntryReadErrorEvent; 046import org.opends.guitools.controlpanel.event.EntryReadEvent; 047import org.opends.guitools.controlpanel.event.EntryReadListener; 048import org.opends.guitools.controlpanel.event.LDAPEntryChangedEvent; 049import org.opends.guitools.controlpanel.event.LDAPEntryChangedListener; 050import org.opends.guitools.controlpanel.task.DeleteEntryTask; 051import org.opends.guitools.controlpanel.task.ModifyEntryTask; 052import org.opends.guitools.controlpanel.task.Task; 053import org.opends.guitools.controlpanel.util.Utilities; 054import org.opends.server.config.ConfigConstants; 055import org.forgerock.opendj.ldap.DN; 056import org.opends.server.types.Entry; 057import org.opends.server.types.OpenDsException; 058import org.opends.server.util.ServerConstants; 059 060/** This is the panel that contains all the different views to display an entry. */ 061public class LDAPEntryPanel extends StatusGenericPanel 062implements EntryReadListener 063{ 064 private static final long serialVersionUID = -6608246173472437830L; 065 private JButton saveChanges; 066 private JButton delete; 067 private JPanel mainPanel; 068 private CardLayout cardLayout; 069 070 private ErrorSearchingEntryPanel errorSearchingPanel; 071 private LDIFViewEntryPanel ldifEntryPanel; 072 private TableViewEntryPanel tableEntryPanel; 073 private SimplifiedViewEntryPanel simplifiedEntryPanel; 074 075 private ViewEntryPanel displayedEntryPanel; 076 077 private CustomSearchResult searchResult; 078 private BrowserController controller; 079 private TreePath treePath; 080 081 private ModifyEntryTask newTask; 082 083 private final String NOTHING_SELECTED = "Nothing Selected"; 084 private final String MULTIPLE_SELECTED = "Multiple Selected"; 085 private final String LDIF_VIEW = "LDIF View"; 086 private final String ATTRIBUTE_VIEW = "Attribute View"; 087 private final String SIMPLIFIED_VIEW = "Simplified View"; 088 private final String ERROR_SEARCHING = "Error Searching"; 089 090 private View view = View.SIMPLIFIED_VIEW; 091 092 /** The different views that we have to display an LDAP entry. */ 093 public enum View 094 { 095 /** Simplified view. */ 096 SIMPLIFIED_VIEW, 097 /** Attribute view (contained in a table). */ 098 ATTRIBUTE_VIEW, 099 /** LDIF view (text based). */ 100 LDIF_VIEW 101 } 102 103 /** Default constructor. */ 104 public LDAPEntryPanel() 105 { 106 super(); 107 createLayout(); 108 } 109 110 /** Creates the layout of the panel (but the contents are not populated here). */ 111 private void createLayout() 112 { 113 GridBagConstraints gbc = new GridBagConstraints(); 114 cardLayout = new CardLayout(); 115 mainPanel = new JPanel(cardLayout); 116 mainPanel.setOpaque(false); 117 gbc.gridx = 0; 118 gbc.gridy = 0; 119 gbc.gridwidth = 2; 120 gbc.weightx = 1.0; 121 gbc.weighty = 1.0; 122 gbc.fill = GridBagConstraints.BOTH; 123 add(mainPanel, gbc); 124 gbc.gridwidth = 1; 125 gbc.anchor = GridBagConstraints.WEST; 126 gbc.insets = new Insets(5, 5, 5, 5); 127 gbc.weighty = 0.0; 128 gbc.gridy ++; 129 gbc.fill = GridBagConstraints.NONE; 130 delete = Utilities.createButton(INFO_CTRL_PANEL_DELETE_ENTRY_BUTTON.get()); 131 delete.setOpaque(false); 132 add(delete, gbc); 133 delete.addActionListener(new ActionListener() 134 { 135 public void actionPerformed(ActionEvent ev) 136 { 137 deleteEntry(); 138 } 139 }); 140 141 gbc.anchor = GridBagConstraints.EAST; 142 gbc.gridx ++; 143 saveChanges = 144 Utilities.createButton(INFO_CTRL_PANEL_SAVE_CHANGES_LABEL.get()); 145 saveChanges.setOpaque(false); 146 add(saveChanges, gbc); 147 saveChanges.addActionListener(new ActionListener() 148 { 149 /** {@inheritDoc} */ 150 public void actionPerformed(ActionEvent ev) 151 { 152 saveChanges(true); 153 } 154 }); 155 156 Border border = new EmptyBorder(10, 10, 10, 10); 157 158 NoItemSelectedPanel noEntryPanel = new NoItemSelectedPanel(); 159 noEntryPanel.setMessage(INFO_CTRL_PANEL_NO_ENTRY_SELECTED_LABEL.get()); 160 Utilities.setBorder(noEntryPanel, border); 161 mainPanel.add(noEntryPanel, NOTHING_SELECTED); 162 163 NoItemSelectedPanel multipleEntryPanel = new NoItemSelectedPanel(); 164 multipleEntryPanel.setMessage( 165 INFO_CTRL_PANEL_MULTIPLE_ENTRIES_SELECTED_LABEL.get()); 166 Utilities.setBorder(multipleEntryPanel, border); 167 mainPanel.add(multipleEntryPanel, MULTIPLE_SELECTED); 168 169 errorSearchingPanel = new ErrorSearchingEntryPanel(); 170 if (errorSearchingPanel.requiresBorder()) 171 { 172 Utilities.setBorder(multipleEntryPanel, border); 173 } 174 mainPanel.add(errorSearchingPanel, ERROR_SEARCHING); 175 176 LDAPEntryChangedListener listener = new LDAPEntryChangedListener() 177 { 178 /** {@inheritDoc} */ 179 public void entryChanged(LDAPEntryChangedEvent ev) 180 { 181 boolean enable = saveChanges.isVisible() && 182 !authenticationRequired(getInfo().getServerDescriptor()); 183 if (enable) 184 { 185 if (ev.getEntry() == null) 186 { 187 // Something changed that is wrong: assume the entry has been 188 // modified, when the user tries to save we will inform of the 189 // problem 190 enable = true; 191 } 192 else 193 { 194 boolean modified = 195 !Utilities.areDnsEqual(ev.getEntry().getName().toString(), 196 searchResult.getDN()) || 197 !ModifyEntryTask.getModifications(ev.getEntry(), searchResult, 198 getInfo()).isEmpty(); 199 enable = modified; 200 } 201 } 202 saveChanges.setEnabled(enable); 203 } 204 }; 205 206 ldifEntryPanel = new LDIFViewEntryPanel(); 207 ldifEntryPanel.addLDAPEntryChangedListener(listener); 208 if (ldifEntryPanel.requiresBorder()) 209 { 210 Utilities.setBorder(ldifEntryPanel, border); 211 } 212 mainPanel.add(ldifEntryPanel, LDIF_VIEW); 213 214 tableEntryPanel = new TableViewEntryPanel(); 215 tableEntryPanel.addLDAPEntryChangedListener(listener); 216 if (tableEntryPanel.requiresBorder()) 217 { 218 Utilities.setBorder(tableEntryPanel, border); 219 } 220 mainPanel.add(tableEntryPanel, ATTRIBUTE_VIEW); 221 222 simplifiedEntryPanel = new SimplifiedViewEntryPanel(); 223 simplifiedEntryPanel.addLDAPEntryChangedListener(listener); 224 if (simplifiedEntryPanel.requiresBorder()) 225 { 226 Utilities.setBorder(simplifiedEntryPanel, border); 227 } 228 mainPanel.add(simplifiedEntryPanel, SIMPLIFIED_VIEW); 229 230 cardLayout.show(mainPanel, NOTHING_SELECTED); 231 } 232 233 /** {@inheritDoc} */ 234 public void okClicked() 235 { 236 // No ok button 237 } 238 239 /** {@inheritDoc} */ 240 public void entryRead(EntryReadEvent ev) 241 { 242 searchResult = ev.getSearchResult(); 243 244 updateEntryView(searchResult, treePath); 245 } 246 247 /** 248 * Updates the panel with the provided search result. 249 * @param searchResult the search result corresponding to the selected node. 250 * @param treePath the tree path of the selected node. 251 */ 252 private void updateEntryView(CustomSearchResult searchResult, 253 TreePath treePath) 254 { 255 boolean isReadOnly = isReadOnly(searchResult.getDN()); 256 boolean canDelete = canDelete(searchResult.getDN()); 257 258 delete.setVisible(canDelete); 259 saveChanges.setVisible(!isReadOnly); 260 String cardKey; 261 switch (view) 262 { 263 case LDIF_VIEW: 264 displayedEntryPanel = ldifEntryPanel; 265 cardKey = LDIF_VIEW; 266 break; 267 case ATTRIBUTE_VIEW: 268 displayedEntryPanel = tableEntryPanel; 269 cardKey = ATTRIBUTE_VIEW; 270 break; 271 default: 272 displayedEntryPanel = simplifiedEntryPanel; 273 cardKey = SIMPLIFIED_VIEW; 274 } 275 displayedEntryPanel.update(searchResult, isReadOnly, treePath); 276 saveChanges.setEnabled(false); 277 cardLayout.show(mainPanel, cardKey); 278 } 279 280 /** 281 * Sets the view to be displayed by this panel. 282 * @param view the view. 283 */ 284 public void setView(View view) 285 { 286 if (view != this.view) 287 { 288 this.view = view; 289 if (searchResult != null) 290 { 291 updateEntryView(searchResult, treePath); 292 } 293 } 294 } 295 296 /** 297 * Displays a message informing that an error occurred reading the entry. 298 * @param ev the entry read error event. 299 */ 300 public void entryReadError(EntryReadErrorEvent ev) 301 { 302 searchResult = null; 303 304 errorSearchingPanel.setError(ev.getDN(), ev.getError()); 305 306 delete.setVisible(false); 307 saveChanges.setVisible(false); 308 309 cardLayout.show(mainPanel, ERROR_SEARCHING); 310 311 displayedEntryPanel = null; 312 } 313 314 /** 315 * Displays a message informing that an error occurred resolving a referral. 316 * @param dn the DN of the local entry. 317 * @param referrals the list of referrals defined in the entry. 318 * @param error the error that occurred resolving the referral. 319 */ 320 public void referralSolveError(String dn, String[] referrals, 321 BasicNodeError error) 322 { 323 searchResult = null; 324 325 errorSearchingPanel.setReferralError(dn, referrals, error); 326 327 delete.setVisible(false); 328 saveChanges.setVisible(false); 329 330 cardLayout.show(mainPanel, ERROR_SEARCHING); 331 332 displayedEntryPanel = null; 333 } 334 335 /** Displays a panel informing that nothing is selected. */ 336 public void noEntrySelected() 337 { 338 searchResult = null; 339 340 delete.setVisible(false); 341 saveChanges.setVisible(false); 342 343 cardLayout.show(mainPanel, NOTHING_SELECTED); 344 345 displayedEntryPanel = null; 346 } 347 348 /** Displays a panel informing that multiple entries are selected. */ 349 public void multipleEntriesSelected() 350 { 351 searchResult = null; 352 353 delete.setVisible(false); 354 saveChanges.setVisible(false); 355 356 cardLayout.show(mainPanel, MULTIPLE_SELECTED); 357 358 displayedEntryPanel = null; 359 } 360 361 /** {@inheritDoc} */ 362 public GenericDialog.ButtonType getButtonType() 363 { 364 return GenericDialog.ButtonType.NO_BUTTON; 365 } 366 367 /** {@inheritDoc} */ 368 public LocalizableMessage getTitle() 369 { 370 return INFO_CTRL_PANEL_EDIT_LDAP_ENTRY_TITLE.get(); 371 } 372 373 /** {@inheritDoc} */ 374 public Component getPreferredFocusComponent() 375 { 376 return saveChanges; 377 } 378 379 /** {@inheritDoc} */ 380 public void configurationChanged(ConfigurationChangeEvent ev) 381 { 382 final ServerDescriptor desc = ev.getNewDescriptor(); 383 SwingUtilities.invokeLater(new Runnable() 384 { 385 /** {@inheritDoc} */ 386 public void run() 387 { 388 boolean isReadOnly = true; 389 boolean canDelete = false; 390 if (searchResult != null && desc.isAuthenticated()) 391 { 392 isReadOnly = isReadOnly(searchResult.getDN()); 393 canDelete = canDelete(searchResult.getDN()); 394 } 395 396 delete.setVisible(canDelete); 397 saveChanges.setVisible(!isReadOnly); 398 } 399 }); 400 } 401 402 /** {@inheritDoc} */ 403 public void setInfo(ControlPanelInfo info) 404 { 405 super.setInfo(info); 406 simplifiedEntryPanel.setInfo(info); 407 ldifEntryPanel.setInfo(info); 408 tableEntryPanel.setInfo(info); 409 errorSearchingPanel.setInfo(info); 410 } 411 412 private List<DN> parentReadOnly; 413 private List<DN> nonDeletable; 414 { 415 try 416 { 417 parentReadOnly = Arrays.asList( 418 DN.valueOf(ConfigConstants.DN_TASK_ROOT), 419 DN.valueOf(ConfigConstants.DN_MONITOR_ROOT), 420 DN.valueOf(ConfigConstants.DN_BACKUP_ROOT), 421 DN.valueOf(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT) 422 ); 423 nonDeletable = Arrays.asList( 424 DN.valueOf(ConfigConstants.DN_CONFIG_ROOT), 425 DN.valueOf(ConfigConstants.DN_DEFAULT_SCHEMA_ROOT), 426 DN.valueOf(ConfigConstants.DN_TRUST_STORE_ROOT) 427 ); 428 } 429 catch (Throwable t) 430 { 431 throw new RuntimeException("Error decoding DNs: "+t, t); 432 } 433 } 434 435 /** 436 * Returns <CODE>true</CODE> if the provided DN corresponds to a read-only 437 * entry and <CODE>false</CODE> otherwise. 438 * @param sDn the DN of the entry. 439 * @return <CODE>true</CODE> if the provided DN corresponds to a read-only 440 * entry and <CODE>false</CODE> otherwise. 441 */ 442 public boolean isReadOnly(String sDn) 443 { 444 boolean isReadOnly = false; 445 try 446 { 447 DN dn = DN.valueOf(sDn); 448 for (DN parentDN : parentReadOnly) 449 { 450 if (dn.isSubordinateOrEqualTo(parentDN)) 451 { 452 isReadOnly = true; 453 break; 454 } 455 } 456 if (!isReadOnly) 457 { 458 isReadOnly = dn.equals(DN.rootDN()); 459 } 460 } 461 catch (Throwable t) 462 { 463 throw new RuntimeException("Error decoding DNs: "+t, t); 464 } 465 return isReadOnly; 466 } 467 468 /** 469 * Returns <CODE>true</CODE> if the provided DN corresponds to an entry that 470 * can be deleted and <CODE>false</CODE> otherwise. 471 * @param sDn the DN of the entry. 472 * @return <CODE>true</CODE> if the provided DN corresponds to an entry that 473 * can be deleted and <CODE>false</CODE> otherwise. 474 */ 475 public boolean canDelete(String sDn) 476 { 477 try 478 { 479 DN dn = DN.valueOf(sDn); 480 return !dn.equals(DN.rootDN()) 481 && !nonDeletable.contains(dn) 482 && isDescendantOfAny(dn, parentReadOnly); 483 } 484 catch (Throwable t) 485 { 486 throw new RuntimeException("Error decoding DNs: "+t, t); 487 } 488 } 489 490 private boolean isDescendantOfAny(DN dn, List<DN> parentDNs) 491 { 492 for (DN parentDN : parentDNs) 493 { 494 if (dn.isSubordinateOrEqualTo(parentDN)) 495 { 496 return false; 497 } 498 } 499 return true; 500 } 501 502 /** 503 * Saves the changes done to the entry. 504 * @param modal whether the progress dialog for the task must be modal or 505 * not. 506 */ 507 private void saveChanges(boolean modal) 508 { 509 newTask = null; 510 final ArrayList<LocalizableMessage> errors = new ArrayList<>(); 511 // Check that the entry is correct. 512 try 513 { 514 ProgressDialog dlg = new ProgressDialog( 515 Utilities.getFrame(this), 516 Utilities.getFrame(this), 517 INFO_CTRL_PANEL_MODIFYING_ENTRY_CHANGES_TITLE.get(), getInfo()); 518 dlg.setModal(modal); 519 Entry entry = displayedEntryPanel.getEntry(); 520 newTask = new ModifyEntryTask(getInfo(), dlg, entry, 521 searchResult, controller, treePath); 522 for (Task task : getInfo().getTasks()) 523 { 524 task.canLaunch(newTask, errors); 525 } 526 527 if (errors.isEmpty()) 528 { 529 if (newTask.hasModifications()) { 530 String dn = entry.getName().toString(); 531 launchOperation(newTask, 532 INFO_CTRL_PANEL_MODIFYING_ENTRY_SUMMARY.get(dn), 533 INFO_CTRL_PANEL_MODIFYING_ENTRY_COMPLETE.get(), 534 INFO_CTRL_PANEL_MODIFYING_ENTRY_SUCCESSFUL.get(dn), 535 ERR_CTRL_PANEL_MODIFYING_ENTRY_ERROR_SUMMARY.get(), 536 ERR_CTRL_PANEL_MODIFYING_ENTRY_ERROR_DETAILS.get(dn), 537 null, 538 dlg); 539 saveChanges.setEnabled(false); 540 dlg.setVisible(true); 541 } 542 else 543 { 544 // Mark the panel as it has no changes. This can happen because every 545 // time the user types something the saveChanges button is enabled 546 // (for performance reasons with huge entries). 547 saveChanges.setEnabled(false); 548 } 549 } 550 } 551 catch (OpenDsException ode) 552 { 553 errors.add(ERR_CTRL_PANEL_INVALID_ENTRY.get(ode.getMessageObject())); 554 } 555 if (!errors.isEmpty()) 556 { 557 displayErrorDialog(errors); 558 } 559 } 560 561 private void deleteEntry() 562 { 563 final ArrayList<LocalizableMessage> errors = new ArrayList<>(); 564 // Check that the entry is correct. 565 // Rely in numsubordinates and hassubordinates 566 boolean isLeaf = !BrowserController.getHasSubOrdinates(searchResult); 567 568 if (treePath != null) 569 { 570 LocalizableMessage title = isLeaf ? INFO_CTRL_PANEL_DELETING_ENTRY_TITLE.get() : 571 INFO_CTRL_PANEL_DELETING_SUBTREE_TITLE.get(); 572 ProgressDialog dlg = new ProgressDialog( 573 Utilities.createFrame(), 574 Utilities.getParentDialog(this), title, getInfo()); 575 DeleteEntryTask newTask = new DeleteEntryTask(getInfo(), dlg, 576 new TreePath[]{treePath}, controller); 577 for (Task task : getInfo().getTasks()) 578 { 579 task.canLaunch(newTask, errors); 580 } 581 if (errors.isEmpty()) 582 { 583 LocalizableMessage confirmationMessage = 584 isLeaf ? INFO_CTRL_PANEL_DELETE_ENTRY_CONFIRMATION_DETAILS.get( 585 searchResult.getDN()) : 586 INFO_CTRL_PANEL_DELETE_SUBTREE_CONFIRMATION_DETAILS.get( 587 searchResult.getDN()); 588 if (displayConfirmationDialog( 589 INFO_CTRL_PANEL_CONFIRMATION_REQUIRED_SUMMARY.get(), 590 confirmationMessage)) 591 { 592 String dn = searchResult.getDN(); 593 if (isLeaf) 594 { 595 launchOperation(newTask, 596 INFO_CTRL_PANEL_DELETING_ENTRY_SUMMARY.get(dn), 597 INFO_CTRL_PANEL_DELETING_ENTRY_COMPLETE.get(), 598 INFO_CTRL_PANEL_DELETING_ENTRY_SUCCESSFUL.get(dn), 599 ERR_CTRL_PANEL_DELETING_ENTRY_ERROR_SUMMARY.get(), 600 ERR_CTRL_PANEL_DELETING_ENTRY_ERROR_DETAILS.get(dn), 601 null, 602 dlg); 603 } 604 else 605 { 606 launchOperation(newTask, 607 INFO_CTRL_PANEL_DELETING_SUBTREE_SUMMARY.get(dn), 608 INFO_CTRL_PANEL_DELETING_SUBTREE_COMPLETE.get(), 609 INFO_CTRL_PANEL_DELETING_SUBTREE_SUCCESSFUL.get(dn), 610 ERR_CTRL_PANEL_DELETING_SUBTREE_ERROR_SUMMARY.get(), 611 ERR_CTRL_PANEL_DELETING_SUBTREE_ERROR_DETAILS.get(dn), 612 null, 613 dlg); 614 } 615 dlg.setVisible(true); 616 } 617 } 618 } 619 if (!errors.isEmpty()) 620 { 621 displayErrorDialog(errors); 622 } 623 } 624 625 /** 626 * Returns the browser controller in charge of the tree. 627 * @return the browser controller in charge of the tree. 628 */ 629 public BrowserController getController() 630 { 631 return controller; 632 } 633 634 /** 635 * Sets the browser controller in charge of the tree. 636 * @param controller the browser controller in charge of the tree. 637 */ 638 public void setController(BrowserController controller) 639 { 640 this.controller = controller; 641 } 642 643 /** 644 * Returns the tree path associated with the node that is being displayed. 645 * @return the tree path associated with the node that is being displayed. 646 */ 647 public TreePath getTreePath() 648 { 649 return treePath; 650 } 651 652 /** 653 * Sets the tree path associated with the node that is being displayed. 654 * @param treePath the tree path associated with the node that is being 655 * displayed. 656 */ 657 public void setTreePath(TreePath treePath) 658 { 659 this.treePath = treePath; 660 } 661 662 /** 663 * Method used to know if there are unsaved changes or not. It is used by 664 * the entry selection listener when the user changes the selection. 665 * @return <CODE>true</CODE> if there are unsaved changes (and so the 666 * selection of the entry should be cancelled) and <CODE>false</CODE> 667 * otherwise. 668 */ 669 public boolean mustCheckUnsavedChanges() 670 { 671 return displayedEntryPanel != null && 672 saveChanges.isVisible() && saveChanges.isEnabled(); 673 } 674 675 /** 676 * Tells whether the user chose to save the changes in the panel, to not save 677 * them or simply canceled the selection change in the tree. 678 * @return the value telling whether the user chose to save the changes in the 679 * panel, to not save them or simply canceled the selection in the tree. 680 */ 681 public UnsavedChangesDialog.Result checkUnsavedChanges() 682 { 683 UnsavedChangesDialog.Result result; 684 UnsavedChangesDialog unsavedChangesDlg = new UnsavedChangesDialog( 685 Utilities.getParentDialog(this), getInfo()); 686 unsavedChangesDlg.setMessage(INFO_CTRL_PANEL_UNSAVED_CHANGES_SUMMARY.get(), 687 INFO_CTRL_PANEL_UNSAVED_ENTRY_CHANGES_DETAILS.get(searchResult.getDN())); 688 Utilities.centerGoldenMean(unsavedChangesDlg, 689 Utilities.getParentDialog(this)); 690 unsavedChangesDlg.setVisible(true); 691 result = unsavedChangesDlg.getResult(); 692 if (result == UnsavedChangesDialog.Result.SAVE) 693 { 694 saveChanges(false); 695 if (newTask == null || // The user data is not valid 696 newTask.getState() != Task.State.FINISHED_SUCCESSFULLY) 697 { 698 result = UnsavedChangesDialog.Result.CANCEL; 699 } 700 } 701 702 return result; 703 } 704}