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-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017
018package org.opends.guitools.controlpanel.ui;
019
020import static org.opends.messages.AdminToolMessages.*;
021
022import java.awt.Component;
023import java.awt.GridBagConstraints;
024import java.awt.datatransfer.Transferable;
025import java.awt.datatransfer.UnsupportedFlavorException;
026import java.awt.dnd.DnDConstants;
027import java.awt.dnd.DropTarget;
028import java.awt.dnd.DropTargetDragEvent;
029import java.awt.dnd.DropTargetDropEvent;
030import java.awt.dnd.DropTargetEvent;
031import java.awt.dnd.DropTargetListener;
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.io.IOException;
035import java.util.ArrayList;
036import java.util.LinkedHashSet;
037import java.util.List;
038import java.util.Set;
039
040import javax.swing.JButton;
041import javax.swing.JLabel;
042import javax.swing.JScrollPane;
043import javax.swing.JTextArea;
044import javax.swing.SwingUtilities;
045
046import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
047import org.opends.guitools.controlpanel.task.AddToGroupTask;
048import org.opends.guitools.controlpanel.task.Task;
049import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo;
050import org.opends.guitools.controlpanel.ui.nodes.DndBrowserNodes;
051import org.opends.guitools.controlpanel.util.BackgroundTask;
052import org.opends.guitools.controlpanel.util.Utilities;
053import org.forgerock.i18n.LocalizableMessage;
054import org.forgerock.i18n.LocalizedIllegalArgumentException;
055import org.forgerock.opendj.ldap.DN;
056import org.opends.server.util.ServerConstants;
057
058/** The dialog that is displayed when we want to add entries to a set of groups. */
059public class AddToGroupPanel extends StatusGenericPanel
060{
061  private static final long serialVersionUID = 1837745944604435848L;
062  private JTextArea groups;
063  private JTextArea entries;
064  private JScrollPane scrollEntries;
065  private JLabel lEntries = Utilities.createDefaultLabel();
066  private JLabel lGroups = Utilities.createDefaultLabel();
067  private LinkedHashSet<DN> dns = new LinkedHashSet<>();
068
069  private GenericDialog browseGroupDlg;
070  private LDAPEntrySelectionPanel browseGroupPanel;
071
072  /**
073   * Default constructor.
074   *
075   */
076  public AddToGroupPanel()
077  {
078    super();
079    createLayout();
080  }
081
082  /**
083   * Sets the entries we want to add to groups.
084   * @param dns the DN of the entries we want to add to groups.
085   */
086  public void setEntriesToAdd(Set<DN> dns)
087  {
088    ArrayList<String> sDns = new ArrayList<>();
089    for (DN dn : dns)
090    {
091      sDns.add(dn.toString());
092    }
093    if (dns.size() > 5)
094    {
095      entries.setText(Utilities.getStringFromCollection(sDns, "\n"));
096      scrollEntries.setVisible(true);
097      lEntries.setVisible(false);
098    }
099    else
100    {
101      lEntries.setText("<html>"+Utilities.applyFont(
102          "<li>"+Utilities.getStringFromCollection(sDns, "<li>"),
103          ColorAndFontConstants.defaultFont));
104      scrollEntries.setVisible(false);
105      lEntries.setVisible(true);
106    }
107    this.dns.clear();
108    this.dns.addAll(dns);
109    packParentDialog();
110  }
111
112  /** {@inheritDoc} */
113  public Component getPreferredFocusComponent()
114  {
115    return groups;
116  }
117
118  /** {@inheritDoc} */
119  public void okClicked()
120  {
121    final ArrayList<LocalizableMessage> errors = new ArrayList<>();
122    BackgroundTask<Void> worker = new BackgroundTask<Void>()
123    {
124      /** {@inheritDoc} */
125      public Void processBackgroundTask()
126      {
127        try
128        {
129          Thread.sleep(2000);
130        }
131        catch (Throwable t)
132        {
133        }
134        updateErrors(errors);
135        return null;
136      }
137      /** {@inheritDoc} */
138      public void backgroundTaskCompleted(Void returnValue, Throwable t)
139      {
140        if (t != null)
141        {
142          errors.add(ERR_CTRL_PANEL_UNEXPECTED_DETAILS.get(t));
143        }
144        displayMainPanel();
145        setEnabledCancel(true);
146        setEnabledOK(true);
147        handleErrorsAndLaunchTask(errors);
148      }
149    };
150    displayMessage(INFO_CTRL_PANEL_CHECKING_SUMMARY.get());
151    setEnabledCancel(false);
152    setEnabledOK(false);
153    worker.startBackgroundTask();
154  }
155
156  /** {@inheritDoc} */
157  public LocalizableMessage getTitle()
158  {
159    return INFO_CTRL_PANEL_ADD_TO_GROUP_TITLE.get();
160  }
161
162  /** {@inheritDoc} */
163  public void configurationChanged(ConfigurationChangeEvent ev)
164  {
165  }
166
167  /**
168   * Creates the layout of the panel (but the contents are not populated here).
169   */
170  private void createLayout()
171  {
172    GridBagConstraints gbc = new GridBagConstraints();
173    gbc.gridx = 0;
174    gbc.gridy = 0;
175    gbc.weightx = 0.0;
176    gbc.weighty = 0.0;
177    gbc.gridwidth = 2;
178    gbc.fill = GridBagConstraints.HORIZONTAL;
179    JLabel l = Utilities.createDefaultLabel(
180        INFO_CTRL_PANEL_ADD_TO_GROUP_ENTRIES_LABEL.get());
181    add(l, gbc);
182    gbc.insets.top = 5;
183    entries = Utilities.createNonEditableTextArea(LocalizableMessage.EMPTY, 6, 40);
184    scrollEntries = Utilities.createScrollPane(entries);
185    gbc.weighty = 0.1;
186    gbc.fill = GridBagConstraints.BOTH;
187    gbc.gridy ++;
188    add(scrollEntries, gbc);
189    gbc.weighty = 0.0;
190    gbc.fill = GridBagConstraints.HORIZONTAL;
191    gbc.insets.top = 0;
192    add(lEntries, gbc);
193
194    gbc.insets.top = 10;
195    gbc.gridy ++ ;
196    lGroups.setText(INFO_CTRL_PANEL_ADD_TO_GROUP_GROUPS_LABEL.get().toString());
197    add(lGroups, gbc);
198    gbc.insets.top = 5;
199    gbc.gridwidth = 1;
200    groups = Utilities.createTextArea(LocalizableMessage.EMPTY, 8, 40);
201    JScrollPane scrollGroups = Utilities.createScrollPane(groups);
202    gbc.weightx = 1.0;
203    gbc.weighty = 1.0;
204    gbc.fill = GridBagConstraints.BOTH;
205    gbc.gridy ++;
206    add(scrollGroups, gbc);
207    gbc.gridx ++;
208    gbc.insets.left = 5;
209    gbc.weightx = 0.0;
210    gbc.fill = GridBagConstraints.HORIZONTAL;
211    JButton browse = Utilities.createButton(
212        INFO_CTRL_PANEL_ADD_GROUPS_BUTTON_LABEL.get());
213    gbc.anchor = GridBagConstraints.NORTH;
214    add(browse, gbc);
215    browse.addActionListener(new ActionListener()
216    {
217      /** {@inheritDoc} */
218      public void actionPerformed(ActionEvent ev)
219      {
220        browseGroupsClicked();
221      }
222    });
223
224    DropTargetListener dropTargetlistener = new DropTargetListener()
225    {
226      /** {@inheritDoc} */
227      public void dragEnter(DropTargetDragEvent e)
228      {
229      }
230
231      /** {@inheritDoc} */
232      public void dragExit(DropTargetEvent e)
233      {
234      }
235
236      /** {@inheritDoc} */
237      public void dragOver(DropTargetDragEvent e)
238      {
239      }
240
241      /** {@inheritDoc} */
242      public void dropActionChanged(DropTargetDragEvent e)
243      {
244      }
245
246      /** {@inheritDoc} */
247      public void drop(DropTargetDropEvent e)
248      {
249        try {
250          Transferable tr = e.getTransferable();
251
252          //flavor not supported, reject drop
253          if (!tr.isDataFlavorSupported(DndBrowserNodes.INFO_FLAVOR))
254          {
255            e.rejectDrop();
256          }
257
258          //cast into appropriate data type
259          DndBrowserNodes nodes =
260            (DndBrowserNodes) tr.getTransferData(DndBrowserNodes.INFO_FLAVOR);
261
262          StringBuilder sb = new StringBuilder();
263          sb.append(groups.getText());
264          for (BrowserNodeInfo node : nodes.getNodes())
265          {
266            if (sb.length() > 0)
267            {
268              sb.append("\n");
269            }
270            sb.append(node.getNode().getDN());
271          }
272          groups.setText(sb.toString());
273          groups.setCaretPosition(sb.length());
274
275          e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
276          e.getDropTargetContext().dropComplete(true);
277        }
278        catch (IOException | UnsupportedFlavorException io)
279        {
280          e.rejectDrop();
281        }
282      }
283    };
284    new DropTarget(groups, dropTargetlistener);
285  }
286
287  private void browseGroupsClicked()
288  {
289    if (browseGroupDlg == null)
290    {
291      browseGroupPanel = new LDAPEntrySelectionPanel();
292      browseGroupPanel.setTitle(INFO_CTRL_PANEL_CHOOSE_GROUP_TITLE.get());
293      browseGroupPanel.setFilter(
294          LDAPEntrySelectionPanel.Filter.STATIC_GROUPS);
295      browseGroupPanel.setMultipleSelection(true);
296      browseGroupPanel.setInfo(getInfo());
297      browseGroupDlg = new GenericDialog(Utilities.getFrame(this),
298          browseGroupPanel);
299      Utilities.centerGoldenMean(browseGroupDlg,
300          Utilities.getParentDialog(this));
301      browseGroupDlg.setModal(true);
302    }
303    browseGroupDlg.setVisible(true);
304    String[] dns = browseGroupPanel.getDNs();
305    if (dns.length > 0)
306    {
307      StringBuilder sb = new StringBuilder();
308      sb.append(groups.getText());
309      for (String dn : dns)
310      {
311        if (sb.length() > 0)
312        {
313          sb.append("\n");
314        }
315        sb.append(dn);
316      }
317      groups.setText(sb.toString());
318      groups.setCaretPosition(sb.length());
319    }
320  }
321
322  private void updateErrors(List<LocalizableMessage> errors)
323  {
324    SwingUtilities.invokeLater(new Runnable()
325    {
326      /** {@inheritDoc} */
327      public void run()
328      {
329        setPrimaryValid(lGroups);
330      }
331    });
332
333    String[] grs = groups.getText().split("\n");
334    boolean oneGroupDefined = false;
335    for (String groupDn : grs)
336    {
337      groupDn = groupDn.trim();
338      if (groupDn.length() > 0)
339      {
340        try
341        {
342          DN.valueOf(groupDn);
343          if (!entryExists(groupDn))
344          {
345            errors.add(
346                ERR_CTRL_PANEL_GROUP_COULD_NOT_BE_FOUND.get(groupDn));
347          }
348          else if (!hasObjectClass(groupDn, ServerConstants.OC_GROUP_OF_NAMES,
349            ServerConstants.OC_GROUP_OF_ENTRIES,
350            ServerConstants.OC_GROUP_OF_UNIQUE_NAMES))
351          {
352            errors.add(ERR_CTRL_PANEL_NOT_A_STATIC_GROUP.get(groupDn));
353          }
354          else
355          {
356            oneGroupDefined = true;
357          }
358        }
359        catch (LocalizedIllegalArgumentException e)
360        {
361          errors.add(INFO_CTRL_PANEL_INVALID_DN_DETAILS.get(groupDn, e.getMessageObject()));
362        }
363      }
364    }
365    if (!oneGroupDefined && errors.isEmpty())
366    {
367      errors.add(ERR_CTRL_PANEL_GROUP_NOT_PROVIDED.get());
368    }
369
370    if (!errors.isEmpty())
371    {
372      SwingUtilities.invokeLater(new Runnable()
373      {
374        /** {@inheritDoc} */
375        public void run()
376        {
377          setPrimaryInvalid(lGroups);
378        }
379      });
380    }
381  }
382
383  private void handleErrorsAndLaunchTask(ArrayList<LocalizableMessage> errors)
384  {
385    if (errors.isEmpty())
386    {
387      ProgressDialog dlg = new ProgressDialog(
388          Utilities.createFrame(),
389          Utilities.getParentDialog(this),
390          INFO_CTRL_PANEL_ADD_TO_GROUP_TITLE.get(), getInfo());
391      LinkedHashSet<DN> groupDns = new LinkedHashSet<>();
392      for (String groupDn : groups.getText().split("\n"))
393      {
394        groupDn = groupDn.trim();
395        if (groupDn.length() > 0)
396        {
397          groupDns.add(DN.valueOf(groupDn));
398        }
399      }
400
401      try
402      {
403        AddToGroupTask newTask =
404          new AddToGroupTask(getInfo(), dlg, dns, groupDns);
405        for (Task task : getInfo().getTasks())
406        {
407          task.canLaunch(newTask, errors);
408        }
409        if (errors.isEmpty())
410        {
411          launchOperation(newTask,
412              INFO_CTRL_PANEL_ADDING_TO_GROUP_SUMMARY.get(),
413              INFO_CTRL_PANEL_ADDING_TO_GROUP_SUCCESSFUL_SUMMARY.get(),
414              INFO_CTRL_PANEL_ADDING_TO_GROUP_SUCCESSFUL_DETAILS.get(),
415              ERR_CTRL_PANEL_ADDING_TO_GROUP_ERROR_SUMMARY.get(),
416              ERR_CTRL_PANEL_ADDING_TO_GROUP_ERROR_DETAILS.get(),
417              null,
418              dlg);
419          dlg.setVisible(true);
420          Utilities.getParentDialog(this).setVisible(false);
421        }
422      }
423      catch (Throwable t)
424      {
425        // Unexpected error: getEntry() should work after calling checkSyntax
426        throw new RuntimeException("Unexpected error: "+t, t);
427      }
428    }
429    if (!errors.isEmpty())
430    {
431      displayErrorDialog(errors);
432    }
433  }
434}