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