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