package org.openslx.dozmod.gui.window; 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.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenuItem; 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; import org.openslx.bwlp.thrift.iface.ImageBaseWrite; import org.openslx.bwlp.thrift.iface.ImageDetailsRead; import org.openslx.bwlp.thrift.iface.ImagePermissions; import org.openslx.bwlp.thrift.iface.ImageSummaryRead; import org.openslx.bwlp.thrift.iface.ImageVersionDetails; import org.openslx.bwlp.thrift.iface.LectureSummary; import org.openslx.bwlp.thrift.iface.OperatingSystem; import org.openslx.bwlp.thrift.iface.ShareMode; import org.openslx.bwlp.thrift.iface.UserInfo; import org.openslx.bwlp.thrift.iface.Virtualizer; import org.openslx.dozmod.Branding; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; import org.openslx.dozmod.gui.changemonitor.AbstractControlWrapper; import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor; import org.openslx.dozmod.gui.changemonitor.DialogChangeMonitor.TextNotEmptyConstraint; import org.openslx.dozmod.gui.helper.DateTimeHelper; import org.openslx.dozmod.gui.helper.ExpiryDateChooser; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.gui.helper.PopupMenu; import org.openslx.dozmod.gui.helper.UiFeedback; import org.openslx.dozmod.gui.window.UserListWindow.UserAddedCallback; import org.openslx.dozmod.gui.window.layout.ImageDetailsWindowLayout; import org.openslx.dozmod.gui.wizard.ImageUpdateWizard; import org.openslx.dozmod.gui.wizard.LectureWizard; import org.openslx.dozmod.permissions.ImagePerms; import org.openslx.dozmod.thrift.ImageDetailsActions; import org.openslx.dozmod.thrift.Session; import org.openslx.dozmod.thrift.ThriftActions; import org.openslx.dozmod.thrift.ThriftActions.DeleteCallback; import org.openslx.dozmod.thrift.ThriftActions.ImageMetaCallback; import org.openslx.dozmod.thrift.ThriftError; import org.openslx.dozmod.thrift.cache.LectureCache; import org.openslx.dozmod.thrift.cache.MetaDataCache; import org.openslx.dozmod.thrift.cache.UserCache; import org.openslx.dozmod.util.DesktopEnvironment; import org.openslx.dozmod.util.FormatHelper; import org.openslx.sat.thrift.version.Feature; import org.openslx.thrifthelper.Comparators; import org.openslx.thrifthelper.TConst; import org.openslx.thrifthelper.ThriftManager; import org.openslx.util.QuickTimer; import org.openslx.util.QuickTimer.Task; import org.openslx.util.ThriftUtil; import org.openslx.util.Util; /** * Window for displaying and editing the details of an image. */ @SuppressWarnings("serial") public class ImageDetailsWindow extends ImageDetailsWindowLayout implements UiFeedback { private static final Logger LOGGER = Logger.getLogger(ImageDetailsWindow.class); /** * Self-reference */ private final ImageDetailsWindow me = this; /** * Action handler proxying thrift calls to differentiate between local and * published image mode */ private final ImageDetailsActions actionHandler; /** * Callback interface to refresh image list after changing image details */ public interface ImageUpdatedCallback { public void updated(); } /** * Callback instance */ private ImageUpdatedCallback callback = null; /** * Image that this window shows the details of */ private ImageDetailsRead image = null; /** * The current state of custom permissions of the image */ private Map customPermissions; /** * Did the user have admin rights due to default permissions? */ private boolean adminRightsFromDefaultPermissions; /** * Popup menu items */ private final JMenuItem mnuNewLecture = new JMenuItem("Neue Veranstaltung"); private final JMenuItem mnuDownload = new JMenuItem("Download"); private final JMenuItem mnuVmConfig = new JMenuItem("VM-Konfiguration"); private final JMenuItem mnuDelete = new JMenuItem("Löschen"); private final JMenuItem mnuExtendExpiryDate = new JMenuItem("Ablaufzeitpunkt verlängern"); private DialogChangeMonitor changeMonitor; private AbstractControlWrapper changeListenerPermissions; /** * Constructor * * @param modalParent parent of this popup window * @param callback callback to be called when the image details have changed */ public ImageDetailsWindow(Frame modalParent, ImageUpdatedCallback callback, ImageDetailsActions actionHandler) { super(modalParent); this.callback = callback; this.actionHandler = actionHandler; // Set up change monitor changeMonitor = new DialogChangeMonitor(new DialogChangeMonitor.Callback() { @Override public void validityChanged(String errorMessage) { lblError.setText(errorMessage); btnSaveChanges.setEnabled(changeMonitor.isValid() && changeMonitor.wasEverModified()); LOGGER.info("Valid: " + changeMonitor.isValid()); } @Override public void modificationChanged() { btnSaveChanges.setEnabled(changeMonitor.isValid() && changeMonitor.wasEverModified()); LOGGER.info("Changed: " + changeMonitor.isCurrentlyModified()); } }); // Hook when user presses X (top right) setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { safeClose(); } }); /** * Button listeners */ btnClose.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { safeClose(); } }); btnSaveChanges.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { saveChanges(); } }); btnUpdateImage.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // FIXME: This will always discard all changes even if you cancel right away new ImageUpdateWizard(me, image).setVisible(true); refresh(true); } }); btnUploadToMaster.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { uploadToMaster(); } }); btnShowLinkingLectures.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (safeClose()) { LectureListWindow page = MainWindow.showPage(LectureListWindow.class); page.filterByImageBaseId(image.imageBaseId); } } }); btnChangeOwner.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { UserListWindow.open(me, new UserAddedCallback() { @Override public void userAdded(UserInfo user, UserListWindow window) { window.dispose(); if (Gui.showMessageBox(me, "Sind Sie sicher, dass sie die Besitzerrechte an " + "einen anderen Benutzer übertragen wollen?", MessageType.QUESTION_YESNO, LOGGER, null)) setImageOwner(user); } }, "Besitzer festlegen", image.ownerId); } }); 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(), "
", 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); /** * Popup menu for the version table on the right side */ final PopupMenu pop = new PopupMenu(new ActionListener() { public void actionPerformed(ActionEvent e) { ImageVersionDetails selected = tblVersions.getSelectedItem(); if (selected == null) return; if (e.getSource().equals(mnuNewLecture)) { ImageSummaryRead summary = new ImageSummaryRead(); summary.setImageName(image.getImageName()); // Maybe create a helper class/function some day that transforms all fields new LectureWizard(me, summary, selected.getVersionId()).setVisible(true); } if (e.getSource().equals(mnuDownload)) { performImageDownload(selected); } if (e.getSource().equals(mnuDelete)) { deleteVersions(tblVersions.getSelectedItems()); } if (e.getSource().equals(mnuVmConfig)) { editVmConfig(selected); } if (e.getSource().equals(mnuExtendExpiryDate)) { extendVersionExpiry(tblVersions.getSelectedItems()); } } }); pop.addMenuItem(mnuNewLecture); pop.addMenuItem(mnuDownload); if (Session.isLectureRestrictionsSupported()) pop.addMenuItem(mnuVmConfig); pop.addSeparator(); pop.addMenuItem(mnuDelete); if (Session.canExtendImageExpiry()) { pop.addSeparator(); pop.addMenuItem(mnuExtendExpiryDate); } // keyboard shortcut tblVersions.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete"); tblVersions.getActionMap().put("delete", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { if (ImagePerms.canEdit(image)) { deleteVersions(tblVersions.getSelectedItems()); } } }); /** * Mouse adapter for the version table */ final MouseAdapter ma = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { processClick(e); } @Override public void mouseReleased(MouseEvent e) { processClick(e); } private void processClick(MouseEvent e) { // rowIndex at mouse cursor int mouseRowIndex = tblVersions.rowAtPoint(e.getPoint()); // is the click event on an already selected row? boolean alreadySelectedRow = false; for (int i : tblVersions.getSelectedRows()) { if (i == mouseRowIndex) { alreadySelectedRow = true; break; } } if (mouseRowIndex >= 0 && mouseRowIndex < tblVersions.getRowCount() && SwingUtilities.isRightMouseButton(e)) { // select row if it wasn't in selection before if (!alreadySelectedRow) { tblVersions.setRowSelectionInterval(mouseRowIndex, mouseRowIndex); } if (e.isPopupTrigger()) { boolean multiSelection = tblVersions.getSelectedRowCount() != 1; ImageVersionDetails selectedVersion = tblVersions.getSelectedItem(); mnuNewLecture.setEnabled( !multiSelection && selectedVersion.isValid && ImagePerms.canLink(image)); mnuDownload.setEnabled(!multiSelection && ImagePerms.canDownload(image)); mnuVmConfig.setEnabled(!multiSelection && selectedVersion.isValid && ImagePerms.canEdit(image)); // Allow VMX edition for downloaded VMs for now mnuDelete.setEnabled(ImagePerms.canEdit(image)); mnuExtendExpiryDate.setEnabled(!multiSelection && (selectedVersion.isValid || Session.hasFeature(Feature.EXTEND_EXPIRED_VM))); pop.show(e.getComponent(), e.getX(), e.getY()); } } } }; scpVersions.addMouseListener(ma); tblVersions.addMouseListener(ma); // add controls to change monitor to handle user actions changeMonitor.add(txtTitle).addConstraint(new TextNotEmptyConstraint("Name darf nicht leer sein")); changeMonitor.add(txtDescription) .addConstraint(new TextNotEmptyConstraint("Beschreibung darf nicht leer sein")); changeMonitor.add(txtTags); changeMonitor.addFixedCombo(cboOperatingSystem, Comparators.operatingSystem) .addConstraint(new DialogChangeMonitor.ValidationConstraint() { public String checkStateValid(OperatingSystem userInput) { if (userInput != null && MetaDataCache.getOsById(userInput.osId) != null) // validating OS coming from the cache makes no sense? return null; return "Ungültiges Betriebssystem ausgewählt!"; } }); changeMonitor.addFixedCombo(cboShareMode, null) .addConstraint(new DialogChangeMonitor.ValidationConstraint() { public String checkStateValid(ShareMode userInput) { if (userInput != null) return null; return "Ungültiges Betriebssystem ausgewählt!"; } }); changeMonitor.add(chkIsTemplate); changeMonitor.add(chkDefaultPermLink); changeMonitor.add(chkDefaultPermDownload); changeMonitor.add(chkDefaultPermEdit); changeMonitor.add(chkDefaultPermAdmin); changeListenerPermissions = changeMonitor.add(ctlImagePermissionConfigurator); // update default permissions hook for the permission configurator to apply to newly added users final ItemListener updateDefaultPermissionsListener = new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (e.getSource() == chkDefaultPermLink) image.defaultPermissions.link = chkDefaultPermLink.isSelected(); if (e.getSource() == chkDefaultPermDownload) image.defaultPermissions.download = chkDefaultPermDownload.isSelected(); if (e.getSource() == chkDefaultPermEdit) image.defaultPermissions.edit = chkDefaultPermEdit.isSelected(); if (e.getSource() == chkDefaultPermAdmin) image.defaultPermissions.admin = chkDefaultPermAdmin.isSelected(); } }; chkDefaultPermLink.addItemListener(updateDefaultPermissionsListener); chkDefaultPermDownload.addItemListener(updateDefaultPermissionsListener); chkDefaultPermEdit.addItemListener(updateDefaultPermissionsListener); chkDefaultPermAdmin.addItemListener(updateDefaultPermissionsListener); /** * Initial state of GUI elements */ setFocusable(true); btnSaveChanges.setEnabled(false); txtVersion.setEditable(false); txtId.setEditable(false); chkIsTemplate.setEnabled(Session.isSuperUser()); btnUploadToMaster.setVisible(actionHandler.isImagePublishSupported()); makeEditable(false); // TODO finish ShareMode in server cboShareMode.setEnabled(false); } private void editVmConfig(final ImageVersionDetails selected) { QuickTimer.scheduleOnce(new Task() { ByteBuffer machineDescription = null; @Override public void fire() { try { machineDescription = ThriftManager.getSatClient() .getImageVersionVirtConfig(Session.getSatelliteToken(), selected.versionId); } catch (TException e) { LOGGER.error("Failed to retrieve virtualizer config for image version " + "'" + image.latestVersionId + ", see trace: ", e); return; } Gui.asyncExec(new Runnable() { @Override public void run() { if (machineDescription == null) { Gui.showMessageBox("Konnte VM-Konfiguration nicht abrufen.", MessageType.ERROR, LOGGER, null); return; } String tmp = ThriftUtil.byteBufferToString(machineDescription); if (TConst.VIRT_QEMU.equals(image.virtId)) { VirtConfigEditorWindow.open(me, actionHandler, selected.versionId, ThriftUtil.byteBufferToString(machineDescription), tmp); } else { VirtDropDownConfigEditorWindow.open(me, actionHandler, selected.versionId, machineDescription); } } }); } }); } /******************************************************************************** * * Helper triggering the actual thrift calls * ********************************************************************************/ /** * Sets the image to the given imageBaseId. This will also trigger fill() which * will set the image details fields to the values represented by this image. * * @param imageBaseId the id of the image to be displayed */ public void setImage(final String imageBaseId) { // Just prime the cache... MetaDataCache.getOperatingSystems(); MetaDataCache.getVirtualizers(); final ImageMetaCallback callback = new ImageMetaCallback() { @Override public void fetchedImageDetails(ImageDetailsRead imageDetails, Map permissions) { if (imageDetails == null) { return; } if (permissions == null) { permissions = new HashMap<>(); } synchronized (me) { image = imageDetails; customPermissions = permissions; } fillDetails(); } }; actionHandler.getImageDetails(imageBaseId, callback); } /** * Sets the owner of the selected image to the given user. * * @param user UserInfo to set the owner to */ private void setImageOwner(final UserInfo user) { if (!actionHandler.setImageOwner(image.getImageBaseId(), user)) { return; } Gui.showMessageBox(me, "Besitzrechte übertragen an " + FormatHelper.userName(user), MessageType.INFO, null, null); makeEditable(false); refresh(true); } /** * Called by the "Publish" button. Tries to upload that image to the * masterserver. */ private void uploadToMaster() { // 04.2018: bail if we the user tries to publish a non-vmware image. if (Session.hasFeature(Feature.MULTIPLE_HYPERVISORS) && !TConst.VIRT_VMWARE.equals(image.virtId)) { Gui.showMessageBox( MetaDataCache.getVirtualizerById(image.virtId).virtName + " ist derzeit nicht für den öffentlichen Austausch freigegeben.", MessageType.ERROR, null, null); return; } if (changeMonitor.isCurrentlyModified()) { if (Gui.showMessageBox("Sie haben unspeicherte Änderungen, wollen Sie diese erst speichern?", MessageType.QUESTION_YESNO, null, null)) { if (!saveChangesInternal()) { return; } } } // let the user confirm or abort if (!Gui.showMessageBox("Wollen Sie diese VM wirklich landesweit veröffentlichen?", MessageType.QUESTION_YESNO, null, null)) return; // start upload to masterserver QuickTimer.scheduleOnce(new Task() { @Override public void fire() { final String transferId; try { transferId = ThriftManager.getSatClient().publishImageVersion(Session.getSatelliteToken(), image.latestVersionId); } catch (TException e1) { ThriftError.showMessage(me, LOGGER, e1, "Upload der VM auf den Masterserver fehlgeschlagen." + " Prüfen Sie, ob Sie bei der Registrierung dem landesweiten VM-Austausch zugestimmt haben.\n" + " Sie können dies hier überprüfen:\n" + DesktopEnvironment.Link.REGISTER_BWIDM.uri.toString() + "\n\n"); return; } Gui.asyncExec(new Runnable() { @Override public void run() { MainWindow.addPassiveTransfer(transferId, image.imageName, true); // Inform user Gui.showMessageBox(ImageDetailsWindow.this, "Die Übertragung läuft direkt zwischen Satellitenserver und" + " dem " + Branding.getServiceName() + " Zentral-Server.\n" + "Wenn Sie die " + Branding.getApplicationName() + " schließen, wird der Transfer trotzdem" + "weiterlaufen.", MessageType.INFO, null, null); } }); } }); } /** * 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(); // if there was nothing to save, saved would be true from the above call // however we wouldn't even get to saving if nothing was changed, so its fine if (saved) { if (callback != null) callback.updated(); dispose(); } else { btnSaveChanges.setEnabled(true); } } /** * 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) if (adminRightsFromDefaultPermissions && changeListenerPermissions.isCurrentlyChanged()) { if (!saveCustomPermissions()) { return false; } changeListenerPermissions.reset(); } // first build the ImageBaseWrite from the GUI fields final ImageBaseWrite ibw = new ImageBaseWrite(txtTitle.getText(), txtDescription.getText(), cboOperatingSystem.getItemAt(cboOperatingSystem.getSelectedIndex()).osId, image.virtId, chkIsTemplate.isSelected(), new ImagePermissions(image.defaultPermissions.link, image.defaultPermissions.download, image.defaultPermissions.edit, image.defaultPermissions.admin), cboShareMode.getItemAt(cboShareMode.getSelectedIndex())); try { actionHandler.updateImageBase(image.getImageBaseId(), ibw); LOGGER.info("Successfully saved new metadata"); } catch (TException e) { ThriftError.showMessage(me, LOGGER, e, "Konnte aktualisierte Metadaten nicht an den Server übermitteln"); return false; } if (changeListenerPermissions.isCurrentlyChanged()) { if (!saveCustomPermissions()) { return false; } } changeMonitor.reset(); return true; } /** * Helper to save the custom user permissions (those not included in * ImageBaseWrite). * * @return true if successfully saved, false otherwise. */ private boolean saveCustomPermissions() { try { actionHandler.writeImagePermissions(image.getImageBaseId(), ctlImagePermissionConfigurator.getPermissions()); LOGGER.info("Successfully saved new custom permissions"); } catch (TException e) { ThriftError.showMessage(me, LOGGER, e, "Konnte geänderte Berechtigungen nicht an den Server übermitteln"); return false; } return true; } /** * Triggers the download of the given image version. * * @param selected image to download. */ private void performImageDownload(ImageVersionDetails selected) { if (selected.getVersionId() == null) { Gui.showMessageBox(this, "Ausgewählte Version ist ungültig", MessageType.ERROR, null, null); return; } // 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); } /** * Triggers the deletion of the given image version. * * @param version image version to delete. */ private void deleteVersion(final ImageVersionDetails version) { if (version == null) return; actionHandler.deleteImageVersion(version, new DeleteCallback() { @Override public void isDeleted(boolean success) { refresh(success); } }); } /** * Triggers the deletion of a list of versions. * * @param versions to delete */ private void deleteVersions(List versions) { if (versions == null || versions.isEmpty()) return; for (ImageVersionDetails version : versions) { deleteVersion(version); } } /** * 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 versions) { int daysToExtend = -1; if (versions.size() > 1) { // 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; } int count = 0; for (ImageVersionDetails img : versions) { long currentExpiryTime = img.expireTime < Util.unixTime() ? Util.unixTime() : img.expireTime; Date currentExpiryTimeAsDate = DateTimeHelper.endOfDay(new Date(currentExpiryTime * 1000l)); if (daysToExtend == -1) { daysToExtend = ExpiryDateChooser.askFutureExpiryDuration(this, currentExpiryTimeAsDate); if (daysToExtend == -1) return; } currentExpiryTimeAsDate = DateTimeHelper.addDaysTo(currentExpiryTimeAsDate, daysToExtend); try { ThriftManager.getSatClient().setImageVersionExpiry(Session.getSatelliteToken(), img.versionId, currentExpiryTimeAsDate.getTime() / 1000L); count++; } catch (TException e) { ThriftError.showMessage(this, LOGGER, e, "Konnte Ablaufdatum der Version " + img.versionId + " nicht verlängern."); } } if (count > 0) { Gui.showMessageBox(this, "Erfolgreich verlängerte Abbilder: " + count, MessageType.INFO, null, null); refresh(true); } } /******************************************************************************** * * General UI helpers * ********************************************************************************/ /** * @param forceRefresh */ private void refresh(boolean forceRefresh) { String baseId = image.getImageBaseId(); synchronized (me) { image = null; } setImage(baseId); } /** * callback function when we received the image's details from the server */ private void fillDetails() { if (image == null) return; txtTitle.setText(image.getImageName()); txtTitle.setCaretPosition(0); txtDescription.setText(image.getDescription()); lblOwner.setUser(UserCache.find(image.getOwnerId())); lblUpdater.setUser(UserCache.find(image.getUpdaterId())); lblCreateTime.setText(FormatHelper.longDate(image.getCreateTime())); lblUpdateTime.setText(FormatHelper.longDate(image.getUpdateTime())); txtVersion.setText(image.getLatestVersionId()); txtId.setText(image.getImageBaseId()); chkIsTemplate.setSelected(image.isTemplate); setTitle(image.getImageName()); // fill os combo, but only once :) if (cboOperatingSystem.getItemCount() == 0) { List osList = MetaDataCache.getOperatingSystems(); // all fine, lets sort it Collections.sort(osList, new Comparator() { public int compare(OperatingSystem o1, OperatingSystem o2) { return o1.getOsName().compareTo(o2.getOsName()); } }); for (OperatingSystem os : osList) { cboOperatingSystem.addItem(os); } cboOperatingSystem.setSelectedItem(new OperatingSystem(image.getOsId(), null, null, null, 0, 0)); } Virtualizer virt = MetaDataCache.getVirtualizerById(image.getVirtId(), true); if (virt != null) lblVirtualizer.setText(virt.getVirtName()); // fill share mode combo, if not already done if (cboShareMode.getItemCount() == 0) { for (ShareMode mode : ShareMode.values()) { cboShareMode.addItem(mode); } } cboShareMode.setSelectedItem(image.getShareMode()); String tagsString = ""; if (image.getTags() != null) { for (String tag : image.getTags()) { tagsString = tagsString + ", " + tag; } } txtTags.setText(tagsString); // 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); chkDefaultPermEdit.setSelected(image.defaultPermissions.edit); chkDefaultPermDownload.setSelected(image.defaultPermissions.download); chkDefaultPermLink.setSelected(image.defaultPermissions.link); // Count the number of linked lectures to the image int lectureCount = 0; for (LectureSummary lecture : LectureCache.get(false)) { if (lecture == null || lecture.imageBaseId == null) continue; if (lecture.imageBaseId.equals(image.imageBaseId)) lectureCount++; } lblLinkedLectureCount.setText(Integer.toString(lectureCount)); lblLinkedLectureCount.setForeground(lectureCount > 0 ? null : Color.RED); btnShowLinkingLectures.setEnabled(lectureCount > 0); // set the versions of the image to the table tblVersions.setData(image.getVersions(), true); // make fields editable is allowed makeEditable(true); changeMonitor.reset(); // finally do show it all setVisible(true); } /** * Enables/disables the editable fields based on 'editable' * * @param editable true to make fields editable, false otherwise. */ private void makeEditable(boolean editable) { // always disable all buttons unless image's share mode is LOCAL or PUBLISH if (image != null) editable &= image.shareMode == ShareMode.LOCAL || image.shareMode == ShareMode.PUBLISH; // now do the regular permission check 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); btnChangeOwner.setEnabled(editable && ImagePerms.canAdmin(image)); btnUpdateImage.setEnabled(editable); if (actionHandler.isImagePublishSupported()) btnUploadToMaster.setEnabled(editable); } /** * Opens a new ImageDetailsWindow showing the details of the image with ID = * imageBaseId * * @param modalParent parent of this window * @param imageBaseId id of the image to set the details of */ public static void open(Frame modalParent, String imageBaseId, ImageUpdatedCallback callback, ImageDetailsActions actionHandler) { ImageDetailsWindow win = new ImageDetailsWindow(modalParent, callback, actionHandler); win.setImage(imageBaseId); win.setVisible(true); } /* * ***************************************************************************** * ** * * Dialog class overrides * * ***************************************************************************** * ** */ @SuppressWarnings("deprecation") @Override public void show() { if (!isVisible()) { pack(); MainWindow.centerShell(this); } super.show(); } /* * ***************************************************************************** * ** * * UIFeedback implementation * * ***************************************************************************** * ** */ @Override public boolean wantConfirmQuit() { return changeMonitor.isCurrentlyModified(); } @Override public void escapePressed() { safeClose(); } /* * Safe close helper: checks if we have unsaved work and prompt the user for * confirmation if so */ private boolean safeClose() { if (changeMonitor.isCurrentlyModified() && !Gui.showMessageBox(me, "Änderungen werden verworfen, wollen Sie wirklich schließen?", MessageType.QUESTION_YESNO, null, null)) return false; dispose(); return true; } }