added config and multi env ui update

Change-Id: I024ce098018a307094dcafae6e2fdeb02d123ae9
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfig.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfig.java
index 398b707..82363bc 100644
--- a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfig.java
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfig.java
@@ -20,4 +20,5 @@
 
    List<ResolutionMethod> getValidResolutions();
 
+   List<MultiEnvTarget> getMultiEnvTargets();
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfigData.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfigData.java
index 719c49e..3063c7a 100644
--- a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfigData.java
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoConfigData.java
@@ -18,6 +18,7 @@
 public class DispoConfigData implements DispoConfig {
 
    List<ResolutionMethod> validResolutions;
+   List<MultiEnvTarget> multiEnvTargets;
 
    public DispoConfigData() {
 
@@ -27,9 +28,17 @@
       this.validResolutions = validResolutions;
    }
 
+   public void setMultiEnvTargets(List<MultiEnvTarget> multiEnvTargets) {
+      this.multiEnvTargets = multiEnvTargets;
+   }
+
    @Override
    public List<ResolutionMethod> getValidResolutions() {
       return validResolutions;
    }
 
+   @Override
+   public List<MultiEnvTarget> getMultiEnvTargets() {
+      return multiEnvTargets;
+   }
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSet.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSet.java
index 641f246..5eeacf4 100644
--- a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSet.java
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSet.java
@@ -36,4 +36,8 @@
    String getRerunList();
 
    Date getTime();
+
+   Boolean getIsMultiEnv();
+
+   MultiEnvSettings getMultiEnvSettings();
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSetData.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSetData.java
index b987e1d..e705479 100644
--- a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSetData.java
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoSetData.java
@@ -14,12 +14,14 @@
 import java.util.Date;
 import java.util.List;
 import javax.xml.bind.annotation.XmlRootElement;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 
 /**
  * @author Angel Avila
  */
 
 @XmlRootElement(name = "DispoSetData")
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class DispoSetData extends DispoSetDescriptorData implements DispoSet {
 
    private String guid;
@@ -30,6 +32,8 @@
    private String ciSet;
    private String rerunList;
    private Date time;
+   private Boolean isMultiEnv;
+   private MultiEnvSettings multiEnvSetting;
 
    public DispoSetData() {
 
@@ -105,4 +109,22 @@
    public void setTime(Date time) {
       this.time = time;
    }
+
+   @Override
+   public Boolean getIsMultiEnv() {
+      return isMultiEnv;
+   }
+
+   public void setIsMultiEnv(boolean isMultiEnv) {
+      this.isMultiEnv = isMultiEnv;
+   }
+
+   @Override
+   public MultiEnvSettings getMultiEnvSettings() {
+      return multiEnvSetting;
+   }
+
+   public void setMultiEnvSettings(MultiEnvSettings multiEnvSetting) {
+      this.multiEnvSetting = multiEnvSetting;
+   }
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoStrings.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoStrings.java
index 255f9ad..f66507d 100644
--- a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoStrings.java
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/DispoStrings.java
@@ -54,7 +54,7 @@
 
    public static final String DeletedDiscrepancy = "Deleted Discrepancy";
 
-   public static final String Dispo_Config_Art = "Dispo_Config";
+   public static final String Dispo_Config_Art = "Program Config";
 
    public static final String Test_Unit_Resolution = "Test_Script";
    public static final String Exception_Handling_Resolution = "Exception_Handling";
@@ -83,4 +83,7 @@
    public static final String STATE_WARNING = "Warnings";
    public static final String STATE_FAIL = "Failed";
 
+   public static final String CONFIG_RESOLUTION_METHODS_KEY = "RESOLUTION_METHODS";
+   public static final String CONFIG_MULTI_ENV_OPTION_KEY = "MULTI_ENV_OPTIONS";
+
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvSettings.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvSettings.java
new file mode 100644
index 0000000..3f21b09
--- /dev/null
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvSettings.java
@@ -0,0 +1,20 @@
+/*
+ * Created on Dec 1, 2018
+ *
+ * PLACE_YOUR_DISTRIBUTION_STATEMENT_RIGHT_HERE
+ */
+package org.eclipse.osee.disposition.model;
+
+public class MultiEnvSettings {
+
+   MultiEnvTarget multiEnvTarget;
+
+   public MultiEnvTarget getMultiEnvTarget() {
+      return multiEnvTarget;
+   }
+
+   public void setMultiEnvTarget(MultiEnvTarget multiEnvTarget) {
+      this.multiEnvTarget = multiEnvTarget;
+   }
+
+}
diff --git a/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvTarget.java b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvTarget.java
new file mode 100644
index 0000000..8efcd5e
--- /dev/null
+++ b/plugins/org.eclipse.osee.disposition.rest.model/src/org/eclipse/osee/disposition/model/MultiEnvTarget.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Boeing.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.disposition.model;
+
+/**
+ * @author Angel Avila
+ */
+public class MultiEnvTarget {
+
+   private String name;
+   private String path;
+
+   public String getName() {
+      return name;
+   }
+
+   public void setName(String name) {
+      this.name = name;
+   }
+
+   public String getPath() {
+      return path;
+   }
+
+   public void setPath(String value) {
+      this.path = value;
+   }
+
+}
diff --git a/plugins/org.eclipse.osee.disposition.rest/META-INF/MANIFEST.MF b/plugins/org.eclipse.osee.disposition.rest/META-INF/MANIFEST.MF
index 6beb135..e754994 100644
--- a/plugins/org.eclipse.osee.disposition.rest/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.osee.disposition.rest/META-INF/MANIFEST.MF
@@ -13,6 +13,7 @@
  javax.ws.rs,
  javax.ws.rs.core,
  javax.ws.rs.ext,
+ org.codehaus.groovy.util;version="2.4.6",
  org.codehaus.jackson,
  org.codehaus.jackson.annotate,
  org.codehaus.jackson.map,
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoApi.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoApi.java
index 45b0fa6..d119131 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoApi.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoApi.java
@@ -81,6 +81,8 @@
 
    void copyDispoSetCoverage(BranchId sourceBranch, Long sourceCoverageUuid, BranchId destBranch, String destSetId, CopySetParams params, String userName);
 
+   boolean editDispoConfig(BranchId branch, DispoConfig config, String userName);
+
    // Deletes
 
    boolean deleteDispoSet(BranchId branch, String dispoSetId, String userName);
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoImporterApi.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoImporterApi.java
index a86cc2b..1ec416e 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoImporterApi.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/DispoImporterApi.java
@@ -14,6 +14,7 @@
 import java.util.List;
 import java.util.Map;
 import org.eclipse.osee.disposition.model.DispoItem;
+import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.OperationReport;
 
 /**
@@ -21,6 +22,6 @@
  */
 public interface DispoImporterApi {
 
-   List<DispoItem> importDirectory(Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report);
+   List<DispoItem> importDirectory(DispoSet set, Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report);
 
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoApiImpl.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoApiImpl.java
index 86d3a7d..9ea4050 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoApiImpl.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoApiImpl.java
@@ -139,6 +139,12 @@
    }
 
    @Override
+   public boolean editDispoConfig(BranchId branch, DispoConfig config, String userName) {
+      ArtifactReadable author = getQuery().findUserByName(userName);
+      return getWriter().editDispoConfig(author, branch, config);
+   }
+
+   @Override
    public Long createDispoProgram(String name, String userName) {
       ArtifactReadable author = getQuery().findUserByName(userName);
       return getWriter().createDispoProgram(author, name);
@@ -553,7 +559,7 @@
             }
 
             List<DispoItem> itemsFromParse =
-               importer.importDirectory(nameToItemMap, new File(setToEdit.getImportPath()), report);
+               importer.importDirectory(setToEdit, nameToItemMap, new File(setToEdit.getImportPath()), report);
 
             List<DispoItem> itemsToCreate = new ArrayList<>();
             List<DispoItem> itemsToEdit = new ArrayList<>();
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoConfigArtifact.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoConfigArtifact.java
index 31d634c..a94a2d1 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoConfigArtifact.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoConfigArtifact.java
@@ -12,9 +12,12 @@
 
 import java.util.List;
 import org.eclipse.osee.disposition.model.DispoConfig;
+import org.eclipse.osee.disposition.model.DispoConfigData;
+import org.eclipse.osee.disposition.model.MultiEnvTarget;
 import org.eclipse.osee.disposition.model.ResolutionMethod;
 import org.eclipse.osee.disposition.rest.util.DispoUtil;
 import org.eclipse.osee.framework.core.enums.CoreAttributeTypes;
+import org.eclipse.osee.framework.core.util.JsonUtil;
 import org.eclipse.osee.orcs.data.ArtifactReadable;
 
 /**
@@ -33,11 +36,26 @@
       List<String> attributes = artifact.getAttributeValues(CoreAttributeTypes.GeneralStringData);
       String resolutions = "";
       for (String attribute : attributes) {
-         if (attribute.startsWith("RESOLUTION_METHODS")) {
+         if (attribute.startsWith("{")) {
+            return JsonUtil.readValue(attribute, DispoConfigData.class).getValidResolutions();
+         } else if (attribute.startsWith("RESOLUTION_METHODS")) {
             resolutions = attribute.replaceFirst("RESOLUTION_METHODS=", "");
-            break;
          }
       }
       return DispoUtil.jsonStringToList(resolutions, ResolutionMethod.class);
    }
+
+   @Override
+   public List<MultiEnvTarget> getMultiEnvTargets() {
+      List<String> attributes = artifact.getAttributeValues(CoreAttributeTypes.GeneralStringData);
+      String resolutionsJson = "";
+      for (String attribute : attributes) {
+         if (attribute.startsWith("{")) {
+            return JsonUtil.readValue(attribute, DispoConfigData.class).getMultiEnvTargets();
+         } else if (attribute.startsWith("RESOLUTION_METHODS")) {
+            resolutionsJson = "[]";
+         }
+      }
+      return DispoUtil.jsonStringToList(resolutionsJson, MultiEnvTarget.class);
+   }
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoSetArtifact.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoSetArtifact.java
index e2fc520..2536604 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoSetArtifact.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoSetArtifact.java
@@ -13,6 +13,8 @@
 import java.util.Date;
 import java.util.List;
 import org.eclipse.osee.disposition.model.DispoSet;
+import org.eclipse.osee.disposition.model.MultiEnvSettings;
+import org.eclipse.osee.disposition.model.MultiEnvTarget;
 import org.eclipse.osee.disposition.model.Note;
 import org.eclipse.osee.disposition.model.OperationReport;
 import org.eclipse.osee.disposition.rest.DispoConstants;
@@ -83,4 +85,21 @@
    public Date getTime() {
       return artifact.getSoleAttributeValue(DispoConstants.DispoTime, null);
    }
+
+   @Override
+   public Boolean getIsMultiEnv() {
+      return true;
+      // need to create attribute
+   }
+
+   @Override
+   public MultiEnvSettings getMultiEnvSettings() {
+      MultiEnvSettings t = new MultiEnvSettings();
+      MultiEnvTarget m = new MultiEnvTarget();
+      m.setName("ARINC");
+      m.setPath("src_host/vx_4080_653");
+      t.setMultiEnvTarget(m);
+      return t;
+      // need to create attribute
+   }
 }
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoWriter.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoWriter.java
index 96e6f6b..6b37d00 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoWriter.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/DispoWriter.java
@@ -12,6 +12,7 @@
 
 import java.util.Collection;
 import java.util.List;
+import org.eclipse.osee.disposition.model.DispoConfig;
 import org.eclipse.osee.disposition.model.DispoItem;
 import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.DispoStorageMetadata;
@@ -44,4 +45,6 @@
 
    String createDispoReport(BranchId branch, ArtifactReadable author, String contens, String operationTitle);
 
+   boolean editDispoConfig(ArtifactReadable author, BranchId branch, DispoConfig config);
+
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/OrcsStorageImpl.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/OrcsStorageImpl.java
index cbc29c9..8d87a5f 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/OrcsStorageImpl.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/OrcsStorageImpl.java
@@ -30,12 +30,15 @@
 import org.eclipse.osee.disposition.model.DispoItem;
 import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.DispoStorageMetadata;
+import org.eclipse.osee.disposition.model.DispoStrings;
+import org.eclipse.osee.disposition.model.MultiEnvSettings;
 import org.eclipse.osee.disposition.model.Note;
 import org.eclipse.osee.disposition.model.OperationReport;
 import org.eclipse.osee.disposition.rest.DispoConstants;
 import org.eclipse.osee.disposition.rest.internal.importer.coverage.CoverageUtil;
 import org.eclipse.osee.disposition.rest.util.DispoUtil;
 import org.eclipse.osee.framework.core.data.ArtifactId;
+import org.eclipse.osee.framework.core.data.ArtifactToken;
 import org.eclipse.osee.framework.core.data.Branch;
 import org.eclipse.osee.framework.core.data.BranchId;
 import org.eclipse.osee.framework.core.data.IArtifactType;
@@ -207,6 +210,46 @@
    }
 
    @Override
+   public boolean editDispoConfig(ArtifactReadable author, BranchId branch, DispoConfig config) {
+      boolean doesConfigExist = false;
+      ArtifactReadable configArt =
+         getQuery().fromBranch(branch).andNameEquals("Program Config").getResults().getOneOrDefault(
+            ArtifactReadable.SENTINEL);
+      if (configArt.isValid()) {
+         doesConfigExist = true;
+      }
+
+      TransactionBuilder tx = getTxFactory().createTransaction(branch, author, "Update Dispo Set");
+      if (doesConfigExist) {
+         List<String> attributes = configArt.getAttributeValues(CoreAttributeTypes.GeneralStringData);
+         if (attributes.isEmpty()) {
+            tx.createAttribute(configArt, CoreAttributeTypes.GeneralStringData, JsonUtil.toJson(config));
+         } else {
+            String legacyConfigString = "";
+            for (String attribute : attributes) {
+               if (attribute.startsWith(DispoStrings.CONFIG_RESOLUTION_METHODS_KEY)) {
+                  legacyConfigString = attribute;
+                  break;
+               }
+            }
+            if (Strings.isValid(legacyConfigString)) {
+               tx.setAttributesFromStrings(configArt, CoreAttributeTypes.GeneralStringData, legacyConfigString,
+                  JsonUtil.toJson(config));
+            } else {
+               tx.setAttributesFromStrings(configArt, CoreAttributeTypes.GeneralStringData, JsonUtil.toJson(config));
+            }
+         }
+      } else {
+         ArtifactToken createArtifact = tx.createArtifact(CoreArtifactTypes.GeneralData, DispoStrings.Dispo_Config_Art);
+         tx.createAttribute(createArtifact, CoreAttributeTypes.GeneralStringData, JsonUtil.toJson(config));
+      }
+      tx.commit();
+
+      return true;
+
+   }
+
+   @Override
    public Long createDispoProgram(ArtifactReadable author, String name) {
       String normalizedName = "(DISPO)" + name;
       IOseeBranch branch = IOseeBranch.create(normalizedName);
@@ -275,6 +318,8 @@
       String ciSet = newData.getCiSet();
       String rerunList = newData.getRerunList();
       Date time = newData.getTime();
+      MultiEnvSettings multiEnvSettings = newData.getMultiEnvSettings();
+      boolean isMuliEnv = newData.getIsMultiEnv();
 
       List<Note> notesList = new LinkedList<>();
       if (newData.getNotesList() != null) {
@@ -301,6 +346,14 @@
       if (time != null && !time.equals(origSetAs.getTime())) {
          tx.setSoleAttributeValue(dispoSet, DispoConstants.DispoTime, time);
       }
+      if (multiEnvSettings != null && !multiEnvSettings.equals(origSetAs.getMultiEnvSettings())) {
+         System.out.println(multiEnvSettings);
+         //         tx.setSoleAttributeFromStream(dispoSet, attributeType, stream);
+      }
+      if (isMuliEnv != origSetAs.getIsMultiEnv()) {
+         System.out.println(isMuliEnv);
+         //         tx.setSoleAttributeFromStream(dispoSet, attributeType, stream);
+      }
       tx.commit();
    }
 
@@ -502,7 +555,7 @@
    @Override
    public DispoConfig findDispoConfig(BranchId branch) {
       ArtifactReadable config =
-         getQuery().fromBranch(branch).andNameEquals("Program Config").getResults().getOneOrDefault(
+         getQuery().fromBranch(branch).andNameEquals(DispoStrings.Dispo_Config_Art).getResults().getOneOrDefault(
             ArtifactReadable.SENTINEL);
 
       if (config.isInvalid()) {
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/MultiEnvCopier.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/MultiEnvCopier.java
index 82dbc1b..f5aa5af 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/MultiEnvCopier.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/MultiEnvCopier.java
@@ -17,7 +17,7 @@
 
 public class MultiEnvCopier {
 
-   public void copy(Map<String, Set<DispoItemData>> itemToMultiEnvTwins, Set<String> foundItems, OperationReport rerpot) {
+   public void copy(Map<String, String> itemToMultiEnvTwins, Set<String> foundItems, OperationReport rerpot) {
       for (String origItemName : foundItems) {
          Set<DispoItemData> twinItems = itemToMultiEnvTwins.get(origItemName);
          if (twinItems != null) {
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmoImporter.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmoImporter.java
index fab0c6f..bd014e8 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmoImporter.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmoImporter.java
@@ -24,6 +24,7 @@
 import org.eclipse.osee.disposition.model.DispoAnnotationData;
 import org.eclipse.osee.disposition.model.DispoItem;
 import org.eclipse.osee.disposition.model.DispoItemData;
+import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.DispoSummarySeverity;
 import org.eclipse.osee.disposition.model.OperationReport;
 import org.eclipse.osee.disposition.rest.DispoImporterApi;
@@ -50,7 +51,7 @@
    }
 
    @Override
-   public List<DispoItem> importDirectory(Map<String, DispoItem> exisitingItems, File tmoDirectory, OperationReport report) {
+   public List<DispoItem> importDirectory(DispoSet set, Map<String, DispoItem> exisitingItems, File tmoDirectory, OperationReport report) {
       List<DispoItem> toReturn = new LinkedList<>();
       if (tmoDirectory.isDirectory()) {
 
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmzImporter.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmzImporter.java
index 5a533db..7728a7d 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmzImporter.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/TmzImporter.java
@@ -28,6 +28,7 @@
 import org.eclipse.osee.disposition.model.Discrepancy;
 import org.eclipse.osee.disposition.model.DispoItem;
 import org.eclipse.osee.disposition.model.DispoItemData;
+import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.OperationReport;
 import org.eclipse.osee.disposition.rest.DispoImporterApi;
 import org.eclipse.osee.disposition.rest.internal.DispoDataFactory;
@@ -54,7 +55,7 @@
    }
 
    @Override
-   public List<DispoItem> importDirectory(Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report) {
+   public List<DispoItem> importDirectory(DispoSet set, Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report) {
       List<DispoItem> toReturn = new LinkedList<>();
       if (!filesDir.exists() || !filesDir.isDirectory()) {
          throw new OseeArgumentException("Input directory does not exists or is not a directory [%s]",
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/coverage/LisFileParser.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/coverage/LisFileParser.java
index bbb30c1..8c3c22e 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/coverage/LisFileParser.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/importer/coverage/LisFileParser.java
@@ -35,7 +35,9 @@
 import org.eclipse.osee.disposition.model.DispoAnnotationData;
 import org.eclipse.osee.disposition.model.DispoItem;
 import org.eclipse.osee.disposition.model.DispoItemData;
+import org.eclipse.osee.disposition.model.DispoSet;
 import org.eclipse.osee.disposition.model.DispoSummarySeverity;
+import org.eclipse.osee.disposition.model.MultiEnvSettings;
 import org.eclipse.osee.disposition.model.OperationReport;
 import org.eclipse.osee.disposition.rest.DispoApiConfiguration;
 import org.eclipse.osee.disposition.rest.DispoImporterApi;
@@ -84,8 +86,9 @@
    private final Set<String> alreadyUsedDatIds = new HashSet<>();
    private final Set<String> alreadyUsedFileNames = new HashSet<>();
    // multi env
-   private final Map<String, Set<DispoItemData>> origNameToTwinItems = new HashMap<>();
+   private final Map<String, String> origNameToTwinItemNames = new HashMap<>();
    private final Set<String> itemsFoundInDatFiles = new HashSet<>();
+   private final Map<String, String> itemNameToDatId = new HashMap<>();
 
    private final DispoConnector dispoConnector;
    private final DispoApiConfiguration config;
@@ -99,7 +102,8 @@
    }
 
    @Override
-   public List<DispoItem> importDirectory(Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report) {
+   public List<DispoItem> importDirectory(DispoSet set, Map<String, DispoItem> exisitingItems, File filesDir, OperationReport report) {
+      MultiEnvSettings multiEnvSettings = set.getMultiEnvSettings();
       vCastDir = filesDir.getAbsolutePath() + File.separator + "vcast";
       File f = new File(vCastDir + File.separator + "cover.db");
 
@@ -113,7 +117,7 @@
       HashMap<String, File> nameToFileMap = createNameToFileMap(report);
 
       for (VCastInstrumentedFile instrumentedFile : instrumentedFiles) {
-         processInstrumented(dataStore, instrumentedFile, nameToFileMap, report);
+         processInstrumented(multiEnvSettings, dataStore, instrumentedFile, nameToFileMap, report);
       }
 
       Collection<VCastResult> results = getResultFiles(dataStore);
@@ -172,7 +176,7 @@
 
       // make a flag for multi env operation, could be time consuming
       MultiEnvCopier multiEnvCopier = new MultiEnvCopier();
-      multiEnvCopier.copy(origNameToTwinItems, itemsFoundInDatFiles, report);
+      multiEnvCopier.copy(origNameToTwinItemNames, itemsFoundInDatFiles, report);
 
       for (DispoItem item : toReturn) {
          if (item.getStatus().equalsIgnoreCase("incomplete")) {
@@ -257,7 +261,7 @@
       return instrumentedFiles;
    }
 
-   private void processInstrumented(VCastDataStore dataStore, VCastInstrumentedFile instrumentedFile, HashMap<String, File> nameToFileMap, OperationReport report) {
+   private void processInstrumented(MultiEnvSettings multiEnvSettings, VCastDataStore dataStore, VCastInstrumentedFile instrumentedFile, HashMap<String, File> nameToFileMap, OperationReport report) {
       VCastSourceFileJoin sourceFile = null;
       try {
          sourceFile = dataStore.getSourceFileJoin(instrumentedFile);
@@ -299,8 +303,8 @@
             }
 
             for (VCastFunction function : functions) {
-               processFunction(instrumentedFile, lisFileParser, fileNum, dataStore, instrumentedFile, function,
-                  dataStore.getIsMCDC(), report);
+               processFunction(multiEnvSettings, instrumentedFile, lisFileParser, fileNum, dataStore, instrumentedFile,
+                  function, dataStore.getIsMCDC(), report);
             }
          } else {
             report.addEntry("VCast", String.format("Could not find file: %s", normalizedName), ERROR);
@@ -309,7 +313,7 @@
       }
    }
 
-   private void processFunction(VCastInstrumentedFile lisFile, VCastLisFileParser lisFileParser, int fileNum, VCastDataStore dataStore, VCastInstrumentedFile instrumentedFile, VCastFunction function, boolean isMCDCFile, OperationReport report) {
+   private void processFunction(MultiEnvSettings multiEnvSettings, VCastInstrumentedFile lisFile, VCastLisFileParser lisFileParser, int fileNum, VCastDataStore dataStore, VCastInstrumentedFile instrumentedFile, VCastFunction function, boolean isMCDCFile, OperationReport report) {
       int functionNum = function.getFindex();
       DispoItemData newItem = new DispoItemData();
       newItem.setAnnotationsList(new ArrayList<DispoAnnotationData>());
@@ -332,14 +336,10 @@
 
       String datId = generateDatId(fileNum, functionNum);
       datIdToItem.put(datId, newItem);
+      //      checkForMultiEnvRename(fileNum, instrumentedFile, newItem);
+      checkForMultiEnv(multiEnvSettings, fileNum, instrumentedFile, newItem);
+      itemNameToDatId.put(newItem.getName(), datId);
 
-      checkForMultiEnvRename(fileNum, instrumentedFile, newItem);
-         Set<DispoItemData> twinItems = origNameToTwinItems.get(nameWithoutId);
-         if (twinItems == null) {
-            twinItems = new HashSet<>();
-         }
-         twinItems.add(newItem);
-         origNameToTwinItems.put(nameWithoutId, twinItems);
       Collection<VCastStatementCoverage> statementCoverageItems = Collections.emptyList();
 
       try {
@@ -362,6 +362,22 @@
       newItem.setDiscrepanciesList(discrepancies);
    }
 
+   private void checkForMultiEnv(MultiEnvSettings settings, int fileNum, VCastInstrumentedFile instrumentedFile, DispoItemData item) {
+      String nameWithouId = item.getName();
+      checkForMultiEnvRename(fileNum, instrumentedFile, item);
+      String pathToCheck = translateToRegex(settings.getMultiEnvTarget().getPath());
+      if (instrumentedFile.getLISFile().matches(pathToCheck)) {
+         origNameToTwinItemNames.put(item.getName(), nameWithouId);
+      } else {
+         origNameToTwinItemNames.put(nameWithouId, item.getName());
+      }
+   }
+
+   private String translateToRegex(String s) {
+      String directorySlash = s.replaceAll("(\\\\|/)", "(\\\\\\\\|/)");
+      return String.format(".*?%s.*", directorySlash);
+   }
+
    private void checkForMultiEnvRename(int fileNum, VCastInstrumentedFile instrumentedFile, DispoItemData newItem) {
       if (instrumentedFile.getLISFile().matches(".*?/vcast/.*?\\d+\\.2\\.lis")) {
          // Making assumption here that the only time we wanna collect these duplicate "twin" files is when vcast tags them with the name
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/report/ExportSet.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/report/ExportSet.java
index 0a42caa..137e536 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/report/ExportSet.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/internal/report/ExportSet.java
@@ -117,7 +117,6 @@
       Map<String, Integer> resolutionToCount = new HashMap<>();
       Map<String, Pair<Integer, Integer>> unitToCovered = new HashMap<>();
       DispoConfig config = dispoApi.getDispoConfig(branch);
-      config.getValidResolutions();
       for (ResolutionMethod resolution : config.getValidResolutions()) {
          resolutionToCount.put(resolution.getText(), 0);
       }
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/resources/DispoConfigResource.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/resources/DispoConfigResource.java
index 8b19e2c..4c70e35 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/resources/DispoConfigResource.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/resources/DispoConfigResource.java
@@ -10,13 +10,19 @@
  *******************************************************************************/
 package org.eclipse.osee.disposition.rest.resources;
 
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.eclipse.osee.disposition.model.DispoConfig;
+import org.eclipse.osee.disposition.model.DispoConfigData;
 import org.eclipse.osee.disposition.rest.DispoApi;
+import org.eclipse.osee.disposition.rest.DispoRoles;
 import org.eclipse.osee.framework.core.data.BranchId;
 
 /**
@@ -32,6 +38,22 @@
       this.branch = branch;
    }
 
+   @PUT
+   @RolesAllowed(DispoRoles.ROLES_ADMINISTRATOR)
+   @Consumes(MediaType.APPLICATION_JSON)
+   public Response updateConfig(DispoConfigData config, @QueryParam("userName") String userName) {
+      Response.Status status;
+      boolean editDispoConfig = dispoApi.editDispoConfig(branch, config, userName);
+      if (editDispoConfig) {
+         status = Status.OK;
+      } else {
+         status = Status.INTERNAL_SERVER_ERROR;
+      }
+
+      return Response.status(status).build();
+
+   }
+
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getDispoConfig() {
diff --git a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/util/DispoUtil.java b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/util/DispoUtil.java
index 0b0824f..6b735fe 100644
--- a/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/util/DispoUtil.java
+++ b/plugins/org.eclipse.osee.disposition.rest/src/org/eclipse/osee/disposition/rest/util/DispoUtil.java
@@ -230,6 +230,7 @@
    public static DispoConfigData configArtToConfigData(DispoConfig config) {
       DispoConfigData configData = new DispoConfigData();
       configData.setValidResolutions(config.getValidResolutions());
+      configData.setMultiEnvTargets(config.getMultiEnvTargets());
 
       return configData;
    }
@@ -241,6 +242,7 @@
       defaultMethod.setValue("INVALID");
       defaultMethod.setIsDefault(true);
       configData.setValidResolutions(Collections.singletonList(defaultMethod));
+      configData.setMultiEnvTargets(Collections.emptyList());
 
       return configData;
    }
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/css/admin.css b/plugins/org.eclipse.osee.web.ui/src/dispo/css/admin.css
index f9adb77..a7e6eee 100644
--- a/plugins/org.eclipse.osee.web.ui/src/dispo/css/admin.css
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/css/admin.css
@@ -72,6 +72,10 @@
     max-width: 0px;
 }
 
-.state-btn {
-	'btn btn-default';
+.config-json-invalid{
+	background-color: pink;
 }
+
+input#multiEnvTargePathInput {
+    border: none;
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/js/adminController.js b/plugins/org.eclipse.osee.web.ui/src/dispo/js/adminController.js
index f117951..e99359b 100644
--- a/plugins/org.eclipse.osee.web.ui/src/dispo/js/adminController.js
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/js/adminController.js
@@ -11,27 +11,28 @@
     'MultiItemEdit',
     'Config',
     'uiGridConstants',
-        function($scope, $rootScope,  $modal, $filter, Program, Set, Report, CopySet, CopySetCoverage, MultiItemEdit, Config, uiGridConstants) {
-            $scope.readOnly = true;
-            $scope.programSelection = null;
-            $scope.modalShown = false;
-            $scope.primarySet = "";
-            $scope.secondarySet = "";
-            $scope.sets = [];
-            $scope.addNew = false;
-            $scope.newProgramName = ""
-            $scope.selectedItems = [];
-            $scope.isRunningOperation = false;
-            $scope.cachedValue = "";
-            $scope.types = [];
-            $scope.isCoverage = $rootScope.type == 'codeCoverage';
-            $scope.programs = Program.query();
-            
-            var isPrimary = function(importState) {
-                return row.entity.importState != "Warnings" && row.entity.importState != "Failed";
+    function ($scope, $rootScope, $modal, $filter, Program, Set, Report, CopySet, CopySetCoverage, MultiItemEdit, Config, uiGridConstants) {
+        $scope.readOnly = true;
+        $scope.programSelection = null;
+        $scope.modalShown = false;
+        $scope.primarySet = "";
+        $scope.secondarySet = "";
+        $scope.sets = [];
+        $scope.addNew = false;
+        $scope.newProgramName = ""
+        $scope.selectedItems = [];
+        $scope.isRunningOperation = false;
+        $scope.cachedValue = "";
+        $scope.types = [];
+        $scope.isCoverage = $rootScope.type == 'codeCoverage';
+        $scope.programs = Program.query();
+        $scope.config = {};
+
+        var isPrimary = function (importState) {
+            return row.entity.importState != "Warnings" && row.entity.importState != "Failed";
         }
-		        
-        $scope.createNewProgram = function() {
+
+        $scope.createNewProgram = function () {
             if ($scope.newProgramName != "") {
                 var loadingModal = $scope.showLoadingModal();
                 var newProgram = new Program;
@@ -39,26 +40,26 @@
                 newProgram.$save({
                     name: $scope.newProgramName,
                     userName: $rootScope.cachedName
-                }, function() {
+                }, function () {
                     $scope.newProgramName = "";
                     $scope.addNew = false;
                     loadingModal.close();
                     $scope.programs = Program.query();
-                }, function() {
+                }, function () {
                     loadingModal.close();
                     alert("Oops...Something went wrong");
                 });
             }
         }
-        
-        $scope.toggleAddNew = function() {
-            if($scope.addNew) {
+
+        $scope.toggleAddNew = function () {
+            if ($scope.addNew) {
                 $scope.addNew = false;
             } else {
                 $scope.addNew = true;
             }
         }
-        
+
         $scope.gridOptions = {
             data: 'sets',
             selectedItems: $scope.selectedItems,
@@ -72,8 +73,8 @@
         var importCellTmpl = '<button width="50px" class="btn btn-primary" ng-disabled="row.entity.processingImport" ng-click="grid.appScope.importSet(row.entity)">Import</button>';
         var exportCellTmpl = '<button width="50px" class="btn btn-primary" ng-disabled="row.entity.processingImport" ng-click="grid.appScope.exportSet(row.entity)">Export</button>';
         var lastOperationCellTmpl = '<id="stateButton" button width="99%" ng-disabled="row.entity.processingImport || row.entity.gettingDetails" ng-class="{btn: true, \'btn-primary\': \'isPrimary(row.entity.importState)\',' +
-        '\'btn-warning\': row.entity.importState == \'Warnings\', \'btn-danger\': row.entity.importState == \'Failed\', \'btn-success\': row.entity.importState == \'OK\'}" ng-click="grid.appScope.getSetImportDetails(row.entity)">{{row.entity.importState}}</button>';
-         
+            '\'btn-warning\': row.entity.importState == \'Warnings\', \'btn-danger\': row.entity.importState == \'Failed\', \'btn-success\': row.entity.importState == \'OK\'}" ng-click="grid.appScope.getSetImportDetails(row.entity)">{{row.entity.importState}}</button>';
+
         $scope.columnDefs1 = [{
             field: 'name',
             displayName: "Import",
@@ -96,12 +97,12 @@
             enableCellEdit: false,
             cellTemplate: lastOperationCellTmpl
         }, {
-		            field: 'time',
-		            displayName: "Timestamp",
-		            width: '15%',
-		            enableColumnMenu: false,
-		            enableCellEdit: false
-		        }, {
+            field: 'time',
+            displayName: "Timestamp",
+            width: '15%',
+            enableColumnMenu: false,
+            enableCellEdit: false
+        }, {
             field: 'name',
             displayName: "Name",
             width: '12%',
@@ -129,19 +130,19 @@
             enableCellEdit: false,
             cellTemplate: exportCellTmpl
         }, {
-        	field: 'name',
+            field: 'name',
             displayName: "Last Operation",
             width: '10%',
             enableColumnMenu: false,
             enableCellEdit: false,
-        	cellTemplate: lastOperationCellTmpl
+            cellTemplate: lastOperationCellTmpl
         }, {
-		            field: 'time',
-		            displayName: "Timestamp",
-		            width: '15%',
-		            enableColumnMenu: false,
-		            enableCellEdit: false
-		        }, {
+            field: 'time',
+            displayName: "Timestamp",
+            width: '15%',
+            enableColumnMenu: false,
+            enableCellEdit: false
+        }, {
             field: 'name',
             displayName: "Name",
             width: '12%',
@@ -159,14 +160,14 @@
             enableColumnMenu: false,
             cellTemplate: dellCellTmpl
         }];
-        
+
         $scope.gridOptions.columnDefs = $scope.columnDefs1;
-        
-        $scope.gridOptions.onRegisterApi = function(gridApi) {
+
+        $scope.gridOptions.onRegisterApi = function (gridApi) {
 
             $scope.subGridApi = gridApi;
 
-            gridApi.edit.on.afterCellEdit($scope, function(rowEntity, colDef, newValue, oldValue) {
+            gridApi.edit.on.afterCellEdit($scope, function (rowEntity, colDef, newValue, oldValue) {
                 if (oldValue != newValue) {
                     $scope.editSet(rowEntity);
                 }
@@ -174,11 +175,11 @@
 
         };
 
-        $scope.toggleModal = function() {
+        $scope.toggleModal = function () {
             $scope.modalShown = !$scope.modalShown
         };
 
-        $scope.toggleReadOnly = function() {
+        $scope.toggleReadOnly = function () {
             if ($scope.readOnly) {
                 $scope.gridOptions.columnDefs = $scope.columnDefs2;
                 $scope.readOnly = false;
@@ -188,92 +189,93 @@
             }
         };
 
-        $scope.generateReport = function() {
+        $scope.generateReport = function () {
             var requst = [];
             requst.push(
-              "/dispo/program/",
-              $scope.programSelection,
-              "/admin/report?primarySet=",
-              $scope.primarySet,
-              "&secondarySet=",
-              $scope.secondarySet
-              );
+                "/dispo/program/",
+                $scope.programSelection,
+                "/admin/report?primarySet=",
+                $scope.primarySet,
+                "&secondarySet=",
+                $scope.secondarySet
+            );
             var url = requst.join("");
-            
+
             window.open(url);
         }
-        
-        $scope.getSetImportDetails = function(set) {
+
+        $scope.getSetImportDetails = function (set) {
             $scope.setAnnotationsSummaryGrid();
             set.gettingDetails = true;
             Set.get({
                 programId: $scope.programSelection,
                 setId: set.guid
-            }, function(data) {
+            }, function (data) {
                 set.gettingDetails = false;
                 $scope.operationSummary = data.operationSummary;
                 $scope.summaryGrid.data = $scope.operationSummary.entries;
                 set.importState = data.importState;
-			        	set.time = data.time;
-            }, function(data) {
+                set.time = data.time;
+            }, function (data) {
                 set.gettingDetails = false;
                 alert("Could not update Set from Server");
             })
         }
-        
-        $scope.getCiSetDetails = function(set) {
+
+        $scope.getCiSetDetails = function (set) {
             $scope.setConfigureCiSetSummaryGrid();
             set.gettingDetails = true;
             Set.get({
                 programId: $scope.programSelection,
                 setId: set.guid
-            }, function(data) {
+            }, function (data) {
                 set.gettingDetails = false;
                 $scope.operationSummary = set.operationSummary;
                 $scope.summaryGrid.data = set.operationSummary.entries;
                 set.importState = data.importState;
-            }, function(data) {
+            }, function (data) {
                 set.gettingDetails = false;
                 alert("Could not update Set from Server");
             })
         }
-        
-        $scope.getMassSendDispoItemStatus = function(set) {
+
+        $scope.getMassSendDispoItemStatus = function (set) {
             set.gettingDetails = true;
             Set.get({
                 programId: $scope.programSelection,
                 setId: set.guid
-            }, function(data) {
+            }, function (data) {
                 set.gettingDetails = false;
                 $scope.operationSummary = data.operationSummary;
                 $scope.summaryGrid.data = data.operationSummary.entries;
                 set.importState = data.importState;
-            }, function(data) {
+            }, function (data) {
                 set.gettingDetails = false;
                 alert("Could not update Set from Server");
             })
         }
-        
+
         $scope.updateProgram = function updateProgram() {
             var loadingModal = $scope.showLoadingModal();
             $scope.loading = true;
             $scope.items = {};
-            Set.query({ 
+            Set.query({
                 programId: $scope.programSelection,
                 type: $rootScope.type
-            }, function(data) {
+            }, function (data) {
                 loadingModal.close();
                 $scope.sets = data;
-            }, function(data) {
+            }, function (data) {
                 loadingModal.close();
                 alert(data.statusText);
             });
-			            Config.get({
-                            programId: $scope.programSelection,
-                            type: $rootScope.type
-                        }, function(data) {
-                       	      $scope.types = data.validResolutions;
-                        }); 
+            Config.get({
+                programId: $scope.programSelection,
+                type: $rootScope.type
+            }, function (data) {
+                $scope.config = data;
+                $scope.types = data.validResolutions;
+            });
         };
 
         $scope.editSet = function editSet(set) {
@@ -284,7 +286,7 @@
             }, set);
         };
 
-        $scope.massAssignTeam = function(setId, team, namesList) {
+        $scope.massAssignTeam = function (setId, team, namesList) {
             $scope.isRunningOperation = true;
             var loadingModal = $scope.showLoadingModal();
             var multiItemEditOp = new MultiItemEdit;
@@ -292,25 +294,25 @@
             multiItemEditOp.team = team;
             multiItemEditOp.setId = setId;
             multiItemEditOp.userName = $rootScope.cachedName;
-            
+
             multiItemEditOp.$save({
                 programId: $scope.programSelection,
                 userName: $rootScope.cachedName
-            }, function(data) {
+            }, function (data) {
                 $scope.isRunningOperation = false;
                 loadingModal.close();
                 $scope.getSetImportDetails($scope.getSetById(setId));
-            }, function() {
+            }, function () {
                 $scope.isRunningOperation = false;
                 loadingModal.close();
                 alert("Oops...Something went wrong");
                 // boo
             })
         };
-        
-        $scope.getSetById = function(setId) {
-            for(var i =0; i < $scope.sets.length; i++) {
-                if($scope.sets[i].guid == setId) {
+
+        $scope.getSetById = function (setId) {
+            for (var i = 0; i < $scope.sets.length; i++) {
+                if ($scope.sets[i].guid == setId) {
                     return $scope.sets[i];
                 }
             }
@@ -320,7 +322,7 @@
         $scope.deleteSet = function deleteSet(set) {
             var loadingModal = $scope.openConfirmDeleteModal(set);
         }
-        
+
         $scope.importSet = function importSet(set) {
             var newSet = new Set;
             newSet.operation = "Import";
@@ -329,26 +331,26 @@
                 programId: $scope.programSelection,
                 setId: set.guid,
                 userName: $rootScope.cachedName
-            }, newSet, function(data){
+            }, newSet, function (data) {
                 set.processingImport = false;
-		            	set.time = data.time;
+                set.time = data.time;
                 $scope.getSetImportDetails(set);
-            }, function() {
+            }, function () {
                 set.processingImport = false;
-		            	set.time = new Date();
+                set.time = new Date();
                 $scope.getSetImportDetails(set);
             });
         };
-        
+
         $scope.exportSet = function importSet(set) {
             var requst = [];
             requst.push(
-              "/dispo/program/",
-              $scope.programSelection,
-              "/admin/export?primarySet=",
-              set.guid,
-              "&option=detailed"
-              );
+                "/dispo/program/",
+                $scope.programSelection,
+                "/admin/export?primarySet=",
+                set.guid,
+                "&option=detailed"
+            );
             var url = requst.join("");
 
             window.open(url);
@@ -363,14 +365,14 @@
                 newSet.$save({
                     programId: $scope.programSelection,
                     userName: $rootScope.cachedName
-                }, function(data) {
-		                	data.time = formatDate(data.time);
+                }, function (data) {
+                    data.time = formatDate(data.time);
                     $scope.sets.push(data);
                 });
             }
         };
 
-        $scope.copySet = function(inputs)	 {			        		        	
+        $scope.copySet = function (inputs) {
             $scope.isRunningOperation = true;
             var destinationSet = $scope.getSetById(inputs.destinationSet);
             var copySetOp = new CopySet;
@@ -378,35 +380,35 @@
             copySetOp.categoryParam = inputs.categoryParam;
             copySetOp.assigneeParam = inputs.assigneeParam;
             copySetOp.noteParam = inputs.noteParam;
-            copySetOp.sourceProgram = inputs.sourceProgram;		        	
-            
+            copySetOp.sourceProgram = inputs.sourceProgram;
+
             copySetOp.$save({
                 programId: $scope.programSelection,
                 destinationSet: inputs.destinationSet,
                 sourceProgram: inputs.sourceProgram,
                 sourceSet: inputs.sourceSet,
                 userName: $rootScope.cachedName
-            }, function(data) {
+            }, function (data) {
                 $scope.isRunningOperation = false;
                 $scope.getSetImportDetails($scope.getSetById(inputs.destinationSet));
-            }, function(data) {
+            }, function (data) {
                 $scope.isRunningOperation = false;
                 $scope.getSetImportDetails($scope.getSetById(inputs.destinationSet));
             });
         };
 
-        $scope.configureCiSet = function setCiSet(inputs) {		        			        	       	
+        $scope.configureCiSet = function setCiSet(inputs) {
             $scope.isRunningOperation = true;
-            var localSet =  $scope.getSetById(inputs.ciDispositionSet);
+            var localSet = $scope.getSetById(inputs.ciDispositionSet);
             var ciSetWas = localSet.ciSet;
             if (inputs.ciSet != "") {
                 localSet.ciSet = inputs.ciSet;
             }
             Set.update({
-            programId: $scope.programSelection,
-            setId: inputs.ciDispositionSet,
-            userName: $rootScope.cachedName
-            }, localSet, function(data) {
+                programId: $scope.programSelection,
+                setId: inputs.ciDispositionSet,
+                userName: $rootScope.cachedName
+            }, localSet, function (data) {
                 var message = "";
                 if (inputs.ciSet == "") {
                     message = "Nothing was entered for a new CI Set name.";
@@ -419,14 +421,14 @@
 
                 localSet.operationSummary.entries.length = 0;
                 localSet.operationSummary.entries.push(
-                        {"message":message,"ciSetIs":data.ciSet,"ciSetWas":ciSetWas,"dispoSet":localSet.name}
+                    { "message": message, "ciSetIs": data.ciSet, "ciSetWas": ciSetWas, "dispoSet": localSet.name }
                 );
 
                 $scope.getCiSetDetails(localSet);
             });
         };
-        		        		        
-        $scope.massSendDispoItemStatus = function massSendDispoItemStatus (set) {
+
+        $scope.massSendDispoItemStatus = function massSendDispoItemStatus(set) {
             $scope.setAnnotationsSummaryGrid();
             $scope.isRunningOperation = true;
             var newSet = $scope.getSetById(set.ciDispositionSet);
@@ -435,58 +437,92 @@
                 programId: $scope.programSelection,
                 setId: set.ciDispositionSet,
                 userName: $rootScope.cachedName
-            }, newSet, function(data) {            	
-                $scope.isRunningOperation = false;		            			            	
+            }, newSet, function (data) {
+                $scope.isRunningOperation = false;
                 $scope.getMassSendDispoItemStatus(newSet);
             });
         };
-        		        
-		        $scope.rerunReportStatus = function rerunReportStatus (input) {
-		        	var newSet = $scope.getSetById(input.rerunDispositionSet);
-		        	var request = [];
-		        	request.push(
-		        	  "/dispo/program/",
-		        	  $scope.programSelection,
-		        	  "/admin/rerun?primarySet=",
-		        	  newSet.guid
-		        	  );
-		        	var url = request.join("");
-		            window.open(url);
-		        };
-		        
-		        
-		        
+
+        $scope.rerunReportStatus = function rerunReportStatus(input) {
+            var newSet = $scope.getSetById(input.rerunDispositionSet);
+            var request = [];
+            request.push(
+                "/dispo/program/",
+                $scope.programSelection,
+                "/admin/rerun?primarySet=",
+                newSet.guid
+            );
+            var url = request.join("");
+            window.open(url);
+        };
+
+        $scope.updateMultiEnv = function (set) {
+            $scope.isRunningOperation = true;
+            Set.update({
+                programId: $scope.programSelection,
+                setId: set.guid,
+                userName: $rootScope.cachedName
+            }, set, function (data) {
+                $scope.isRunningOperation = false;
+            }, function (data) {
+                $scope.isRunningOperation = false;
+                alert("Could not save Set to Server");
+            });
+        }
+
+        $scope.updateDispoConfig = function updateDispoConfig(config) {
+            $scope.isRunningOperation = true;
+            Config.update({
+                programId: $scope.programSelection,
+                userName: $rootScope.cachedName
+            }, config, function (data) {
+                Config.get({
+                    programId: $scope.programSelection,
+                    type: $rootScope.type
+                }, function (data) {
+                    $scope.isRunningOperation = false;
+                    $scope.config = data;
+                    $scope.types = data.validResolutions;
+                });
+            }, function (data) {
+                $scope.isRunningOperation = false;
+                alert("Could not update Set from Server");
+            })
+        }
+
+
+
         // -------------------- Summary Grids ----------------------\\
         var filterBarPlugin = {
-            init: function(scope, grid) {
+            init: function (scope, grid) {
                 filterBarPlugin.scope = scope;
                 filterBarPlugin.grid = grid;
-                $scope.$watch(function() {
+                $scope.$watch(function () {
                     var searchQuery = "";
-                    angular.forEach(filterBarPlugin.scope.columns, function(col) {
+                    angular.forEach(filterBarPlugin.scope.columns, function (col) {
                         if (col.visible && col.filterText) {
                             var filterText = (col.filterText.indexOf('*') == 0 ? col.filterText.replace('*', '') : "^" + col.filterText) + ";";
                             searchQuery += col.displayName + ": " + filterText;
                         }
                     });
                     return searchQuery;
-                }, function(searchQuery) {
+                }, function (searchQuery) {
                     filterBarPlugin.scope.$parent.filterText = searchQuery;
                     filterBarPlugin.grid.searchProvider.evalFilter();
                 });
             },
             scope: undefined,
             grid: undefined,
-        };		                
+        };
 
-        $scope.summaryGrid = {}	
-        
-        $scope.summaryGrid.onRegisterApi = function(gridApi) {
+        $scope.summaryGrid = {}
+
+        $scope.summaryGrid.onRegisterApi = function (gridApi) {
             $scope.subGridApi = gridApi;
-        };	
-        
-        $scope.setConfigureCiSetSummaryGrid = function() {
-            $scope.summaryGrid = { 
+        };
+
+        $scope.setConfigureCiSetSummaryGrid = function () {
+            $scope.summaryGrid = {
                 configureCiSet: true,
                 data: 'operationSummary.entries',
                 enableHighlighting: true,
@@ -495,28 +531,28 @@
                 showFilter: true,
                 enableFiltering: true,
                 headerRowHeight: 60, // give room for filter bar
-            
-                columnDefs : [{
+
+                columnDefs: [{
                     field: "dispoSet",
                     displayName: "Disposition Set",
                     width: '15%',
-                },{
+                }, {
                     field: "ciSetWas",
                     displayName: "CI Set Was",
                     width: '15%',
-                },{
+                }, {
                     field: "ciSetIs",
                     displayName: "CI Set Is",
                     width: '15%',
-                },{
+                }, {
                     field: "message",
                     displayName: "Message",
                 }]
             }
         }
-        
+
         $scope.setAnnotationsSummaryGrid = function () {
-            $scope.summaryGrid = { 
+            $scope.summaryGrid = {
                 configureCiSet: true,
                 data: 'operationSummary.entries',
                 enableHighlighting: true,
@@ -525,43 +561,43 @@
                 showFilter: true,
                 enableFiltering: true,
                 headerRowHeight: 60, // give room for filter bar
-            
-                columnDefs : [{
+
+                columnDefs: [{
                     field: "severity",
                     displayName: "Severity",
                     width: '10%',
-                },{
+                }, {
                     field: "name",
                     displayName: "Name",
                     width: '20%',
-                },{
+                }, {
                     field: "message",
                     displayName: "Message",
                 }]
-            }		        	
+            }
         }
 
         // Loading Modal
-        $scope.showLoadingModal = function() {
+        $scope.showLoadingModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'loadingModal.html',
                 size: 'sm',
                 windowClass: 'needsRerunModal',
                 backdrop: 'static'
             });
-            
+
             return modalInstance;
         }
-        
-        $scope.copySetCoverage = function(inputs)	 {
+
+        $scope.copySetCoverage = function (inputs) {
             var copySetOp = new CopySetCoverage;
-        	
+
             copySetOp.$save({
                 programId: $scope.programSelection,
                 destinationSet: inputs.destinationSet,
                 sourceBranch: inputs.sourceBranch,
                 sourcePackage: inputs.sourcePackage,
-            }, function(data) {
+            }, function (data) {
                 var reportUrl = data.operationStatus;
                 window.open(reportUrl);
                 console.log(data);
@@ -569,7 +605,7 @@
         }
 
         // Create Set Modal
-        $scope.createNewSetModal = function() {
+        $scope.createNewSetModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'popup.html',
                 controller: CreateSetModalCtrl,
@@ -577,40 +613,40 @@
                 windowClass: 'createSetModal'
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.createNewSet(inputs.name, inputs.path);
             });
         }
 
-        var CreateSetModalCtrl = function($scope, $modalInstance) {
+        var CreateSetModalCtrl = function ($scope, $modalInstance) {
             $scope.setName = "";
             $scope.importPath = "";
-            
-            $scope.ok = function() {
+
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.name = this.setName;
                 inputs.path = this.importPath;
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         };
-               
+
         // Mass Assign Modal
-        $scope.openMassAssignTeamModal = function() {
+        $scope.openMassAssignTeamModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'massAssignTeam.html',
                 controller: MassAssignTeamCtrl,
                 size: 'lg',
                 windowClass: 'massAssignTeamModal',
                 resolve: {
-                    sets: function() {
+                    sets: function () {
                         return $scope.sets;
                     },
-                    gridSelectedSetId: function() {
-                        if($scope.selectedItems.legnth > 0) {
+                    gridSelectedSetId: function () {
+                        if ($scope.selectedItems.length > 0) {
                             return $scope.selectedItems[0].guid;
                         } else {
                             return null;
@@ -619,92 +655,92 @@
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.massAssignTeam(inputs.setId, inputs.team, inputs.nameList);
             });
         }
 
-        var MassAssignTeamCtrl = function($scope, $modalInstance, gridSelectedSetId, sets) {	
+        var MassAssignTeamCtrl = function ($scope, $modalInstance, gridSelectedSetId, sets) {
             $scope.setsLocal = sets.slice();
             $scope.nameListAsString = "";
             $scope.team = "";
             $scope.setId = gridSelectedSetId;
 
-            $scope.ok = function() {
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.nameList = this.nameListAsString.split(",");
                 inputs.team = this.team;
                 inputs.setId = this.setId;
-                
+
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         }
-        
+
         // Copy Set Modal
-        $scope.openCopySetModal = function() {		        			        	
+        $scope.openCopySetModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'copySets.html',
                 controller: CopySetModalCtrl,
                 size: 'md',
                 windowClass: 'copySetModal',
                 resolve: {
-                    sets: function() {
+                    sets: function () {
                         return $scope.sets;
-                    }, 
-                    programs: function() {
+                    },
+                    programs: function () {
                         return $scope.programs;
-                    }, 
-                    showLoadingModal: function() {
+                    },
+                    showLoadingModal: function () {
                         return $scope.showLoadingModal;
-                    }, 
-                    currentlySelectedProgram: function() {
+                    },
+                    currentlySelectedProgram: function () {
                         return $scope.programSelection;
                     }
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.copySet(inputs);
             });
         }
-        
-        
-        var CopySetModalCtrl = function($scope, $modalInstance, programs, currentlySelectedProgram, sets, showLoadingModal) {
+
+
+        var CopySetModalCtrl = function ($scope, $modalInstance, programs, currentlySelectedProgram, sets, showLoadingModal) {
             $scope.setsLocal = sets.slice();
             $scope.programsLocal = programs.slice();
             $scope.setsLocalSource = sets.slice();
             $scope.sourceProgram = currentlySelectedProgram;
-            
-            $scope.updateProgramLocal = function() {
+
+            $scope.updateProgramLocal = function () {
                 var loadingModal = showLoadingModal();
                 $scope.loading = true;
                 Set.query({
                     programId: $scope.sourceProgram,
                     type: $rootScope.type
-                }, function(data) {
+                }, function (data) {
                     loadingModal.close();
                     $scope.setsLocalSource = data;
-                }, function(data) {
+                }, function (data) {
                     loadingModal.close();
                     alert(data.statusText);
                 });
             };
-            
-            $scope.annotationOptions = [{ value: 0, text: 'NONE'}, { value: 1, text: 'OVERRIDE'}];
-            $scope.categoryOptions = [{ value: 0, text: 'NONE'}, { value: 1, text: 'OVERRIDE'}, { value: 2, text: 'ONLY COPY IF DEST IS EMPTY'}, { value: 3, text: 'MERGE DEST AND SOURCE'}];
-            $scope.assigneeOptions = [{ value: 0, text: 'NONE'}, { value: 1, text: 'OVERRIDE'}, { value: 2, text: 'ONLY COPY IF DEST IS UNASSIGNED'}];
-            $scope.noteOptions = [{ value: 0, text: 'NONE'}, { value: 1, text: 'OVERRIDE'}, { value: 2, text: 'ONLY COPY IF DEST IS EMPTY'}, { value: 3, text: 'MERGE DEST AND SOURCE'}];
-            
+
+            $scope.annotationOptions = [{ value: 0, text: 'NONE' }, { value: 1, text: 'OVERRIDE' }];
+            $scope.categoryOptions = [{ value: 0, text: 'NONE' }, { value: 1, text: 'OVERRIDE' }, { value: 2, text: 'ONLY COPY IF DEST IS EMPTY' }, { value: 3, text: 'MERGE DEST AND SOURCE' }];
+            $scope.assigneeOptions = [{ value: 0, text: 'NONE' }, { value: 1, text: 'OVERRIDE' }, { value: 2, text: 'ONLY COPY IF DEST IS UNASSIGNED' }];
+            $scope.noteOptions = [{ value: 0, text: 'NONE' }, { value: 1, text: 'OVERRIDE' }, { value: 2, text: 'ONLY COPY IF DEST IS EMPTY' }, { value: 3, text: 'MERGE DEST AND SOURCE' }];
+
             $scope.annotationParam = 0;
             $scope.categoryParam = 0;
             $scope.assigneeParam = 0;
             $scope.noteParam = 0;
 
-            $scope.ok = function() {
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.destinationSet = this.destinationSet;
                 inputs.sourceProgram = this.sourceProgram;
@@ -713,77 +749,77 @@
                 inputs.categoryParam = this.categoryParam;
                 inputs.noteParam = this.noteParam;
                 inputs.assigneeParam = this.assigneeParam;
-                
+
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         };
-        
-        
+
+
         // Copy Coverage Modal
-        $scope.openCopyCoverageModal = function() {
+        $scope.openCopyCoverageModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'copySetCoverage.html',
                 controller: CopyCoverageModalCtrl,
                 size: 'md',
                 windowClass: 'copyCoverageModal',
                 resolve: {
-                    sets: function() {
+                    sets: function () {
                         return $scope.sets;
                     }
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.copySetCoverage(inputs);
             });
         }
-        
-        
-        var CopyCoverageModalCtrl = function($scope, $modalInstance, sets) {
+
+
+        var CopyCoverageModalCtrl = function ($scope, $modalInstance, sets) {
             $scope.setsLocal = angular.copy(sets);
-            
-            $scope.ok = function() {
+
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.destinationSet = this.destinationSet;
                 inputs.sourceBranch = this.sourceBranch;
                 inputs.sourcePackage = this.sourcePackage;
-                
+
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         };
-        
+
         // Confirm Delete Modal
-        $scope.openConfirmDeleteModal = function(set) {
+        $scope.openConfirmDeleteModal = function (set) {
             var modalInstance = $modal.open({
                 templateUrl: 'confirmDelete.html',
                 controller: ConfirmDeleteCtrl,
                 size: 'sm',
                 windowClass: 'confirmDeleteModal',
                 resolve: {
-                    selectedProgram: function() {
+                    selectedProgram: function () {
                         return $scope.programSelection;
                     },
-                    selectedSet: function() {
+                    selectedSet: function () {
                         return set;
-                    }              	
+                    }
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
-                if(inputs.isConfirmed) {
+            modalInstance.result.then(function (inputs) {
+                if (inputs.isConfirmed) {
                     Set.delete({
                         programId: inputs.program,
                         setId: inputs.set.guid,
                         userName: $rootScope.cachedName
-                    }, function() {
+                    }, function () {
                         var index = $scope.sets.indexOf(inputs.set);
                         if (index > -1) {
                             $scope.sets.splice(index, 1);
@@ -793,135 +829,259 @@
             });
         }
 
-        var ConfirmDeleteCtrl = function($scope, $modalInstance, selectedProgram, selectedSet) {
+        var ConfirmDeleteCtrl = function ($scope, $modalInstance, selectedProgram, selectedSet) {
             $scope.text = "";
-            
-            $scope.ok = function() {
+
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.isConfirmed = false;
                 inputs.program = selectedProgram;
                 inputs.set = selectedSet;
-                
-                if(this.text.toUpperCase() == "DELETE") {
+
+                if (this.text.toUpperCase() == "DELETE") {
                     inputs.isConfirmed = true;
                 }
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         };
-        
+
         // Configure/Set CI Set
-        $scope.openConfigureCiSetModal = function() {
+        $scope.openConfigureCiSetModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'configureCiSet.html',
                 controller: ConfigureCiSetCtrl,
                 size: 'sm',
                 windowClass: 'ConfigureCiSetModal',
                 resolve: {
-                    sets: function() {
+                    sets: function () {
                         return $scope.sets;
                     }
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.configureCiSet(inputs);
             });
         }
-        
-        var ConfigureCiSetCtrl = function($scope, $modalInstance, sets) {
-            $scope.ciSet =  "";
+
+        var ConfigureCiSetCtrl = function ($scope, $modalInstance, sets) {
+            $scope.ciSet = "";
             $scope.ciDispositionSet = "";
             $scope.setsLocal = angular.copy(sets);
-            $scope.ok = function() {
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.ciSet = this.ciSet
                 inputs.ciDispositionSet = this.dispositionSet;
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         }
-        
+
         // Mass Send Disposition Item Status
-        $scope.openMassSendDispoItemStatusModal = function() {
+        $scope.openMassSendDispoItemStatusModal = function () {
             var modalInstance = $modal.open({
                 templateUrl: 'massSendDispoItemStatus.html',
                 controller: MassSendDispoItemStatusCtrl,
                 size: 'sm',
                 windowClass: 'MassSendDispoItemStatusModal',
                 resolve: {
-                    sets: function() {
+                    sets: function () {
                         return $scope.sets;
                     }
                 }
             });
 
-            modalInstance.result.then(function(inputs) {
+            modalInstance.result.then(function (inputs) {
                 $scope.massSendDispoItemStatus(inputs);
             });
         }
-        
-        var MassSendDispoItemStatusCtrl = function($scope, $modalInstance, sets) {
+
+        var MassSendDispoItemStatusCtrl = function ($scope, $modalInstance, sets) {
             $scope.ciDispositionSet = "";
             $scope.setsLocal = angular.copy(sets);
-            $scope.ok = function() {
+            $scope.ok = function () {
                 var inputs = {};
                 inputs.ciDispositionSet = this.dispositionSet;
-		                $modalInstance.close(inputs);
-		            };
-
-		            $scope.cancel = function() {
-		                $modalInstance.dismiss('cancel');
-		            };
-		        }
-		        
-		        
-		        // Report Reruns
-		        $scope.openRerunReportStatusModal = function() {
-		        	 var modalInstance = $modal.open({
-			                templateUrl: 'rerunReportStatus.html',
-			                controller: RerunReportStatusCtrl,
-			                size: 'sm',
-			                windowClass: 'rerunReportStatusModal',
-			                resolve: {
-			                	sets: function() {
-			                		return $scope.sets;
-			                	},
-			                	types: function() {
-			                		return $scope.types;
-			                	}
-			                }
-			            });
-
-			            modalInstance.result.then(function(inputs) {
-			            	$scope.rerunReportStatus(inputs);
-			            });
-		        }
-		        
-		        var RerunReportStatusCtrl = function($scope, $modalInstance, sets, types) {
-		        	$scope.rerunDispositionSet = "";
-		        	$scope.setsLocal = angular.copy(sets);
-		        	$scope.typesLocal = angular.copy(types);
-		            $scope.ok = function() {
-		                var inputs = {};
-		                inputs.rerunDispositionSet = this.dispositionSet;
-		                inputs.rerunResolutionTypes = this.resolutionTypes;
                 $modalInstance.close(inputs);
             };
 
-            $scope.cancel = function() {
+            $scope.cancel = function () {
                 $modalInstance.dismiss('cancel');
             };
         }
-		        
-		        var formatDate = function(time) {
-		        	return $filter('date')(new Date(time), 'EEE MMM dd HH:mm:ss UTC yyyy');
-		        }
+
+
+        // Report Reruns
+        $scope.openRerunReportStatusModal = function () {
+            var modalInstance = $modal.open({
+                templateUrl: 'rerunReportStatus.html',
+                controller: RerunReportStatusCtrl,
+                size: 'sm',
+                windowClass: 'rerunReportStatusModal',
+                resolve: {
+                    sets: function () {
+                        return $scope.sets;
+                    },
+                    types: function () {
+                        return $scope.types;
+                    }
+                }
+            });
+
+            modalInstance.result.then(function (inputs) {
+                $scope.rerunReportStatus(inputs);
+            });
+        }
+
+        var RerunReportStatusCtrl = function ($scope, $modalInstance, sets, types) {
+            $scope.rerunDispositionSet = "";
+            $scope.setsLocal = angular.copy(sets);
+            $scope.typesLocal = angular.copy(types);
+            $scope.ok = function () {
+                var inputs = {};
+                inputs.rerunDispositionSet = this.dispositionSet;
+                inputs.rerunResolutionTypes = this.resolutionTypes;
+                $modalInstance.close(inputs);
+            };
+
+            $scope.cancel = function () {
+                $modalInstance.dismiss('cancel');
+            };
+        }
+
+        // Multi Env
+        $scope.openMultiEnvModal = function () {
+            var modalInstance = $modal.open({
+                templateUrl: '/dispo/views/multiEnv.html',
+                controller: MultiEnvCtrl,
+                size: 'sm',
+                windowClass: 'multiEnvModal',
+                resolve: {
+                    sets: function () {
+                        return $scope.sets;
+                    },
+                    multiEnvTargets: function () {
+                        return $scope.config.multiEnvTargets;
+                    }
+                }
+            });
+
+            modalInstance.result.then(function (inputs) {
+                $scope.editSet(inputs.set);
+            });
+        }
+
+        var MultiEnvCtrl = function ($scope, $modalInstance, sets, multiEnvTargets) {
+            $scope.setsLocal = angular.copy(sets);
+            $scope.multiEnvTargets = multiEnvTargets;
+
+            // Hack needed because angular compares object references so even though the Multi Env Target object from the set
+            // matches one of the Target objects from the multiEnvTargets array angular doesn't think they're the same object
+            // this is only needed to set the select option initially i.e on change of the Sets select
+            $scope.initTargetsSelect = function () {
+                for (var i = 0; i < multiEnvTargets.length; i++) {
+                    var targetFromSelectArray = multiEnvTargets[i];
+                    var targetFromSetObj = $scope.selectedSet.multiEnvSettings.multiEnvTarget
+                    if (targetFromSelectArray.name === targetFromSetObj.name && targetFromSelectArray.path === targetFromSetObj.path) {
+                        $scope.selectedSet.multiEnvSettings.multiEnvTarget = targetFromSelectArray;
+                        break;
+                    }
+                }
+            }
+
+            $scope.ok = function () {
+                var inputs = {
+                    set: this.selectedSet,
+                };
+                $modalInstance.close(inputs);
+            };
+
+            $scope.cancel = function () {
+                $modalInstance.dismiss('cancel');
+            };
+        }
+
+        // Dispo Config
+        $scope.openEditDispoConfigModal = function () {
+            var loadingModal = $scope.showLoadingModal();
+
+            Config.get({
+                programId: $scope.programSelection,
+                type: $rootScope.type
+            }, function (data) {
+                $scope.isRunningOperation = false;
+                $scope.config = data;
+                $scope.types = data.validResolutions;
+                loadingModal.close();
+
+                var modalInstance = $modal.open({
+                    templateUrl: '/dispo/views/editDispoConfig.html',
+                    controller: EditDispoConfigCtrl,
+                    size: 'lg',
+                    windowClass: 'editDispoConfigModal',
+                    resolve: {
+                        config: function () {
+                            return $scope.config;
+                        }
+                    }
+                });
+
+                modalInstance.result.then(function (inputs) {
+                    $scope.updateDispoConfig(inputs.config);
+                });
+            });
+        }
+
+        var EditDispoConfigCtrl = function ($scope, $modalInstance, config) {
+            $scope.validResolutionsJson = JSON.stringify(config.validResolutions);
+            $scope.multiEnvTargetsJson = JSON.stringify(config.multiEnvTargets);
+            $scope.ok = function () {
+                var inputs = {};
+                var newValidResolutions = JSON.parse(this.validResolutionsJson);
+                var newMultiEvnOptions = JSON.parse(this.multiEnvTargetsJson);
+                inputs.config = {
+                    validResolutions: newValidResolutions,
+                    multiEnvTargets: newMultiEvnOptions,
+                }
+                $modalInstance.close(inputs);
+            };
+
+            $scope.cancel = function () {
+                $modalInstance.dismiss('cancel');
+            };
+
+            $scope.isResolutionTypesValidJson = function () {
+                try {
+                    JSON.parse(this.validResolutionsJson);
+                } catch {
+                    return false;
+                }
+                return true;
+            }
+
+            $scope.ismultiEnvTargetsValidJson = function () {
+                try {
+                    JSON.parse(this.multiEnvTargetsJson);
+                } catch {
+                    return false;
+                }
+                return true;
+            }
+            $scope.isValidJson = function () {
+                return $scope.isResolutionTypesValidJson && $scope.ismultiEnvTargetsValidJson;
+
+            }
+        }
+
+        var formatDate = function (time) {
+            return $filter('date')(new Date(time), 'EEE MMM dd HH:mm:ss UTC yyyy');
+        }
     }
 ]);
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/js/dispoApp.js b/plugins/org.eclipse.osee.web.ui/src/dispo/js/dispoApp.js
index 34171a0..efe1805 100644
--- a/plugins/org.eclipse.osee.web.ui/src/dispo/js/dispoApp.js
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/js/dispoApp.js
@@ -231,6 +231,12 @@
     this.$get = ['$resource',
         function($resource) {
             var Config = $resource('/dispo/program/:programId/config', {}, {
+                update: {
+                    method: 'PUT',
+                    headers: {
+                        "Accept": "application/json"
+                    }
+                }
             });
             return Config;
         }
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/views/admin.html b/plugins/org.eclipse.osee.web.ui/src/dispo/views/admin.html
index 779e9da..50937f6 100644
--- a/plugins/org.eclipse.osee.web.ui/src/dispo/views/admin.html
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/views/admin.html
@@ -1,69 +1,89 @@
 <!doctype html>
 <html>
+
 <body>
-  <div class="container-fluid" id="content">
-    <div class="row">
-        <div class="col-md-2 col-md-offset-1">
-            <div class="row">
-                <h5>Select ATS Version</h5>
-                <select class="form-control" ng-model="programSelection" ng-change="updateProgram()" ng-options="obj.value as obj.text for obj in programs"></select>
-            	 <div>
-                	<u ng-click="toggleAddNew()">Add New</u> <div  ng-show="addNew"><input class="form-control" ng-model="newProgramName" type="text" name="newProgram"><button ng-click="createNewProgram()" class="form-control">Submit</button></div>
+    <div class="container-fluid" id="content">
+        <div class="row">
+            <div class="col-md-2 col-md-offset-1">
+                <div class="row">
+                    <h5>Select ATS Version</h5>
+                    <select class="form-control" ng-model="programSelection" ng-change="updateProgram()" ng-options="obj.value as obj.text for obj in programs"></select>
+                    <div>
+                        <u ng-click="toggleAddNew()">Add New</u>
+                        <div ng-show="addNew"><input class="form-control" ng-model="newProgramName" type="text" name="newProgram"><button
+                                ng-click="createNewProgram()" class="form-control">Submit</button></div>
+                    </div>
+                </div>
+                <div ng-show="programSelection != null" class="row" id=reportRow>
+
+                    <div class="panel panel-default">
+                        <div class="panel-heading">
+                            <h3 class="panel-title">STRS Report</h3>
+                        </div>
+                        <div class="panel-body" class="col-sm-10" id=reportContainer>
+                            <h5>Select Primary Set (Demo Set)</h5>
+                            <select class="form-control" ng-model="primarySet" ng-options="obj.guid as obj.name for obj in sets"></select>
+                            <h5>Select Secondary Set (Dryrun Set)</h5>
+                            <select class="form-control" ng-model="secondarySet" ng-options="obj.guid as obj.name for obj in sets"></select>
+                            <button ng-click="generateReport()" class="btn btn-primary" id=generateReportBtn>Generate
+                                Report</button>
+                        </div>
+                    </div>
+
+                </div>
+                <div class="row" ng-show="false" id=coverageCopyRow>
+                    <div class="col-sm-10 col-sm-offset-2" id=coverageCopyContainer>
+                        <button ng-click="openCopyCoverageModal()" ng-show="sets.length > 0" class="btn btn-primary active"
+                            id=coverageCopyRowBtn>Import From Coverage</button>
+                    </div>
+                </div>
+
+
+            </div>
+            <div ng-show="programSelection != null" class="col-md-7 col-md-offset-1 ">
+                <div class="row">
+                    <button ng-disabled="programSelection == null" ng-click="createNewSetModal()" class="btn pull-left">New</button>
+                    <button ng-disabled="programSelection == null" ng-click="toggleReadOnly()" class="btn pull-right">Edit</button>
+                </div>
+                <div class="row">
+                    <div id="itemsGrid" class="grid" ui-grid="gridOptions" ui-grid-edit ui-grid-auto-resize
+                        ui-grid-resize-columns></div>
+                    <div class="panel panel-default">
+                        <div class="panel-heading" id="operationsPanel">
+                            <h3 class="panel-title">Operations</h3>
+                        </div>
+                        <div class="panel-body">
+                            <div class="btn-group btn-group-md" role="group"">
+                    		<button ng-disabled="
+                                isRunningOperation" ng-click="openCopySetModal()" ng-show="sets.length > 0" class="btn btn-primary"
+                                id=generateReportBtn>Merge Annotations</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openMassAssignTeamModal()" ng-show="sets.length > 0"
+                                    class="btn btn-primary" id=generateReportBtn>Mass Assign Team</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openConfigureCiSetModal()" ng-show="sets.length > 0"
+                                    class="btn btn-primary" id=generateReportBtn>Configure CI Set</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openMassSendDispoItemStatusModal()"
+                                    ng-show="!isCoverage && sets.length > 0" class="btn btn-primary" id=generateReportBtn>Mass
+                                    Send Dispo Item Status</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openRerunReportStatusModal()"
+                                    ng-show="isCoverage && sets.length > 0" class="btn btn-primary" id=generateReportBtn>Get
+                                    Rerun Report</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openMultiEnvModal()" ng-show="isCoverage && sets.length > 0"
+                                    class="btn btn-primary" id=multiEnvBtn>Multi Environment</button>
+                                <button ng-disabled="isRunningOperation" ng-click="openEditDispoConfigModal()" ng-show="sets.length > 0"
+                                        class="btn btn-primary" id=multiEnvBtn>Edit Config</button>
+                            </div>
+                        </div>
+                    </div>
                 </div>
             </div>
-            <div ng-show="programSelection != null"  class="row" id=reportRow>
-            
-               <div class="panel panel-default">
-  						<div class="panel-heading">
-   						<h3 class="panel-title">STRS Report</h3>
-  						</div>
-							<div class="panel-body" class="col-sm-10" id=reportContainer>
-	                    <h5>Select Primary Set (Demo Set)</h5>
-	                    <select class="form-control" ng-model="primarySet" ng-options="obj.guid as obj.name for obj in sets"></select>
-	                    <h5>Select Secondary Set (Dryrun Set)</h5>
-	                    <select class="form-control" ng-model="secondarySet" ng-options="obj.guid as obj.name for obj in sets"></select>
-	                    <button ng-click="generateReport()" class="btn btn-primary" id=generateReportBtn>Generate Report</button>
-	                </div>
-	  				</div>
-  				
-            </div>
-            <div class="row" ng-show="false" id=coverageCopyRow>
-                <div class="col-sm-10 col-sm-offset-2" id=coverageCopyContainer>
-                    <button ng-click="openCopyCoverageModal()" ng-show="sets.length > 0" class="btn btn-primary active" id=coverageCopyRowBtn>Import From Coverage</button>
+        </div>
+        <div ng-show="programSelection != null" class="row">
+            <div class="col-md-11 col-md-offset-1">
+                <div class="row" id="summaryContainer">
+                    <div id="summaryGrid" class="grid" ui-grid="summaryGrid" ui-grid-resize-columns ui-grid-auto-resize></div>
                 </div>
             </div>
-            
-            
         </div>
-        <div ng-show="programSelection != null" class="col-md-7 col-md-offset-1 ">
-            <div class="row">
-                <button ng-disabled="programSelection == null" ng-click="createNewSetModal()" class="btn pull-left">New</button>
-                <button ng-disabled="programSelection == null" ng-click="toggleReadOnly()" class="btn pull-right">Edit</button>
-            </div>
-            <div class="row">
-		      	<div id="itemsGrid" class="grid" ui-grid="gridOptions" ui-grid-edit ui-grid-auto-resize ui-grid-resize-columns></div>
-                <div class="panel panel-default">
-                	<div class="panel-heading" id="operationsPanel">
-                		<h3 class="panel-title">Operations</h3>
-  						</div>
-					   <div class="panel-body">
-					    	<div class="btn-group btn-group-md" role="group"">
-                    		<button ng-disabled="isRunningOperation" ng-click="openCopySetModal()" ng-show="sets.length > 0" class="btn btn-primary" id=generateReportBtn>Merge Annotations</button>
-                    		<button ng-disabled="isRunningOperation" ng-click="openMassAssignTeamModal()" ng-show="sets.length > 0" class="btn btn-primary" id=generateReportBtn>Mass Assign Team</button>
-                    		<button ng-disabled="isRunningOperation" ng-click="openConfigureCiSetModal()" ng-show="sets.length > 0" class="btn btn-primary" id=generateReportBtn>Configure CI Set</button>
-                    		<button ng-disabled="isRunningOperation" ng-click="openMassSendDispoItemStatusModal()" ng-show="!isCoverage && sets.length > 0" class="btn btn-primary" id=generateReportBtn>Mass Send Dispo Item Status</button>
-                    		<button ng-disabled="isRunningOperation" ng-click="openRerunReportStatusModal()" ng-show="isCoverage && sets.length > 0" class="btn btn-primary" id=generateReportBtn>Get Rerun Report</button>
-							</div>
-					 	</div>
-					</div>
-            </div>
-        </div>
-    </div>
-    <div ng-show="programSelection != null" class="row">
-    	<div class="col-md-11 col-md-offset-1">
-            <div class="row" id="summaryContainer">
-		      	<div id="summaryGrid" class="grid" ui-grid="summaryGrid" ui-grid-resize-columns ui-grid-auto-resize></div>
-            </div>
     </div>
 
     <!--  definitions for modal forms -->
@@ -84,10 +104,8 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
-    
+
     <script type="text/ng-template" id="copySets.html">
         <div class="modal-header">
             <h3 class="modal-title">Copy Annotations</h3>
@@ -106,7 +124,6 @@
                 <select class="form-control" ng-model="sourceSet" ng-options="obj.guid as obj.name for obj in setsLocalSource">
 					 </select>
             </div>
-
 			<div class="form-inline">
 				    <h5>Copy Dispositions</h5>
                 <select class="form-control" ng-model="annotationParam" ng-options="obj.value as obj.text for obj in annotationOptions"></select>
@@ -117,15 +134,13 @@
 				    <h5>Copy Item Notes</h5>
                 <select class="form-control" ng-model="noteParam" ng-options="obj.value as obj.text for obj in noteOptions"></select>
 			</div>
-
         </div>
         <div class="modal-footer">
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
+
     <script type="text/ng-template" id="copySetCoverage.html">
         <div class="modal-header">
             <h3 class="modal-title">Copy From Coverage</h3>
@@ -149,7 +164,6 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
 
     <script type="text/ng-template" id="massAssignTeam.html">
@@ -177,14 +191,13 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
-   <script type="text/ng-template" id="loadingModal.html">
+
+    <script type="text/ng-template" id="loadingModal.html">
         <div id="loadingModal" class="alert alert-info">
 				Loading...
         </div>
-    </script>   
+    </script>
 
     <script type="text/ng-template" id="confirmDelete.html">
         <div class="modal-header">
@@ -200,9 +213,8 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
+
     <script type="text/ng-template" id="configureCiSet.html">
         <div class="modal-header">
             <h3 class="modal-title">Add CI Set</h3>
@@ -222,10 +234,9 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
-   <script type="text/ng-template" id="massSendDispoItemStatus.html">
+
+    <script type="text/ng-template" id="massSendDispoItemStatus.html">
         <div class="modal-header">
             <h3 class="modal-title">Mass Send Disposition Items Status</h3>
         </div>
@@ -240,10 +251,9 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
-       <script type="text/ng-template" id="rerunReportStatus.html">
+
+    <script type="text/ng-template" id="rerunReportStatus.html">
         <div class="modal-header">
             <h3 class="modal-title">Get Rerun Report</h3>
         </div>
@@ -261,9 +271,7 @@
             <button class="btn btn-primary" ng-click="ok()">OK</button>
             <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
         </div>
-
     </script>
-    
 </body>
 
-</html>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/views/editDispoConfig.html b/plugins/org.eclipse.osee.web.ui/src/dispo/views/editDispoConfig.html
new file mode 100644
index 0000000..8f95658
--- /dev/null
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/views/editDispoConfig.html
@@ -0,0 +1,18 @@
+<div class="modal-header">
+    <h2 class="modal-title">Edit Dispo Config</h2>
+    <h4 class="modal-title">Each field must be a valid JSON Array</h4>
+</div>
+<div class="modal-body" overflow: auto;>
+    Resolution Types:
+    <textarea ng-class="{ 'config-json-invalid': !isResolutionTypesValidJson() }" rows=10 style="resize: vertical"
+        class="form-control" ng-model="validResolutionsJson"></textarea>
+    <br>
+    Multi Env Targets:
+    <textarea placeholder='[{"name": "example", "path": "src_host/example"}]' ng-class="{ 'config-json-invalid': !ismultiEnvTargetsValidJson() }"
+        rows=10 style="resize: vertical" class="form-control" ng-model="multiEnvTargetsJson"></textarea>
+
+</div>
+<div class="modal-footer">
+    <button ng-disabled="!isValidJson()" class="btn btn-primary" ng-click="ok()">OK</button>
+    <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
+</div>
\ No newline at end of file
diff --git a/plugins/org.eclipse.osee.web.ui/src/dispo/views/multiEnv.html b/plugins/org.eclipse.osee.web.ui/src/dispo/views/multiEnv.html
new file mode 100644
index 0000000..7dda8a7
--- /dev/null
+++ b/plugins/org.eclipse.osee.web.ui/src/dispo/views/multiEnv.html
@@ -0,0 +1,24 @@
+<div class="modal-header">
+    <h3 class="modal-title">Copy From Coverage</h3>
+</div>
+<div class="modal-body" overflow: auto;>
+    <div class="form-group">
+        Select a Set to Configure for Multi Env
+        <select class="form-control" ng-change="initTargetsSelect()" ng-model="selectedSet" ng-options="obj as obj.name for obj in setsLocal"></select>
+    </div>
+    <div ng-show="selectedSet != null">
+        <div class="form-group">
+            Enable
+            <input type="checkbox" class="form-control" ng-model="selectedSet.isMultiEnv">
+        </div>
+        <div class="form-group">
+            Select Multi Environment Target
+            <select class="form-control" ng-model="selectedSet.multiEnvSettings.multiEnvTarget" ng-options="obj as obj.name for obj in multiEnvTargets"></select>
+        </div>
+        Path: <input id="multiEnvTargePathInput" readonly type="text" ng-model="selectedSet.multiEnvSettings.multiEnvTarget.path">
+    </div>
+</div>
+<div class="modal-footer">
+    <button class="btn btn-primary" ng-click="ok()">OK</button>
+    <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
+</div>
\ No newline at end of file