summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Schwär2020-12-11 00:49:46 +0100
committerStephan Schwär2020-12-11 00:49:46 +0100
commitf0a78f80e0096732a30904a3e3d383b60b70e25c (patch)
tree2d0ca9f54423373ef7525be9398e46ff8a28d6e0
parent[client] Prevent scaling of fonts multiple times (diff)
parent[client] improved htmleditor in imageMetadataPage (diff)
downloadtutor-module-f0a78f80e0096732a30904a3e3d383b60b70e25c.tar.gz
tutor-module-f0a78f80e0096732a30904a3e3d383b60b70e25c.tar.xz
tutor-module-f0a78f80e0096732a30904a3e3d383b60b70e25c.zip
Merge remote-tracking branch 'origin/feature/htmlEditorInDescrioptionText' into feature-merge
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java154
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java217
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java91
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java76
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageMetaDataPageLayout.java78
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureCreationPageLayout.java72
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageMetaDataPage.java108
-rw-r--r--dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureCreationPage.java102
-rw-r--r--dozentenmodul/src/main/resources/img/bold.pngbin0 -> 11833 bytes
-rw-r--r--dozentenmodul/src/main/resources/img/italic.pngbin0 -> 4482 bytes
-rw-r--r--dozentenmodul/src/main/resources/img/underline.pngbin0 -> 5621 bytes
-rw-r--r--dozentenmodulserver/pom.xml4
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java18
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java3
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java123
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java12
16 files changed, 930 insertions, 128 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java
index 35399d32..b718c1e3 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java
@@ -2,11 +2,26 @@ package org.openslx.dozmod.gui.window;
import java.awt.*;
import java.awt.event.*;
+import java.awt.Color;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.Encoder;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.List;
import javax.swing.AbstractAction;
+import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
@@ -14,6 +29,12 @@ import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;
@@ -209,6 +230,99 @@ import org.openslx.util.Util;
}
});
+ cbTxtSize.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int size = Integer.parseInt((String) cbTxtSize.getSelectedItem());
+ Action act = new StyledEditorKit.FontSizeAction(String.valueOf(size),size);
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ cbTxtColor.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String color = (String) cbTxtColor.getSelectedItem();
+ Action act = null;
+
+ switch(color) {
+ case "Black":
+ act = new StyledEditorKit.ForegroundAction("Black", Color.black);
+ break;
+ case "Blue":
+ act = new StyledEditorKit.ForegroundAction("Blue", Color.blue);
+ break;
+ case "Yellow":
+ act = new StyledEditorKit.ForegroundAction("Yellow", Color.yellow);
+ break;
+ case "Red":
+ act = new StyledEditorKit.ForegroundAction("Red", Color.red);
+ break;
+ case "Green":
+ act = new StyledEditorKit.ForegroundAction("Green", Color.green);
+ break;
+ }
+
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ btnWysiwyg.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String tmp = txtDescription.getText();
+ if (txtDescription.getContentType().equals("text/html")) {
+ txtDescription.setContentType("text/plain");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Wysiwyg");
+ if(ImagePerms.canEdit(image) || ImagePerms.canAdmin(image)) {
+ btnSaveChanges.setEnabled(true);
+ }
+
+ btnBold.setEnabled(false);
+ btnUnderline.setEnabled(false);
+ btnItalic.setEnabled(false);
+ cbTxtColor.setEnabled(false);
+ cbTxtSize.setEnabled(false);
+ } else {
+ txtDescription.setContentType("text/html");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Html");
+
+ btnBold.setEnabled(true);
+ btnUnderline.setEnabled(true);
+ btnItalic.setEnabled(true);
+ cbTxtColor.setEnabled(true);
+ cbTxtSize.setEnabled(true);
+ }
+ }
+ });
+
+ txtDescription.addKeyListener(new KeyListener() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER && txtDescription.getContentType().equals("text/html")) {
+ try {
+ kit.insertHTML((HTMLDocument) txtDescription.getDocument(), txtDescription.getCaretPosition(), "<br>", 0, 0,
+ HTML.Tag.BR);
+ txtDescription.setCaretPosition(txtDescription.getCaretPosition()); // This moves caret to next line
+ } catch (BadLocationException | IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ });
+
tblVersions.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
/*
@@ -469,8 +583,8 @@ import org.openslx.util.Util;
MetaDataCache.getOperatingSystems();
MetaDataCache.getVirtualizers();
final ImageMetaCallback callback = new ImageMetaCallback() {
- @Override public void fetchedImageDetails(ImageDetailsRead imageDetails,
- Map<String, ImagePermissions> permissions) {
+ @Override
+ public void fetchedImageDetails(ImageDetailsRead imageDetails, Map<String, ImagePermissions> permissions) {
if (imageDetails == null) {
return;
}
@@ -559,8 +673,8 @@ import org.openslx.util.Util;
}
/**
- * Called by the "Save" button, tries to save the changes internally and
- * then react based depending on the outcome of the save
+ * Called by the "Save" button, tries to save the changes internally and then
+ * react based depending on the outcome of the save
*/
private void saveChanges() {
boolean saved = saveChangesInternal();
@@ -576,15 +690,16 @@ import org.openslx.util.Util;
}
/**
- * Helper to only save the changes, nothing else. Updating GUI elements is
- * done by saveChanges()
+ * Helper to only save the changes, nothing else. Updating GUI elements is done
+ * by saveChanges()
*
* @return false if any try to save changes failed, true otherwise
*/
private boolean saveChangesInternal() {
// Special case: User has admin rights through default permissions
- // -> user removes default admin permissions
- // -> first save custom permissions, then the rest (including default permissions)
+ // -> user removes default admin permissions
+ // -> first save custom permissions, then the rest (including default
+ // permissions)
if (adminRightsFromDefaultPermissions && changeListenerPermissions.isCurrentlyChanged()) {
if (!saveCustomPermissions()) {
return false;
@@ -668,10 +783,11 @@ import org.openslx.util.Util;
MessageType.ERROR, null, null);
return;
}
- // using actionHandler here is not needed, as this ThriftAction works for downloads
+ // using actionHandler here is not needed, as this ThriftAction works for
+ // downloads
// from either the master server or the satellite server
- ThriftActions.initDownload(JOptionPane.getFrameForComponent(this), selected.versionId,
- image.imageName, image.virtId, image.osId, selected.fileSize, null);
+ ThriftActions.initDownload(JOptionPane.getFrameForComponent(this), selected.versionId, image.imageName,
+ image.virtId, image.osId, selected.fileSize, null);
}
/**
@@ -703,15 +819,16 @@ import org.openslx.util.Util;
}
/**
- * Extends the expiration date for given image versions to current date
- * plus the user-supplied duration.
+ * Extends the expiration date for given image versions to current date plus the
+ * user-supplied duration.
*
* @param versions to extend the validity of
*/
private void extendVersionExpiry(List<ImageVersionDetails> versions) {
int daysToExtend = -1;
if (versions.size() > 1) {
- // more than one version given, ask the user once and use the value for all versions.
+ // more than one version given, ask the user once and use the value for all
+ // versions.
daysToExtend = ExpiryDateChooser.askFutureExpiryDuration(this, null);
if (daysToExtend == -1)
return;
@@ -852,7 +969,8 @@ import org.openslx.util.Util;
}
txtTags.setText(tagsString);
- // init permissions, remember if the user had admin rights through default permissions
+ // init permissions, remember if the user had admin rights through default
+ // permissions
adminRightsFromDefaultPermissions = image.defaultPermissions.admin;
ctlImagePermissionConfigurator.initPanel(customPermissions, image.defaultPermissions, image.ownerId);
chkDefaultPermAdmin.setSelected(image.defaultPermissions.admin);
@@ -895,6 +1013,12 @@ import org.openslx.util.Util;
editable = editable && (ImagePerms.canEdit(image) || ImagePerms.canAdmin(image));
txtTitle.setEditable(editable);
txtDescription.setEditable(editable);
+ btnBold.setEnabled(editable);
+ btnItalic.setEnabled(editable);
+ btnUnderline.setEnabled(editable);
+ btnWysiwyg.setEnabled(editable);
+ cbTxtColor.setEnabled(editable);
+ cbTxtSize.setEnabled(editable);
txtTags.setEditable(editable);
cboOperatingSystem.setEnabled(editable);
// cboShareMode.setEnabled(editable);
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java
index 62cf10cc..2f53e8c5 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/LectureDetailsWindow.java
@@ -1,5 +1,6 @@
package org.openslx.dozmod.gui.window;
+import java.awt.Color;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -7,6 +8,9 @@ import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -16,11 +20,18 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import org.apache.commons.codec.language.ColognePhonetic;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
@@ -99,7 +110,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
* Image, that the lecture is linked to.
*/
private ImageDetailsRead image = null;
-
+
/**
* Per-User permissions of this lecture
*/
@@ -114,11 +125,11 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
private final AbstractControlWrapper<?> changeListenerPermissions;
- /**
+ /**
* Constructor
*
* @param modalParent parent of this popup window
- * @param callback function to be called when a lecture update occured
+ * @param callback function to be called when a lecture update occured
*/
public LectureDetailsWindow(Frame modalParent, LectureUpdatedCallback callback) {
super(modalParent);
@@ -127,7 +138,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
this.callback = callback;
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
-
+
// Set up change monitor
changeMonitor = new DialogChangeMonitor(new DialogChangeMonitor.Callback() {
@Override
@@ -136,6 +147,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
btnSaveChanges.setEnabled(changeMonitor.isValid() && changeMonitor.wasEverModified());
LOGGER.info("Valid: " + changeMonitor.isValid());
}
+
@Override
public void modificationChanged() {
btnSaveChanges.setEnabled(changeMonitor.isValid() && changeMonitor.wasEverModified());
@@ -145,8 +157,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
// Add controls to change monitor
changeMonitor.addFixedCombo(cboVersions, Comparators.imageVersionDetails)
- .addConstraint(
- new DialogChangeMonitor.ValidationConstraint<ImageVersionDetails>() {
+ .addConstraint(new DialogChangeMonitor.ValidationConstraint<ImageVersionDetails>() {
public String checkStateValid(ImageVersionDetails userInput) {
if (userInput != null && userInput.isValid)
return null;
@@ -192,7 +203,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
ctlRunscriptConfigurator.addToChangeMonitor(changeMonitor);
ctlNetshareConfigurator.addToChangeMonitor(changeMonitor);
ctlLdapFilterConfigurator.addToChangeMonitor(changeMonitor);
- // TODO: LDAP/NetShare: Having uncommitted changes in the input fields should be handled too
+ // TODO: LDAP/NetShare: Having uncommitted changes in the input fields should be
+ // handled too
// End change monitor
addWindowListener(new WindowAdapter() {
@@ -264,8 +276,105 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}, I18n.WINDOW.getString("LectureDetails.Button.changeOwner.caption"), lecture.ownerId);
}
});
-
- // Update default permissions in the permission manager immediately, so it affects
+
+ cbTxtSize.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int size = Integer.parseInt((String) cbTxtSize.getSelectedItem());
+ Action act = new StyledEditorKit.FontSizeAction(String.valueOf(size),size);
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ cbTxtColor.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String color = (String) cbTxtColor.getSelectedItem();
+ Action act = null;
+
+ switch(color) {
+ case "Black":
+ act = new StyledEditorKit.ForegroundAction("Black", Color.black);
+ break;
+ case "Blue":
+ act = new StyledEditorKit.ForegroundAction("Blue", Color.blue);
+ break;
+ case "Yellow":
+ act = new StyledEditorKit.ForegroundAction("Yellow", Color.yellow);
+ break;
+ case "Red":
+ act = new StyledEditorKit.ForegroundAction("Red", Color.red);
+ break;
+ case "Green":
+ act = new StyledEditorKit.ForegroundAction("Green", Color.green);
+ break;
+ }
+
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+
+ btnWysiwyg.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String tmp = txtDescription.getText();
+ if (txtDescription.getContentType().equals("text/html")) {
+ txtDescription.setContentType("text/plain");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Wysiwyg");
+ if (ImagePerms.canEdit(image) || ImagePerms.canAdmin(image)) {
+ btnSaveChanges.setEnabled(true);
+ }
+
+ btnBold.setEnabled(false);
+ btnUnderline.setEnabled(false);
+ btnItalic.setEnabled(false);
+ cbTxtColor.setEnabled(false);
+ cbTxtSize.setEnabled(false);
+ } else {
+ txtDescription.setContentType("text/html");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Html");
+
+ btnBold.setEnabled(true);
+ btnUnderline.setEnabled(true);
+ btnItalic.setEnabled(true);
+ cbTxtColor.setEnabled(true);
+ cbTxtSize.setEnabled(true);
+ }
+ }
+ });
+
+ txtDescription.addKeyListener(new KeyListener() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER && txtDescription.getContentType().equals("text/html")) {
+ try {
+ kit.insertHTML((HTMLDocument) txtDescription.getDocument(), txtDescription.getCaretPosition(),
+ "<br>", 0, 0, HTML.Tag.BR);
+ txtDescription.setCaretPosition(txtDescription.getCaretPosition()); // This moves caret to next
+ // line
+ } catch (BadLocationException | IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ });
+
+
+ // Update default permissions in the permission manager immediately, so it
+ // affects
// newly added users
final ItemListener updateDefaultPermissionListener = new ItemListener() {
@Override
@@ -294,10 +403,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Sets the lecture to show the details of by setting the 'lecture' and
- * 'image'
- * members to its metadata. This method will fetch the information from the
- * sat
+ * Sets the lecture to show the details of by setting the 'lecture' and 'image'
+ * members to its metadata. This method will fetch the information from the sat
*
* @param lectureId the id of the lecture to be displayed
*/
@@ -311,8 +418,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
lecture = lectureDetails;
image = imageDetails;
if (lecture != null) {
- customPermissions = ThriftActions.getLecturePermissions(
- JOptionPane.getFrameForComponent(me), lecture.lectureId);
+ customPermissions = ThriftActions
+ .getLecturePermissions(JOptionPane.getFrameForComponent(me), lecture.lectureId);
}
fillDetails();
}
@@ -321,7 +428,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Internal callback function when we received the lecture's details from the server
+ * Internal callback function when we received the lecture's details from the
+ * server
*/
private void fillDetails() {
if (lecture == null) {
@@ -383,10 +491,9 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
Calendar endCal = Calendar.getInstance();
endCal.setTime(new Date(lecture.getEndTime() * 1000l));
- dtpEndDate.getModel().setDate(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH),
- endCal.get(Calendar.DATE));
+ dtpEndDate.getModel().setDate(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH), endCal.get(Calendar.DATE));
spnEndTime.getModel().setValue(endCal.getTime());
-
+
// now enable the tabs the user can see given its permissions
toggleEditable(true);
// and always switch to the "About" tab
@@ -396,8 +503,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Helper to fill the combobox with the versions of the image. The list will
- * be sorted by creation timestamp
+ * Helper to fill the combobox with the versions of the image. The list will be
+ * sorted by creation timestamp
*/
private void fillVersionsCombo() {
List<ImageVersionDetails> versions;
@@ -424,8 +531,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
cboVersions.setModel(new DefaultComboBoxModel<ImageVersionDetails>(
versions.toArray(new ImageVersionDetails[versions.size()])));
cboVersions.setSelectedIndex(-1); // To make the change monitor happy
- cboVersions.setSelectedItem(new ImageVersionDetails(lecture.getImageVersionId(), 0, 0, 0, null, true,
- true, true, null));
+ cboVersions.setSelectedItem(
+ new ImageVersionDetails(lecture.getImageVersionId(), 0, 0, 0, null, true, true, true, null));
}
/**
@@ -434,8 +541,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
* @param user UserInfo representation of the new owner
*/
private void setLectureOwner(final UserInfo user) {
- if (!ThriftActions.setLectureOwner(JOptionPane.getFrameForComponent(this), lecture.getLectureId(),
- user)) {
+ if (!ThriftActions.setLectureOwner(JOptionPane.getFrameForComponent(this), lecture.getLectureId(), user)) {
return;
}
// success
@@ -478,8 +584,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
MessageType.ERROR, null, null);
return;
}
- ThriftActions.initDownload(JOptionPane.getFrameForComponent(this), lecture.imageVersionId,
- image.imageName, image.virtId, image.osId, versionSize, new DownloadCallback() {
+ ThriftActions.initDownload(JOptionPane.getFrameForComponent(this), lecture.imageVersionId, image.imageName,
+ image.virtId, image.osId, versionSize, new DownloadCallback() {
@Override
public void downloadInitialized(boolean success) {
if (!success) {
@@ -495,8 +601,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Triggers the saving of the changes of this lecture
- * And inform the callback function about the outcome
+ * Triggers the saving of the changes of this lecture And inform the callback
+ * function about the outcome
*/
private void saveChanges() {
boolean saved = saveChangesInternal();
@@ -518,7 +624,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
long endTime = DateTimeHelper.getDateFrom(dtpEndDate, spnEndTime).getTime() / 1000L;
if (!isPeriodValid(startTime, endTime, true))
return false;
-
+
// check, whether autoupdate is selected and choose version accordingly
if (image != null) {
lecture.imageVersionId = chkAutoUpdate.isSelected() ? image.latestVersionId
@@ -536,11 +642,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
// first build the LectureWrite from the GUI fields
StartupSettings startupSettings = ctlRunscriptConfigurator.getState();
final LectureWrite metadata = new LectureWrite(txtTitle.getText(), txtDescription.getText(),
- lecture.getImageVersionId(), chkAutoUpdate.isSelected(), chkIsActive.isSelected(),
- startTime, endTime,
- startupSettings.serializeItems(), null,
- chkIsExam.isSelected(),
- chkHasInternetAccess.isSelected(),
+ lecture.getImageVersionId(), chkAutoUpdate.isSelected(), chkIsActive.isSelected(), startTime, endTime,
+ startupSettings.serializeItems(), null, chkIsExam.isSelected(), chkHasInternetAccess.isSelected(),
lecture.getDefaultPermissions(), ctlLocationSelector.getSelectedLocationsAsIds(),
ctlLocationSelector.getOnlyInSelection(),
// TODO limitOnlyToAllowedUsers, default to false for now
@@ -552,10 +655,9 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
metadata.setLdapFilters(ctlLdapFilterConfigurator.getState());
metadata.setPresetScriptIds(startupSettings.selectedScripts);
- // now trigger the actual action
+ // now trigger the actual action
try {
- ThriftManager.getSatClient().updateLecture(Session.getSatelliteToken(),
- lecture.getLectureId(), metadata);
+ ThriftManager.getSatClient().updateLecture(Session.getSatelliteToken(), lecture.getLectureId(), metadata);
LOGGER.info("Successfully saved new metadata");
} catch (TException e) {
ThriftError.showMessage(JOptionPane.getFrameForComponent(this), LOGGER, e,
@@ -579,8 +681,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
*/
private boolean saveCustomPermissions() {
try {
- ThriftManager.getSatClient().writeLecturePermissions(Session.getSatelliteToken(),
- lecture.lectureId, ctlPermissionManager.getPermissions());
+ ThriftManager.getSatClient().writeLecturePermissions(Session.getSatelliteToken(), lecture.lectureId,
+ ctlPermissionManager.getPermissions());
LOGGER.info("Successfully saved custom permissions");
return true;
} catch (TException e) {
@@ -591,13 +693,12 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Checks if the given start and end date represent a valid time period.
- * This is the case, if start < end and if current time < end
+ * Checks if the given start and end date represent a valid time period. This is
+ * the case, if start < end and if current time < end
*
- * @param start date of the period to check
- * @param end date of the period to check
- * @param feedback true if the user should be shown feedback, false
- * otherwise
+ * @param start date of the period to check
+ * @param end date of the period to check
+ * @param feedback true if the user should be shown feedback, false otherwise
* @return true if the period is valid, false otherwise
*/
private boolean isPeriodValid(long start, long end, boolean feedback) {
@@ -617,8 +718,9 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
/**
* Enables/Disables the tabs based on the given flag 'editable'.
*
- * @param editable when true, will enable the tabs if the user is allowed to see them.
- * If false, this will disable all tabs but the first tab "About".
+ * @param editable when true, will enable the tabs if the user is allowed to see
+ * them. If false, this will disable all tabs but the first tab
+ * "About".
*/
protected void toggleEditable(boolean editable) {
// if we don't have a lecture and an image set, just disable
@@ -626,7 +728,8 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
// enable the standard tabs that are always enabled
setTabEnabled(pnlTabGeneral, editable);
setTabEnabled(pnlTabPermissions, editable && LecturePerms.canAdmin(lecture));
- // enable the other tabs that might have been added to the panel (depends on API version)
+ // enable the other tabs that might have been added to the panel (depends on API
+ // version)
setTabEnabled(pnlTabRestrictions, editable);
setTabEnabled(pnlTabLocations, editable);
setTabEnabled(pnlTabRunscript, editable);
@@ -636,7 +739,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
btnChangeOwner.setEnabled(LecturePerms.canAdmin(lecture));
btnDownloadImage.setEnabled(ImagePerms.canDownload(image));
}
-
+
private void setTabEnabled(JPanel tab, boolean editable) {
int index = pnlTabs.indexOfComponent(tab);
if (index == -1) // Check if tab exists -- we don't add some, depending on server version
@@ -645,11 +748,11 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
}
/**
- * Opens a new LectureDetailsWindow showing the details of the
- * lecture with ID = lectureId
+ * Opens a new LectureDetailsWindow showing the details of the lecture with ID =
+ * lectureId
*
* @param modalParent parent of this window
- * @param lectureId id of the lecture to set the details of
+ * @param lectureId id of the lecture to set the details of
*/
public static void open(Frame modalParent, String lectureId, LectureUpdatedCallback callback) {
LectureDetailsWindow win = new LectureDetailsWindow(modalParent, callback);
@@ -667,9 +770,11 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
super.show();
}
- /* *******************************************************************************
+ /*
+ * *****************************************************************************
+ * **
*
- * UIFeedback implementation
+ * UIFeedback implementation
*
********************************************************************************/
@Override
@@ -692,7 +797,7 @@ public class LectureDetailsWindow extends LectureDetailsWindowLayout implements
&& !Gui.showMessageBox(me, I18n.WINDOW.getString("LectureDetails.Message.yesNo.safeClose"),
MessageType.QUESTION_YESNO, null, null))
return;
- synchronized(me) {
+ synchronized (me) {
lecture = null;
image = null;
}
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java
index e96345c0..c83d511c 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/ImageDetailsWindowLayout.java
@@ -7,7 +7,9 @@ import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
import javax.swing.JDialog;
+import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
@@ -15,6 +17,8 @@ import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTMLEditorKit;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.bwlp.thrift.iface.ShareMode;
@@ -36,7 +40,7 @@ import org.openslx.thrifthelper.Comparators;
private static final int ICON_SIZE_Y = 24;
protected final JTextField txtTitle;
- protected final JTextArea txtDescription;
+ protected final JEditorPane txtDescription;
private final JPanel pnlTabContainer;
protected final JTextArea txtContainerDescription;
@@ -78,6 +82,14 @@ import org.openslx.thrifthelper.Comparators;
protected final ImageVersionTable tblVersions;
protected final QScrollPane scpVersions;
+ protected final JButton btnBold;
+ protected final JButton btnItalic;
+ protected final JButton btnUnderline;
+ protected final JButton btnWysiwyg;
+
+ protected final JComboBox cbTxtSize;
+ protected final JComboBox cbTxtColor;
+ protected final HTMLEditorKit kit;
protected JTabbedPane pnlTabs;
protected ImagePermissionConfigurator ctlImagePermissionConfigurator;
@@ -89,9 +101,11 @@ import org.openslx.thrifthelper.Comparators;
setPreferredSize(Gui.getScaledDimension(630, 680));
((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
- /* *******************************************************************************
+ /*
+ * *****************************************************************************
+ * **
*
- * Tab: Overview
+ * Tab: Overview
*
********************************************************************************/
JPanel pnlTabOverview = new JPanel();
@@ -99,16 +113,55 @@ import org.openslx.thrifthelper.Comparators;
// name
txtTitle = new JTextField();
txtTitle.setFont(txtTitle.getFont().deriveFont(Font.BOLD, txtTitle.getFont().getSize2D() * 1.4f));
- //txtTitle.setMinimumSize(Gui.getScaledDimension(0, 24));
grid.add(txtTitle, 3).expand(true, false).fill(true, false);
grid.nextRow();
+ // buttons for text editing
+ JPanel editingPanel = new JPanel();
+
+ editingPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
+ JPanel emptyPanel = new JPanel();
+ grid.add(emptyPanel);
+ btnBold = new JButton(new StyledEditorKit.BoldAction());
+ btnBold.setIcon(Gui.getScaledIconResource("/img/bold.png", "B", 15, this));
+ btnBold.setText("");
+ btnItalic = new JButton(new StyledEditorKit.ItalicAction());
+ btnItalic.setIcon(Gui.getScaledIconResource("/img/italic.png", "B", 15, this));
+ btnItalic.setText("");
+ btnUnderline = new JButton(new StyledEditorKit.UnderlineAction());
+ btnUnderline.setIcon(Gui.getScaledIconResource("/img/underline.png", "B", 15, this));
+ btnUnderline.setText("");
+ editingPanel.add(btnBold);
+ editingPanel.add(btnItalic);
+ editingPanel.add(btnUnderline);
+
+ String[] textsizes = {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"};
+ cbTxtSize = new JComboBox<>(textsizes);
+ cbTxtSize.setPreferredSize(new Dimension(65,25));
+ editingPanel.add(cbTxtSize);
+
+ Object[] colors = {"Black", "Blue", "Red", "Yellow", "Green"};
+ cbTxtColor = new JComboBox<>(colors);
+ cbTxtColor.setPreferredSize(new Dimension(100,25));
+ editingPanel.add(cbTxtColor);
+
+ grid.add(editingPanel).expand(false, true);
+
+ emptyPanel.setLayout(new FlowLayout());
+ btnWysiwyg = new JButton("HTML");
+ btnWysiwyg.setPreferredSize(new Dimension(100,25));
+ emptyPanel.add(btnWysiwyg);
+
+ grid.add(emptyPanel);
+ grid.nextRow();
+
// description
- txtDescription = new JTextArea();
- txtDescription.setLineWrap(true);
- txtDescription.setWrapStyleWord(true);
- grid.add(new QLabel(I18n.WINDOW_LAYOUT.getString("ImageDetails.Label.description.text")))
- .anchor = GridBagConstraints.FIRST_LINE_START;
+ txtDescription = new JEditorPane();
+ kit = new HTMLEditorKit();
+ txtDescription.setEditorKit(kit);
+ txtDescription.setContentType("text/html");
+
+ grid.add(new QLabel(I18n.WINDOW_LAYOUT.getString("ImageDetails.Label.description.text"))).anchor = GridBagConstraints.FIRST_LINE_START;
JScrollPane jsp = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
jsp.setMinimumSize(Gui.getScaledDimension(0, 110));
@@ -223,9 +276,11 @@ import org.openslx.thrifthelper.Comparators;
grid.finish(false);
- /* *******************************************************************************
+ /*
+ * *****************************************************************************
+ * **
*
- * Tab: VM versions
+ * Tab: VM versions
*
********************************************************************************/
JPanel pnlTabVersions = new JPanel();
@@ -235,9 +290,11 @@ import org.openslx.thrifthelper.Comparators;
grdVersions.add(scpVersions).fill(true, true).expand(true, true);
grdVersions.finish(false);
- /* *******************************************************************************
+ /*
+ * *****************************************************************************
+ * **
*
- * Tab: Permissions
+ * Tab: Permissions
*
********************************************************************************/
JPanel pnlTabPermissions = new JPanel();
@@ -325,7 +382,7 @@ import org.openslx.thrifthelper.Comparators;
/* *******************************************************************************
*
- * Bottom panel for buttons
+ * Bottom panel for buttons
*
********************************************************************************/
JPanel pnlButtons = new JPanel();
@@ -341,9 +398,11 @@ import org.openslx.thrifthelper.Comparators;
pnlButtons.add(btnClose);
pnlButtons.add(btnSaveChanges);
- /* *******************************************************************************
+ /*
+ * *****************************************************************************
+ * **
*
- * Main panel containing the tabs
+ * Main panel containing the tabs
*
********************************************************************************/
pnlTabs = new JTabbedPane();
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java
index 09ad8918..3a100edb 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/layout/LectureDetailsWindowLayout.java
@@ -2,18 +2,24 @@ package org.openslx.dozmod.gui.window.layout;
import java.awt.BorderLayout;
import java.awt.Color;
+import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.util.Calendar;
+import java.awt.GridLayout;
+import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
+import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
import javax.swing.JDialog;
+import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
@@ -23,6 +29,8 @@ import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SpinnerDateModel;
import javax.swing.text.DateFormatter;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTMLEditorKit;
import org.jdatepicker.JDatePicker;
import org.openslx.bwlp.thrift.iface.ImageVersionDetails;
@@ -54,7 +62,7 @@ public abstract class LectureDetailsWindowLayout extends JDialog {
// stuff ending in '...Info' are supposed to be the read-only labels for the information tab
protected final QLabel lblTitleInfo;
protected final JTextField txtTitle;
- protected final JTextArea txtDescription;
+ protected final JEditorPane txtDescription;
protected final QLabel lblImageNameInfo;
protected final QLabel txtImageName;
protected final JButton btnLinkImage;
@@ -107,11 +115,21 @@ public abstract class LectureDetailsWindowLayout extends JDialog {
protected final JPanel pnlTabNetshare;
protected final JPanel pnlTabLdapFilter;
+ protected final JButton btnBold;
+ protected final JButton btnItalic;
+ protected final JButton btnUnderline;
+ protected final JButton btnWysiwyg;
+
+ protected final JComboBox cbTxtSize;
+ protected final JComboBox cbTxtColor;
+ protected final HTMLEditorKit kit;
+
+
public LectureDetailsWindowLayout(Frame modalParent) {
super(modalParent, I18n.WINDOW_LAYOUT.getString("LectureDetails.Dialog.title"), ModalityType.APPLICATION_MODAL);
setResizable(true);
setPreferredSize(Gui.getScaledDimension(570, 650));
- setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+ setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE );
setLayout(new BorderLayout());
((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
@@ -175,12 +193,55 @@ public abstract class LectureDetailsWindowLayout extends JDialog {
grdGeneral.add(txtTitle, 4).expand(true, false).fill(true, false);
grdGeneral.nextRow();
+ // buttons for text editing
+ JPanel editingPanel = new JPanel();
+
+ editingPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
+ JPanel emptyPanel = new JPanel();
+ grdGeneral.add(emptyPanel);
+ btnBold = new JButton(new StyledEditorKit.BoldAction());
+ btnBold.setIcon(Gui.getScaledIconResource("/img/bold.png", "B", 15, this));
+ btnBold.setText("");
+ btnItalic = new JButton(new StyledEditorKit.ItalicAction());
+ btnItalic.setIcon(Gui.getScaledIconResource("/img/italic.png", "B", 15, this));
+ btnItalic.setText("");
+ btnUnderline = new JButton(new StyledEditorKit.UnderlineAction());
+ btnUnderline.setIcon(Gui.getScaledIconResource("/img/underline.png", "B", 15, this));
+ btnUnderline.setText("");
+ editingPanel.add(btnBold);
+ editingPanel.add(btnItalic);
+ editingPanel.add(btnUnderline);
+
+ String[] textsizes = {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"};
+ cbTxtSize = new JComboBox<>(textsizes);
+ cbTxtSize.setPreferredSize(new Dimension(65,25));
+ editingPanel.add(cbTxtSize);
+
+ Object[] colors = {"Black", "Blue", "Red", "Yellow", "Green"};
+ cbTxtColor = new JComboBox<>(colors);
+ cbTxtColor.setPreferredSize(new Dimension(100,25));
+ editingPanel.add(cbTxtColor);
+
+ grdGeneral.add(editingPanel);
+
+
+ emptyPanel.setLayout(new FlowLayout());
+ btnWysiwyg = new JButton("HTML");
+ btnWysiwyg.setPreferredSize(new Dimension(100,25));
+ emptyPanel.add(btnWysiwyg);
+
+ grdGeneral.add(emptyPanel);
+ grdGeneral.add(new JPanel());
+ grdGeneral.add(new JPanel());
+
+ grdGeneral.nextRow();
+
// description
- txtDescription = new JTextArea();
- txtDescription.setLineWrap(true);
- txtDescription.setWrapStyleWord(true);
- grdGeneral.add(new QLabel(I18n.WINDOW_LAYOUT.getString("LectureDetails.Label.description.text")))
- .anchor(GridBagConstraints.FIRST_LINE_START);
+ txtDescription = new JEditorPane();
+ kit = new HTMLEditorKit();
+ txtDescription.setEditorKit(kit);
+ txtDescription.setContentType("text/html");
+ grdGeneral.add(new QLabel(I18n.WINDOW_LAYOUT.getString("LectureDetails.Label.description.text"))).anchor(GridBagConstraints.FIRST_LINE_START);
JScrollPane jsp = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
jsp.setMinimumSize(Gui.getScaledDimension(450, 120));
@@ -188,6 +249,7 @@ public abstract class LectureDetailsWindowLayout extends JDialog {
grdGeneral.add(jsp, 4).expand(true, true).fill(true, true);
grdGeneral.nextRow();
+
// ID. NOTE: currently disabled
txtId = new JTextField();
txtId.setEditable(false);
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageMetaDataPageLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageMetaDataPageLayout.java
index 707959f3..4e6257e3 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageMetaDataPageLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/ImageMetaDataPageLayout.java
@@ -1,10 +1,17 @@
package org.openslx.dozmod.gui.wizard.layout;
+import java.awt.FlowLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTMLEditorKit;
+import java.awt.Dimension;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.control.ComboBox;
@@ -20,13 +27,23 @@ import org.openslx.thrifthelper.Comparators;
public abstract class ImageMetaDataPageLayout extends WizardPage {
protected final JComboBox<OperatingSystem> cboOperatingSystem;
- protected final JTextArea txtDescription;
+ protected final JEditorPane txtDescription;
protected final JTextArea startCommand;
protected final QLabel sCommandCaption;
protected final JScrollPane startCommandPane;
protected final JCheckBox chkLicenseRestricted;
protected final JCheckBox chkIsTemplate;
+ protected final JButton btnBold;
+ protected final JButton btnItalic;
+ protected final JButton btnUnderline;
+ protected final JButton btnWysiwyg;
+
+ protected final JComboBox cbTxtSize;
+ protected final JComboBox cbTxtColor;
+ protected final HTMLEditorKit kit;
+
+
/**
* wizard page for entering image data at creating or editing an image
*
@@ -36,7 +53,7 @@ public abstract class ImageMetaDataPageLayout extends WizardPage {
super(wizard, I18n.PAGE_LAYOUT.getString("ImageMetaData.WizardPage.title"));
setDescription(I18n.PAGE_LAYOUT.getString("ImageMetaData.WizardPage.description"));
- GridManager grid = new GridManager(this, 2, false);
+ GridManager grid = new GridManager(this, 3, false);
QLabel osCaption = new QLabel(I18n.PAGE_LAYOUT.getString("ImageMetaData.Label.OS.text"));
cboOperatingSystem = new ComboBox<>(Comparators.operatingSystem, new ComboBoxRenderer<OperatingSystem>() {
@@ -49,7 +66,8 @@ public abstract class ImageMetaDataPageLayout extends WizardPage {
});
cboOperatingSystem.setEditable(false);
grid.add(osCaption);
- grid.add(cboOperatingSystem);
+ grid.add(cboOperatingSystem, 2);
+ grid.nextRow();
sCommandCaption = new QLabel(I18n.PAGE_LAYOUT.getString("ImageMetaData.Label.startCommand.text"));
startCommand = new JTextArea(1, 50);
@@ -61,29 +79,67 @@ public abstract class ImageMetaDataPageLayout extends WizardPage {
startCommandPane.setMinimumSize(startCommand.getMinimumSize());
grid.add(sCommandCaption);
grid.add(startCommandPane).fill(true, false).expand(true, false);
+ grid.add(new JPanel());
+ grid.nextRow();
+
+ // buttons for text editing
+ JPanel editingPanel = new JPanel();
+ editingPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
+ JPanel emptyPanel = new JPanel();
+ grid.add(emptyPanel);
+ btnBold = new JButton(new StyledEditorKit.BoldAction());
+ btnBold.setIcon(Gui.getScaledIconResource("/img/bold.png", "B", 15, this));
+ btnBold.setText("");
+ btnItalic = new JButton(new StyledEditorKit.ItalicAction());
+ btnItalic.setIcon(Gui.getScaledIconResource("/img/italic.png", "B", 15, this));
+ btnItalic.setText("");
+ btnUnderline = new JButton(new StyledEditorKit.UnderlineAction());
+ btnUnderline.setIcon(Gui.getScaledIconResource("/img/underline.png", "B", 15, this));
+ btnUnderline.setText("");
+ editingPanel.add(btnBold);
+ editingPanel.add(btnItalic);
+ editingPanel.add(btnUnderline);
+
+ String[] textsizes = {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"};
+ cbTxtSize = new JComboBox<>(textsizes);
+ cbTxtSize.setPreferredSize(new Dimension(65,25));
+ editingPanel.add(cbTxtSize);
+
+ Object[] colors = {"Black", "Blue", "Red", "Yellow", "Green"};
+ cbTxtColor = new JComboBox<>(colors);
+ cbTxtColor.setPreferredSize(new Dimension(95,25));
+ editingPanel.add(cbTxtColor);
+
+ grid.add(editingPanel);
+ emptyPanel.setLayout(new FlowLayout());
+ btnWysiwyg = new JButton("HTML");
+ btnWysiwyg.setPreferredSize(new Dimension(100,25));
+ emptyPanel.add(btnWysiwyg);
+ grid.add(emptyPanel);
grid.nextRow();
+ // description
QLabel descriptionCaption = new QLabel(I18n.PAGE_LAYOUT.getString("ImageMetaData.Label.description.text"));
- txtDescription = new JTextArea(5, 50);
+ txtDescription = new JEditorPane();
txtDescription.setMinimumSize(Gui.getScaledDimension(0, 70));
- txtDescription.setLineWrap(true);
- txtDescription.setWrapStyleWord(true);
- JScrollPane descPane = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ kit = new HTMLEditorKit();
+ txtDescription.setEditorKit(kit);
+ txtDescription.setContentType("text/html");
+ JScrollPane descPane = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
- descPane.setMinimumSize(txtDescription.getMinimumSize());
grid.add(descriptionCaption);
- grid.add(descPane).fill(true, false).expand(true, false);
+ grid.add(descPane, 2).fill(true, true).expand(true, true);
grid.nextRow();
chkLicenseRestricted = new JCheckBox(
I18n.PAGE_LAYOUT.getString("ImageMetaData.CheckBox.licenseRestricted.text"));
chkLicenseRestricted.setSelected(true);
- grid.add(chkLicenseRestricted, 2);
+ grid.add(chkLicenseRestricted, 3);
grid.nextRow();
// -- end permissions group --
chkIsTemplate = new JCheckBox(I18n.PAGE_LAYOUT.getString("ImageMetaData.CheckBox.isTemplate.text"));
- grid.add(chkIsTemplate, 2);
+ grid.add(chkIsTemplate, 3);
grid.nextRow();
grid.finish(true);
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureCreationPageLayout.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureCreationPageLayout.java
index 91abcf92..771dc60e 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureCreationPageLayout.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/layout/LectureCreationPageLayout.java
@@ -1,13 +1,19 @@
package org.openslx.dozmod.gui.wizard.layout;
import java.util.Calendar;
+import java.awt.FlowLayout;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
-import javax.swing.JTextArea;
import javax.swing.JTextField;
+import javax.swing.JPanel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
import javax.swing.SpinnerDateModel;
import javax.swing.text.DateFormatter;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTMLEditorKit;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.control.QDatePickerImpl;
@@ -17,22 +23,34 @@ import org.openslx.dozmod.gui.helper.I18n;
import org.openslx.dozmod.gui.wizard.Wizard;
import org.openslx.dozmod.gui.wizard.WizardPage;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+
@SuppressWarnings("serial")
public abstract class LectureCreationPageLayout extends WizardPage {
protected final JTextField txtName;
- protected final JTextArea txtDescription;
+ protected final JEditorPane txtDescription;
protected final QDatePickerImpl dtpStartDate;
protected final QDatePickerImpl dtpEndDate;
protected final JSpinner spnStartTime;
protected final JSpinner spnEndTime;
protected final QLabel lblCalcPeriod;
+ protected final JButton btnBold;
+ protected final JButton btnItalic;
+ protected final JButton btnUnderline;
+ protected final JButton btnWysiwyg;
+
+ protected final JComboBox cbTxtSize;
+ protected final JComboBox cbTxtColor;
+ protected final HTMLEditorKit kit;
+
+
/**
* Page for creating lectures
*
- * @param editExistingLecture whether to edit existing lecture or create new
- * one
+ * @param editExistingLecture whether to edit existing lecture or create new one
*/
public LectureCreationPageLayout(Wizard wizard, String title) {
super(wizard, title);
@@ -45,9 +63,49 @@ public abstract class LectureCreationPageLayout extends WizardPage {
grid.add(txtName, 2).fill(true, false).expand(true, false);
grid.nextRow();
- txtDescription = new JTextArea(3, 50);
- txtDescription.setLineWrap(true);
- txtDescription.setWrapStyleWord(true);
+ // buttons for text editing
+ JPanel editingPanel = new JPanel();
+ editingPanel.setLayout(new FlowLayout(FlowLayout.LEADING));
+ JPanel emptyPanel = new JPanel();
+ grid.add(emptyPanel);
+ btnBold = new JButton(new StyledEditorKit.BoldAction());
+ btnBold.setIcon(Gui.getScaledIconResource("/img/bold.png", "B", 15, this));
+ btnBold.setText("");
+ btnItalic = new JButton(new StyledEditorKit.ItalicAction());
+ btnItalic.setIcon(Gui.getScaledIconResource("/img/italic.png", "B", 15, this));
+ btnItalic.setText("");
+ btnUnderline = new JButton(new StyledEditorKit.UnderlineAction());
+ btnUnderline.setIcon(Gui.getScaledIconResource("/img/underline.png", "B", 15, this));
+ btnUnderline.setText("");
+ editingPanel.add(btnBold);
+ editingPanel.add(btnItalic);
+ editingPanel.add(btnUnderline);
+
+ String[] textsizes = {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29"};
+ cbTxtSize = new JComboBox<>(textsizes);
+ cbTxtSize.setPreferredSize(new Dimension(65,25));
+ editingPanel.add(cbTxtSize);
+
+ Object[] colors = {"Black", "Blue", "Red", "Yellow", "Green"};
+ cbTxtColor = new JComboBox<>(colors);
+ cbTxtColor.setPreferredSize(new Dimension(95,25));
+ editingPanel.add(cbTxtColor);
+
+ grid.add(editingPanel);
+ emptyPanel.setLayout(new FlowLayout());
+ btnWysiwyg = new JButton("HTML");
+ btnWysiwyg.setPreferredSize(new Dimension(100,25));
+ emptyPanel.add(btnWysiwyg);
+
+ grid.add(emptyPanel);
+ grid.nextRow();
+
+ // description
+ txtDescription = new JEditorPane();
+ kit = new HTMLEditorKit();
+ txtDescription.setEditorKit(kit);
+ txtDescription.setContentType("text/html");
+ txtDescription.setPreferredSize(new Dimension(600,800));
JScrollPane descPane = new JScrollPane(txtDescription, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
txtDescription.setMinimumSize(Gui.getScaledDimension(0, 60));
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageMetaDataPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageMetaDataPage.java
index 6394ef79..c3fce45c 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageMetaDataPage.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/ImageMetaDataPage.java
@@ -4,6 +4,19 @@ import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collections;
import java.util.List;
+import java.awt.Color;
+
+import javax.swing.Action;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.io.IOException;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.OperatingSystem;
@@ -71,8 +84,100 @@ public class ImageMetaDataPage extends ImageMetaDataPageLayout {
reactToUserInput();
}
});
+
+ cbTxtSize.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int size = Integer.parseInt((String) cbTxtSize.getSelectedItem());
+ Action act = new StyledEditorKit.FontSizeAction(String.valueOf(size), size);
+ act.actionPerformed(new ActionEvent(act, ActionEvent.ACTION_PERFORMED,
+ (String) act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ cbTxtColor.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String color = (String) cbTxtColor.getSelectedItem();
+ Action act = null;
+
+ switch (color) {
+ case "Black":
+ act = new StyledEditorKit.ForegroundAction("Black", Color.black);
+ break;
+ case "Blue":
+ act = new StyledEditorKit.ForegroundAction("Blue", Color.blue);
+ break;
+ case "Yellow":
+ act = new StyledEditorKit.ForegroundAction("Yellow", Color.yellow);
+ break;
+ case "Red":
+ act = new StyledEditorKit.ForegroundAction("Red", Color.red);
+ break;
+ case "Green":
+ act = new StyledEditorKit.ForegroundAction("Green", Color.green);
+ break;
+ }
+
+ act.actionPerformed(new ActionEvent(act, ActionEvent.ACTION_PERFORMED,
+ (String) act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ btnWysiwyg.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String tmp = txtDescription.getText();
+ if (txtDescription.getContentType().equals("text/html")) {
+ txtDescription.setContentType("text/plain");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Wysiwyg");
+
+ btnBold.setEnabled(false);
+ btnUnderline.setEnabled(false);
+ btnItalic.setEnabled(false);
+ cbTxtColor.setEnabled(false);
+ cbTxtSize.setEnabled(false);
+ } else {
+ txtDescription.setContentType("text/html");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Html");
+
+ btnBold.setEnabled(true);
+ btnUnderline.setEnabled(true);
+ btnItalic.setEnabled(true);
+ cbTxtColor.setEnabled(true);
+ cbTxtSize.setEnabled(true);
+ }
+ }
+ });
+
+ txtDescription.addKeyListener(new KeyListener() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER && txtDescription.getContentType().equals("text/html")) {
+ try {
+ kit.insertHTML((HTMLDocument) txtDescription.getDocument(), txtDescription.getCaretPosition(),
+ "<br>", 0, 0, HTML.Tag.BR);
+ txtDescription.setCaretPosition(txtDescription.getCaretPosition()); // This moves caret to next
+ // line
+ } catch (BadLocationException | IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ });
}
+
@Override
protected void onPageEnter() {
// Preselect OS if possible
@@ -107,8 +212,7 @@ public class ImageMetaDataPage extends ImageMetaDataPageLayout {
}
/**
- * Called by event listeners. This will set guidance message or error
- * message
+ * Called by event listeners. This will set guidance message or error message
* and call setPageComplete(bool) accordingly.
*/
private void reactToUserInput() {
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureCreationPage.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureCreationPage.java
index 17d71312..71e6fe6d 100644
--- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureCreationPage.java
+++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/wizard/page/LectureCreationPage.java
@@ -4,9 +4,17 @@ import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
+import java.io.IOException;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import javax.swing.Action;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
import org.apache.log4j.Logger;
import org.openslx.dozmod.gui.helper.DateTimeHelper;
@@ -67,8 +75,102 @@ public class LectureCreationPage extends LectureCreationPageLayout {
dtpStartDate.addActionListener(actionListener);
dtpEndDate.addActionListener(actionListener);
calculateDatePeriod();
+
+ cbTxtSize.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int size = Integer.parseInt((String) cbTxtSize.getSelectedItem());
+ Action act = new StyledEditorKit.FontSizeAction(String.valueOf(size),size);
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+ cbTxtColor.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String color = (String) cbTxtColor.getSelectedItem();
+ Action act = null;
+
+ switch(color) {
+ case "Black":
+ act = new StyledEditorKit.ForegroundAction("Black", Color.black);
+ break;
+ case "Blue":
+ act = new StyledEditorKit.ForegroundAction("Blue", Color.blue);
+ break;
+ case "Yellow":
+ act = new StyledEditorKit.ForegroundAction("Yellow", Color.yellow);
+ break;
+ case "Red":
+ act = new StyledEditorKit.ForegroundAction("Red", Color.red);
+ break;
+ case "Green":
+ act = new StyledEditorKit.ForegroundAction("Green", Color.green);
+ break;
+ }
+
+ act.actionPerformed(new ActionEvent(act,ActionEvent.ACTION_PERFORMED,
+ (String)act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ });
+
+
+ btnWysiwyg.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String tmp = txtDescription.getText();
+ if (txtDescription.getContentType().equals("text/html")) {
+ txtDescription.setContentType("text/plain");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Wysiwyg");
+
+ btnBold.setEnabled(false);
+ btnUnderline.setEnabled(false);
+ btnItalic.setEnabled(false);
+ cbTxtColor.setEnabled(false);
+ cbTxtSize.setEnabled(false);
+ } else {
+ txtDescription.setContentType("text/html");
+ txtDescription.setText(tmp);
+ btnWysiwyg.setText("Html");
+
+ btnBold.setEnabled(true);
+ btnUnderline.setEnabled(true);
+ btnItalic.setEnabled(true);
+ cbTxtColor.setEnabled(true);
+ cbTxtSize.setEnabled(true);
+ }
+ }
+ });
+
+ txtDescription.addKeyListener(new KeyListener() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER && txtDescription.getContentType().equals("text/html")) {
+ try {
+ kit.insertHTML((HTMLDocument) txtDescription.getDocument(), txtDescription.getCaretPosition(),
+ "<br>", 0, 0, HTML.Tag.BR);
+ txtDescription.setCaretPosition(txtDescription.getCaretPosition()); // This moves caret to next
+ // line
+ } catch (BadLocationException | IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ });
}
+
+
@Override
protected boolean wantNextOrFinish() {
return reactToUserInput();
diff --git a/dozentenmodul/src/main/resources/img/bold.png b/dozentenmodul/src/main/resources/img/bold.png
new file mode 100644
index 00000000..30baeaa7
--- /dev/null
+++ b/dozentenmodul/src/main/resources/img/bold.png
Binary files differ
diff --git a/dozentenmodul/src/main/resources/img/italic.png b/dozentenmodul/src/main/resources/img/italic.png
new file mode 100644
index 00000000..c7d6393d
--- /dev/null
+++ b/dozentenmodul/src/main/resources/img/italic.png
Binary files differ
diff --git a/dozentenmodul/src/main/resources/img/underline.png b/dozentenmodul/src/main/resources/img/underline.png
new file mode 100644
index 00000000..b049a9ad
--- /dev/null
+++ b/dozentenmodul/src/main/resources/img/underline.png
Binary files differ
diff --git a/dozentenmodulserver/pom.xml b/dozentenmodulserver/pom.xml
index f660d51d..ac41c772 100644
--- a/dozentenmodulserver/pom.xml
+++ b/dozentenmodulserver/pom.xml
@@ -38,8 +38,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
</configuration>
</plugin>
<plugin>
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
index 36c3a466..da52a5b3 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
@@ -1097,4 +1097,22 @@ public class DbImage {
}
}
+ /**
+ * Get all known file names of images, regardless of whether they are working/valid.
+ */
+ public static Set<String> getAllFilenames() throws SQLException {
+ try (MysqlConnection connection = Database.getConnection()) {
+ MysqlStatement stmt = connection.prepareStatement("SELECT filepath FROM imageversion");
+ ResultSet rs = stmt.executeQuery();
+ Set<String> result = new HashSet<>();
+ while (rs.next()) {
+ result.add(rs.getString("filepath"));
+ }
+ return result;
+ } catch (SQLException e) {
+ LOGGER.error("Query failed in DbImage.getAllFilenames()", e);
+ throw e;
+ }
+ }
+
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
index 4ecb9d99..c07a0ed9 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
@@ -30,7 +30,6 @@ import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.util.Json;
import org.openslx.util.Util;
-import org.openslx.util.vm.UnsupportedVirtualizerFormatException;
import org.openslx.util.vm.VmMetaData;
import org.openslx.util.vm.VmMetaData.UsbSpeed;
@@ -553,7 +552,7 @@ public class DbLecture {
}
public static LaunchData getClientLaunchData(String lectureId) throws SQLException,
- TNotFoundException, UnsupportedVirtualizerFormatException {
+ TNotFoundException {
LaunchData retval = new LaunchData();
byte[] config;
String lectureName;
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
index 0e47994a..3a6f39ad 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebRpc.java
@@ -5,16 +5,25 @@ import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
+import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.security.auth.login.LoginException;
import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.log4j.Logger;
import org.openslx.bwlp.sat.database.mappers.DbConfiguration;
+import org.openslx.bwlp.sat.database.mappers.DbImage;
import org.openslx.bwlp.sat.mail.MailTemplate;
import org.openslx.bwlp.sat.mail.MailTemplatePlain.Template;
import org.openslx.bwlp.sat.mail.SmtpMailer;
@@ -23,6 +32,8 @@ import org.openslx.bwlp.sat.maintenance.DeleteOldImages;
import org.openslx.bwlp.sat.maintenance.ImageValidCheck;
import org.openslx.bwlp.sat.maintenance.ImageValidCheck.CheckResult;
import org.openslx.bwlp.sat.maintenance.ImageValidCheck.SubmitResult;
+import org.openslx.bwlp.sat.util.Configuration;
+import org.openslx.bwlp.sat.util.FileSystem;
import org.openslx.util.Json;
import org.openslx.util.Util;
@@ -31,6 +42,8 @@ import fi.iki.elonen.NanoHTTPD.Response;
public class WebRpc {
+ private static final Logger LOGGER = Logger.getLogger(WebRpc.class);
+
public static Response handle(String uri, Map<String, String> params) {
if (uri.equals("mailtest")) {
return mailTest(params);
@@ -47,6 +60,9 @@ public class WebRpc {
if (uri.equals("reset-mail-templates")) {
return resetMailTemplates();
}
+ if (uri.equals("scan-orphaned-files")) {
+ return scanForOrphanedFiles(params);
+ }
return WebServer.notFound();
}
@@ -55,6 +71,102 @@ public class WebRpc {
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", "OK");
}
+ /**
+ * Scan the vmstore for orphaned files and images, return a list.
+ * If POST param 'action' is 'delete', all those files will be deleted.
+ */
+ private static Response scanForOrphanedFiles(Map<String, String> params) {
+ if (!FileSystem.isStorageMounted())
+ return WebServer.internalServerError("VMstore not mounted");
+ final Map<String, DeleteResult> orphanedFiles = new HashMap<>();
+ final String baseDir = Configuration.getVmStoreBasePath().toString();
+ final int baseLen = baseDir.length() + (baseDir.endsWith("/") ? 0 : 1);
+ final boolean del = params.containsKey("action") && params.get("action").equals("delete");
+ final Set<String> known; // These we want to keep
+ try {
+ known = DbImage.getAllFilenames();
+ } catch (SQLException e1) {
+ return WebServer.internalServerError("Cannot query list of known images from database");
+ }
+ if (known.isEmpty()) {
+ return WebServer.internalServerError("SAFTY CHECK: Known image list empty, aborting");
+ }
+ AtomicInteger matches = new AtomicInteger();
+ try {
+ // Consider only regular files, call checkFile for each one
+ Files.find(Configuration.getVmStoreProdPath().toPath(), 8,
+ (filePath, fileAttr) -> fileAttr.isRegularFile())
+ .forEach((fileName) -> checkFile(fileName, orphanedFiles, baseLen, known, matches));
+ } catch (IOException e) {
+ return WebServer.internalServerError(e.toString());
+ }
+ if (del) {
+ for (Entry<String, DeleteResult> it : orphanedFiles.entrySet()) {
+ if (matches.get() == 0) {
+ // Don't delete anything if the set of known files and the set of files we actually
+ // found are disjoint
+ it.setValue(DeleteResult.SAFETY_ABORT);
+ continue;
+ }
+ Path filePath = Paths.get(baseDir + "/" + it.getKey());
+ try {
+ Files.delete(filePath);
+ it.setValue(DeleteResult.DELETED);
+ } catch (Exception e) {
+ LOGGER.warn("Cannot delete " + filePath, e);
+ it.setValue(DeleteResult.ERROR);
+ }
+ }
+ }
+ return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "application/json; charset=utf-8",
+ Json.serialize(orphanedFiles));
+ }
+
+ private static enum DeleteResult {
+ EXISTS,
+ DELETED,
+ ERROR,
+ SAFETY_ABORT;
+ }
+
+ /**
+ * Function called for each file found on the VMstore to determine if it's
+ * orphaned.
+ *
+ * @param filePath File to check
+ * @param result Map to add the check result to
+ * @param baseLen length of the base path we need to strip from the absolute
+ * path
+ * @param known list of known images from the db
+ * @param matches counter for files that match a DB entry
+ */
+ private static void checkFile(Path filePath, Map<String, DeleteResult> result, int baseLen,
+ Set<String> known, AtomicInteger matches) {
+ if (filePath.endsWith("dozmod.lock"))
+ return;
+ final String relativeFileName;
+ try {
+ relativeFileName = filePath.toAbsolutePath().toString().substring(baseLen);
+ } catch (IndexOutOfBoundsException e) {
+ LOGGER.warn("Cannot make image path relative", e);
+ return;
+ }
+ // Handle special dnbd3 files
+ String compareFileName;
+ if (relativeFileName.endsWith(".crc") || relativeFileName.endsWith(".map")) {
+ compareFileName = relativeFileName.substring(0, relativeFileName.length() - 4);
+ } else if (relativeFileName.endsWith(".meta")) {
+ compareFileName = relativeFileName.substring(0, relativeFileName.length() - 5);
+ } else {
+ compareFileName = relativeFileName;
+ }
+ if (known.contains(compareFileName)) {
+ matches.incrementAndGet();
+ } else {
+ result.put(relativeFileName, DeleteResult.EXISTS);
+ }
+ }
+
private static Response checkImage(Map<String, String> params) {
String versionId = params.get("versionid");
if (versionId == null)
@@ -88,7 +200,8 @@ public class WebRpc {
StringBuilder res = DeleteOldImages.hardDeleteImages();
if (res == null)
return WebServer.internalServerError();
- return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8", res.toString());
+ return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8",
+ res.toString());
}
/**
@@ -132,11 +245,9 @@ public class WebRpc {
}
smtpc = null;
}
-
boolean ret = false;
- if (smtpc != null) {
-
+ if (smtpc != null) {
MailTemplate template = DbConfiguration.getMailTemplate(Template.TEST_MAIL);
Map<String, String> templateArgs = new HashMap<>();
templateArgs.put("host", host);
@@ -149,8 +260,8 @@ public class WebRpc {
ret = smtpc.send(recipient, "bwLehrpool Mail Test", msg, "<sat.bwlehrpool.de>");
}
try {
- baos.write(("\n\n-----------------------------------------\nTestergebnis: " + (ret ? "" : "nicht ")
- + "erfolgreich").getBytes(StandardCharsets.UTF_8));
+ baos.write(("\n\n-----------------------------------------\nTestergebnis: "
+ + (ret ? "" : "nicht ") + "erfolgreich").getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
}
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.OK, "text/plain; charset=utf-8",
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java
index 3e91cfc5..6357e411 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/web/WebServer.java
@@ -31,7 +31,6 @@ import org.openslx.bwlp.thrift.iface.TNotFoundException;
import org.openslx.util.GrowingThreadPoolExecutor;
import org.openslx.util.Json;
import org.openslx.util.Util;
-import org.openslx.util.vm.UnsupportedVirtualizerFormatException;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
@@ -167,7 +166,7 @@ public class WebServer extends NanoHTTPD {
final LaunchData ld;
try {
ld = DbLecture.getClientLaunchData(lectureId);
- } catch (UnsupportedVirtualizerFormatException | TNotFoundException e) {
+ } catch (TNotFoundException e) {
// TODO better virt error handling
return notFound();
} catch (SQLException e) {
@@ -288,10 +287,15 @@ public class WebServer extends NanoHTTPD {
/**
* Helper for returning "Internal Server Error" Status
+ * @param body Message
*/
- public static Response internalServerError() {
+ public static Response internalServerError(String body) {
return new NanoHTTPD.Response(NanoHTTPD.Response.Status.INTERNAL_ERROR, "text/plain",
- "Internal Server Error");
+ body);
+ }
+
+ public static Response internalServerError() {
+ return internalServerError("Internal Server Error");
}
/**