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