Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.Designer.cs	(revision 3072)
@@ -0,0 +1,181 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class ConnectionSetupView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.updateAddressTextBox = new System.Windows.Forms.TextBox();
+      this.urlLabel = new System.Windows.Forms.Label();
+      this.userLabel = new System.Windows.Forms.Label();
+      this.userTextBox = new System.Windows.Forms.TextBox();
+      this.passwordLabel = new System.Windows.Forms.Label();
+      this.passwordTextBox = new System.Windows.Forms.TextBox();
+      this.applyButton = new System.Windows.Forms.Button();
+      this.saveCredentialsButton = new System.Windows.Forms.Button();
+      this.updateLocationGroupBox = new System.Windows.Forms.GroupBox();
+      this.label1 = new System.Windows.Forms.Label();
+      this.adminAddressTextBox = new System.Windows.Forms.TextBox();
+      this.updateLocationGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // updateAddressTextBox
+      // 
+      this.updateAddressTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.updateAddressTextBox.Location = new System.Drawing.Point(126, 19);
+      this.updateAddressTextBox.Name = "updateAddressTextBox";
+      this.updateAddressTextBox.Size = new System.Drawing.Size(212, 20);
+      this.updateAddressTextBox.TabIndex = 0;
+      // 
+      // urlLabel
+      // 
+      this.urlLabel.AutoSize = true;
+      this.urlLabel.Location = new System.Drawing.Point(36, 22);
+      this.urlLabel.Name = "urlLabel";
+      this.urlLabel.Size = new System.Drawing.Size(84, 13);
+      this.urlLabel.TabIndex = 1;
+      this.urlLabel.Text = "Update Service:";
+      // 
+      // userLabel
+      // 
+      this.userLabel.AutoSize = true;
+      this.userLabel.Location = new System.Drawing.Point(88, 74);
+      this.userLabel.Name = "userLabel";
+      this.userLabel.Size = new System.Drawing.Size(32, 13);
+      this.userLabel.TabIndex = 3;
+      this.userLabel.Text = "User:";
+      // 
+      // userTextBox
+      // 
+      this.userTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.userTextBox.Location = new System.Drawing.Point(126, 71);
+      this.userTextBox.Name = "userTextBox";
+      this.userTextBox.Size = new System.Drawing.Size(212, 20);
+      this.userTextBox.TabIndex = 2;
+      // 
+      // passwordLabel
+      // 
+      this.passwordLabel.AutoSize = true;
+      this.passwordLabel.Location = new System.Drawing.Point(64, 100);
+      this.passwordLabel.Name = "passwordLabel";
+      this.passwordLabel.Size = new System.Drawing.Size(56, 13);
+      this.passwordLabel.TabIndex = 5;
+      this.passwordLabel.Text = "Password:";
+      // 
+      // passwordTextBox
+      // 
+      this.passwordTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.passwordTextBox.Location = new System.Drawing.Point(126, 97);
+      this.passwordTextBox.Name = "passwordTextBox";
+      this.passwordTextBox.Size = new System.Drawing.Size(212, 20);
+      this.passwordTextBox.TabIndex = 4;
+      this.passwordTextBox.UseSystemPasswordChar = true;
+      // 
+      // applyButton
+      // 
+      this.applyButton.Location = new System.Drawing.Point(3, 133);
+      this.applyButton.Name = "applyButton";
+      this.applyButton.Size = new System.Drawing.Size(75, 23);
+      this.applyButton.TabIndex = 6;
+      this.applyButton.Text = "Apply";
+      this.applyButton.UseVisualStyleBackColor = true;
+      this.applyButton.Click += new System.EventHandler(this.applyButton_Click);
+      // 
+      // saveCredentialsButton
+      // 
+      this.saveCredentialsButton.Location = new System.Drawing.Point(84, 133);
+      this.saveCredentialsButton.Name = "saveCredentialsButton";
+      this.saveCredentialsButton.Size = new System.Drawing.Size(120, 23);
+      this.saveCredentialsButton.TabIndex = 8;
+      this.saveCredentialsButton.Text = "Store Credentials";
+      this.saveCredentialsButton.UseVisualStyleBackColor = true;
+      this.saveCredentialsButton.Click += new System.EventHandler(this.saveCredentialsButton_Click);
+      // 
+      // updateLocationGroupBox
+      // 
+      this.updateLocationGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.updateLocationGroupBox.Controls.Add(this.adminAddressTextBox);
+      this.updateLocationGroupBox.Controls.Add(this.label1);
+      this.updateLocationGroupBox.Controls.Add(this.passwordTextBox);
+      this.updateLocationGroupBox.Controls.Add(this.updateAddressTextBox);
+      this.updateLocationGroupBox.Controls.Add(this.urlLabel);
+      this.updateLocationGroupBox.Controls.Add(this.userTextBox);
+      this.updateLocationGroupBox.Controls.Add(this.passwordLabel);
+      this.updateLocationGroupBox.Controls.Add(this.userLabel);
+      this.updateLocationGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.updateLocationGroupBox.Name = "updateLocationGroupBox";
+      this.updateLocationGroupBox.Size = new System.Drawing.Size(344, 124);
+      this.updateLocationGroupBox.TabIndex = 10;
+      this.updateLocationGroupBox.TabStop = false;
+      this.updateLocationGroupBox.Text = "Update Location";
+      // 
+      // label1
+      // 
+      this.label1.AutoSize = true;
+      this.label1.Location = new System.Drawing.Point(6, 48);
+      this.label1.Name = "label1";
+      this.label1.Size = new System.Drawing.Size(114, 13);
+      this.label1.TabIndex = 7;
+      this.label1.Text = "Administration Service:";
+      // 
+      // adminAddressTextBox
+      // 
+      this.adminAddressTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.adminAddressTextBox.Location = new System.Drawing.Point(126, 45);
+      this.adminAddressTextBox.Name = "adminAddressTextBox";
+      this.adminAddressTextBox.Size = new System.Drawing.Size(212, 20);
+      this.adminAddressTextBox.TabIndex = 6;
+      // 
+      // ConnectionSetupView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.updateLocationGroupBox);
+      this.Controls.Add(this.saveCredentialsButton);
+      this.Controls.Add(this.applyButton);
+      this.Name = "ConnectionSetupView";
+      this.Size = new System.Drawing.Size(350, 161);
+      this.updateLocationGroupBox.ResumeLayout(false);
+      this.updateLocationGroupBox.PerformLayout();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.TextBox updateAddressTextBox;
+    private System.Windows.Forms.Label urlLabel;
+    private System.Windows.Forms.Label userLabel;
+    private System.Windows.Forms.TextBox userTextBox;
+    private System.Windows.Forms.Label passwordLabel;
+    private System.Windows.Forms.TextBox passwordTextBox;
+    private System.Windows.Forms.Button applyButton;
+    private System.Windows.Forms.Button saveCredentialsButton;
+    private System.Windows.Forms.GroupBox updateLocationGroupBox;
+    private System.Windows.Forms.TextBox adminAddressTextBox;
+    private System.Windows.Forms.Label label1;
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.cs	(revision 3072)
@@ -0,0 +1,37 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  internal partial class ConnectionSetupView : HeuristicLab.MainForm.WindowsForms.View {
+    public ConnectionSetupView() {
+      InitializeComponent();
+      Caption = "Edit Connection Settings";
+      updateAddressTextBox.Text = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocation;
+      adminAddressTextBox.Text = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationAdministrationAddress;
+      userTextBox.Text = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationUserName;
+      passwordTextBox.Text = HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationPassword;
+    }
+
+    private void applyButton_Click(object sender, EventArgs e) {
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocation = updateAddressTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationAdministrationAddress = adminAddressTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationUserName = userTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationPassword = passwordTextBox.Text;
+    }
+
+    private void saveCredentialsButton_Click(object sender, EventArgs e) {
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocation = updateAddressTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationAdministrationAddress = adminAddressTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationUserName = userTextBox.Text;
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocationPassword = passwordTextBox.Text;
+      // save
+      HeuristicLab.PluginInfrastructure.Properties.Settings.Default.Save();
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ConnectionSetupView.resx	(revision 3072)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionAction.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionAction.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionAction.cs	(revision 3072)
@@ -0,0 +1,14 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  static class EditConnectionAction {
+    internal static void Execute(IMainForm mainForm) {
+      ConnectionSetupView connectionSetup = new ConnectionSetupView();
+      connectionSetup.Show();
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionMenuItem.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionMenuItem.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/EditConnectionMenuItem.cs	(revision 3072)
@@ -0,0 +1,25 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  class EditConnectionMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IUserInterfaceItemProvider {
+    public override string Name {
+      get { return "Edit Connection Properties"; }
+    }
+
+    public override IEnumerable<string> Structure {
+      get { return new string[] { "Plugin Server" }; }
+    }
+
+    public override int Position {
+      get { return 1300; }
+    }
+
+    public override void Execute() {
+      EditConnectionAction.Execute(MainFormManager.MainForm);
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLab.PluginAdministrator.csproj
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLab.PluginAdministrator.csproj	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLab.PluginAdministrator.csproj	(revision 3072)
@@ -0,0 +1,196 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{C458C60D-33CE-42B8-8B7A-651B698A3C96}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.DeploymentService.AdminClient</RootNamespace>
+    <AssemblyName>HeuristicLab.DeploymentService.AdminClient-3.3</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x86\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <OutputPath>bin\x86\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ICSharpCode.SharpZipLib, Version=0.85.4.369, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\HeuristicLab.PluginInfrastructure\ICSharpCode.SharpZipLib.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Runtime.Serialization">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.ServiceModel">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data.DataSetExtensions">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ConnectionSetupView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ConnectionSetupView.Designer.cs">
+      <DependentUpon>ConnectionSetupView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="EditConnectionAction.cs" />
+    <Compile Include="EditConnectionMenuItem.cs" />
+    <Compile Include="LicenseView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="LicenseView.Designer.cs">
+      <DependentUpon>LicenseView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="MultiSelectListView.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="MultiSelectListView.Designer.cs">
+      <DependentUpon>MultiSelectListView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PluginComparisonView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PluginComparisonView.Designer.cs">
+      <DependentUpon>PluginComparisonView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PluginEditor.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PluginEditor.Designer.cs">
+      <DependentUpon>PluginEditor.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PluginListView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PluginListView.Designer.cs">
+      <DependentUpon>PluginListView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="PluginView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="PluginView.Designer.cs">
+      <DependentUpon>PluginView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="ProductEditor.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="ProductEditor.Designer.cs">
+      <DependentUpon>ProductEditor.cs</DependentUpon>
+    </Compile>
+    <Compile Include="OpenProductEditorAction.cs" />
+    <Compile Include="OpenServerProductsEditMenuItem.cs" />
+    <Compile Include="HeuristicLabDeploymentService.AdminClientApplication.cs" />
+    <Compile Include="HeuristicLabDeploymentService.AdminClientPlugin.cs" />
+    <Compile Include="IUserInterfaceItemProvider.cs" />
+    <Compile Include="MainForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HeuristicLab.Common.Resources\3.2\HeuristicLab.Common.Resources-3.2.csproj">
+      <Project>{0E27A536-1C4A-4624-A65E-DC4F4F23E3E1}</Project>
+      <Name>HeuristicLab.Common.Resources-3.2</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HeuristicLab.MainForm.WindowsForms\3.2\HeuristicLab.MainForm.WindowsForms-3.2.csproj">
+      <Project>{AB687BBE-1BFE-476B-906D-44237135431D}</Project>
+      <Name>HeuristicLab.MainForm.WindowsForms-3.2</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HeuristicLab.MainForm\3.2\HeuristicLab.MainForm-3.2.csproj">
+      <Project>{3BD61258-31DA-4B09-89C0-4F71FEF5F05A}</Project>
+      <Name>HeuristicLab.MainForm-3.2</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\HeuristicLab.PluginInfrastructure\HeuristicLab.PluginInfrastructure.csproj">
+      <Project>{94186A6A-5176-4402-AE83-886557B53CCA}</Project>
+      <Name>HeuristicLab.PluginInfrastructure</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLabDeploymentServiceAdminClientPlugin.cs.frame" />
+    <None Include="Properties\AssemblyInfo.frame" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="ConnectionSetupView.resx">
+      <DependentUpon>ConnectionSetupView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="LicenseView.resx">
+      <DependentUpon>LicenseView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="MainForm.resx">
+      <DependentUpon>MainForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="PluginComparisonView.resx">
+      <DependentUpon>PluginComparisonView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="PluginEditor.resx">
+      <DependentUpon>PluginEditor.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="PluginListView.resx">
+      <DependentUpon>PluginListView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="PluginView.resx">
+      <DependentUpon>PluginView.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="ProductEditor.resx">
+      <DependentUpon>ProductEditor.cs</DependentUpon>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\DataSources\HeuristicLab.PluginInfrastructure.PluginUpdateService.PluginDescription.datasource" />
+    <None Include="Properties\DataSources\HeuristicLab.PluginInfrastructure.PluginUpdateService.ProductDescription.datasource" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorApplication.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorApplication.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorApplication.cs	(revision 3072)
@@ -0,0 +1,38 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using HeuristicLab.PluginInfrastructure;
+using System.Windows.Forms;
+using HeuristicLab.MainForm.WindowsForms;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  [Application("Deployment service administration client")]
+  public class HeuristicLabDeploymentServiceAdminClientApplication : ApplicationBase {
+    public override void Run() {
+      MainForm mainForm = new MainForm(typeof(IUserInterfaceItemProvider));
+      mainForm.Title = "Deployment Service Administration Client";
+      Application.Run(mainForm);
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs	(revision 3072)
@@ -0,0 +1,32 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  [Plugin("HeuristicLab.DeploymentService.AdminClient", "3.3.0.9999")]
+  [PluginFile("HeuristicLab.DeploymentService.AdminClient-3.3.dll", PluginFileType.Assembly)]
+  public class HeuristicLabDeploymentServiceAdminClientPlugin : PluginBase {
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs.frame
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs.frame	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/HeuristicLabPluginAdministratorPlugin.cs.frame	(revision 3072)
@@ -0,0 +1,32 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  [Plugin("HeuristicLab.DeploymentService.AdminClient", "3.3.0.$WCREV$")]
+  [PluginFile("HeuristicLab.DeploymentService.AdminClient-3.3.dll", PluginFileType.Assembly)]
+  public class HeuristicLabDeploymentServiceAdminClientPlugin : PluginBase {
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/IUserInterfaceItemProvider.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/IUserInterfaceItemProvider.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/IUserInterfaceItemProvider.cs	(revision 3072)
@@ -0,0 +1,9 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  interface IUserInterfaceItemProvider {
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.Designer.cs	(revision 3072)
@@ -0,0 +1,57 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class LicenseView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.textBox = new System.Windows.Forms.TextBox();
+      this.SuspendLayout();
+      // 
+      // textBox
+      // 
+      this.textBox.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.textBox.Location = new System.Drawing.Point(0, 0);
+      this.textBox.Multiline = true;
+      this.textBox.Name = "textBox";
+      this.textBox.ReadOnly = true;
+      this.textBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+      this.textBox.Size = new System.Drawing.Size(426, 481);
+      this.textBox.TabIndex = 0;
+      // 
+      // LicenseView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.textBox);
+      this.Name = "LicenseView";
+      this.Size = new System.Drawing.Size(426, 481);
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.TextBox textBox;
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.cs	(revision 3072)
@@ -0,0 +1,25 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class LicenseView : HeuristicLab.MainForm.WindowsForms.View {
+
+    public LicenseView() {
+      InitializeComponent();
+      Caption = "License";
+    }
+
+    public LicenseView(IPluginDescription plugin) {
+      InitializeComponent();
+      Caption = "License of: " + plugin;
+      textBox.Text = plugin.LicenseText;
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/LicenseView.resx	(revision 3072)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.cs	(revision 3072)
@@ -0,0 +1,47 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.MainForm.WindowsForms;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  class MainForm : DockingMainForm {
+    private System.Windows.Forms.ToolStripProgressBar progressBar;
+
+    public MainForm(Type type)
+      : base(type) {
+      InitializeComponent();
+    }
+
+    protected override void OnInitialized(EventArgs e) {
+      base.OnInitialized(e);
+      (new PluginEditor()).Show();
+    }
+
+    private void InitializeComponent() {
+      this.SuspendLayout();
+
+      progressBar = new System.Windows.Forms.ToolStripProgressBar();
+      progressBar.MarqueeAnimationSpeed = 30;
+      progressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
+      progressBar.Visible = false;
+      statusStrip.Items.Add(progressBar);
+      // 
+      // MainForm
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.ClientSize = new System.Drawing.Size(770, 550);
+      this.Name = "MainForm";
+      this.ResumeLayout(false);
+      this.PerformLayout();
+    }
+
+    public void ShowProgressBar() {
+      progressBar.Visible = true;
+    }
+
+    public void HideProgressBar() {
+      progressBar.Visible = false;
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MainForm.resx	(revision 3072)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.Designer.cs	(revision 3072)
@@ -0,0 +1,31 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class MultiSelectListView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      components = new System.ComponentModel.Container();
+    }
+
+    #endregion
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/MultiSelectListView.cs	(revision 3072)
@@ -0,0 +1,78 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class MultiSelectListView : ListView {
+    public MultiSelectListView() {
+      InitializeComponent();
+    }
+
+    private bool inhibitAutoCheck;
+    protected override void OnMouseDown(MouseEventArgs e) {
+      inhibitAutoCheck = true;
+      base.OnMouseDown(e);
+    }
+
+    protected override void OnMouseUp(MouseEventArgs e) {
+      base.OnMouseUp(e);
+      inhibitAutoCheck = false;
+    }
+
+    // item check is raised for each selected item that was not directly clicked (those should be ignored)
+    // and then one the item whose checkbox was clicked
+    protected override void OnItemCheck(ItemCheckEventArgs ice) {
+      // don't change the checked state of items that were not clicked directly
+      if (inhibitAutoCheck) {
+        ice.NewValue = ice.CurrentValue;
+      }
+      // if this item was directly clicked then check if it is selected as well
+      var item = Items[ice.Index];
+      if (!item.Selected) {
+        // when the item was not selected previously 
+        // clear any selection 
+        List<ListViewItem> selectedItems = new List<ListViewItem>(SelectedItems.OfType<ListViewItem>());
+        selectedItems.ForEach(x => x.Selected = false);
+        // select only the clicked item
+        item.Selected = true;
+      }
+      base.OnItemCheck(ice);
+    }
+
+    private bool suppressItemCheckedEvents;
+    public bool SuppressItemCheckedEvents {
+      get {
+        return suppressItemCheckedEvents;
+      }
+      set {
+        suppressItemCheckedEvents = value;
+      }
+    }
+    protected override void OnItemChecked(ItemCheckedEventArgs e) {
+      if (!suppressItemCheckedEvents)
+        base.OnItemChecked(e);
+    }
+
+    public void CheckItems(IEnumerable<ListViewItem> items) {
+      suppressItemCheckedEvents = true;
+
+      foreach (var item in items)
+        item.Checked = true;
+      suppressItemCheckedEvents = false;
+    }
+
+    public void UncheckItems(IEnumerable<ListViewItem> items) {
+      suppressItemCheckedEvents = true;
+
+      foreach (var item in items)
+        item.Checked = false;
+      suppressItemCheckedEvents = false;
+    }
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenProductEditorAction.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenProductEditorAction.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenProductEditorAction.cs	(revision 3072)
@@ -0,0 +1,14 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  static class OpenProductEditorAction {
+    internal static void Execute(IMainForm mainForm) {
+      ProductEditor editor = new ProductEditor();
+      editor.Show();
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenServerProductsEditMenuItem.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenServerProductsEditMenuItem.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/OpenServerProductsEditMenuItem.cs	(revision 3072)
@@ -0,0 +1,25 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  class OpenServerProductsEditorMenuItem : HeuristicLab.MainForm.WindowsForms.MenuItem, IUserInterfaceItemProvider {
+    public override string Name {
+      get { return "Edit Products"; }
+    }
+
+    public override IEnumerable<string> Structure {
+      get { return new string[] { "Plugin Server" }; }
+    }
+
+    public override int Position {
+      get { return 1300; }
+    }
+
+    public override void Execute() {
+      OpenProductEditorAction.Execute(MainFormManager.MainForm);
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.Designer.cs	(revision 3072)
@@ -0,0 +1,58 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class PluginComparisonView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
+      this.SuspendLayout();
+      // 
+      // tableLayoutPanel
+      // 
+      this.tableLayoutPanel.ColumnCount = 2;
+      this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
+      this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
+      this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.tableLayoutPanel.Location = new System.Drawing.Point(0, 0);
+      this.tableLayoutPanel.Name = "tableLayoutPanel";
+      this.tableLayoutPanel.RowCount = 1;
+      this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+      this.tableLayoutPanel.Size = new System.Drawing.Size(539, 505);
+      this.tableLayoutPanel.TabIndex = 0;
+      // 
+      // PluginComparisonView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.tableLayoutPanel);
+      this.Name = "PluginComparisonView";
+      this.Size = new System.Drawing.Size(539, 505);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.TableLayoutPanel tableLayoutPanel;
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.cs	(revision 3072)
@@ -0,0 +1,40 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.PluginInfrastructure;
+using PluginDeploymentService = HeuristicLab.PluginInfrastructure.Advanced.DeploymentService;
+using HeuristicLab.PluginInfrastructure.Manager;
+using System.ServiceModel;
+using ICSharpCode.SharpZipLib.Zip;
+using System.IO;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class PluginComparisonView : HeuristicLab.MainForm.WindowsForms.View {
+
+    public PluginComparisonView(IPluginDescription localPlugin, IPluginDescription serverPlugin) {
+      InitializeComponent();
+      Caption = "Compare plugins";
+
+      var localPluginView = new PluginView(localPlugin);
+      localPluginView.Dock = DockStyle.Fill;
+      tableLayoutPanel.Controls.Add(localPluginView, 0, 0);
+      if (serverPlugin != null) {
+        var serverPluginView = new PluginView(serverPlugin);
+        serverPluginView.Dock = DockStyle.Fill;
+        tableLayoutPanel.Controls.Add(serverPluginView, 1, 0);
+      } else {
+        Label emptyLabel = new Label();
+        emptyLabel.Text = "No matching server plugin";
+        emptyLabel.TextAlign = ContentAlignment.MiddleCenter;
+        emptyLabel.Dock = DockStyle.Fill;
+        tableLayoutPanel.Controls.Add(emptyLabel, 1, 0);
+      }
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginComparisonView.resx	(revision 3072)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.Designer.cs	(revision 3072)
@@ -0,0 +1,130 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class PluginEditor {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.uploadButton = new System.Windows.Forms.Button();
+      this.listView = new HeuristicLab.DeploymentService.AdminClient.MultiSelectListView();
+      this.pluginNameHeader = new System.Windows.Forms.ColumnHeader();
+      this.localVersionHeader = new System.Windows.Forms.ColumnHeader();
+      this.serverVersionHeader = new System.Windows.Forms.ColumnHeader();
+      this.descriptionHeader = new System.Windows.Forms.ColumnHeader();
+      this.SuspendLayout();
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(75, 23);
+      this.refreshButton.TabIndex = 6;
+      this.refreshButton.Text = "Refresh";
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // uploadButton
+      // 
+      this.uploadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.uploadButton.Location = new System.Drawing.Point(3, 482);
+      this.uploadButton.Name = "uploadButton";
+      this.uploadButton.Size = new System.Drawing.Size(75, 23);
+      this.uploadButton.TabIndex = 7;
+      this.uploadButton.Text = "Upload";
+      this.uploadButton.UseVisualStyleBackColor = true;
+      this.uploadButton.Click += new System.EventHandler(this.uploadButton_Click);
+      // 
+      // listView
+      // 
+      this.listView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.listView.CheckBoxes = true;
+      this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.pluginNameHeader,
+            this.localVersionHeader,
+            this.serverVersionHeader,
+            this.descriptionHeader});
+      this.listView.Location = new System.Drawing.Point(3, 32);
+      this.listView.Name = "listView";
+      this.listView.Size = new System.Drawing.Size(536, 444);
+      this.listView.StateImageList = this.imageList;
+      this.listView.SuppressItemCheckedEvents = false;
+      this.listView.TabIndex = 8;
+      this.listView.UseCompatibleStateImageBehavior = false;
+      this.listView.View = System.Windows.Forms.View.Details;
+      this.listView.ItemActivate += new System.EventHandler(this.listView_ItemActivate);
+      this.listView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.listView_ItemChecked);
+      // 
+      // pluginNameHeader
+      // 
+      this.pluginNameHeader.Text = "Name";
+      this.pluginNameHeader.Width = 161;
+      // 
+      // localVersionHeader
+      // 
+      this.localVersionHeader.Text = "Local Version";
+      this.localVersionHeader.Width = 85;
+      // 
+      // serverVersionHeader
+      // 
+      this.serverVersionHeader.Text = "Server Version";
+      this.serverVersionHeader.Width = 91;
+      // 
+      // descriptionHeader
+      // 
+      this.descriptionHeader.Text = "Description";
+      this.descriptionHeader.Width = 194;
+      // 
+      // PluginEditor
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.listView);
+      this.Controls.Add(this.uploadButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "PluginEditor";
+      this.Size = new System.Drawing.Size(539, 508);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button uploadButton;
+    private System.Windows.Forms.ImageList imageList;
+    private MultiSelectListView listView;
+    private System.Windows.Forms.ColumnHeader pluginNameHeader;
+    private System.Windows.Forms.ColumnHeader localVersionHeader;
+    private System.Windows.Forms.ColumnHeader serverVersionHeader;
+    private System.Windows.Forms.ColumnHeader descriptionHeader;
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.cs	(revision 3072)
@@ -0,0 +1,292 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using HeuristicLab.PluginInfrastructure;
+using PluginDeploymentService = HeuristicLab.PluginInfrastructure.Advanced.DeploymentService;
+using HeuristicLab.PluginInfrastructure.Manager;
+using System.ServiceModel;
+using ICSharpCode.SharpZipLib.Zip;
+using System.IO;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class PluginEditor : HeuristicLab.MainForm.WindowsForms.View {
+    private Dictionary<IPluginDescription, PluginDeploymentService.PluginDescription> localAndServerPlugins;
+    private BackgroundWorker pluginUploadWorker;
+    private BackgroundWorker updateServerPluginsWorker;
+
+    public PluginEditor() {
+      InitializeComponent();
+      imageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.Assembly);
+      imageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.ArrowUp);
+      Caption = "Plugins";
+
+      localAndServerPlugins = new Dictionary<IPluginDescription, PluginDeploymentService.PluginDescription>();
+
+      #region initialize backgroundworkers
+      pluginUploadWorker = new BackgroundWorker();
+      pluginUploadWorker.DoWork += new DoWorkEventHandler(pluginUploadWorker_DoWork);
+      pluginUploadWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(pluginUploadWorker_RunWorkerCompleted);
+
+      updateServerPluginsWorker = new BackgroundWorker();
+      updateServerPluginsWorker.DoWork += new DoWorkEventHandler(updateServerPluginsWorker_DoWork);
+      updateServerPluginsWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateServerPluginsWorker_RunWorkerCompleted);
+      #endregion
+    }
+
+    #region refresh plugins from server backgroundworker
+    void updateServerPluginsWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+      if (!e.Cancelled && e.Result != null) {
+        // refresh local plugins
+        localAndServerPlugins.Clear();
+        foreach (var plugin in ApplicationManager.Manager.Plugins) {
+          localAndServerPlugins.Add(plugin, null);
+        }
+        // refresh server plugins (find matching local plugins)
+        var plugins = (PluginDeploymentService.PluginDescription[])e.Result;
+        foreach (var plugin in plugins) {
+          var matchingLocalPlugin = (from localPlugin in localAndServerPlugins.Keys
+                                     where localPlugin.Name == plugin.Name
+                                     where localPlugin.Version == localPlugin.Version
+                                     select localPlugin).SingleOrDefault();
+          if (matchingLocalPlugin != null) {
+            localAndServerPlugins[matchingLocalPlugin] = plugin;
+          }
+        }
+        // refresh the list view with plugins
+        listView.Items.Clear();
+        listView.CheckBoxes = false;
+        //suppressCheckedEvents = true;
+        foreach (var pair in localAndServerPlugins) {
+          var item = MakeListViewItem(pair.Key);
+          listView.Items.Add(item);
+        }
+        //listView.suppressCheckedEvents = false;
+        listView.CheckBoxes = true;
+        UpdateControlsConnectedState();
+      } else {
+        UpdateControlsDisconnectedState();
+      }
+      // make sure cursor is set correctly
+      Cursor = Cursors.Default;
+    }
+
+    void updateServerPluginsWorker_DoWork(object sender, DoWorkEventArgs e) {
+      try {
+        var client = PluginDeploymentService.UpdateClientFactory.CreateClient();
+        e.Result = client.GetPlugins();
+        e.Cancel = false;
+      }
+      catch (EndpointNotFoundException) {
+        e.Result = null;
+        e.Cancel = true;
+      }
+      catch (FaultException) {
+        e.Result = null;
+        e.Cancel = true;
+      }
+    }
+    #endregion
+
+    #region upload plugins to server backgroundworker
+    void pluginUploadWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+      Cursor = Cursors.Default;
+      if (e.Cancelled) {
+        UpdateControlsDisconnectedState();
+      } else {
+        UpdateControlsConnectedState();
+        // start another async call to refresh plugin information from server
+        RefreshPluginsAsync();
+      }
+    }
+
+    void pluginUploadWorker_DoWork(object sender, DoWorkEventArgs e) {
+      try {
+        var selectedPlugins = (IEnumerable<IPluginDescription>)e.Argument;
+        PluginDeploymentService.AdminClient adminClient = PluginDeploymentService.AdminClientFactory.CreateClient();
+
+        foreach (var plugin in IteratePlugins(selectedPlugins)) {
+          adminClient.DeployPlugin(MakePluginDescription(plugin), CreateZipPackage(plugin));
+        }
+        e.Cancel = false;
+      }
+      catch (EndpointNotFoundException) {
+        e.Cancel = true;
+      }
+      catch (FaultException) {
+        e.Cancel = true;
+      }
+    }
+    #endregion
+
+
+    #region button events
+    private void uploadButton_Click(object sender, EventArgs e) {
+      var selectedPlugins = from item in listView.Items.Cast<ListViewItem>()
+                            where item.Checked
+                            where item.Tag is IPluginDescription
+                            select item.Tag as IPluginDescription;
+      if (selectedPlugins.Count() > 0) {
+        Cursor = Cursors.AppStarting;
+        DisableControl();
+        pluginUploadWorker.RunWorkerAsync(selectedPlugins.ToList());
+      }
+    }
+
+    private void connectButton_Click(object sender, EventArgs e) {
+      var connectionSetupView = new ConnectionSetupView();
+      connectionSetupView.Show();
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      DisableControl();
+      RefreshPluginsAsync();
+    }
+
+    #endregion
+
+    #region item list events
+    private void listView_ItemActivate(object sender, EventArgs e) {
+      foreach (var item in listView.SelectedItems) {
+        var plugin = (IPluginDescription)((ListViewItem)item).Tag;
+        var compView = new PluginComparisonView(plugin, localAndServerPlugins[plugin]);
+        compView.Show();
+      }
+    }
+
+    private void listView_ItemChecked(object sender, ItemCheckedEventArgs e) {
+      List<IPluginDescription> modifiedPlugins = new List<IPluginDescription>();
+      if (e.Item.Checked) {
+        foreach (ListViewItem item in listView.SelectedItems) {
+          var plugin = (IPluginDescription)item.Tag;
+          // also check all dependencies
+          modifiedPlugins.Add(plugin);
+          foreach (var dep in GetAllDependencies(plugin)) {
+            modifiedPlugins.Add(dep);
+          }
+        }
+        listView.CheckItems(modifiedPlugins.Select(x => FindItemForPlugin(x)));
+      } else {
+        foreach (ListViewItem item in listView.SelectedItems) {
+          var plugin = (IPluginDescription)item.Tag;
+          // also uncheck all dependent plugins
+          modifiedPlugins.Add(plugin);
+          foreach (var dep in GetAllDependents(plugin)) {
+            modifiedPlugins.Add(dep);
+          }
+        }
+        listView.UncheckItems(modifiedPlugins.Select(x => FindItemForPlugin(x)));
+      }
+      uploadButton.Enabled = (from i in listView.Items.OfType<ListViewItem>()
+                              where i.Checked
+                              select i).Any();
+    }
+    #endregion
+
+    #region helper methods
+    private IEnumerable<IPluginDescription> GetAllDependents(IPluginDescription plugin) {
+      return from p in localAndServerPlugins.Keys
+             let matchingEntries = from dep in GetAllDependencies(p)
+                                   where dep.Name == plugin.Name
+                                   where dep.Version == plugin.Version
+                                   select dep
+             where matchingEntries.Any()
+             select p;
+    }
+
+    private IEnumerable<IPluginDescription> GetAllDependencies(IPluginDescription plugin) {
+      foreach (var dep in plugin.Dependencies) {
+        foreach (var recDep in GetAllDependencies(dep)) {
+          yield return recDep;
+        }
+        yield return dep;
+      }
+    }
+
+    private IEnumerable<IPluginDescription> IteratePlugins(IEnumerable<IPluginDescription> plugins) {
+      foreach (var plugin in plugins) {
+        foreach (var dependency in IteratePlugins(plugin.Dependencies)) {
+          yield return dependency;
+        }
+        yield return plugin;
+      }
+    }
+
+    private byte[] CreateZipPackage(IPluginDescription plugin) {
+      using (MemoryStream stream = new MemoryStream()) {
+        ZipFile zipFile = new ZipFile(stream);
+        zipFile.BeginUpdate();
+        foreach (var file in plugin.Files) {
+          zipFile.Add(file.Name);
+        }
+        zipFile.CommitUpdate();
+        stream.Seek(0, SeekOrigin.Begin);
+        return stream.GetBuffer();
+      }
+    }
+
+    private ListViewItem MakeListViewItem(IPluginDescription plugin) {
+      ListViewItem item;
+      if (localAndServerPlugins[plugin] != null) {
+        item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), 
+          localAndServerPlugins[plugin].Version.ToString(), localAndServerPlugins[plugin].Description });
+      } else {
+        item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), 
+          string.Empty, plugin.Description });
+      }
+      item.Tag = plugin;
+      item.Checked = false;
+      return item;
+    }
+
+    private ListViewItem FindItemForPlugin(IPluginDescription dep) {
+      return (from i in listView.Items.Cast<ListViewItem>()
+              where i.Tag == dep
+              select i).Single();
+    }
+
+    private PluginDeploymentService.PluginDescription MakePluginDescription(IPluginDescription plugin) {
+      var dependencies = from dep in plugin.Dependencies
+                         select MakePluginDescription(dep);
+      return new PluginDeploymentService.PluginDescription(plugin.Name, plugin.Version, dependencies, plugin.ContactName, plugin.ContactEmail, plugin.LicenseText);
+    }
+
+    // start background process to refresh the plugin list (local and server)
+    private void RefreshPluginsAsync() {
+      Cursor = Cursors.AppStarting;
+      DisableControl();
+      updateServerPluginsWorker.RunWorkerAsync();
+    }
+
+    // is called by all methods that start a background process
+    // controls must be enabled manuall again when the backgroundworker finishes
+    private void DisableControl() {
+      MainFormManager.GetMainForm<MainForm>().ShowProgressBar();
+      foreach (Control ctrl in Controls)
+        ctrl.Enabled = false;
+    }
+
+    private void UpdateControlsDisconnectedState() {
+      refreshButton.Enabled = false;
+
+      localAndServerPlugins.Clear();
+      listView.Items.Clear();
+      listView.Enabled = false;
+      uploadButton.Enabled = false;
+      MainFormManager.GetMainForm<MainForm>().HideProgressBar();
+    }
+
+    private void UpdateControlsConnectedState() {
+      refreshButton.Enabled = true;
+      listView.Enabled = true;
+      uploadButton.Enabled = false;
+      MainFormManager.GetMainForm<MainForm>().HideProgressBar();
+    }
+    #endregion
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginEditor.resx	(revision 3072)
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="imageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.Designer.cs	(revision 3072)
@@ -0,0 +1,77 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient
+{
+	partial class PluginListView
+	{
+		/// <summary> 
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.IContainer components = null;
+
+		/// <summary> 
+		/// Clean up any resources being used.
+		/// </summary>
+		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+		protected override void Dispose(bool disposing)
+		{
+			if (disposing && (components != null))
+			{
+				components.Dispose();
+			}
+			base.Dispose(disposing);
+		}
+
+		#region Component Designer generated code
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+      this.listView = new HeuristicLab.DeploymentService.AdminClient.MultiSelectListView();
+      this.nameHeader = new System.Windows.Forms.ColumnHeader();
+      this.versionHeader = new System.Windows.Forms.ColumnHeader();
+      this.SuspendLayout();
+      // 
+      // listView
+      // 
+      this.listView.CheckBoxes = true;
+      this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.nameHeader,
+            this.versionHeader});
+      this.listView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.listView.Location = new System.Drawing.Point(0, 0);
+      this.listView.Name = "listView";
+      this.listView.Size = new System.Drawing.Size(341, 320);
+      this.listView.TabIndex = 1;
+      this.listView.UseCompatibleStateImageBehavior = false;
+      this.listView.View = System.Windows.Forms.View.Details;
+      this.listView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.listView_ItemChecked);
+      // 
+      // nameHeader
+      // 
+      this.nameHeader.Text = "Name";
+      // 
+      // versionHeader
+      // 
+      this.versionHeader.Text = "Version";
+      // 
+      // PluginListView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.listView);
+      this.Name = "PluginListView";
+      this.Size = new System.Drawing.Size(341, 320);
+      this.ResumeLayout(false);
+
+		}
+
+		#endregion
+
+    private MultiSelectListView listView;
+    private System.Windows.Forms.ColumnHeader nameHeader;
+    private System.Windows.Forms.ColumnHeader versionHeader;
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.cs	(revision 3072)
@@ -0,0 +1,141 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  /// <summary>
+  /// Wraps a ListView and adds functionality to automatically check and uncheck dependencies of plugins.
+  /// </summary>
+  public partial class PluginListView : UserControl {
+    public event ItemCheckedEventHandler ItemChecked;
+
+    private IEnumerable<IPluginDescription> plugins;
+    public IEnumerable<IPluginDescription> Plugins {
+      get { return plugins; }
+      set {
+        plugins = value;
+        checkedPlugins.Clear();
+        UpdateControls();
+      }
+    }
+
+    private List<IPluginDescription> checkedPlugins = new List<IPluginDescription>();
+    public IEnumerable<IPluginDescription> CheckedPlugins {
+      get {
+        return checkedPlugins;
+      }
+    }
+
+    public PluginListView() {
+      InitializeComponent();
+    }
+
+    public void CheckPlugin(IPluginDescription plugin) {
+      MarkPluginChecked(plugin);
+      listView.CheckItems(new IPluginDescription[] { plugin }.Select(x => FindItemsForPlugin(x).Single()));
+    }
+
+
+    private void UpdateControls() {
+      if (plugins != null) {
+        listView.Items.Clear();
+        listView.SuppressItemCheckedEvents = true;
+        foreach (var plugin in plugins) {
+          listView.Items.Add(CreateListViewItem(plugin));
+        }
+        listView.SuppressItemCheckedEvents = false;
+      }
+    }
+
+    private ListViewItem CreateListViewItem(IPluginDescription plugin) {
+      var item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString() });
+      item.Checked = (from p in checkedPlugins where p.Name == plugin.Name where p.Version == plugin.Version select p).Any();
+      item.Tag = plugin;
+      return item;
+    }
+
+    private void listView_ItemChecked(object sender, ItemCheckedEventArgs e) {
+      List<IPluginDescription> modifiedPlugins = new List<IPluginDescription>();
+      if (e.Item.Checked) {
+        foreach (ListViewItem item in listView.SelectedItems) {
+          var plugin = (IPluginDescription)item.Tag;
+          // also check all dependencies
+          MarkPluginChecked(plugin);
+          modifiedPlugins.Add(plugin);
+          foreach (var dep in GetAllDependencies(plugin)) {
+            MarkPluginChecked(dep);
+            modifiedPlugins.Add(dep);
+          }
+        }
+        listView.CheckItems(modifiedPlugins.Select(x => FindItemsForPlugin(x).Single()));
+        OnItemChecked(e);
+      } else {
+        foreach (ListViewItem item in listView.SelectedItems) {
+          var plugin = (IPluginDescription)item.Tag;
+          // also uncheck all dependent plugins
+          MarkPluginUnchecked(plugin);
+          modifiedPlugins.Add(plugin);
+          foreach (var dep in GetAllDependents(plugin)) {
+            MarkPluginUnchecked(dep);
+            modifiedPlugins.Add(dep);
+          }
+
+        }
+        listView.UncheckItems(modifiedPlugins.Select(x => FindItemsForPlugin(x).Single()));
+        OnItemChecked(e);
+      }
+    }
+
+    private void MarkPluginChecked(IPluginDescription plugin) {
+      var matching = from p in checkedPlugins
+                     where p.Name == plugin.Name
+                     where p.Version == plugin.Version
+                     select p;
+      if (!matching.Any()) checkedPlugins.Add(plugin);
+    }
+
+    private void MarkPluginUnchecked(IPluginDescription plugin) {
+      checkedPlugins = (from p in checkedPlugins
+                        where p.Name != plugin.Name ||
+                              p.Version != plugin.Version
+                        select p).ToList();
+    }
+
+    private IEnumerable<ListViewItem> FindItemsForPlugin(IPluginDescription plugin) {
+      return from item in listView.Items.OfType<ListViewItem>()
+             let p = item.Tag as IPluginDescription
+             where p.Name == plugin.Name
+             where p.Version == plugin.Version
+             select item;
+    }
+
+    private IEnumerable<IPluginDescription> GetAllDependents(IPluginDescription plugin) {
+      return from p in plugins
+             let matchingEntries = from dep in GetAllDependencies(p)
+                                   where dep.Name == plugin.Name
+                                   where dep.Version == plugin.Version
+                                   select dep
+             where matchingEntries.Any()
+             select p;
+    }
+
+    private IEnumerable<IPluginDescription> GetAllDependencies(IPluginDescription plugin) {
+      foreach (var dep in plugin.Dependencies) {
+        foreach (var recDep in GetAllDependencies(dep)) {
+          yield return recDep;
+        }
+        yield return dep;
+      }
+    }
+
+    private void OnItemChecked(ItemCheckedEventArgs e) {
+      if (ItemChecked != null) ItemChecked(this, e);
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginListView.resx	(revision 3072)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.Designer.cs	(revision 3072)
@@ -0,0 +1,237 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class PluginView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.versionTextBox = new System.Windows.Forms.TextBox();
+      this.versionLabel = new System.Windows.Forms.Label();
+      this.contactTextBox = new System.Windows.Forms.TextBox();
+      this.contactInfoLabel = new System.Windows.Forms.Label();
+      this.licenseButton = new System.Windows.Forms.Button();
+      this.dependenciesGroupBox = new System.Windows.Forms.GroupBox();
+      this.dependenciesListView = new System.Windows.Forms.ListView();
+      this.pluginNameHeader = new System.Windows.Forms.ColumnHeader();
+      this.pluginVersionHeader = new System.Windows.Forms.ColumnHeader();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.filesListView = new System.Windows.Forms.ListView();
+      this.fileNameHeader = new System.Windows.Forms.ColumnHeader();
+      this.fileTypeHeader = new System.Windows.Forms.ColumnHeader();
+      this.filesGroupBox = new System.Windows.Forms.GroupBox();
+      this.dependenciesGroupBox.SuspendLayout();
+      this.filesGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 6);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 0;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(74, 3);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.ReadOnly = true;
+      this.nameTextBox.Size = new System.Drawing.Size(267, 20);
+      this.nameTextBox.TabIndex = 1;
+      // 
+      // versionTextBox
+      // 
+      this.versionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.versionTextBox.Location = new System.Drawing.Point(74, 29);
+      this.versionTextBox.Name = "versionTextBox";
+      this.versionTextBox.ReadOnly = true;
+      this.versionTextBox.Size = new System.Drawing.Size(267, 20);
+      this.versionTextBox.TabIndex = 3;
+      // 
+      // versionLabel
+      // 
+      this.versionLabel.AutoSize = true;
+      this.versionLabel.Location = new System.Drawing.Point(3, 32);
+      this.versionLabel.Name = "versionLabel";
+      this.versionLabel.Size = new System.Drawing.Size(45, 13);
+      this.versionLabel.TabIndex = 2;
+      this.versionLabel.Text = "Version:";
+      // 
+      // contactTextBox
+      // 
+      this.contactTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.contactTextBox.Location = new System.Drawing.Point(74, 55);
+      this.contactTextBox.Name = "contactTextBox";
+      this.contactTextBox.ReadOnly = true;
+      this.contactTextBox.Size = new System.Drawing.Size(267, 20);
+      this.contactTextBox.TabIndex = 5;
+      // 
+      // contactInfoLabel
+      // 
+      this.contactInfoLabel.AutoSize = true;
+      this.contactInfoLabel.Location = new System.Drawing.Point(3, 58);
+      this.contactInfoLabel.Name = "contactInfoLabel";
+      this.contactInfoLabel.Size = new System.Drawing.Size(47, 13);
+      this.contactInfoLabel.TabIndex = 4;
+      this.contactInfoLabel.Text = "Contact:";
+      // 
+      // licenseButton
+      // 
+      this.licenseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.licenseButton.Location = new System.Drawing.Point(3, 486);
+      this.licenseButton.Name = "licenseButton";
+      this.licenseButton.Size = new System.Drawing.Size(103, 23);
+      this.licenseButton.TabIndex = 10;
+      this.licenseButton.Text = "Show license";
+      this.licenseButton.UseVisualStyleBackColor = true;
+      this.licenseButton.Click += new System.EventHandler(this.licenseButton_Click);
+      // 
+      // dependenciesGroupBox
+      // 
+      this.dependenciesGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.dependenciesGroupBox.Controls.Add(this.dependenciesListView);
+      this.dependenciesGroupBox.Location = new System.Drawing.Point(3, 255);
+      this.dependenciesGroupBox.Name = "dependenciesGroupBox";
+      this.dependenciesGroupBox.Size = new System.Drawing.Size(338, 225);
+      this.dependenciesGroupBox.TabIndex = 1;
+      this.dependenciesGroupBox.TabStop = false;
+      this.dependenciesGroupBox.Text = "Dependencies";
+      // 
+      // dependenciesListView
+      // 
+      this.dependenciesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.pluginNameHeader,
+            this.pluginVersionHeader});
+      this.dependenciesListView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.dependenciesListView.Location = new System.Drawing.Point(3, 16);
+      this.dependenciesListView.Name = "dependenciesListView";
+      this.dependenciesListView.Size = new System.Drawing.Size(332, 206);
+      this.dependenciesListView.SmallImageList = this.imageList;
+      this.dependenciesListView.TabIndex = 0;
+      this.dependenciesListView.UseCompatibleStateImageBehavior = false;
+      this.dependenciesListView.View = System.Windows.Forms.View.Details;
+      this.dependenciesListView.ItemActivate += new System.EventHandler(this.dependenciesListView_ItemActivate);
+      // 
+      // pluginNameHeader
+      // 
+      this.pluginNameHeader.Text = "Name";
+      this.pluginNameHeader.Width = 200;
+      // 
+      // pluginVersionHeader
+      // 
+      this.pluginVersionHeader.Text = "Version";
+      this.pluginVersionHeader.Width = 120;
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // filesListView
+      // 
+      this.filesListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.fileNameHeader,
+            this.fileTypeHeader});
+      this.filesListView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.filesListView.Location = new System.Drawing.Point(3, 16);
+      this.filesListView.Name = "filesListView";
+      this.filesListView.Size = new System.Drawing.Size(329, 149);
+      this.filesListView.SmallImageList = this.imageList;
+      this.filesListView.TabIndex = 0;
+      this.filesListView.UseCompatibleStateImageBehavior = false;
+      this.filesListView.View = System.Windows.Forms.View.Details;
+      // 
+      // fileNameHeader
+      // 
+      this.fileNameHeader.Text = "Name";
+      this.fileNameHeader.Width = 200;
+      // 
+      // fileTypeHeader
+      // 
+      this.fileTypeHeader.Text = "Type";
+      this.fileTypeHeader.Width = 120;
+      // 
+      // filesGroupBox
+      // 
+      this.filesGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.filesGroupBox.Controls.Add(this.filesListView);
+      this.filesGroupBox.Location = new System.Drawing.Point(3, 81);
+      this.filesGroupBox.Name = "filesGroupBox";
+      this.filesGroupBox.Size = new System.Drawing.Size(335, 168);
+      this.filesGroupBox.TabIndex = 11;
+      this.filesGroupBox.TabStop = false;
+      this.filesGroupBox.Text = "Files";
+      // 
+      // PluginView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.dependenciesGroupBox);
+      this.Controls.Add(this.filesGroupBox);
+      this.Controls.Add(this.licenseButton);
+      this.Controls.Add(this.contactTextBox);
+      this.Controls.Add(this.contactInfoLabel);
+      this.Controls.Add(this.versionTextBox);
+      this.Controls.Add(this.versionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Name = "PluginView";
+      this.Size = new System.Drawing.Size(344, 512);
+      this.dependenciesGroupBox.ResumeLayout(false);
+      this.filesGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    protected System.Windows.Forms.Label nameLabel;
+    protected System.Windows.Forms.TextBox nameTextBox;
+    protected System.Windows.Forms.TextBox versionTextBox;
+    protected System.Windows.Forms.Label versionLabel;
+    protected System.Windows.Forms.TextBox contactTextBox;
+    protected System.Windows.Forms.Label contactInfoLabel;
+    protected System.Windows.Forms.Button licenseButton;
+    protected System.Windows.Forms.GroupBox dependenciesGroupBox;
+    private System.Windows.Forms.ColumnHeader pluginNameHeader;
+    private System.Windows.Forms.ColumnHeader pluginVersionHeader;
+    protected System.Windows.Forms.ListView dependenciesListView;
+    private System.Windows.Forms.ListView filesListView;
+    private System.Windows.Forms.GroupBox filesGroupBox;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.ColumnHeader fileNameHeader;
+    private System.Windows.Forms.ColumnHeader fileTypeHeader;
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.cs	(revision 3072)
@@ -0,0 +1,89 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.PluginInfrastructure;
+using System.IO;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class PluginView : UserControl {
+    private const string IMAGE_KEY_ASSEMBLY = "Assembly";
+    private const string IMAGE_KEY_FILE = "File";
+    private const string IMAGE_KEY_DOCUMENT = "Document";
+
+    private IPluginDescription plugin;
+
+    public PluginView() {
+      InitializeComponent();
+      PopulateImageList();
+    }
+
+    public PluginView(IPluginDescription plugin) {
+      InitializeComponent();
+      PopulateImageList();
+
+      this.plugin = plugin;
+      this.Name = "Plugin Details: " + plugin.ToString();
+      UpdateControls();
+    }
+
+    private void PopulateImageList() {
+      imageList.Images.Add(IMAGE_KEY_ASSEMBLY, HeuristicLab.Common.Resources.VS2008ImageLibrary.Assembly);
+      imageList.Images.Add(IMAGE_KEY_FILE, HeuristicLab.Common.Resources.VS2008ImageLibrary.File);
+      imageList.Images.Add(IMAGE_KEY_DOCUMENT, HeuristicLab.Common.Resources.VS2008ImageLibrary.Document);
+    }
+
+    public void UpdateControls() {
+      string appDir = Path.GetDirectoryName(Application.ExecutablePath);
+      nameTextBox.Text = plugin.Name;
+      versionTextBox.Text = plugin.Version.ToString();
+      contactTextBox.Text = CombineStrings(plugin.ContactName, plugin.ContactEmail);
+      foreach (IPluginDescription dependency in plugin.Dependencies) {
+        var depItem = new ListViewItem(new string[] { dependency.Name, dependency.Version.ToString() });
+        depItem.Tag = dependency;
+        depItem.ImageKey = IMAGE_KEY_ASSEMBLY;
+        dependenciesListView.Items.Add(depItem);
+      }
+      foreach (var file in plugin.Files) {
+        string displayedFileName = file.Name.Replace(appDir, string.Empty);
+        displayedFileName = displayedFileName.TrimStart(Path.DirectorySeparatorChar);
+        var fileItem = new ListViewItem(new string[] { displayedFileName, file.Type.ToString() });
+        if (file.Type == PluginFileType.Assembly) {
+          fileItem.ImageKey = IMAGE_KEY_ASSEMBLY;
+        } else if (file.Type == PluginFileType.License) {
+          fileItem.ImageKey = IMAGE_KEY_DOCUMENT;
+        } else fileItem.ImageKey = IMAGE_KEY_FILE;
+        filesListView.Items.Add(fileItem);
+      }
+      licenseButton.Enabled = !string.IsNullOrEmpty(plugin.LicenseText);
+    }
+
+    private string CombineStrings(string a, string b) {
+      if (string.IsNullOrEmpty(a))
+        // a is empty
+        if (!string.IsNullOrEmpty(b)) return CombineStrings(b, string.Empty);
+        else return string.Empty;
+      // a is not empty
+      else if (string.IsNullOrEmpty(b)) return a;
+      // and b are not empty
+      else return a + ", " + b;
+    }
+
+    private void licenseButton_Click(object sender, EventArgs e) {
+      LicenseView view = new LicenseView(plugin);
+      view.Show();
+    }
+
+    private void dependenciesListView_ItemActivate(object sender, EventArgs e) {
+      if (dependenciesListView.SelectedItems.Count > 0) {
+        var dep = (IPluginDescription)dependenciesListView.SelectedItems[0].Tag;
+        PluginView view = new PluginView(dep);
+        view.Show();
+      }
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/PluginView.resx	(revision 3072)
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="imageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.Designer.cs	(revision 3072)
@@ -0,0 +1,247 @@
+﻿namespace HeuristicLab.DeploymentService.AdminClient {
+  partial class ProductEditor {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.newProductButton = new System.Windows.Forms.Button();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.productsListView = new System.Windows.Forms.ListView();
+      this.productNameHeader = new System.Windows.Forms.ColumnHeader();
+      this.productVersionHeader = new System.Windows.Forms.ColumnHeader();
+      this.productImageList = new System.Windows.Forms.ImageList(this.components);
+      this.pluginImageList = new System.Windows.Forms.ImageList(this.components);
+      this.pluginsLabel = new System.Windows.Forms.Label();
+      this.versionTextBox = new System.Windows.Forms.TextBox();
+      this.versionLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
+      this.pluginListView = new HeuristicLab.DeploymentService.AdminClient.PluginListView();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(75, 23);
+      this.refreshButton.TabIndex = 1;
+      this.refreshButton.Text = "Refresh";
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+      this.saveButton.Location = new System.Drawing.Point(3, 365);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(96, 23);
+      this.saveButton.TabIndex = 2;
+      this.saveButton.Text = "Upload Products";
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // newProductButton
+      // 
+      this.newProductButton.Location = new System.Drawing.Point(84, 3);
+      this.newProductButton.Name = "newProductButton";
+      this.newProductButton.Size = new System.Drawing.Size(91, 23);
+      this.newProductButton.TabIndex = 3;
+      this.newProductButton.Text = "New product";
+      this.newProductButton.UseVisualStyleBackColor = true;
+      this.newProductButton.Click += new System.EventHandler(this.newProductButton_Click);
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 32);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.productsListView);
+      this.splitContainer.Panel1.Controls.Add(this.saveButton);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.pluginListView);
+      this.splitContainer.Panel2.Controls.Add(this.pluginsLabel);
+      this.splitContainer.Panel2.Controls.Add(this.versionTextBox);
+      this.splitContainer.Panel2.Controls.Add(this.versionLabel);
+      this.splitContainer.Panel2.Controls.Add(this.nameTextBox);
+      this.splitContainer.Panel2.Controls.Add(this.nameLabel);
+      this.splitContainer.Size = new System.Drawing.Size(659, 391);
+      this.splitContainer.SplitterDistance = 319;
+      this.splitContainer.TabIndex = 4;
+      // 
+      // productsListView
+      // 
+      this.productsListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.productsListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.productNameHeader,
+            this.productVersionHeader});
+      this.productsListView.FullRowSelect = true;
+      this.productsListView.Location = new System.Drawing.Point(3, 3);
+      this.productsListView.MultiSelect = false;
+      this.productsListView.Name = "productsListView";
+      this.productsListView.Size = new System.Drawing.Size(313, 356);
+      this.productsListView.SmallImageList = this.productImageList;
+      this.productsListView.TabIndex = 4;
+      this.productsListView.UseCompatibleStateImageBehavior = false;
+      this.productsListView.View = System.Windows.Forms.View.Details;
+      this.productsListView.SelectedIndexChanged += new System.EventHandler(this.productsListBox_SelectedIndexChanged);
+      // 
+      // productNameHeader
+      // 
+      this.productNameHeader.Text = "Name";
+      this.productNameHeader.Width = 150;
+      // 
+      // productVersionHeader
+      // 
+      this.productVersionHeader.Text = "Version";
+      this.productVersionHeader.Width = 100;
+      // 
+      // productImageList
+      // 
+      this.productImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.productImageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.productImageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // pluginImageList
+      // 
+      this.pluginImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.pluginImageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.pluginImageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // pluginsLabel
+      // 
+      this.pluginsLabel.AutoSize = true;
+      this.pluginsLabel.Location = new System.Drawing.Point(11, 69);
+      this.pluginsLabel.Name = "pluginsLabel";
+      this.pluginsLabel.Size = new System.Drawing.Size(44, 13);
+      this.pluginsLabel.TabIndex = 6;
+      this.pluginsLabel.Text = "Plugins:";
+      // 
+      // versionTextBox
+      // 
+      this.versionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.versionTextBox.Location = new System.Drawing.Point(68, 29);
+      this.versionTextBox.Name = "versionTextBox";
+      this.versionTextBox.Size = new System.Drawing.Size(233, 20);
+      this.versionTextBox.TabIndex = 5;
+      this.versionTextBox.TextChanged += new System.EventHandler(this.versionTextBox_TextChanged);
+      // 
+      // versionLabel
+      // 
+      this.versionLabel.AutoSize = true;
+      this.versionLabel.Location = new System.Drawing.Point(10, 32);
+      this.versionLabel.Name = "versionLabel";
+      this.versionLabel.Size = new System.Drawing.Size(45, 13);
+      this.versionLabel.TabIndex = 4;
+      this.versionLabel.Text = "Version:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(68, 3);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(233, 20);
+      this.nameTextBox.TabIndex = 3;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(17, 6);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 2;
+      this.nameLabel.Text = "Name:";
+      // 
+      // errorProvider
+      // 
+      this.errorProvider.ContainerControl = this;
+      // 
+      // pluginListView
+      // 
+      this.pluginListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+                  | System.Windows.Forms.AnchorStyles.Left)
+                  | System.Windows.Forms.AnchorStyles.Right)));
+      this.pluginListView.Location = new System.Drawing.Point(3, 85);
+      this.pluginListView.Name = "pluginListView";
+      this.pluginListView.Plugins = null;
+      this.pluginListView.Size = new System.Drawing.Size(330, 303);
+      this.pluginListView.TabIndex = 7;
+      this.pluginListView.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.pluginListView_ItemChecked);
+      // 
+      // ProductEditor
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.newProductButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProductEditor";
+      this.Size = new System.Drawing.Size(665, 426);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      this.splitContainer.Panel2.PerformLayout();
+      this.splitContainer.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.Button newProductButton;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.Label pluginsLabel;
+    private System.Windows.Forms.TextBox versionTextBox;
+    private System.Windows.Forms.Label versionLabel;
+    private System.Windows.Forms.TextBox nameTextBox;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.ErrorProvider errorProvider;
+    private System.Windows.Forms.ListView productsListView;
+    private System.Windows.Forms.ColumnHeader productNameHeader;
+    private System.Windows.Forms.ColumnHeader productVersionHeader;
+    private System.Windows.Forms.ImageList productImageList;
+    private System.Windows.Forms.ImageList pluginImageList;
+    private PluginListView pluginListView;
+
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.cs
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.cs	(revision 3072)
@@ -0,0 +1,213 @@
+﻿using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using HeuristicLab.MainForm;
+using PluginDeploymentService = HeuristicLab.PluginInfrastructure.Advanced.DeploymentService;
+using System.ServiceModel;
+using HeuristicLab.PluginInfrastructure;
+
+namespace HeuristicLab.DeploymentService.AdminClient {
+  public partial class ProductEditor : HeuristicLab.MainForm.WindowsForms.View {
+    private BackgroundWorker refreshProductsWorker;
+    private BackgroundWorker uploadChangedProductsWorker;
+    private List<PluginDeploymentService.ProductDescription> products;
+    private List<PluginDeploymentService.PluginDescription> plugins;
+    private HashSet<PluginDeploymentService.ProductDescription> dirtyProducts;
+
+    public ProductEditor() {
+      InitializeComponent();
+      Caption = "Products";
+
+      productImageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.Assembly);
+      productImageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.ArrowUp);
+      pluginImageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.Assembly);
+
+      dirtyProducts = new HashSet<PluginDeploymentService.ProductDescription>();
+      refreshProductsWorker = new BackgroundWorker();
+      refreshProductsWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshProductsWorker_RunWorkerCompleted);
+      refreshProductsWorker.DoWork += new DoWorkEventHandler(refreshProductsWorker_DoWork);
+
+      uploadChangedProductsWorker = new BackgroundWorker();
+      uploadChangedProductsWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(uploadChangedProductsWorker_RunWorkerCompleted);
+      uploadChangedProductsWorker.DoWork += new DoWorkEventHandler(uploadChangedProductsWorker_DoWork);
+    }
+
+    #region event handlers for upload products background worker
+    private void uploadChangedProductsWorker_DoWork(object sender, DoWorkEventArgs e) {
+      var products = (IEnumerable<PluginDeploymentService.ProductDescription>)e.Argument;
+      var adminClient = PluginDeploymentService.AdminClientFactory.CreateClient();
+      try {
+        foreach (var product in products) {
+          adminClient.DeployProduct(product);
+        }
+        e.Cancel = false;
+      }
+      catch (FaultException) {
+      }
+    }
+
+    private void uploadChangedProductsWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+      this.Enabled = true;
+      refreshProductsWorker.RunWorkerAsync();
+    }
+    #endregion
+
+    #region event handlers for refresh products background worker
+    private void refreshProductsWorker_DoWork(object sender, DoWorkEventArgs e) {
+      var updateClient = PluginDeploymentService.UpdateClientFactory.CreateClient();
+      try {
+        e.Result = new object[] { updateClient.GetProducts(), updateClient.GetPlugins() };
+      }
+      catch (FaultException) {
+        e.Cancel = true;
+      }
+    }
+
+    private void refreshProductsWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
+      this.products = new List<PluginDeploymentService.ProductDescription>(
+        (PluginDeploymentService.ProductDescription[])((object[])e.Result)[0]);
+      this.plugins = new List<PluginDeploymentService.PluginDescription>(
+        (PluginDeploymentService.PluginDescription[])((object[])e.Result)[1]);
+
+      UpdateProductsList();
+      dirtyProducts.Clear();
+
+      Cursor = Cursors.Default;
+      SetControlsEnabled(true);
+    }
+    #endregion
+
+    private void UpdateProductsList() {
+      productsListView.Items.Clear();
+      foreach (var prodDesc in products) {
+        productsListView.Items.Add(CreateListViewItem(prodDesc));
+      }
+    }
+
+    private void productsListBox_SelectedIndexChanged(object sender, EventArgs e) {
+      if (productsListView.SelectedItems.Count == 0) return;
+      PluginDeploymentService.ProductDescription activeProduct = (PluginDeploymentService.ProductDescription)((ListViewItem)productsListView.SelectedItems[0]).Tag;
+      UpdateProductDetails(activeProduct);
+    }
+
+    private void UpdateProductDetails(PluginDeploymentService.ProductDescription activeProduct) {
+      nameTextBox.Text = activeProduct.Name;
+      versionTextBox.Text = activeProduct.Version.ToString();
+
+      UpdatePluginsListView();
+    }
+
+    private ListViewItem CreateListViewItem(PluginDeploymentService.ProductDescription productDescription) {
+      ListViewItem item = new ListViewItem(new string[] { productDescription.Name, productDescription.Version.ToString() });
+      item.Tag = productDescription;
+      item.ImageIndex = 0;
+      return item;
+    }
+
+    private void SetControlsEnabled(bool enabled) {
+      saveButton.Enabled = enabled;
+      refreshButton.Enabled = enabled;
+      newProductButton.Enabled = enabled;
+      splitContainer.Enabled = enabled;
+    }
+
+    #region button event handlers
+    private void newProductButton_Click(object sender, EventArgs e) {
+      var newProduct = new PluginDeploymentService.ProductDescription("New product", new Version("0.0.0.0"));
+      ListViewItem item = CreateListViewItem(newProduct);
+      productsListView.Items.Add(item);
+      MarkProductDirty(newProduct);
+    }
+
+    private void saveButton_Click(object sender, EventArgs e) {
+      this.Enabled = false;
+      uploadChangedProductsWorker.RunWorkerAsync(dirtyProducts);
+    }
+    private void refreshButton_Click(object sender, EventArgs e) {
+      SetControlsEnabled(false);
+      Cursor = Cursors.AppStarting;
+      refreshProductsWorker.RunWorkerAsync();
+    }
+
+    #endregion
+
+    #region textbox changed event handlers
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      ListViewItem activeItem = (ListViewItem)productsListView.SelectedItems[0];
+      PluginDeploymentService.ProductDescription activeProduct = (PluginDeploymentService.ProductDescription)activeItem.Tag;
+      if (string.IsNullOrEmpty(nameTextBox.Name)) {
+        errorProvider.SetError(nameTextBox, "Invalid value");
+      } else {
+        if (activeProduct.Name != nameTextBox.Text) {
+          activeProduct.Name = nameTextBox.Text;
+          activeItem.SubItems[0].Text = activeProduct.Name;
+          errorProvider.SetError(nameTextBox, string.Empty);
+          MarkProductDirty(activeProduct);
+        }
+      }
+    }
+
+
+    private void versionTextBox_TextChanged(object sender, EventArgs e) {
+      ListViewItem activeItem = (ListViewItem)productsListView.SelectedItems[0];
+      PluginDeploymentService.ProductDescription activeProduct = (PluginDeploymentService.ProductDescription)activeItem.Tag;
+      try {
+        var newVersion = new Version(versionTextBox.Text);
+        if (activeProduct.Version != newVersion) {
+          activeProduct.Version = newVersion;
+          activeItem.SubItems[1].Text = versionTextBox.Text;
+          errorProvider.SetError(versionTextBox, string.Empty);
+          MarkProductDirty(activeProduct);
+        }
+      }
+      catch (OverflowException ex) {
+        errorProvider.SetError(versionTextBox, "Invalid value");
+      }
+
+      catch (ArgumentException ex) {
+        errorProvider.SetError(versionTextBox, "Invalid value");
+      }
+      catch (FormatException ex) {
+        errorProvider.SetError(versionTextBox, "Invalid value");
+      }
+    }
+    #endregion
+
+
+    #region plugin list view
+    private void UpdatePluginsListView() {
+      ListViewItem activeItem = (ListViewItem)productsListView.SelectedItems[0];
+      PluginDeploymentService.ProductDescription activeProduct = (PluginDeploymentService.ProductDescription)activeItem.Tag;
+      pluginListView.Plugins = plugins.OfType<IPluginDescription>();
+      foreach (var plugin in activeProduct.Plugins) pluginListView.CheckPlugin(plugin);
+    }
+
+    private void pluginListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
+      ListViewItem activeItem = (ListViewItem)productsListView.SelectedItems[0];
+      PluginDeploymentService.ProductDescription activeProduct = (PluginDeploymentService.ProductDescription)activeItem.Tag;
+      activeProduct.Plugins = pluginListView.CheckedPlugins.Cast<PluginDeploymentService.PluginDescription>().ToArray();
+      MarkProductDirty(activeProduct);
+    }
+    #endregion
+
+    private void MarkProductDirty(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription activeProduct) {
+      if (!dirtyProducts.Contains(activeProduct)) {
+        dirtyProducts.Add(activeProduct);
+        var item = FindItemForProduct(activeProduct);
+        item.ImageIndex = 1;
+      }
+    }
+    private ListViewItem FindItemForProduct(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription activeProduct) {
+      return (from item in productsListView.Items.OfType<ListViewItem>()
+              let product = item.Tag as PluginDeploymentService.ProductDescription
+              where product != null
+              where product == activeProduct
+              select item).Single();
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.resx
===================================================================
--- /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.resx	(revision 3072)
+++ /trunk/sources/HeuristicLab.PluginAdministrator/3.3/ProductEditor.resx	(revision 3072)
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="productImageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>140, 17</value>
+  </metadata>
+  <metadata name="pluginImageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>287, 17</value>
+  </metadata>
+  <metadata name="errorProvider.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/HeuristicLab.Services.Deployment.DataAccess.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/HeuristicLab.Services.Deployment.DataAccess.csproj	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/HeuristicLab.Services.Deployment.DataAccess.csproj	(revision 3072)
@@ -0,0 +1,98 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{EBEBA863-DD05-4331-BE25-5758B15F7407}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.Services.Deployment.DataAccess</RootNamespace>
+    <AssemblyName>HeuristicLab.Services.Deployment.DataAccess</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Runtime.Serialization">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Transactions" />
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data.DataSetExtensions">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PluginStoreClasses.cs">
+      <DependentUpon>PluginStoreClasses.dbml</DependentUpon>
+    </Compile>
+    <Compile Include="PluginStoreClasses.designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>PluginStoreClasses.dbml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="PluginStoreClasses.dbml">
+      <Generator>MSLinqToSQLGenerator</Generator>
+      <LastGenOutput>PluginStoreClasses.designer.cs</LastGenOutput>
+      <SubType>Designer</SubType>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{3259AA49-8AA1-44D3-9025-A0B520596A8C}" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+    <None Include="PluginStoreClasses.dbml.layout">
+      <DependentUpon>PluginStoreClasses.dbml</DependentUpon>
+    </None>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml	(revision 3072)
@@ -0,0 +1,41 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Database Name="HeuristicLab.PluginStore" Class="PluginStoreClassesDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">
+  <Connection Mode="AppSettings" ConnectionString="Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True" SettingsObjectName="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings" SettingsPropertyName="HeuristicLab_PluginStoreConnectionString" Provider="System.Data.SqlClient" />
+  <Table Name="dbo.Dependencies" Member="Dependencies">
+    <Type Name="Dependency">
+      <Column Name="PluginId" Type="System.Int64" DbType="BigInt NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="DependencyId" Type="System.Int64" DbType="BigInt NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+    </Type>
+  </Table>
+  <Table Name="dbo.ProductPlugin" Member="ProductPlugins">
+    <Type Name="ProductPlugin">
+      <Column Name="ProductId" Type="System.Int64" DbType="BigInt NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="PluginId" Type="System.Int64" DbType="BigInt NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+    </Type>
+  </Table>
+  <Table Name="dbo.Plugin" Member="Plugins">
+    <Type Name="Plugin">
+      <Column Name="Id" Type="System.Int64" DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
+      <Column Name="Name" Type="System.String" DbType="NVarChar(300) NOT NULL" CanBeNull="false" />
+      <Column Name="Version" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" />
+      <Column Name="ContactName" Type="System.String" DbType="Text" CanBeNull="true" UpdateCheck="Never" />
+      <Column Name="ContactEmail" Type="System.String" DbType="Text" CanBeNull="true" UpdateCheck="Never" />
+      <Column Name="License" Type="System.String" DbType="Text" CanBeNull="true" UpdateCheck="Never" />
+      <Association Name="Plugin_PluginPackage" Member="PluginPackage" ThisKey="Id" OtherKey="PluginId" Type="PluginPackage" Cardinality="One" />
+    </Type>
+  </Table>
+  <Table Name="dbo.PluginPackage" Member="PluginPackages">
+    <Type Name="PluginPackage">
+      <Column Name="PluginId" Type="System.Int64" DbType="BigInt NOT NULL" IsPrimaryKey="true" CanBeNull="false" />
+      <Column Name="Data" Type="System.Data.Linq.Binary" DbType="Image NOT NULL" CanBeNull="false" UpdateCheck="Never" />
+      <Association Name="Plugin_PluginPackage" Member="Plugin" ThisKey="PluginId" OtherKey="Id" Type="Plugin" IsForeignKey="true" />
+    </Type>
+  </Table>
+  <Table Name="dbo.Product" Member="Products">
+    <Type Name="Product">
+      <Column Name="Id" Type="System.Int64" DbType="BigInt NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
+      <Column Name="Name" Type="System.String" DbType="NVarChar(300) NOT NULL" CanBeNull="false" />
+      <Column Name="Version" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" />
+    </Type>
+  </Table>
+</Database>
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml.layout
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml.layout	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.dbml.layout	(revision 3072)
@@ -0,0 +1,43 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<ordesignerObjectsDiagram dslVersion="1.0.0.0" absoluteBounds="0, 0, 11, 8.5" name="PluginStoreClasses">
+  <DataContextMoniker Name="/PluginStoreClassesDataContext" />
+  <nestedChildShapes>
+    <classShape Id="16e9d26d-f78e-4457-8cdb-0cee0835a0b6" absoluteBounds="3.5, 2, 2, 1.1939925130208335">
+      <DataClassMoniker Name="/PluginStoreClassesDataContext/Dependency" />
+      <nestedChildShapes>
+        <elementListCompartment Id="90573696-5cb0-44c5-a492-6e26800047b3" absoluteBounds="3.515, 2.46, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="86708203-9aa2-44f4-a85f-7fad2c542e23" absoluteBounds="3.5, 4, 2, 1.1939925130208344">
+      <DataClassMoniker Name="/PluginStoreClassesDataContext/ProductPlugin" />
+      <nestedChildShapes>
+        <elementListCompartment Id="da9607ee-b60e-44f0-8e80-704abd6dd898" absoluteBounds="3.515, 4.46, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="1e228e3e-9436-4cd0-b7f7-492427268001" absoluteBounds="0.75, 2.625, 2, 1.9631982421874996">
+      <DataClassMoniker Name="/PluginStoreClassesDataContext/Plugin" />
+      <nestedChildShapes>
+        <elementListCompartment Id="81f74f1f-d4d0-4dce-991e-fb10afa63eb5" absoluteBounds="0.765, 3.085, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="d660db81-846c-4661-9a86-09567e927f9d" absoluteBounds="3.5, 6, 2, 1.1939925130208344">
+      <DataClassMoniker Name="/PluginStoreClassesDataContext/PluginPackage" />
+      <nestedChildShapes>
+        <elementListCompartment Id="31a89e0e-c915-41d6-926b-d8c0f029f27d" absoluteBounds="3.515, 6.46, 1.9700000000000002, 0.63399251302083326" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <classShape Id="55486421-017b-44b9-894d-6cc4501cda04" absoluteBounds="0.75, 5.875, 2, 1.3862939453125005">
+      <DataClassMoniker Name="/PluginStoreClassesDataContext/Product" />
+      <nestedChildShapes>
+        <elementListCompartment Id="5abad42a-034c-4331-916c-e757c0c6c777" absoluteBounds="0.765, 6.335, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" />
+      </nestedChildShapes>
+    </classShape>
+    <associationConnector edgePoints="[(2.75 : 4.5881982421875); (3.125 : 4.9631982421875); (3.125 : 6.59699625651042); (3.5 : 6.59699625651042)]" fixedFrom="NotFixed" fixedTo="Algorithm">
+      <AssociationMoniker Name="/PluginStoreClassesDataContext/Plugin/Plugin_PluginPackage" />
+      <nodes>
+        <classShapeMoniker Id="1e228e3e-9436-4cd0-b7f7-492427268001" />
+        <classShapeMoniker Id="d660db81-846c-4661-9a86-09567e927f9d" />
+      </nodes>
+    </associationConnector>
+  </nestedChildShapes>
+</ordesignerObjectsDiagram>
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/PluginStoreClasses.designer.cs	(revision 3072)
@@ -0,0 +1,744 @@
+﻿#pragma warning disable 1591
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.50727.4200
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HeuristicLab.Services.Deployment.DataAccess
+{
+	using System.Data.Linq;
+	using System.Data.Linq.Mapping;
+	using System.Data;
+	using System.Collections.Generic;
+	using System.Reflection;
+	using System.Linq;
+	using System.Linq.Expressions;
+	using System.ComponentModel;
+	using System;
+	
+	
+	[System.Data.Linq.Mapping.DatabaseAttribute(Name="HeuristicLab.PluginStore")]
+	public partial class PluginStoreClassesDataContext : System.Data.Linq.DataContext
+	{
+		
+		private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
+		
+    #region Extensibility Method Definitions
+    partial void OnCreated();
+    partial void InsertDependency(Dependency instance);
+    partial void UpdateDependency(Dependency instance);
+    partial void DeleteDependency(Dependency instance);
+    partial void InsertProductPlugin(ProductPlugin instance);
+    partial void UpdateProductPlugin(ProductPlugin instance);
+    partial void DeleteProductPlugin(ProductPlugin instance);
+    partial void InsertPlugin(Plugin instance);
+    partial void UpdatePlugin(Plugin instance);
+    partial void DeletePlugin(Plugin instance);
+    partial void InsertPluginPackage(PluginPackage instance);
+    partial void UpdatePluginPackage(PluginPackage instance);
+    partial void DeletePluginPackage(PluginPackage instance);
+    partial void InsertProduct(Product instance);
+    partial void UpdateProduct(Product instance);
+    partial void DeleteProduct(Product instance);
+    #endregion
+		
+		public PluginStoreClassesDataContext() : 
+				base(global::HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.Default.HeuristicLab_PluginStoreConnectionString, mappingSource)
+		{
+			OnCreated();
+		}
+		
+		public PluginStoreClassesDataContext(string connection) : 
+				base(connection, mappingSource)
+		{
+			OnCreated();
+		}
+		
+		public PluginStoreClassesDataContext(System.Data.IDbConnection connection) : 
+				base(connection, mappingSource)
+		{
+			OnCreated();
+		}
+		
+		public PluginStoreClassesDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) : 
+				base(connection, mappingSource)
+		{
+			OnCreated();
+		}
+		
+		public PluginStoreClassesDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) : 
+				base(connection, mappingSource)
+		{
+			OnCreated();
+		}
+		
+		public System.Data.Linq.Table<Dependency> Dependencies
+		{
+			get
+			{
+				return this.GetTable<Dependency>();
+			}
+		}
+		
+		public System.Data.Linq.Table<ProductPlugin> ProductPlugins
+		{
+			get
+			{
+				return this.GetTable<ProductPlugin>();
+			}
+		}
+		
+		public System.Data.Linq.Table<Plugin> Plugins
+		{
+			get
+			{
+				return this.GetTable<Plugin>();
+			}
+		}
+		
+		public System.Data.Linq.Table<PluginPackage> PluginPackages
+		{
+			get
+			{
+				return this.GetTable<PluginPackage>();
+			}
+		}
+		
+		public System.Data.Linq.Table<Product> Products
+		{
+			get
+			{
+				return this.GetTable<Product>();
+			}
+		}
+	}
+	
+	[Table(Name="dbo.Dependencies")]
+	public partial class Dependency : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private long _PluginId;
+		
+		private long _DependencyId;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnPluginIdChanging(long value);
+    partial void OnPluginIdChanged();
+    partial void OnDependencyIdChanging(long value);
+    partial void OnDependencyIdChanged();
+    #endregion
+		
+		public Dependency()
+		{
+			OnCreated();
+		}
+		
+		[Column(Storage="_PluginId", DbType="BigInt NOT NULL", IsPrimaryKey=true)]
+		public long PluginId
+		{
+			get
+			{
+				return this._PluginId;
+			}
+			set
+			{
+				if ((this._PluginId != value))
+				{
+					this.OnPluginIdChanging(value);
+					this.SendPropertyChanging();
+					this._PluginId = value;
+					this.SendPropertyChanged("PluginId");
+					this.OnPluginIdChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_DependencyId", DbType="BigInt NOT NULL", IsPrimaryKey=true)]
+		public long DependencyId
+		{
+			get
+			{
+				return this._DependencyId;
+			}
+			set
+			{
+				if ((this._DependencyId != value))
+				{
+					this.OnDependencyIdChanging(value);
+					this.SendPropertyChanging();
+					this._DependencyId = value;
+					this.SendPropertyChanged("DependencyId");
+					this.OnDependencyIdChanged();
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[Table(Name="dbo.ProductPlugin")]
+	public partial class ProductPlugin : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private long _ProductId;
+		
+		private long _PluginId;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnProductIdChanging(long value);
+    partial void OnProductIdChanged();
+    partial void OnPluginIdChanging(long value);
+    partial void OnPluginIdChanged();
+    #endregion
+		
+		public ProductPlugin()
+		{
+			OnCreated();
+		}
+		
+		[Column(Storage="_ProductId", DbType="BigInt NOT NULL", IsPrimaryKey=true)]
+		public long ProductId
+		{
+			get
+			{
+				return this._ProductId;
+			}
+			set
+			{
+				if ((this._ProductId != value))
+				{
+					this.OnProductIdChanging(value);
+					this.SendPropertyChanging();
+					this._ProductId = value;
+					this.SendPropertyChanged("ProductId");
+					this.OnProductIdChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_PluginId", DbType="BigInt NOT NULL", IsPrimaryKey=true)]
+		public long PluginId
+		{
+			get
+			{
+				return this._PluginId;
+			}
+			set
+			{
+				if ((this._PluginId != value))
+				{
+					this.OnPluginIdChanging(value);
+					this.SendPropertyChanging();
+					this._PluginId = value;
+					this.SendPropertyChanged("PluginId");
+					this.OnPluginIdChanged();
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[Table(Name="dbo.Plugin")]
+	public partial class Plugin : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private long _Id;
+		
+		private string _Name;
+		
+		private string _Version;
+		
+		private string _ContactName;
+		
+		private string _ContactEmail;
+		
+		private string _License;
+		
+		private EntityRef<PluginPackage> _PluginPackage;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnIdChanging(long value);
+    partial void OnIdChanged();
+    partial void OnNameChanging(string value);
+    partial void OnNameChanged();
+    partial void OnVersionChanging(string value);
+    partial void OnVersionChanged();
+    partial void OnContactNameChanging(string value);
+    partial void OnContactNameChanged();
+    partial void OnContactEmailChanging(string value);
+    partial void OnContactEmailChanged();
+    partial void OnLicenseChanging(string value);
+    partial void OnLicenseChanged();
+    #endregion
+		
+		public Plugin()
+		{
+			this._PluginPackage = default(EntityRef<PluginPackage>);
+			OnCreated();
+		}
+		
+		[Column(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="BigInt NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+		public long Id
+		{
+			get
+			{
+				return this._Id;
+			}
+			set
+			{
+				if ((this._Id != value))
+				{
+					this.OnIdChanging(value);
+					this.SendPropertyChanging();
+					this._Id = value;
+					this.SendPropertyChanged("Id");
+					this.OnIdChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_Name", DbType="NVarChar(300) NOT NULL", CanBeNull=false)]
+		public string Name
+		{
+			get
+			{
+				return this._Name;
+			}
+			set
+			{
+				if ((this._Name != value))
+				{
+					this.OnNameChanging(value);
+					this.SendPropertyChanging();
+					this._Name = value;
+					this.SendPropertyChanged("Name");
+					this.OnNameChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_Version", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+		public string Version
+		{
+			get
+			{
+				return this._Version;
+			}
+			set
+			{
+				if ((this._Version != value))
+				{
+					this.OnVersionChanging(value);
+					this.SendPropertyChanging();
+					this._Version = value;
+					this.SendPropertyChanged("Version");
+					this.OnVersionChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_ContactName", DbType="Text", UpdateCheck=UpdateCheck.Never)]
+		public string ContactName
+		{
+			get
+			{
+				return this._ContactName;
+			}
+			set
+			{
+				if ((this._ContactName != value))
+				{
+					this.OnContactNameChanging(value);
+					this.SendPropertyChanging();
+					this._ContactName = value;
+					this.SendPropertyChanged("ContactName");
+					this.OnContactNameChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_ContactEmail", DbType="Text", UpdateCheck=UpdateCheck.Never)]
+		public string ContactEmail
+		{
+			get
+			{
+				return this._ContactEmail;
+			}
+			set
+			{
+				if ((this._ContactEmail != value))
+				{
+					this.OnContactEmailChanging(value);
+					this.SendPropertyChanging();
+					this._ContactEmail = value;
+					this.SendPropertyChanged("ContactEmail");
+					this.OnContactEmailChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_License", DbType="Text", UpdateCheck=UpdateCheck.Never)]
+		public string License
+		{
+			get
+			{
+				return this._License;
+			}
+			set
+			{
+				if ((this._License != value))
+				{
+					this.OnLicenseChanging(value);
+					this.SendPropertyChanging();
+					this._License = value;
+					this.SendPropertyChanged("License");
+					this.OnLicenseChanged();
+				}
+			}
+		}
+		
+		[Association(Name="Plugin_PluginPackage", Storage="_PluginPackage", ThisKey="Id", OtherKey="PluginId", IsUnique=true, IsForeignKey=false)]
+		public PluginPackage PluginPackage
+		{
+			get
+			{
+				return this._PluginPackage.Entity;
+			}
+			set
+			{
+				PluginPackage previousValue = this._PluginPackage.Entity;
+				if (((previousValue != value) 
+							|| (this._PluginPackage.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._PluginPackage.Entity = null;
+						previousValue.Plugin = null;
+					}
+					this._PluginPackage.Entity = value;
+					if ((value != null))
+					{
+						value.Plugin = this;
+					}
+					this.SendPropertyChanged("PluginPackage");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[Table(Name="dbo.PluginPackage")]
+	public partial class PluginPackage : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private long _PluginId;
+		
+		private System.Data.Linq.Binary _Data;
+		
+		private EntityRef<Plugin> _Plugin;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnPluginIdChanging(long value);
+    partial void OnPluginIdChanged();
+    partial void OnDataChanging(System.Data.Linq.Binary value);
+    partial void OnDataChanged();
+    #endregion
+		
+		public PluginPackage()
+		{
+			this._Plugin = default(EntityRef<Plugin>);
+			OnCreated();
+		}
+		
+		[Column(Storage="_PluginId", DbType="BigInt NOT NULL", IsPrimaryKey=true)]
+		public long PluginId
+		{
+			get
+			{
+				return this._PluginId;
+			}
+			set
+			{
+				if ((this._PluginId != value))
+				{
+					if (this._Plugin.HasLoadedOrAssignedValue)
+					{
+						throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
+					}
+					this.OnPluginIdChanging(value);
+					this.SendPropertyChanging();
+					this._PluginId = value;
+					this.SendPropertyChanged("PluginId");
+					this.OnPluginIdChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_Data", DbType="Image NOT NULL", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
+		public System.Data.Linq.Binary Data
+		{
+			get
+			{
+				return this._Data;
+			}
+			set
+			{
+				if ((this._Data != value))
+				{
+					this.OnDataChanging(value);
+					this.SendPropertyChanging();
+					this._Data = value;
+					this.SendPropertyChanged("Data");
+					this.OnDataChanged();
+				}
+			}
+		}
+		
+		[Association(Name="Plugin_PluginPackage", Storage="_Plugin", ThisKey="PluginId", OtherKey="Id", IsForeignKey=true)]
+		public Plugin Plugin
+		{
+			get
+			{
+				return this._Plugin.Entity;
+			}
+			set
+			{
+				Plugin previousValue = this._Plugin.Entity;
+				if (((previousValue != value) 
+							|| (this._Plugin.HasLoadedOrAssignedValue == false)))
+				{
+					this.SendPropertyChanging();
+					if ((previousValue != null))
+					{
+						this._Plugin.Entity = null;
+						previousValue.PluginPackage = null;
+					}
+					this._Plugin.Entity = value;
+					if ((value != null))
+					{
+						value.PluginPackage = this;
+						this._PluginId = value.Id;
+					}
+					else
+					{
+						this._PluginId = default(long);
+					}
+					this.SendPropertyChanged("Plugin");
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+	
+	[Table(Name="dbo.Product")]
+	public partial class Product : INotifyPropertyChanging, INotifyPropertyChanged
+	{
+		
+		private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
+		
+		private long _Id;
+		
+		private string _Name;
+		
+		private string _Version;
+		
+    #region Extensibility Method Definitions
+    partial void OnLoaded();
+    partial void OnValidate(System.Data.Linq.ChangeAction action);
+    partial void OnCreated();
+    partial void OnIdChanging(long value);
+    partial void OnIdChanged();
+    partial void OnNameChanging(string value);
+    partial void OnNameChanged();
+    partial void OnVersionChanging(string value);
+    partial void OnVersionChanged();
+    #endregion
+		
+		public Product()
+		{
+			OnCreated();
+		}
+		
+		[Column(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="BigInt NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
+		public long Id
+		{
+			get
+			{
+				return this._Id;
+			}
+			set
+			{
+				if ((this._Id != value))
+				{
+					this.OnIdChanging(value);
+					this.SendPropertyChanging();
+					this._Id = value;
+					this.SendPropertyChanged("Id");
+					this.OnIdChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_Name", DbType="NVarChar(300) NOT NULL", CanBeNull=false)]
+		public string Name
+		{
+			get
+			{
+				return this._Name;
+			}
+			set
+			{
+				if ((this._Name != value))
+				{
+					this.OnNameChanging(value);
+					this.SendPropertyChanging();
+					this._Name = value;
+					this.SendPropertyChanged("Name");
+					this.OnNameChanged();
+				}
+			}
+		}
+		
+		[Column(Storage="_Version", DbType="NVarChar(50) NOT NULL", CanBeNull=false)]
+		public string Version
+		{
+			get
+			{
+				return this._Version;
+			}
+			set
+			{
+				if ((this._Version != value))
+				{
+					this.OnVersionChanging(value);
+					this.SendPropertyChanging();
+					this._Version = value;
+					this.SendPropertyChanged("Version");
+					this.OnVersionChanged();
+				}
+			}
+		}
+		
+		public event PropertyChangingEventHandler PropertyChanging;
+		
+		public event PropertyChangedEventHandler PropertyChanged;
+		
+		protected virtual void SendPropertyChanging()
+		{
+			if ((this.PropertyChanging != null))
+			{
+				this.PropertyChanging(this, emptyChangingEventArgs);
+			}
+		}
+		
+		protected virtual void SendPropertyChanged(String propertyName)
+		{
+			if ((this.PropertyChanged != null))
+			{
+				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+			}
+		}
+	}
+}
+#pragma warning restore 1591
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/AssemblyInfo.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/AssemblyInfo.cs	(revision 3072)
@@ -0,0 +1,36 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.Services.Deployment.DataAccess")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HeuristicLab.Services.Deployment.DataAccess")]
+[assembly: AssemblyCopyright("Copyright ©  2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8716dca8-fe60-472e-a331-c0b534557e01")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.Designer.cs	(revision 3072)
@@ -0,0 +1,37 @@
+﻿//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.50727.4200
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HeuristicLab.Services.Deployment.DataAccess.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=HEUREKA01\\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integra" +
+            "ted Security=True")]
+        public string HeuristicLab_PluginStoreConnectionString {
+            get {
+                return ((string)(this["HeuristicLab_PluginStoreConnectionString"]));
+            }
+        }
+    }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/Properties/Settings.settings	(revision 3072)
@@ -0,0 +1,14 @@
+﻿<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="HeuristicLab.Services.Deployment.DataAccess.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="HeuristicLab_PluginStoreConnectionString" Type="(Connection string)" Scope="Application">
+      <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
+&lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
+  &lt;ConnectionString&gt;Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True&lt;/ConnectionString&gt;
+  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+&lt;/SerializableConnectionString&gt;</DesignTimeValue>
+      <Value Profile="(Default)">Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>
Index: /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment.DataAccess/3.3/app.config	(revision 3072)
@@ -0,0 +1,10 @@
+﻿<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <configSections>
+    </configSections>
+    <connectionStrings>
+        <add name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_PluginStoreConnectionString"
+            connectionString="Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True"
+            providerName="System.Data.SqlClient" />
+    </connectionStrings>
+</configuration>
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/Admin.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/Admin.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/Admin.cs	(revision 3072)
@@ -0,0 +1,27 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.Text;
+using HeuristicLab.Services.Deployment.DataAccess;
+using System.Security.Permissions;
+
+namespace HeuristicLab.Services.Deployment {
+  // NOTE: If you change the class name "Admin" here, you must also update the reference to "Admin" in App.config.
+  public class Admin : IAdmin {
+    #region IAdmin Members
+    [PrincipalPermission(SecurityAction.Demand, Role = "Managers")]
+    public void DeployProduct(ProductDescription product) {
+      var store = new PluginStore();
+      store.Persist(product);
+    }
+    [PrincipalPermission(SecurityAction.Demand, Role = "Managers")]
+    public void DeployPlugin(PluginDescription plugin, byte[] zipFile) {
+      var store = new PluginStore();
+      store.Persist(plugin, zipFile);
+    }
+
+    #endregion
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/App.config
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/App.config	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/App.config	(revision 3072)
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <configSections>
+  </configSections>
+  <system.diagnostics>
+  </system.diagnostics>
+  <connectionStrings>
+    <remove name="LocalSqlServer" />
+    <add connectionString="Server=SERVDEV;Database=HeuristicLab.Deployment;Integrated Security=SSPI" name="HeuristicLab.Services.Deployment.DataAccess.Properties.Settings.HeuristicLab_PluginStoreConnectionString" />
+    <add name="MyLocalSQLServer" connectionString="Initial Catalog=aspnetdb;data source=localhost;Integrated Security=SSPI;" />
+  </connectionStrings>
+  <system.web>
+    <compilation debug="false" />
+    <membership defaultProvider="MySqlMembershipProvider">
+      <providers>
+        <clear />
+        <add name="MySqlMemberShipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="MyLocalSqlServer" enablePasswordReset="false" enablePasswordRetrieval="false" passwordFormat="Hashed" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"  minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="8" />
+      </providers>
+    </membership>
+    <authentication mode="Windows" />
+    <roleManager enabled="true" defaultProvider="MySqlRoleProvider">
+      <providers>
+        <clear />
+        <add name="MySqlRoleProvider" connectionStringName="MyLocalSQLServer" applicationName="HeuristicLab.Deployment" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+      </providers>
+    </roleManager>
+  </system.web>
+  <!-- When deploying the service library project, the content of the config file must be added to the host's 
+  app.config file. System.Configuration does not support config files for libraries. -->
+  <system.serviceModel>
+    <bindings>
+      <wsHttpBinding>
+        <binding name="DefaultWsHttpBinding" maxBufferPoolSize="10000000" maxReceivedMessageSize="1000000">
+          <readerQuotas maxDepth="1000" maxStringContentLength="16000" maxArrayLength="10000000" maxBytesPerRead="10000000" maxNameTableCharCount="16000" />
+          <security mode="Message">
+            <message clientCredentialType="UserName" />
+          </security>
+        </binding>
+      </wsHttpBinding>
+      <mexHttpBinding>
+        <binding name="DefaultMexHttpBinding" />
+      </mexHttpBinding>
+    </bindings>
+    <diagnostics performanceCounters="Default">
+      <messageLogging logMalformedMessages="false" logMessagesAtTransportLevel="false" />
+    </diagnostics>
+    <services>
+      <service behaviorConfiguration="HeuristicLab.Services.Deployment.UpdateBehavior" name="HeuristicLab.Services.Deployment.Update">
+        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="DefaultWsHttpBinding" contract="HeuristicLab.Services.Deployment.IUpdate">
+        </endpoint>
+        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="DefaultMexHttpBinding" contract="IMetadataExchange" />
+      </service>
+      <service behaviorConfiguration="HeuristicLab.Services.Deployment.AdminBehavior" name="HeuristicLab.Services.Deployment.Admin">
+        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="DefaultWsHttpBinding" contract="HeuristicLab.Services.Deployment.IAdmin">
+        </endpoint>
+        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="DefaultMexHttpBinding" contract="IMetadataExchange" />
+      </service>
+    </services>
+    <behaviors>
+      <serviceBehaviors>
+        <behavior name="HeuristicLab.Services.Deployment.UpdateBehavior">
+          <serviceMetadata httpGetEnabled="true" />
+          <serviceDebug includeExceptionDetailInFaults="false" />
+          <serviceCredentials>
+            <serviceCertificate findValue="servdev.heuristiclab.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
+            <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="MySqlMembershipProvider" />
+          </serviceCredentials>
+          <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MySqlRoleProvider" />
+        </behavior>
+        <behavior name="HeuristicLab.Services.Deployment.AdminBehavior">
+          <serviceMetadata httpGetEnabled="true" />
+          <serviceDebug includeExceptionDetailInFaults="false" />
+          <serviceCredentials>
+            <serviceCertificate findValue="servdev.heuristiclab.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
+            <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="MySqlMembershipProvider" />
+          </serviceCredentials>
+          <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MySqlRoleProvider" />
+        </behavior>
+      </serviceBehaviors>
+    </behaviors>
+  </system.serviceModel>
+</configuration>
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/HeuristicLab.Services.Deployment.csproj
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/HeuristicLab.Services.Deployment.csproj	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/HeuristicLab.Services.Deployment.csproj	(revision 3072)
@@ -0,0 +1,107 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{30D8C5F1-CD3A-4EC1-907F-430177A03FBE}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>HeuristicLab.Services.Deployment</RootNamespace>
+    <AssemblyName>HeuristicLab.Services.Deployment</AssemblyName>
+    <ProjectTypeGuids>{3D9AD99F-2412-4246-B90B-4EAA41C64699};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <StartArguments>/client:"WcfTestClient.exe"</StartArguments>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data" />
+    <Reference Include="System.Data.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Runtime.Serialization">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.ServiceModel">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Transactions" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Admin.cs" />
+    <Compile Include="IAdmin.cs" />
+    <Compile Include="IUpdate.cs" />
+    <Compile Include="PluginDescription.cs" />
+    <Compile Include="PluginStore.cs" />
+    <Compile Include="ProductDescription.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+    <Compile Include="Update.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{3259AA49-8AA1-44D3-9025-A0B520596A8C}" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HeuristicLab.Services.Deployment.DataAccess\HeuristicLab.Services.Deployment.DataAccess.csproj">
+      <Project>{EBEBA863-DD05-4331-BE25-5758B15F7407}</Project>
+      <Name>HeuristicLab.Services.Deployment.DataAccess</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{3D9AD99F-2412-4246-B90B-4EAA41C64699}">
+        <WcfProjectProperties>
+          <AutoStart>True</AutoStart>
+        </WcfProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/IAdmin.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/IAdmin.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/IAdmin.cs	(revision 3072)
@@ -0,0 +1,19 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.Text;
+using HeuristicLab.Services.Deployment.DataAccess;
+
+namespace HeuristicLab.Services.Deployment {
+  // NOTE: If you change the interface name "IAdmin" here, you must also update the reference to "IAdmin" in App.config.
+  [ServiceContract]
+  public interface IAdmin {
+    [OperationContract]
+    void DeployProduct(ProductDescription product);
+
+    [OperationContract]
+    void DeployPlugin(PluginDescription plugin, byte[] zipFile);
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/IUpdate.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/IUpdate.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/IUpdate.cs	(revision 3072)
@@ -0,0 +1,22 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.Text;
+using HeuristicLab.Services.Deployment.DataAccess;
+
+namespace HeuristicLab.Services.Deployment {
+  // NOTE: If you change the interface name "IUpdate" here, you must also update the reference to "IUpdate" in App.config.
+  [ServiceContract]
+  public interface IUpdate {
+    [OperationContract]
+    byte[] GetPlugin(PluginDescription description);
+
+    [OperationContract]
+    IEnumerable<ProductDescription> GetProducts();
+
+    [OperationContract]
+    IEnumerable<PluginDescription> GetPlugins();
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginDescription.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginDescription.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginDescription.cs	(revision 3072)
@@ -0,0 +1,69 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Deployment {
+  [DataContract(Name = "PluginDescription")]
+  public class PluginDescription {
+
+    [DataMember(Name = "Name")]
+    private string name;
+    public string Name {
+      get { return name; }
+    }
+
+    [DataMember(Name = "Version")]
+    private Version version;
+    public Version Version {
+      get { return version; }
+    }
+
+    [DataMember(Name = "ContactName")]
+    private string contactName;
+    public string ContactName {
+      get { return contactName; }
+    }
+
+    [DataMember(Name = "ContactEmail")]
+    private string contactEmail;
+    public string ContactEmail {
+      get { return contactEmail; }
+    }
+
+    [DataMember(Name = "LicenseText")]
+    private string licenseText;
+    public string LicenseText {
+      get { return licenseText; }
+    }
+
+    [DataMember(Name = "Dependencies")]
+    private List<PluginDescription> dependencies;
+    public List<PluginDescription> Dependencies {
+      get { return dependencies; }
+    }
+
+    public PluginDescription(string name, Version version, IEnumerable<PluginDescription> dependencies, 
+      string contactName, string contactEmail, string license) {
+      if (string.IsNullOrEmpty(name)) throw new ArgumentException("name is empty");
+      if (version == null || dependencies == null ||
+        contactName == null || contactEmail == null ||
+        license == null) throw new ArgumentNullException();
+      this.name = name;
+      this.version = version;
+      this.dependencies = new List<PluginDescription>(dependencies);
+      this.licenseText = license;
+      this.contactName = contactName;
+      this.contactEmail = contactEmail;
+    }
+
+    public PluginDescription(string name, Version version)
+      : this(name, version, Enumerable.Empty<PluginDescription>()) {
+    }
+
+    public PluginDescription(string name, Version version, IEnumerable<PluginDescription> dependencies)
+      : this(name, version, dependencies, string.Empty, string.Empty, string.Empty) {
+    }
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginStore.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginStore.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/PluginStore.cs	(revision 3072)
@@ -0,0 +1,244 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Data.Common;
+using System.Transactions;
+using System.Data.SqlClient;
+using HeuristicLab.Services.Deployment.DataAccess;
+
+namespace HeuristicLab.Services.Deployment {
+  public class PluginStore {
+
+    public PluginStore() {
+    }
+
+    #region context creating members
+    public IEnumerable<ProductDescription> Products {
+      get {
+        using (var ctx = new PluginStoreClassesDataContext()) {
+          return (from p in ctx.Products
+                  let plugins = from pair in ctx.ProductPlugins
+                                from plugin in ctx.Plugins
+                                where pair.ProductId == p.Id
+                                where plugin.Id == pair.PluginId
+                                select plugin
+                  select MakeProductDescription(ctx, p, plugins)).ToList();
+        }
+      }
+    }
+
+    public IEnumerable<PluginDescription> Plugins {
+      get {
+        using (var ctx = new PluginStoreClassesDataContext()) {
+          return (from plugin in ctx.Plugins
+                  select MakePluginDescription(ctx, plugin)).ToList();
+        }
+      }
+    }
+
+    public byte[] PluginFile(PluginDescription pluginDescription) {
+      using (var ctx = new PluginStoreClassesDataContext()) {
+        return GetExistingPlugin(ctx, pluginDescription.Name, pluginDescription.Version).PluginPackage.Data.ToArray();
+      }
+    }
+
+    public void Persist(PluginDescription pluginDescription, byte[] pluginPackage) {
+      using (var ctx = new PluginStoreClassesDataContext()) {
+        try {
+          using (var transaction = new TransactionScope()) {
+            Plugin pluginEntity = InsertOrUpdatePlugin(ctx, pluginDescription);
+            if (pluginEntity.PluginPackage == null) {
+              // insert
+              pluginEntity.PluginPackage = MakePluginPackage(pluginEntity, pluginPackage);
+            } else {
+              // update
+              pluginEntity.PluginPackage.Data = pluginPackage;
+            }
+            ctx.SubmitChanges();
+            transaction.Complete();
+          }
+        }
+        catch (SqlException ex) {
+          throw new ArgumentException("Something went wrong while trying to persist plugin", ex);
+        }
+        catch (InvalidOperationException ex) {
+          throw new ArgumentException("Something went wrong while trying to persist plugin", ex);
+        }
+      }
+    }
+
+    public void Persist(ProductDescription productDescription) {
+      using (var ctx = new PluginStoreClassesDataContext()) {
+        try {
+          using (var transaction = new TransactionScope()) {
+            foreach (var plugin in productDescription.Plugins) {
+              var pluginEntity = GetExistingPlugin(ctx, plugin.Name, plugin.Version);
+              UpdatePlugin(ctx, pluginEntity, plugin);
+            }
+            InsertOrUpdateProduct(ctx, productDescription);
+            ctx.SubmitChanges();
+            transaction.Complete();
+          }
+        }
+        catch (SqlException ex) {
+          throw new ArgumentException("Something went wrong while trying to persist product", ex);
+        }
+        catch (InvalidOperationException ex) {
+          throw new ArgumentException("Something went wrong while trying to persist product", ex);
+        }
+      }
+    }
+
+    #endregion
+
+    #region insert/update product
+    private void InsertOrUpdateProduct(PluginStoreClassesDataContext ctx, ProductDescription product) {
+      var productEntity = (from p in ctx.Products
+                           where p.Name == product.Name
+                           where p.Version == product.Version.ToString()
+                           select p).FirstOrDefault() ?? MakeProductFromDescription(product);
+
+      if (productEntity.Id <= 0) {
+        ctx.Products.InsertOnSubmit(productEntity);
+        ctx.SubmitChanges();
+      }
+
+      DeleteOldPlugins(ctx, productEntity);
+
+      foreach (var plugin in product.Plugins) {
+        var existingPlugin = GetExistingPlugin(ctx, plugin.Name, plugin.Version);
+        ProductPlugin prodPlugin = new ProductPlugin();
+        prodPlugin.PluginId = existingPlugin.Id;
+        prodPlugin.ProductId = productEntity.Id;
+        ctx.ProductPlugins.InsertOnSubmit(prodPlugin);
+      }
+    }
+
+    private void DeleteOldPlugins(PluginStoreClassesDataContext ctx, Product productEntity) {
+      var oldPlugins = (from p in ctx.ProductPlugins
+                        where p.ProductId == productEntity.Id
+                        select p).ToList();
+      ctx.ProductPlugins.DeleteAllOnSubmit(oldPlugins);
+      ctx.SubmitChanges();
+    }
+    #endregion
+
+    #region insert/update plugins
+    private Plugin InsertOrUpdatePlugin(PluginStoreClassesDataContext ctx, PluginDescription pluginDescription) {
+      var pluginEntity = (from p in ctx.Plugins
+                          where p.Name == pluginDescription.Name
+                          where p.Version == pluginDescription.Version.ToString()
+                          select p).FirstOrDefault() ?? MakePluginFromDescription(pluginDescription);
+
+      if (pluginEntity.Id <= 0) {
+        ctx.Plugins.InsertOnSubmit(pluginEntity);
+        ctx.SubmitChanges();
+      }
+
+      UpdatePlugin(ctx, pluginEntity, pluginDescription);
+      return pluginEntity;
+    }
+
+    private void UpdatePlugin(PluginStoreClassesDataContext ctx, Plugin pluginEntity, PluginDescription pluginDescription) {
+      // update plugin data
+      pluginEntity.License = pluginDescription.LicenseText;
+      pluginEntity.ContactName = pluginDescription.ContactName;
+      pluginEntity.ContactEmail = pluginDescription.ContactEmail;
+
+      // delete cached entry
+      if (pluginDescriptions.ContainsKey(pluginEntity)) pluginDescriptions.Remove(pluginEntity);
+
+      DeleteOldDependencies(ctx, pluginEntity);
+
+      foreach (var dependency in pluginDescription.Dependencies) {
+        var dependencyEntity = GetExistingPlugin(ctx, dependency.Name, dependency.Version);
+        Dependency d = new Dependency();
+        d.PluginId = pluginEntity.Id;
+        d.DependencyId = dependencyEntity.Id;
+        ctx.Dependencies.InsertOnSubmit(d);
+      }
+    }
+
+
+
+    private void DeleteOldDependencies(PluginStoreClassesDataContext ctx, Plugin pluginEntity) {
+      var oldDependencies = (from dep in ctx.Dependencies
+                             where dep.PluginId == pluginEntity.Id
+                             select dep).ToList();
+
+      ctx.Dependencies.DeleteAllOnSubmit(oldDependencies);
+      ctx.SubmitChanges();
+    }
+    #endregion
+
+    #region product <-> productDescription transformation
+    private ProductDescription MakeProductDescription(PluginStoreClassesDataContext ctx, Product p, IQueryable<Plugin> plugins) {
+      var desc = new ProductDescription(p.Name, new Version(p.Version), from plugin in plugins
+                                                                        select MakePluginDescription(ctx, plugin));
+      return desc;
+    }
+    private Product MakeProductFromDescription(ProductDescription desc) {
+      var product = new Product();
+      product.Name = desc.Name;
+      product.Version = desc.Version.ToString();
+      return product;
+    }
+    #endregion
+
+    #region plugin <-> pluginDescription transformation
+    // cache for plugin descriptions
+    private Dictionary<Plugin, PluginDescription> pluginDescriptions = new Dictionary<Plugin, PluginDescription>();
+    private PluginDescription MakePluginDescription(PluginStoreClassesDataContext ctx, Plugin plugin) {
+      if (!pluginDescriptions.ContainsKey(plugin)) {
+        // no cached description -> create new
+        var desc = new PluginDescription(plugin.Name,
+          new Version(plugin.Version),
+          from dep in GetDependencies(ctx, plugin)
+          select MakePluginDescription(ctx, dep),
+          plugin.ContactName ?? string.Empty,
+          plugin.ContactEmail ?? string.Empty,
+          plugin.License ?? string.Empty
+          );
+        pluginDescriptions[plugin] = desc;
+      }
+      return pluginDescriptions[plugin];
+    }
+
+    private Plugin MakePluginFromDescription(PluginDescription pluginDescription) {
+      var plugin = new Plugin();
+      plugin.Name = pluginDescription.Name;
+      plugin.Version = pluginDescription.Version.ToString();
+      plugin.ContactName = pluginDescription.ContactName;
+      plugin.ContactEmail = pluginDescription.ContactEmail;
+      plugin.License = pluginDescription.LicenseText;
+      return plugin;
+    }
+
+    private PluginPackage MakePluginPackage(Plugin plugin, byte[] pluginPackage) {
+      var package = new PluginPackage();
+      package.Data = pluginPackage;
+      package.PluginId = plugin.Id;
+      return package;
+    }
+
+    #endregion
+
+    #region helper queries
+    private Plugin GetExistingPlugin(PluginStoreClassesDataContext ctx, string name, Version version) {
+      return (from p in ctx.Plugins
+              where p.Name == name
+              where p.Version == version.ToString()
+              select p).Single();
+    }
+
+    private IEnumerable<Plugin> GetDependencies(PluginStoreClassesDataContext ctx, Plugin plugin) {
+      return from pair in ctx.Dependencies
+             from dependency in ctx.Plugins
+             where pair.PluginId == plugin.Id
+             where pair.DependencyId == dependency.Id
+             select dependency;
+    }
+    #endregion
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/ProductDescription.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/ProductDescription.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/ProductDescription.cs	(revision 3072)
@@ -0,0 +1,38 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.Serialization;
+
+namespace HeuristicLab.Services.Deployment {
+  [DataContract(Name = "ProductDescription")]
+  public class ProductDescription {
+    [DataMember(Name = "Name")]
+    private string name;
+    public string Name {
+      get { return name; }
+    }
+
+    [DataMember(Name = "Version")]
+    private Version version;
+    public Version Version {
+      get { return version; }
+    }
+
+    [DataMember(Name = "Plugins")]
+    private IEnumerable<PluginDescription> plugins;
+    public IEnumerable<PluginDescription> Plugins {
+      get { return plugins; }
+    }
+
+    public ProductDescription(string name, Version version, IEnumerable<PluginDescription> plugins) {
+      if (string.IsNullOrEmpty(name)) throw new ArgumentException("name is empty");
+      if (version == null || plugins == null) throw new ArgumentNullException();
+      this.name = name;
+      this.version = version;
+      this.plugins = new List<PluginDescription>(plugins).AsReadOnly();
+    }
+
+    public ProductDescription(string name, Version version) : this(name, version, Enumerable.Empty<PluginDescription>()) { }
+  }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/AssemblyInfo.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/AssemblyInfo.cs	(revision 3072)
@@ -0,0 +1,36 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HeuristicLab.Services.Deployment")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HeuristicLab.Services.Deployment")]
+[assembly: AssemblyCopyright("Copyright ©  2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("227d8d28-de87-417c-8b61-f001a7850f5c")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.Designer.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.Designer.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.Designer.cs	(revision 3072)
@@ -0,0 +1,37 @@
+﻿//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.50727.4200
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HeuristicLab.Services.Deployment.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
+        [global::System.Configuration.DefaultSettingValueAttribute("Data Source=HEUREKA01\\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integra" +
+            "ted Security=True")]
+        public string HeuristicLab_PluginStoreConnectionString {
+            get {
+                return ((string)(this["HeuristicLab_PluginStoreConnectionString"]));
+            }
+        }
+    }
+}
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.settings
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.settings	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/Properties/Settings.settings	(revision 3072)
@@ -0,0 +1,14 @@
+﻿<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="HeuristicLab.Services.Deployment.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="HeuristicLab_PluginStoreConnectionString" Type="(Connection string)" Scope="Application">
+      <DesignTimeValue Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
+&lt;SerializableConnectionString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
+  &lt;ConnectionString&gt;Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True&lt;/ConnectionString&gt;
+  &lt;ProviderName&gt;System.Data.SqlClient&lt;/ProviderName&gt;
+&lt;/SerializableConnectionString&gt;</DesignTimeValue>
+      <Value Profile="(Default)">Data Source=HEUREKA01\SQLEXPRESS;Initial Catalog=HeuristicLab.PluginStore;Integrated Security=True</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>
Index: /trunk/sources/HeuristicLab.Services.Deployment/3.3/Update.cs
===================================================================
--- /trunk/sources/HeuristicLab.Services.Deployment/3.3/Update.cs	(revision 3072)
+++ /trunk/sources/HeuristicLab.Services.Deployment/3.3/Update.cs	(revision 3072)
@@ -0,0 +1,31 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.Text;
+using HeuristicLab.Services.Deployment.DataAccess;
+
+namespace HeuristicLab.Services.Deployment {
+  // NOTE: If you change the class name "Update" here, you must also update the reference to "Update" in App.config.
+  public class Update : IUpdate {
+    #region IUpdate Members
+
+    public byte[] GetPlugin(PluginDescription description) {
+      PluginStore store = new PluginStore();
+      return store.PluginFile(description);
+    }
+
+
+    public IEnumerable<ProductDescription> GetProducts() {
+      PluginStore store = new PluginStore();
+      return store.Products;
+    }
+
+    public IEnumerable<PluginDescription> GetPlugins() {
+      PluginStore store = new PluginStore();
+      return store.Plugins;
+    }
+    #endregion
+  }
+}
