summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/window/ImageDetailsWindow.java
blob: f237e8bcad5941ab9b8915abb7e4863c1a716646 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                      
                      
                      

                                     

                                   
                               
                                  

                                   

                                    

                           
                           

                             
                      
                         
                      
                     
 
                                  
                          
                              
                          
                             
                               


                                      

                                             
                                        


                                           
 
                               
                                    
                                                    
                                                      
                                                      

                                                         
                                                    
                                                     
                                               
                                              
                                                 
                                   
                                  
                                         


                                                                                       
                                                    
                                                       
                                                 
                                               
                                                
                                                                      
                                                                     
                                                       
                                                   
                                                 
                                                     
                                         
                                               
                                                              
                                                                 
                                             
                                                    

                                                     
                                                  
                                            
                                              
                                            
                                       


                                              
                                   
                             
 


                                                             
                           
                                                                                        


                                                                                        


                         
                                                   
 
           

                                                                                  
           
                                                        
 


                                                                                


                                               
 


                            
                                                     
 


                                                      
                                              
 
           
                                                               
           
                                                                
 
           
                                                                     
           
                                                          
 


                           

                                                                                    
                                                                                
                                                                      
                                                                                                   
 


                                                                    
 



                                                         
                                                                                    
           

                                                                                   
                                   
                                         

                                                   














                                                                                                                      







                                                                     


                                   
                                                                 
                                 
                                                                    
                                            

                         
                                                                       
                                 
                                                                    
                                              

                         


                                                                       
                                                                                                            
                                                                                  
                                              

                         





                                                                          







                                                                                                              
 



                                 


                                                                       
                                                                                 


                                                                                                     



                                                                                                                               
                                                                            
                                         
 
                                                                        

                         
 

































                                                                                                                   

                                                                                                     


                         
                                                  



                                                                      

                                                                                          

                                                                      


                                                                                                     





                                                                       
                                        
                                                                                   

                                                                    





                                                                      


                                 


                                                                              





















                                                                                                                                                            
 
                                                                                             
 


                                                                     
                                                                          
                                                                    
                                                                                             

                                                     
                                                                          


                                                                                                                                                               
                                 
                                                                        
                                                                       
                                 

                                                                                       
                                 


                                                                        


                                                                                            
 

                         

                                               

                                                             
                                   
                                           
                                                     


                                                             
 
                                    

                                                                                              
                                                                               

                                                                     
                                                                
                                                                                       
                                 


                         


                                                      
                                                            

                                                                
                                                
                         
 

                                                                 
                                                
                         
 
                                                                 
                                                           
                                                                                         


                                                                                 
                                                                             





                                                                          
                                                                                                   


                                                                                          
                                                                                                                  
                                         
                                                                 
                                                                                                                
                                                                                                                    

                                                                                                                                         
                                                                                                                         
                                                                                                                 
                                                                                                                                              
                                                                                                

                                                                                                                          

                                                                                               
                                 
 

                         

                                                 
 












                                                                                                                                                                                
                                                               















                                                                                                                

                                                                   







                                                                                                                

                         



                                                                                         
 







                                                                
                                                                                      


                                                  
         
 


                                                                       
 


                                            

                                                                                                                                    
                                                        

                                                                                                                     
                                               




                                                                                 

                                                                                                                                               

                                                               
 
                                                                                                               
                                                                                            
                                                                                                                          
                                                                                                                                



                                                                                                                                  





                                         


                                                                                         
           
                                                                                          
           

                                                                                       
           
                                                                 
           
                                                        




                                                                            
                                                                                                                                   

                                                           
                                 
                                                          
                                                                      
                                 
 
                                                   
                                                             
                                                                        
 
                                 
                                              

                         
 
                                                                     

         
           




                                                                  
                                                                                 

                               

                                                                                                                            

                                    


           

                                                                            
           
                                       

                                                                                                                   





                                                                                                                             
                                                          





                                                                                                                      
                 
                                                

                                                                                                                                

                                               


                                                    
                                                        
                                     

                                                                                                                                  
                                                         



                                                                                                                                                        
                                               
                                 




                                                                                                                 
                                                                                           


                                                                                                                                                           

                                                                                                  

                                         

                         


           

                                                                                     














                                                                                             

                                                                                       
          


                                                                          
                                                                                  


                                                                                     
                                                                                                          
                                                       
                                             

                                                          
                 








                                                                                                                       
                                                                       
                                        
                                                                                                                                  
                                     
                 
 

                                                                     
                                             
                         
                 
                                      
                            
         
 






                                                                            
                     

                                                                                         
                                                                                 
                                        
                                                                                                                                    




                                     
           


                                                            

                                                                         
                                                      
                                                                                                                      

                               

                                                                                         
                                                                        

                                                                                                                       

         
           


                                                            

                                                                       
                                    
                               





                                                                                

         
           
                                                       
           
                                    








                                                                         
           

                                                                                        


                                                    
                                                                              

                                          

                                                                                                   
                                                                                             


                                               

                                                          

                                                                                                                     
                                                 
                                                                                                                        


                                                       
                                                                                                                  
                             
                                                                                                                              
                                                                                           

                                                

                                                                                                                           


                                
                                                                                                                              


                                      
 


                                                                                         
           

                                                                                          








                                                       
 
           

                                                                                 
                                    
                                  
                               
 
                                                       
                                             
                                                               

                                                                         

                                                                                    
                                                               
                                                      
                                                            
                                               
 










                                                                                            
                         
                                                                                                                         
                 
 
                                                                                             

                                                                   
 




                                                                   
                 
                                                                   
 
                                       



                                                                     

                                            
 

                                                                                          





                                                                                                                     


                                                                        

                                                                           


                                                                          

                                                                                         
                                                                    
 
                                                             
                                                               
 

                                                  
                                      
                                         
                                 
         
 
           




                                                                         




                                                                                                               


                                                                                                 


                                                  


                                                

                                                        
                                                     

                                                                                  

                                                               


           





                                                                                    

                                                                                                     
                                                                                                      
                                          
                                     
         
 


                                                                                        
           

                                 

                                                                                        
           








                                                     
 


                                                                                        
           

                                    

                                                                                        
           

                                          
                                                           



                                     

                            
 



                                                                                    
                                     

                                                                                                                                         
                                     
                          
                            
         
 
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<String, ImagePermissions> 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)));
			}
		});

		// change between html and Wysiwyg
		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);
				}
			}
		});
		
		// insertion of <br> in needed because textpane is not able to
		// to insert <br> when enter is pressed. 
		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);

		/**
		 * 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<OperatingSystem>() {
					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<ShareMode>() {
					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<String, ImagePermissions> 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<ImageVersionDetails> 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<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.
			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<OperatingSystem> osList = MetaDataCache.getOperatingSystems();
			// all fine, lets sort it
			Collections.sort(osList, new Comparator<OperatingSystem>() {
				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;
	}
}