summaryrefslogtreecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator
diff options
context:
space:
mode:
authorSimon Rettberg2018-06-21 15:41:44 +0200
committerSimon Rettberg2018-06-21 15:41:44 +0200
commitc0003a559a36dfca1bdc4add0034e67bd22824ed (patch)
treed0f03daa4eb8b94cbfb9472213a109eade52a0dc /dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator
parent[client] Refactor change monitor classes, better error message handling (diff)
downloadtutor-module-c0003a559a36dfca1bdc4add0034e67bd22824ed.tar.gz
tutor-module-c0003a559a36dfca1bdc4add0034e67bd22824ed.tar.xz
tutor-module-c0003a559a36dfca1bdc4add0034e67bd22824ed.zip
[client] Sanitize class/var names, split up control package
All configurators have moved from *.control to *.configurator *.control should be used for simple controls that feel like they're really just one thing. The configurators are more like a group of controls.
Diffstat (limited to 'dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator')
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/ImagePermissionConfigurator.java201
-rwxr-xr-xdozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LdapFilterConfigurator.java221
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LecturePermissionConfigurator.java236
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetrulesConfigurator.java450
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetshareConfigurator.java372
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/RunscriptConfigurator.java266
6 files changed, 1746 insertions, 0 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/ImagePermissionConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/ImagePermissionConfigurator.java
new file mode 100644
index 00000000..e20378f9
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/ImagePermissionConfigurator.java
@@ -0,0 +1,201 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.ImagePermissions;
+import org.openslx.bwlp.thrift.iface.UserInfo;
+import org.openslx.dozmod.gui.control.table.ImagePermissionTable;
+import org.openslx.dozmod.gui.control.table.QScrollPane;
+import org.openslx.dozmod.gui.control.table.ImagePermissionTable.UserImagePermissions;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.window.UserListWindow;
+import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback;
+import org.openslx.dozmod.thrift.cache.UserCache;
+import org.openslx.dozmod.util.FormatHelper;
+
+/**
+ * Panel including ImagePermissionTable and add/remove buttons for setting
+ * customImagePermissions.
+ */
+@SuppressWarnings("serial")
+public class ImagePermissionConfigurator extends JPanel {
+
+ protected ImagePermissionTable permissionTable;
+
+ protected JButton btnAddUser;
+ protected JButton btnRemoveUser;
+
+ private ImagePermissionConfigurator me;
+
+ private String ownerId;
+
+ private ArrayList<UserImagePermissions> permissionList = new ArrayList<UserImagePermissions>();
+ private Map<String, ImagePermissions> newPermissionMap;
+
+ private ImagePermissions defaultPermissions;
+
+ private static final Logger LOGGER = Logger.getLogger(ImagePermissionConfigurator.class);
+
+ public ImagePermissionConfigurator() {
+ super();
+ me = this;
+ GridManager grid = new GridManager(this, 1);
+
+ permissionTable = new ImagePermissionTable();
+
+ // Panel for the add- and remove buttons
+ JPanel userButtonPane = new JPanel();
+ userButtonPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ userButtonPane.setLayout(new BoxLayout(userButtonPane, BoxLayout.LINE_AXIS));
+
+ btnAddUser = new JButton("Benutzer hinzufügen");
+ userButtonPane.add(btnAddUser);
+ btnRemoveUser = new JButton("Benutzer entfernen");
+ userButtonPane.add(btnRemoveUser);
+ userButtonPane.add(Box.createGlue());
+
+ // Put everything into the grid
+ QScrollPane jsp = new QScrollPane(permissionTable);
+ jsp.setBackground(UIManager.getColor("Table.background"));
+ grid.add(jsp).fill(true, true).expand(true, true);
+ grid.nextRow();
+ grid.add(userButtonPane).fill(true, false).expand(true, false);
+ grid.nextRow();
+ grid.finish(false);
+
+ // add user button listener
+ btnAddUser.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ UserListWindow.open(SwingUtilities.getWindowAncestor(me), new UserAddedCallback() {
+ @Override
+ public void userAdded(final UserInfo newUser, UserListWindow window) {
+ // check if we have this user already
+ for (UserImagePermissions current : permissionList) {
+ if (current.userId.equals(newUser.userId)) {
+ LOGGER.debug("User already present in the list, skipping!");
+ return;
+ }
+ }
+ // add it to the list with default permissions
+ permissionList.add(new UserImagePermissions(newUser.userId, new ImagePermissions(
+ defaultPermissions)));
+ permissionTable.setData(permissionList, false);
+ }
+ }, "Hinzufügen", ownerId);
+ }
+ });
+
+ // delete user button listener
+ btnRemoveUser.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final UserImagePermissions selected = permissionTable.getSelectedItem();
+ if (selected != null && !permissionList.remove(selected)) {
+ LOGGER.debug("Could not remove: " + selected);
+ }
+ permissionTable.setData(permissionList, false);
+ }
+ });
+
+ // keyboard shortcut
+ permissionTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
+ KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete");
+ permissionTable.getActionMap().put("delete", new AbstractAction() {
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ btnRemoveUser.doClick();
+ }
+ });
+ }
+
+ /**
+ * Initialize the PermissionManager. Note that the given permissionMap and
+ * defaultPermissions reference
+ * will get changed when the user interacts with the controls.
+ *
+ * @param permissionMap the old permission, to initialize the table with,
+ * null creates empty table.
+ * @param defaultPermissions the permissions for a newly added user
+ * @param ownerId The user to exclude from the add user list. Can be null.
+ */
+ public void initPanel(Map<String, ImagePermissions> permissionMap,
+ final ImagePermissions defaultPermissions, String ownerId) {
+ this.ownerId = ownerId;
+ this.newPermissionMap = permissionMap == null ? new HashMap<String, ImagePermissions>()
+ : permissionMap;
+ this.defaultPermissions = defaultPermissions;
+
+ permissionList.clear();
+ for (Entry<String, ImagePermissions> e : newPermissionMap.entrySet()) {
+ permissionList.add(new UserImagePermissions(e.getKey(), e.getValue()));
+ }
+ // lexicographic sort on the last names
+ Collections.sort(permissionList, new Comparator<UserImagePermissions>() {
+ @Override
+ public int compare(UserImagePermissions o1, UserImagePermissions o2) {
+ UserInfo u1 = UserCache.find(o1.userId);
+ UserInfo u2 = UserCache.find(o2.userId);
+ if (u1 != null && u2 != null)
+ return FormatHelper.userName(u1).compareTo(FormatHelper.userName(u2));
+ else
+ return 0;
+ }
+ });
+ permissionTable.setData(permissionList, false);
+ }
+
+ /**
+ * Get map with the permissions set in the table of the manager.
+ *
+ * @return Map with new custom permissions, null if something went wrong
+ */
+ public Map<String, ImagePermissions> updatePermissionReferences() {
+ if (permissionList == null)
+ return null;
+
+ newPermissionMap.clear();
+
+ // put permissions of the list into the map
+ for (UserImagePermissions perm : permissionList) {
+ newPermissionMap.put(perm.userId, perm.permissions);
+ }
+ return newPermissionMap;
+ }
+
+ /**
+ * Update the used default permissions of the manager.
+ *
+ * @param link
+ * @param download
+ * @param edit
+ * @param admin
+ */
+ public void updateDefaultPermissions(boolean link, boolean download, boolean edit, boolean admin) {
+ this.defaultPermissions.link = link;
+ this.defaultPermissions.download = download;
+ this.defaultPermissions.edit = edit;
+ this.defaultPermissions.admin = admin;
+ }
+
+}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LdapFilterConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LdapFilterConfigurator.java
new file mode 100755
index 00000000..3727b50e
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LdapFilterConfigurator.java
@@ -0,0 +1,221 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.LdapFilter;
+import org.openslx.dozmod.gui.Gui;
+import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor;
+import org.openslx.dozmod.gui.control.QLabel;
+import org.openslx.dozmod.gui.control.WordWrapLabel;
+import org.openslx.dozmod.gui.control.table.LectureLdapFilterTable;
+import org.openslx.dozmod.gui.control.table.QScrollPane;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.helper.MessageType;
+
+/**
+ * Widget for network share configuration of lectures
+ */
+public class LdapFilterConfigurator extends LdapFilterConfiguratorLayout {
+
+ private static final long serialVersionUID = -3336605759245603655L;
+ private final static Logger LOGGER = Logger.getLogger(LdapFilterConfigurator.class);
+ private List<LdapFilter> filterList = null;
+
+ public LdapFilterConfigurator() {
+
+ super();
+
+ tblFilters.getModel().addTableModelListener(new TableModelListener() {
+ @Override
+ public void tableChanged(TableModelEvent e) {
+ fireChangeEvent();
+ }
+ });
+
+ tblFilters.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ LdapFilter item = tblFilters.getSelectedItem();
+ if (item == null) {
+ clearInputFields();
+ return;
+ }
+ // share from the list is selected: fill bottom form and change "Add" to "Apply"
+ btnDel.setEnabled(true);
+ txtAttribute.setText(item.attribute);
+ txtValue.setText(item.value);
+ btnAdd.setText("Ändern");
+ }
+ });
+
+ btnAdd.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // check if we are editing an existing share entry or
+ // creating a new one, check for input either way
+ LdapFilter input = new LdapFilter();
+
+ input.attribute = txtAttribute.getText();
+ if (input.attribute == null || input.attribute.isEmpty()) {
+ Gui.showMessageBox("Kein Attribut angegeben", MessageType.ERROR, null, null);
+ return;
+ }
+ input.value = txtValue.getText();
+ if (input.value == null) {
+ input.value = "";
+ }
+
+ // either we delete the existing share from the data or we are
+ // creating a new one, either way add it to the list and update
+ // the table, if its not present already
+ if (filterList.contains(input)) {
+ Gui.showMessageBox("Eintrag bereits vorhanden", MessageType.ERROR, null, null);
+ return;
+ }
+ // now decide whether to create a new entry or update existing one
+ LdapFilter oldEntry = tblFilters.getSelectedItem();
+ if (oldEntry != null) {
+ // editing existing one, delete it from the internal data
+ filterList.remove(oldEntry);
+ }
+ filterList.add(input);
+ tblFilters.setData(filterList, false);
+ clearInputFields();
+ }
+ });
+
+ btnDel.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // try to delete the selected share
+ LdapFilter selection = tblFilters.getSelectedItem();
+ if (selection == null) {
+ return;
+ }
+ try {
+ if (!filterList.remove(selection)) {
+ // false if it was not found
+ LOGGER.error("Could not remove non-existent filter '" + selection.toString()
+ + "' from the table data: " + filterList.toString());
+ return;
+ }
+ // refresh table data
+ tblFilters.setData(filterList, false);
+ } catch (Exception ex) {
+ LOGGER.debug("Failed to remove " + selection.toString() + " from the table data.", ex);
+ return;
+ }
+ }
+ });
+ btnDel.setEnabled(false);
+ }
+
+ private void clearInputFields() {
+ btnDel.setEnabled(false);
+ txtAttribute.setText(null);
+ txtValue.setText(null);
+ btnAdd.setText("Hinzufügen");
+ }
+
+ public List<LdapFilter> getState() {
+ return filterList;
+ }
+
+ public boolean setState(List<LdapFilter> data) {
+ if (data == null)
+ return false;
+ if (filterList == null) {
+ filterList = new ArrayList<>();
+ }
+ filterList = data;
+ tblFilters.setData(filterList, false);
+ return true;
+ }
+
+ protected List<ChangeListener> listenerList = new ArrayList<>();
+
+ public void addLdapFilterListTableContentConfigurationChangeEventListener(ChangeListener listener) {
+ listenerList.add(listener);
+ }
+
+ public void removeNetshareConfigurationChangeEventListener(ChangeListener listener) {
+ listenerList.remove(listener);
+ }
+
+ void fireChangeEvent() {
+ for (ChangeListener listener : new ArrayList<>(listenerList)) {
+ listener.stateChanged(null);
+ }
+ }
+
+ public void addToChangeMonitor(DialogChangeMonitor changeMonitor) {
+ changeMonitor.add(tblFilters);
+ }
+
+}
+
+/**
+ * Internal layout class for this widget
+ */
+class LdapFilterConfiguratorLayout extends JPanel {
+
+ private static final long serialVersionUID = 6479838641542743622L;
+
+ private final static String HELPTEXT = "Geben Sie hier LDAP Filter ein, die die Sichtbarkeit"
+ + " der Veranstaltung abhängig vom angemeldeten Benutzer einschränken. Eine Veranstaltung"
+ + " ist sichtbar, sobald einer der angegebenen Filter zutrifft. Zusätzliche Raumbeschränkungen"
+ + " greifen weiterhin.";
+
+ protected final LectureLdapFilterTable tblFilters = new LectureLdapFilterTable();
+ protected final JTextField txtAttribute, txtValue;
+ protected final JButton btnAdd, btnDel;
+
+ public LdapFilterConfiguratorLayout() {
+ GridManager grid = new GridManager(this, 1, true, new Insets(3, 3, 3, 3));
+ // top info panel
+ grid.add(new WordWrapLabel(HELPTEXT)).fill(true, false).expand(true, false);
+ grid.nextRow();
+ // middle filter list
+ grid.add(new QScrollPane(tblFilters)).fill(true, true).expand(true, true);
+ grid.nextRow();
+
+ btnDel = new JButton("Entfernen");
+ grid.add(btnDel).anchor(GridBagConstraints.EAST);
+ grid.nextRow();
+
+ JPanel pnlNewShare = new JPanel();
+ GridManager gridNewFilter = new GridManager(pnlNewShare, 2, true);
+ pnlNewShare.setBorder(BorderFactory.createTitledBorder("Filter definieren"));
+ gridNewFilter.add(new QLabel("Attribut"));
+ txtAttribute = new JTextField();
+ gridNewFilter.add(txtAttribute).fill(true, false).expand(true, false);
+ gridNewFilter.nextRow();
+ gridNewFilter.add(new QLabel("Wert"));
+ txtValue = new JTextField();
+ gridNewFilter.add(txtValue).fill(true, false).expand(true, false);
+ gridNewFilter.nextRow();
+ btnAdd = new JButton("Hinzufügen");
+ gridNewFilter.add(btnAdd, 2).anchor(GridBagConstraints.EAST);
+ gridNewFilter.nextRow();
+ gridNewFilter.finish(false);
+ grid.add(pnlNewShare).fill(true, false).expand(true, false);
+ grid.nextRow();
+ grid.finish(false);
+ }
+}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LecturePermissionConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LecturePermissionConfigurator.java
new file mode 100644
index 00000000..b99df3f2
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/LecturePermissionConfigurator.java
@@ -0,0 +1,236 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.EventListenerList;
+
+import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.LecturePermissions;
+import org.openslx.bwlp.thrift.iface.UserInfo;
+import org.openslx.dozmod.gui.control.table.LecturePermissionTable;
+import org.openslx.dozmod.gui.control.table.LecturePermissionTable.UserLecturePermissions;
+import org.openslx.dozmod.gui.control.table.QScrollPane;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.window.UserListWindow;
+import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback;
+
+/**
+ * Panel including LecturePermissionTable and add/remove buttons for setting
+ * customLecturePermissions.
+ */
+/**
+ * @author joe
+ *
+ */
+@SuppressWarnings("serial")
+public class LecturePermissionConfigurator extends JPanel {
+
+ /**
+ * Self reference
+ */
+ private LecturePermissionConfigurator me;
+
+ protected LecturePermissionTable permissionTable;
+
+ protected JButton btnAddUser;
+ protected JButton btnRemoveUser;
+
+ private final ArrayList<UserLecturePermissions> permissionList = new ArrayList<UserLecturePermissions>();
+
+ private LecturePermissions defaultPermissions;
+
+ private String ownerId = null;
+
+ private static final Logger LOGGER = Logger.getLogger(LecturePermissionConfigurator.class);
+
+ public LecturePermissionConfigurator() {
+ super();
+ me = this;
+
+ GridManager grid = new GridManager(this, 1);
+
+ permissionTable = new LecturePermissionTable();
+
+ // Panel for the add- and remove buttons
+ JPanel userButtonPane = new JPanel();
+ userButtonPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ userButtonPane.setLayout(new BoxLayout(userButtonPane, BoxLayout.LINE_AXIS));
+
+ btnAddUser = new JButton("Benutzer hinzufügen");
+ userButtonPane.add(btnAddUser);
+ btnRemoveUser = new JButton("Benutzer entfernen");
+ userButtonPane.add(btnRemoveUser);
+ userButtonPane.add(Box.createGlue());
+
+ // Put everything into the grid
+ QScrollPane jsp = new QScrollPane(permissionTable);
+ jsp.setBackground(UIManager.getColor("Table.background"));
+ grid.add(jsp).fill(true, true).expand(true, true);
+ grid.nextRow();
+ grid.add(userButtonPane).fill(true, false).expand(true, false);
+ grid.nextRow();
+ grid.finish(false);
+
+ // add user button listener
+ btnAddUser.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ UserListWindow.open(SwingUtilities.getWindowAncestor(me), new UserAddedCallback() {
+ @Override
+ public void userAdded(final UserInfo newUser, UserListWindow window) {
+ // check if we have this user already
+ for (UserLecturePermissions current : permissionList) {
+ if (current.userId.equals(newUser.userId)) {
+ LOGGER.debug("User already present in the list, skipping!");
+ return;
+ }
+ }
+ // add it to the list with default permissions
+ permissionList.add(new UserLecturePermissions(newUser.userId, new LecturePermissions(
+ defaultPermissions)));
+ permissionTable.setData(permissionList, false);
+ fireUserChangeEvent();
+ }
+ }, "Hinzufügen", ownerId);
+ }
+ });
+
+ // delete user button listener
+ btnRemoveUser.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final UserLecturePermissions selected = permissionTable.getSelectedItem();
+ if (selected != null && !permissionList.remove(selected)) {
+ LOGGER.debug("Could not remove: " + selected);
+ }
+ permissionTable.setData(permissionList, false);
+ fireUserChangeEvent();
+ }
+ });
+
+ // keyboard shortcut
+ permissionTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
+ KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete");
+ permissionTable.getActionMap().put("delete", new AbstractAction() {
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ btnRemoveUser.doClick();
+ }
+ });
+ permissionTable.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ // TODO: This is stupid; permissionTable should fire an event
+ // if one of the check boxes changes.
+ fireUserChangeEvent();
+ }
+ });
+ }
+
+ /**
+ * Initialise the PermissionManager
+ *
+ * @param permissionMap the old permission, to initialise the table with,
+ * null creates empty table.
+ * @param defaultPermissions the permissions for a newly added user
+ * @param ownerId The user to exclude in the list do add user. May be null.
+ */
+ public void initPanel(Map<String, LecturePermissions> permissionMap,
+ final LecturePermissions defaultPermissions, String ownerId) {
+ this.ownerId = ownerId;
+ this.defaultPermissions = defaultPermissions;
+ permissionList.clear();
+ if (permissionMap != null) {
+ for (Entry<String, LecturePermissions> e : permissionMap.entrySet()) {
+ permissionList.add(new UserLecturePermissions(e.getKey(), e.getValue()));
+ }
+ }
+ permissionTable.setData(permissionList, false);
+ }
+
+ /**
+ * Get map with the permissions set in the table of the manager.
+ *
+ * @return Map with new custom permissions, null if something went wrong
+ */
+ public Map<String, LecturePermissions> getPermissions() {
+ Map<String, LecturePermissions> newPermissionMap = new HashMap<>(permissionList.size());
+ // put permissions of the list into the map
+ for (UserLecturePermissions perm : permissionList) {
+ newPermissionMap.put(perm.userId, perm.permissions);
+ }
+ return newPermissionMap;
+ }
+
+ /**
+ * Update the default permissions used by the manager.
+ *
+ * @param admin
+ * @param edit
+ */
+ public void updateDefaultPermissions(boolean admin, boolean edit) {
+ defaultPermissions.admin = admin;
+ defaultPermissions.edit = edit;
+ }
+
+
+ /**
+ * Custom event mechanism to detect changes to the user list
+ * (Mostly needed for the reactToChange() stuff in LectureDetailsWindow)
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Shared instance, since there is no data attached (yet)
+ */
+ private static final UserChangeEvent CHANGE_EVENT = new UserChangeEvent();
+
+ public static class UserChangeEvent extends EventObject {
+
+ public UserChangeEvent() {
+ super(new Object());
+ }
+ }
+
+ public interface UserChangeEventListener extends EventListener {
+ public void stateChanged(UserChangeEvent event);
+ }
+
+ public void addUserChangeEventListener(UserChangeEventListener listener) {
+ listenerList.add(UserChangeEventListener.class, listener);
+ }
+
+ public void removeUserChangeEventListener(UserChangeEventListener listener) {
+ listenerList.remove(UserChangeEventListener.class, listener);
+ }
+
+ void fireUserChangeEvent() {
+ Object[] listeners = listenerList.getListenerList();
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == UserChangeEventListener.class) {
+ ((UserChangeEventListener) listeners[i + 1])
+ .stateChanged(CHANGE_EVENT);
+ }
+ }
+ }
+}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetrulesConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetrulesConfigurator.java
new file mode 100644
index 00000000..cb80fc3e
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetrulesConfigurator.java
@@ -0,0 +1,450 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.Color;
+import java.awt.Insets;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.EventListenerList;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.NetDirection;
+import org.openslx.bwlp.thrift.iface.NetRule;
+import org.openslx.dozmod.gui.Gui;
+import org.openslx.dozmod.gui.control.WordWrapLabel;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.helper.MessageType;
+import org.openslx.dozmod.gui.helper.TextChangeListener;
+import org.openslx.util.Util;
+
+/**
+ * Widget for netrules configuration of lectures
+ */
+public class NetrulesConfigurator extends NetrulesConfiguratorLayout {
+
+ private static final long serialVersionUID = -3497629601818983994L;
+ private final static Logger LOGGER = Logger
+ .getLogger(NetrulesConfigurator.class);
+
+ private String originalRawRuleText = null;
+ /**
+ * Character defining how the rules are parsed, e.g. for whitespace \\s
+ * Example: "8.8.8.8 80 in" would be split in -hostname "8.8.8.8" -port "80"
+ * -direction "in"
+ */
+ private static final String FIELD_DELIMITER = "\\s+";
+
+ private static final Color FOREGROUND_TEXT_COLOR;
+
+ static {
+ Color fgOrigColor = UIManager.getDefaults().getColor("ColorChooser.foreground");
+ if (fgOrigColor == null) {
+ // use black as fallback
+ fgOrigColor = Color.BLACK;
+ }
+ FOREGROUND_TEXT_COLOR = fgOrigColor;
+ }
+
+ public NetrulesConfigurator() {
+ super();
+
+ final TextChangeListener docListener = new TextChangeListener() {
+ @Override
+ public void changed() {
+ fireNetrulesConfigurationChangeEvent(new NetrulesConfigurationChangeEvent(NetrulesConfigurator.this));
+ }
+ };
+ final SimpleAttributeSet as = new SimpleAttributeSet();
+ StyleConstants.setForeground(as, FOREGROUND_TEXT_COLOR);
+ tpNetworkRules.getDocument().addDocumentListener(docListener);
+ tpNetworkRules.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ int pos = tpNetworkRules.getCaretPosition();
+ if (pos < 0) {
+ return;
+ }
+ // Odd: On windows, getText() returns the text with CRLF line breaks,
+ // regardless of what you put into the text box.
+ // The offsets passed to setCharAttrs() below and the caret position
+ // you get from getCaretPosition() however have to adhere to the
+ // text version with just LF, exactly how we created the document.
+ String text = tpNetworkRules.getText().replace("\r", "");
+ if (pos >= text.length()) {
+ return;
+ }
+ int start = text.lastIndexOf('\n', pos == 0 ? 0 : pos - 1);
+ int end = text.indexOf('\n', pos);
+ if (start == -1) {
+ start = 0;
+ }
+ if (end == -1) {
+ end = text.length() - 1;
+ }
+ if (end <= start) {
+ return;
+ }
+ tpNetworkRules.getStyledDocument().setCharacterAttributes(start, end - start, as, true);
+ }
+ });
+ }
+ });
+ }
+
+ public boolean hasChanged() {
+ return !originalRawRuleText.equalsIgnoreCase(tpNetworkRules.getText());
+ }
+
+ /**
+ * Gets the state of the widget as a list of netrules. Internally it first
+ * transforms the input text in a list of netrules.
+ *
+ * @return the list of rules as parsed by parseNetRules()
+ */
+ public List<NetRule> getState(boolean silent) {
+ // cleanup the TextPane for network rules if needed
+ return parseNetRules(silent);
+ }
+
+ /**
+ * Sets the state of this widget to the given list of netrules. This will
+ * internally transform the list to its string representation
+ *
+ * @param netrules
+ * as a list of NetRule to set the state to
+ */
+ public void setState(final List<NetRule> netrules) {
+ // setText() blanks the text area if null is given, so no null checks
+ originalRawRuleText = decodeNetRulesToText(netrules);
+ this.tpNetworkRules.setText(originalRawRuleText);
+ }
+
+ /**
+ * "Decodes" the given list of NetRule to a single String. This should be
+ * used to set the text in the TextPane for the network rules
+ *
+ * @param netRulesList
+ * list of NetRule to decode
+ * @return String representation of the list of rules
+ */
+ public static String decodeNetRulesToText(final List<NetRule> netRulesList) {
+ if (netRulesList == null || netRulesList.isEmpty())
+ return "";
+
+ String decodedRules = "";
+ Iterator<NetRule> it = netRulesList.iterator();
+ while (it.hasNext()) {
+ String currentLine = "";
+ NetRule currentRule = it.next();
+ // simple test for validity (since this comes from the server it
+ // should be correct anyways)
+ if (currentRule.host.isEmpty() || currentRule.port > 65535) {
+ LOGGER.error("Invalid rule! Ignoring: " + currentRule.host
+ + ":" + currentRule.port);
+ continue;
+ }
+ currentLine += currentRule.host + " \t ";
+ currentLine += currentRule.port + " \t ";
+ currentLine += currentRule.direction.name();
+ decodedRules += currentLine
+ + (it.hasNext() ? "\n" : "");
+ }
+ return decodedRules;
+ }
+
+ /**
+ * Parses the given rawNetRules String to a list of NetRule
+ *
+ * @param rawNetRules
+ * the raw text to be parsed
+ * @return list of netrules if successful. If any errors occured while
+ * parsing, null is returned.
+ */
+ public List<NetRule> parseNetRules(boolean silent) {
+ String rawNetRules = tpNetworkRules.getText().trim();
+ List<NetRule> rulesList = new ArrayList<NetRule>();
+ if (rawNetRules.isEmpty()) {
+ return rulesList;
+ }
+
+ // split it line by line
+ boolean invalid = false; // True if the rules are invalid and null should be returned
+ DefaultStyledDocument newdoc = null;
+ if (!silent) {
+ newdoc = new DefaultStyledDocument(); // Used to build new document with colors
+ }
+ StringBuilder errors = new StringBuilder(); // Error messages to show (if not silent)
+ int lineNo = 0; // Show line numbers in error messages
+ Set<String> warnedHosts = new HashSet<>(); // Ask only once about each unknown host
+ for (String ruleLine : rawNetRules.split("[\r\n]+")) {
+ if (silent && invalid)
+ return null;
+ Color lineColor = null;
+ LOGGER.debug("Parsing rule: " + ruleLine);
+ // split the fields and check if we have 3 as expected.
+ String[] fields = ruleLine.trim().split(FIELD_DELIMITER);
+ if (fields.length != 3) {
+ lineNo += addLine(newdoc, ruleLine, Color.RED, true);
+ // log numbers for fields independently...
+ LOGGER.debug("Invalid number of fields! Expected 3, got: " + fields.length);
+ if (fields.length > 3) {
+ errors.append("Zeile " + lineNo + ": Zu viele Felder.\n");
+ } else {
+ errors.append("Zeile " + lineNo + ": Zu wenig Felder.\n");
+ }
+ invalid = true;
+ continue;
+ }
+ // Have 3 fields, pretty up
+ String ruleDirection = fields[2].toUpperCase();
+ ruleLine = fields[0] + " \t " + fields[1] + " \t " + ruleDirection;
+
+ // start to check fields one by one from the last to the first ....
+ // check net direction: accept either 'in' or 'out' (case
+ // insensitive)
+ // TODO support combined 'in/out' rules
+ if (!ruleDirection.equals("IN") && !ruleDirection.equals("OUT")) {
+ lineNo += addLine(newdoc, ruleLine, Color.RED, true);
+ LOGGER.debug("Invalid net direction! Expected 'in' or out'. Got: " + ruleDirection);
+ errors.append("Zeile " + lineNo + ": Ungültige Richtung. Bitte nutzen Sie 'IN' bzw. 'OUT'.\n");
+ invalid = true;
+ continue;
+ }
+ // check port: accept if >= 0 and <= 65535
+ int port = Util.parseInt(fields[1], -1);
+
+ // port = 0 means match only host (all protocols and ports)
+ if (port < 0 || port > 65535) {
+ lineNo += addLine(newdoc, ruleLine, Color.RED, true);
+ LOGGER.debug("Invalid port! Got: " + port);
+ errors.append("Zeile " + lineNo + ": Ungültiger Port. Gültiger Bereich ist 0-65535.\n");
+ invalid = true;
+ continue;
+ }
+ // check hostname: bit more to do here
+ // for IPs and/or resolvable hostnames we make use of java.net's
+ // InetAddress.getByName() method which checks the validity of
+ // an IP-Address represented by a string or if the given hostname
+ // is resolvable. If any of these happen, we have a valid hostname.
+ // Non-resolvable hostnames are handled differently, see after the
+ // try/catch-block
+ InetAddress ruleHost = null;
+ try {
+ // TODO: Find a way to reliably set a timeout (external DNS library?)
+ // Otherwise just remove this check
+ ruleHost = InetAddress.getByName(fields[0]);
+ } catch (UnknownHostException e) {
+ // might be good to see this exception in the log file
+ // LOGGER.debug("Invalid hostname (java.net): ", e);
+ }
+ if (ruleHost == null) {
+ // either invalid IP-Address or an invalid resolvable hostname
+ // however it might also be a non-resolvable hostname that would
+ // be valid in the actual pool-rooms, so lets check its syntax
+ // according to: http://tools.ietf.org/html/rfc1034#section-3.1
+ LOGGER.debug("Invalid host/IP! Got: " + fields[0]);
+ String checkRes = checkHostnameSimple(fields[0]);
+ if (checkRes == null) {
+ if (!silent
+ && !warnedHosts.contains(fields[0])
+ && !Gui.showMessageBox("Konnte '" + fields[0]
+ + "' nicht auflösen. Wollen Sie diesen Hostnamen trotzdem verwenden?",
+ MessageType.WARNING_RETRY, null, null)) {
+ invalid = true;
+ }
+ warnedHosts.add(fields[0]);
+ lineColor = Color.ORANGE;
+ } else {
+ lineNo += addLine(newdoc, ruleLine, Color.RED, true);
+ errors.append("Zeile " + lineNo + ": " + checkRes + "\n");
+ invalid = true;
+ continue;
+ }
+ }
+ // Made it to here - line is valid
+ lineNo += addLine(newdoc, ruleLine, lineColor, false);
+ rulesList.add(new NetRule(NetDirection.valueOf(ruleDirection), fields[0], port));
+ }
+ if (!silent && errors.length() != 0) {
+ Gui.showMessageBox("Fehler beim Auswerten der angegebenen Netzwerkregeln.\n\n" + errors.toString()
+ + "\nBitte geben Sie die Regeln zeilenweise im Format\n"
+ + "<host> <port> <IN|OUT>\n"
+ + "an.",
+ MessageType.ERROR, null, null);
+ }
+ if (newdoc != null) {
+ tpNetworkRules.setDocument(newdoc);
+ }
+ if (invalid) {
+ return null;
+ }
+ // Success
+ return rulesList;
+ }
+
+ private int addLine(DefaultStyledDocument doc, String line, Color color, boolean bold) {
+ if (doc == null)
+ return 0;
+ if (color == null) {
+ color = FOREGROUND_TEXT_COLOR;
+ }
+ SimpleAttributeSet attrs = new SimpleAttributeSet();
+ StyleConstants.setForeground(attrs, color);
+ StyleConstants.setBold(attrs, bold);
+ try {
+ doc.insertString(doc.getLength(), line + "\n", attrs);
+ } catch (BadLocationException e) {
+ LOGGER.warn("Cannot append to new textbox document", e);
+ }
+ return 1;
+ }
+
+ /**
+ * Very simple hostname check for the given String. This will only check for
+ * some requirements of valid hostnames as stated in
+ * http://tools.ietf.org/html/rfc1034#section-3.1 To recap: max length of
+ * the whole hostname must be < 254 ASCII characters, all domain labels
+ * (between two dots) must be between 1 and 63 chars long and domain labels
+ * can only contain digits, letters and hyphen. (Note: we also accept
+ * forward slash to accept subnets!)
+ *
+ * @param hostname the hostname to check for syntactical validity
+ * @return null if valid, error string otherwise
+ */
+ private String checkHostnameSimple(final String hostname) {
+ if (hostname.length() > 254) {
+ return "Hostname ist zu lang.";
+ }
+ // split by '.' to get domain levels
+ boolean allNumeric = true;
+ String[] domainLabels = hostname.split("\\.");
+ for (String domainLabel : domainLabels) {
+ if (domainLabel.length() > 63) {
+ // fail since domain level should be max 63 chars
+ return "Domain-Ebene '" + domainLabel + "' länger als 63 Zeichen.";
+ }
+ if (Util.parseInt(domainLabel, -1) == -1) {
+ allNumeric = false;
+ }
+ // checking for valid chars is pointless with punycode
+ }
+ if (allNumeric && domainLabels.length != 4) {
+ return "Unvollständige IP-Adresse.";
+ }
+ return null;
+ }
+
+ /**
+ * Custom event mechanism to detect changes to the netrules list (Mostly
+ * needed for the reactToChange() stuff in LectureDetailsWindow)
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ public class NetrulesConfigurationChangeEvent extends EventObject {
+
+ private static final long serialVersionUID = -8779550754760035845L;
+
+ public NetrulesConfigurationChangeEvent(Object source) {
+ super(source);
+ }
+ }
+
+ public interface NetrulesConfigurationChangeEventListener extends
+ EventListener {
+ public void stateChanged(NetrulesConfigurationChangeEvent event);
+ }
+
+ public void addNetrulesConfigurationChangeEventListener(
+ NetrulesConfigurationChangeEventListener listener) {
+ listenerList.add(NetrulesConfigurationChangeEventListener.class,
+ listener);
+ }
+
+ public void removeNetrulesConfigurationChangeEventListener(
+ NetrulesConfigurationChangeEventListener listener) {
+ listenerList.remove(NetrulesConfigurationChangeEventListener.class,
+ listener);
+ }
+
+ void fireNetrulesConfigurationChangeEvent(
+ NetrulesConfigurationChangeEvent evt) {
+ Object[] listeners = listenerList.getListenerList();
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == NetrulesConfigurationChangeEventListener.class) {
+ ((NetrulesConfigurationChangeEventListener) listeners[i + 1])
+ .stateChanged(evt);
+ }
+ }
+ }
+}
+
+/**
+ * Internal layout class for this widget
+ */
+class NetrulesConfiguratorLayout extends JPanel {
+
+ private static final long serialVersionUID = 5266120380443817325L;
+ private final static String txtNetworkOptionsTitle = "Netzwerk Einstellungen";
+ private final static String txtNetworkOptionsDesc = "Wenn Sie den Internetzugriff deaktiviert haben,"
+ + " können Sie hier Ausnahmen definieren (Whitelist)."
+ + " Bitte definieren Sie Ihre Regeln im Format\n<host> <port> <in|out>.\n"
+ + "Sie können Port 0 angeben, was sämtlichen TCP und UDP Ports eines Hosts entspricht.\n\n"
+ + "Wenn Sie Internetzugriff aktivieren, hat diese Liste den gegenteiligen Effekt";
+ private final static String txtNetworkRulesTitle = "Netzwerkregeln";
+
+ private final JPanel pnlNetworkOptions;
+ protected final JTextPane tpNetworkRules;
+
+ public NetrulesConfiguratorLayout() {
+
+ GridManager grid = new GridManager(this, 1, true,
+ new Insets(5, 5, 5, 5));
+
+ // middle panel for network rules
+ pnlNetworkOptions = new JPanel();
+ GridManager gridNetworkOptions = new GridManager(pnlNetworkOptions, 1,
+ true, new Insets(2, 2, 2, 2));
+ pnlNetworkOptions.setBorder(BorderFactory
+ .createTitledBorder(txtNetworkOptionsTitle));
+ tpNetworkRules = new JTextPane();
+
+ JScrollPane scpNetworkRules = new JScrollPane(tpNetworkRules,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ pnlNetworkOptions.setBorder(BorderFactory
+ .createTitledBorder(txtNetworkRulesTitle));
+ gridNetworkOptions
+ .add(new WordWrapLabel(txtNetworkOptionsDesc))
+ .fill(true, false).expand(true, false);
+ gridNetworkOptions.nextRow();
+ gridNetworkOptions.add(scpNetworkRules).fill(true, true)
+ .expand(true, true);
+ gridNetworkOptions.finish(false);
+
+ // build the final grid
+ grid.add(pnlNetworkOptions).fill(true, true).expand(true, true);
+ grid.finish(false);
+ }
+}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetshareConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetshareConfigurator.java
new file mode 100644
index 00000000..6141992c
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/NetshareConfigurator.java
@@ -0,0 +1,372 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableModelListener;
+
+import org.apache.log4j.Logger;
+import org.openslx.bwlp.thrift.iface.NetShare;
+import org.openslx.bwlp.thrift.iface.NetShareAuth;
+import org.openslx.dozmod.gui.Gui;
+import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor;
+import org.openslx.dozmod.gui.control.ComboBox;
+import org.openslx.dozmod.gui.control.QLabel;
+import org.openslx.dozmod.gui.control.ComboBox.ComboBoxRenderer;
+import org.openslx.dozmod.gui.control.table.NetshareTable;
+import org.openslx.dozmod.gui.control.table.QScrollPane;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.helper.MessageType;
+import org.openslx.dozmod.util.FormatHelper;
+
+/**
+ * Widget for network share configuration of lectures
+ */
+public class NetshareConfigurator extends NetshareConfiguratorLayout {
+
+ private static final long serialVersionUID = -3336605759245603655L;
+ private final static Logger LOGGER = Logger.getLogger(NetshareConfigurator.class);
+ private List<NetShare> shareList = null;
+
+ // mount points / win drive letters - ideally, we would check whether the image is linux or windows based
+ // and either show a drive selection list like this one, or a textfield for a user-defined mount point...
+ private Character[] mountPoints = { 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
+ 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
+
+ public NetshareConfigurator() {
+ super();
+ tblNetshare.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ NetShare item = tblNetshare.getSelectedItem();
+ // ugly block-wise sets, but only one test needed compared to
+ // doing lots of item != null ? ... : ...
+ if (item == null) {
+ clearInputFields();
+ return;
+ }
+ // share from the list is selected: fill bottom form and change "Add" to "Apply"
+ btnDel.setEnabled(true);
+ tfSharePath.setText(item.path);
+ tfShareName.setText(item.displayname);
+ tfUsername.setText(item.username);
+ tfPassword.setText(item.password);
+ cboNetshareAuth.setSelectedItem(item.auth);
+ cboNetshareMountPoint.setSelectedItem(Character.valueOf(item.mountpoint.charAt(0)));
+ btnAdd.setText("Ändern");
+ }
+ });
+
+ cboNetshareMountPoint.setModel(new DefaultComboBoxModel<Character>(mountPoints));
+ cboNetshareMountPoint.setSelectedItem(null);
+
+ // combobox for share authentication types
+ cboNetshareAuth.setModel(new DefaultComboBoxModel<NetShareAuth>(NetShareAuth.values()));
+ cboNetshareAuth.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ NetShareAuth selectedAuth = cboNetshareAuth.getItemAt(cboNetshareAuth.getSelectedIndex());
+ if (selectedAuth == null)
+ return;
+ boolean activate = selectedAuth == NetShareAuth.OTHER_USER;
+ // username field is needed to either special or guest user
+ tfUsername.setEnabled(activate);
+ lblUsername.setEnabled(activate);
+ tfPassword.setEnabled(activate);
+ lblPassword.setEnabled(activate);
+ chkShowPass.setEnabled(activate);
+ }
+ });
+ cboNetshareAuth.setSelectedItem(null);
+
+ btnAdd.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // check if we are editing an existing share entry or
+ // creating a new one, check for input either way
+ NetShare input = new NetShare();
+
+ input.path = tfSharePath.getText();
+ if (input.path == null || input.path.isEmpty()) {
+ lblError.setText("Kein Pfad angegeben!");
+ return;
+ }
+ String inputShareName = tfShareName.getText();
+ if (inputShareName.isEmpty()) {
+ lblError.setText("Kein Anzeigename angegeben!");
+ return;
+ }
+ input.displayname = inputShareName;
+
+ NetShareAuth inputNetShareAuth = cboNetshareAuth
+ .getItemAt(cboNetshareAuth.getSelectedIndex());
+ if (inputNetShareAuth == null) {
+ lblError.setText("Kein Authentifizierungstyp angegeben!");
+ return;
+ }
+ input.auth = inputNetShareAuth;
+ switch (inputNetShareAuth) {
+ case LOGIN_USER:
+ // this uses the bwLehrpool client's logged in user
+ // we don't need to have anything
+ break;
+ case OTHER_USER:
+ // save given username/password
+ input.username = tfUsername.getText();
+ input.password = new String(tfPassword.getPassword());
+ if (input.username.isEmpty()) {
+ lblError.setText("Kein Nutzername angegeben!");
+ return;
+ }
+ break;
+ default:
+ input = null;
+ break;
+ }
+ if (input == null) {
+ lblError.setText("Fehlerhafte Eingabe");
+ LOGGER.debug("Bad input, aborting.");
+ return;
+ }
+ // now check for optional mount path
+ Character inputMountPoint = cboNetshareMountPoint
+ .getItemAt(cboNetshareMountPoint.getSelectedIndex());
+ if (inputMountPoint == null) {
+ lblError.setText("Kein Ziel angegeben!");
+ return;
+ }
+ input.mountpoint = inputMountPoint.toString();
+ // now decide whether to create a new entry or update existing one
+ NetShare oldEntry = tblNetshare.getSelectedItem();
+ if (oldEntry != null) {
+ // editing existing one, delete it from the internal data
+ if (!shareList.remove(oldEntry)) {
+ lblError.setText("Änderung fehlgeschlagen!");
+ LOGGER.debug("Failed to remove selected share for replacement: " + oldEntry);
+ return;
+ }
+ tblNetshare.setData(shareList, false);
+ }
+ // either we delete the existing share from the data or we are
+ // creating a new one, either way add it to the list and update
+ // the table, if its not present already
+ if (shareList.contains(input)) {
+ lblError.setText("Existiert bereits!");
+ LOGGER.error("Network share already in the list, aborting.");
+ return;
+ }
+ // if a password is set, warn the user about its unsafe storage
+ // which we might want to implement one day...
+ if (input.password != null && !input.password.isEmpty()) {
+ if (!Gui.showMessageBox(
+ "Das eingebene Passwort wird im Klartext gespeichert "
+ + "und ist in der VM für jeden Nutzer sichtbar.\n"
+ + "Verwenden Sie auf keinen Fall sicherheitskritische Passwörter!"
+ + "\n\nMöchten Sie diesen Netzlaufwerk trotzdem hinzufügen?",
+ MessageType.QUESTION_YESNO, LOGGER, null)) {
+ return;
+ }
+ }
+ lblError.setText(null);
+ shareList.add(input);
+ tblNetshare.setData(shareList, false);
+ clearInputFields();
+ }
+ });
+
+ btnDel.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // try to delete the selected share
+ NetShare selection = tblNetshare.getSelectedItem();
+ if (selection == null) {
+ return;
+ }
+ try {
+ if (!shareList.remove(selection)) {
+ // false if it was not found
+ LOGGER.error("Could not remove non-existant network share '" + selection.toString()
+ + "' from the table data: " + shareList.toString());
+ return;
+ }
+ // refresh table data
+ tblNetshare.getModel().setData(shareList);
+ } catch (Exception ex) {
+ LOGGER.debug("Failed to remove " + selection.toString() + " from the table data.", ex);
+ return;
+ }
+ }
+ });
+
+ chkShowPass.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() != ItemEvent.SELECTED) {
+ tfPassword.setEchoChar('*');
+ } else {
+ tfPassword.setEchoChar((char) 0);
+ }
+ }
+ });
+ chkShowPass.setEnabled(false);
+ tfUsername.setEnabled(false);
+ lblUsername.setEnabled(false);
+ tfPassword.setEnabled(false);
+ lblPassword.setEnabled(false);
+ }
+
+ private void clearInputFields() {
+ btnDel.setEnabled(false);
+ tfSharePath.setText(null);
+ tfShareName.setText(null);
+ tfUsername.setText(null);
+ tfPassword.setText(null);
+ cboNetshareAuth.setSelectedItem(null);
+ cboNetshareMountPoint.setSelectedItem(null);
+ btnAdd.setText("Hinzufügen");
+ }
+ public List<NetShare> getState() {
+ return shareList;
+ }
+
+ public boolean setState(List<NetShare> data) {
+ if (data == null)
+ return false;
+ if (shareList == null) {
+ shareList = new ArrayList<>();
+ }
+ shareList = data;
+ tblNetshare.setData(shareList, false);
+ return true;
+ }
+
+ public void addTableModelListener(TableModelListener listener) {
+ tblNetshare.getModel().addTableModelListener(listener);
+ }
+
+ public void addToChangeMonitor(DialogChangeMonitor changeMonitor) {
+ changeMonitor.add(tblNetshare);
+ }
+}
+
+/**
+ * Internal layout class for this widget
+ */
+class NetshareConfiguratorLayout extends JPanel {
+
+ private static final long serialVersionUID = 6479525981542743622L;
+
+ private final static String txtNetshareDesc = "<html>Hier können Sie Netzlaufwerke angeben,"
+ + " die automatisch beim Start der Veranstaltung eingebunden werden sollen."
+ + " Der Platzhalter <em>%loginuser%</em> wird im Pfad durch den Loginnamen des Nutzers ersetzt.</html>";
+
+ protected final QLabel lblShareAuth, lblSharePath, lblShareName, lblMountPoint, lblUsername, lblPassword,
+ lblError;
+ protected final NetshareTable tblNetshare = new NetshareTable();
+ protected final JTextField tfSharePath, tfShareName, tfUsername;
+ protected final JPasswordField tfPassword;
+ protected final JButton btnAdd, btnDel;
+ protected final JCheckBox chkShowPass;
+ protected final ComboBox<NetShareAuth> cboNetshareAuth = new ComboBox<>(new ComboBoxRenderer<NetShareAuth>() {
+ @Override
+ public String renderItem(NetShareAuth item) {
+ if (item == null)
+ return null;
+ return FormatHelper.netShareAuthName(item);
+ }
+ });
+ protected final ComboBox<Character> cboNetshareMountPoint = new ComboBox<>(new ComboBoxRenderer<Character>() {
+ @Override
+ public String renderItem(Character letter) {
+ if (letter == null)
+ return null;
+ return letter.toString() + ":";
+ }
+ });
+
+ public NetshareConfiguratorLayout() {
+ GridManager grid = new GridManager(this, 5, true, new Insets(3, 3, 3, 3));
+ // top info panel
+ grid.add(new JLabel(txtNetshareDesc), 5).fill(true, false).expand(true, false);
+ grid.nextRow();
+ // middle netshare list
+ grid.add(new QScrollPane(tblNetshare), 5).fill(true, true).expand(true, true);
+ grid.nextRow();
+
+ JPanel pnlNewShare = new JPanel();
+ GridManager gridNewShare = new GridManager(pnlNewShare, 6, true);
+ pnlNewShare.setBorder(BorderFactory.createTitledBorder("Details"));
+ lblSharePath = new QLabel("Pfad");
+ gridNewShare.add(lblSharePath);
+ tfSharePath = new JTextField();
+ gridNewShare.add(tfSharePath, 5).fill(true, false).expand(true, false);
+ gridNewShare.nextRow();
+ // bottom form to add a new share
+ lblShareName = new QLabel("Anzeigename");
+ lblMountPoint = new QLabel("Ziel");
+ tfShareName = new JTextField();
+ // extra panel for fancy layout purposes...
+ JPanel pnlShareName = new JPanel();
+ pnlShareName.setLayout(new BoxLayout(pnlShareName, BoxLayout.LINE_AXIS));
+ pnlShareName.add(lblShareName);
+ pnlShareName.add(tfShareName);
+ pnlShareName.add(lblMountPoint);
+ pnlShareName.add(cboNetshareMountPoint);
+ gridNewShare.add(lblShareName);
+ gridNewShare.add(pnlShareName, 5).fill(true, false).expand(true, false);
+ gridNewShare.nextRow();
+ lblShareAuth = new QLabel("Authentifizierung");
+ gridNewShare.add(lblShareAuth);
+ gridNewShare.add(cboNetshareAuth, 5).fill(true, false).expand(true, false);
+ gridNewShare.nextRow();
+ lblUsername = new QLabel("Username");
+ gridNewShare.add(lblUsername);
+ tfUsername = new JTextField(20);
+ gridNewShare.add(tfUsername, 2).fill(true, false).expand(true, false);
+ lblPassword = new QLabel("Passwort");
+ gridNewShare.add(lblPassword);
+ tfPassword = new JPasswordField(20);
+ gridNewShare.add(tfPassword, 2).fill(true, false).expand(true, false);
+ gridNewShare.nextRow();
+ chkShowPass = new JCheckBox("Passwort anzeigen");
+ JPanel pnlShowPass = new JPanel();
+ pnlShowPass.setLayout(new BoxLayout(pnlShowPass, BoxLayout.LINE_AXIS));
+ pnlShowPass.add(Box.createGlue());
+ pnlShowPass.add(chkShowPass, BorderLayout.LINE_END);
+ gridNewShare.add(pnlShowPass, 6).fill(true, false).expand(true, false);
+ gridNewShare.nextRow();
+ grid.add(pnlNewShare, 5).fill(true, false).expand(true, false);
+ grid.nextRow();
+ // bottom panel for right-aligned button...
+ JPanel pnlButtonAdd = new JPanel();
+ pnlButtonAdd.setLayout(new BoxLayout(pnlButtonAdd, BoxLayout.LINE_AXIS));
+ btnAdd = new JButton("Hinzufügen");
+ btnDel = new JButton("Entfernen");
+ lblError = new QLabel("");
+ lblError.setForeground(Color.RED);
+ pnlButtonAdd.add(lblError);
+ pnlButtonAdd.add(Box.createGlue());
+ pnlButtonAdd.add(btnAdd, BorderLayout.LINE_END);
+ pnlButtonAdd.add(btnDel, BorderLayout.LINE_END);
+ grid.add(pnlButtonAdd, 5).fill(true, false).expand(true, false);
+ grid.finish(false);
+ }
+}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/RunscriptConfigurator.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/RunscriptConfigurator.java
new file mode 100644
index 00000000..52a686bb
--- /dev/null
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/configurator/RunscriptConfigurator.java
@@ -0,0 +1,266 @@
+package org.openslx.dozmod.gui.configurator;
+
+import java.awt.Color;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.EventListener;
+import java.util.EventObject;
+
+import javax.swing.Box;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.event.EventListenerList;
+
+import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor;
+import org.openslx.dozmod.gui.configurator.RunscriptConfigurator.RunscriptType;
+import org.openslx.dozmod.gui.control.ComboBox;
+import org.openslx.dozmod.gui.control.QLabel;
+import org.openslx.dozmod.gui.control.WordWrapLabel;
+import org.openslx.dozmod.gui.control.ComboBox.ComboBoxRenderer;
+import org.openslx.dozmod.gui.helper.GridManager;
+import org.openslx.dozmod.gui.helper.TextChangeListener;
+
+/**
+ * Widget for advanced configuration options for lectures. This handles
+ * following options - Network rules - Runscript - USB
+ */
+public class RunscriptConfigurator extends RunscriptConfiguratorLayout {
+
+ private static final long serialVersionUID = -3497629601818983994L;
+
+ public static enum RunscriptType {
+ SHELL("Shellskript", "sh"), BATCH("Windows-Batch", "bat");
+
+ private final String displayName;
+ private final String extension;
+
+ private RunscriptType(String name, String extension) {
+ this.displayName = name;
+ this.extension = extension;
+ }
+
+ @Override
+ public String toString() {
+ return extension + " (" + displayName + ")";
+ }
+ }
+
+ public RunscriptConfigurator() {
+ super();
+
+ final TextChangeListener docListener = new TextChangeListener() {
+ @Override
+ public void changed() {
+ fireRunscriptConfigurationChangeEvent(new RunscriptConfigurationChangeEvent(
+ new Object()));
+ }
+ };
+ taRunScript.getDocument().addDocumentListener(docListener);
+ cboRunscriptType.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ fireRunscriptConfigurationChangeEvent(new RunscriptConfigurationChangeEvent(
+ new Object()));
+ }
+ });
+ cboRunscriptType.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ fireRunscriptConfigurationChangeEvent(new RunscriptConfigurationChangeEvent(
+ new Object()));
+ }
+ });
+ }
+
+ private void setError(final String msg) {
+ lblError.setText(msg);
+ }
+ /**
+ * Gets the runscript as String. The chosen interpreter will get encoded as
+ * the first line of the script.
+ *
+ * @return runscript as String. If no text was entered, returns a empty
+ * string.
+ */
+ public String getState() {
+ setError(""); // fill remove any prior errors, we'll reset them if needed
+ // handle user input, this is tricky since
+ // * either an item has been selected -> editorContent will be of our enum type
+ // * user typed its own interpreter into the box -> editorContent will be a castable String
+ String extension = null;
+ Object cboContent = cboRunscriptType.getEditor().getItem();
+ if (cboContent instanceof RunscriptType) {
+ extension = ((RunscriptType) cboContent).extension;
+ } else if (cboContent instanceof String) {
+ extension = (String) cboContent;
+ }
+ String taInputText = taRunScript.getText();
+ if (taInputText.isEmpty())
+ return "";
+ if (extension == null || extension.isEmpty()) {
+ // this should never happen, so return null to report this invalid state
+ setError("Fehlende Dateinamenerweiterung!");
+ return null;
+ }
+ setError("");
+ return "ext=" + extension + "\n" + taInputText;
+ }
+
+ /**
+ * Sets the state of this widget to the given AdvancedConfiguration. Basicly
+ * this sets the content of the text areas to the corresponding network
+ * rules/runscript as given by the AdvancedConfiguration object
+ *
+ * @param config
+ * AdvancedConfiguration to set the state to
+ */
+ public void setState(final String config) {
+ if (config == null || config.isEmpty()) {
+ cboRunscriptType.setSelectedItem(null);
+ taRunScript.setText("");
+ return;
+ }
+ String extensionHeader = null;
+ try {
+ BufferedReader reader = new BufferedReader(new StringReader(config));
+ extensionHeader = reader.readLine();
+ reader.close();
+ } catch (IOException e) {
+ // swallow ...
+ }
+ if (extensionHeader != null) {
+ // we should have following format: ext=<interpreter>
+ // e.g. ext=sh
+ extensionHeader = extensionHeader.replace("ext=", "");
+ for (RunscriptType type : RunscriptType.values()) {
+ if (type.extension.equals(extensionHeader)) {
+ cboRunscriptType.setSelectedItem(type);
+ // mark that we found it by nulling the shebang...
+ extensionHeader = null;
+ continue;
+ }
+ }
+ if (extensionHeader != null) {
+ // user specific shebang, so just write the text to the cbo
+ cboRunscriptType.getEditor().setItem(extensionHeader);
+ }
+ }
+ // finished with the interpreter, remove that line from the given config
+ // before setting that text
+ taRunScript.setText(config.replaceFirst("^ext=.*\n", ""));
+ }
+
+ /**
+ * Custom event mechanism to detect changes to the user list (Mostly needed
+ * for the reactToChange() stuff in LectureDetailsWindow)
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ public class RunscriptConfigurationChangeEvent extends EventObject {
+
+ private static final long serialVersionUID = -8779550754760035845L;
+
+ public RunscriptConfigurationChangeEvent(Object source) {
+ super(source);
+ }
+ }
+
+ public interface RunscriptConfigurationChangeEventListener extends
+ EventListener {
+ public void stateChanged(RunscriptConfigurationChangeEvent event);
+ }
+
+ public void addRunscriptConfigurationChangeEventListener(
+ RunscriptConfigurationChangeEventListener listener) {
+ listenerList.add(RunscriptConfigurationChangeEventListener.class,
+ listener);
+ }
+
+ public void removeRunscriptConfigurationChangeEventListener(
+ RunscriptConfigurationChangeEventListener listener) {
+ listenerList.remove(RunscriptConfigurationChangeEventListener.class,
+ listener);
+ }
+
+ void fireRunscriptConfigurationChangeEvent(
+ RunscriptConfigurationChangeEvent evt) {
+ Object[] listeners = listenerList.getListenerList();
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] == RunscriptConfigurationChangeEventListener.class) {
+ ((RunscriptConfigurationChangeEventListener) listeners[i + 1])
+ .stateChanged(evt);
+ }
+ }
+ }
+
+ public void addToChangeMonitor(DialogChangeMonitor changeMonitor) {
+ changeMonitor.add(taRunScript);
+ changeMonitor.addEditableCombo(cboRunscriptType, null);
+ }
+
+}
+
+/**
+ * Internal layout class for the advanced configurator (to keep it clean even
+ * for widgets)
+ */
+class RunscriptConfiguratorLayout extends JPanel {
+
+ private static final long serialVersionUID = 648729071828404053L;
+
+ private final static String txtRunScriptDesc = "Ein hier eingetragenes Skript wird nach dem Start dieser VM automatisch ausgeführt.";
+ protected final QLabel lblError;
+ protected final JTextArea taRunScript;
+ protected final ComboBox<RunscriptType> cboRunscriptType;
+
+ public RunscriptConfiguratorLayout() {
+
+ GridManager grid = new GridManager(this, 2, true, new Insets(5, 5, 5, 5));
+ taRunScript = new JTextArea("", 5, 20);
+ JScrollPane scpRunScript = new JScrollPane(taRunScript,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ taRunScript.setLineWrap(true);
+ taRunScript.setWrapStyleWord(true);
+ grid.add(new WordWrapLabel(txtRunScriptDesc, false, true), 2)
+ .fill(true, false).expand(true, false);
+ grid.nextRow();
+ cboRunscriptType = new ComboBox<RunscriptType>(
+ new ComboBoxRenderer<RunscriptType>() {
+ @Override
+ public String renderItem(RunscriptType item) {
+ if (item == null)
+ return null;
+ return item.toString();
+ }
+ });
+ cboRunscriptType.setModel(new DefaultComboBoxModel<RunscriptType>(
+ RunscriptType.values()));
+ ;
+ cboRunscriptType.setEditable(true);
+ grid.add(new QLabel("Dateinamenserweiterung: ")).fill(false, false)
+ .expand(false, false);
+ grid.add(cboRunscriptType).fill(true, false)
+ .expand(true, false);
+ grid.nextRow();
+ grid.add(scpRunScript, 2).fill(true, true).expand(true, true);
+ grid.nextRow();
+ lblError = new QLabel("");
+ lblError.setForeground(Color.RED);
+ JPanel pnlError = new JPanel();
+ pnlError.add(Box.createGlue());
+ pnlError.add(lblError);
+ pnlError.add(Box.createGlue());
+ grid.add(pnlError, 2).fill(true, false).expand(true, false);
+ grid.finish(false);
+
+ }
+}