/// <summary> /// Apply registry changes to the projects manifest. /// </summary> /// <param name="manifestModifier">Object that modifies the project's manifest.</param> /// <param name="availableRegistries">Registries that are available in the /// configuration.</param> /// <param name="manifestRegistries">Registries that are present in the manifest.</param> /// <param name="selectedRegistryUrls">URLs of selected registries, these should be items in /// availableRegistries.</param> /// <param name="addRegistries">Whether to add selected registries to the manifest.</param> /// <param name="removeRegistries">Whether to remove unselected registries from the /// manifest.</param> /// <param name="invertSelection">If false, adds the selected registries and removes the /// unselected registries. If true, removes the selected registries and adds the unselected /// registries.</param> /// <param name="addedRegistries">If specified, is extended with the list of registries added /// to the manifest.<param> /// <returns>true if successful, false otherwise.</returns> private static bool SyncRegistriesToManifest( PackageManifestModifier manifestModifier, Dictionary <string, PackageManagerRegistry> availableRegistries, Dictionary <string, List <PackageManagerRegistry> > manifestRegistries, HashSet <string> selectedRegistryUrls, bool addRegistries = true, bool removeRegistries = true, bool invertSelection = false, List <PackageManagerRegistry> addedRegistries = null) { List <PackageManagerRegistry> registriesToAdd; List <PackageManagerRegistry> registriesToRemove; bool manifestModified = SyncRegistriesToModifier( manifestModifier, out registriesToAdd, out registriesToRemove, availableRegistries, manifestRegistries, selectedRegistryUrls, addRegistries, removeRegistries, invertSelection); bool successful = true; if (manifestModified) { successful = manifestModifier.WriteManifest(); if (successful) { if (registriesToAdd.Count > 0) { logger.Log(String.Format( "Added registries to {0}:\n{1}", PackageManifestModifier.MANIFEST_FILE_PATH, PackageManagerRegistry.ToString(registriesToAdd))); if (addedRegistries != null) { addedRegistries.AddRange(registriesToAdd); } } if (registriesToRemove.Count > 0) { logger.Log(String.Format( "Removed registries from {0}:\n{1}", PackageManifestModifier.MANIFEST_FILE_PATH, PackageManagerRegistry.ToString(registriesToRemove))); } analytics.Report( "registry_manifest/write/success", new KeyValuePair <string, string>[] { new KeyValuePair <string, string>("added", registriesToAdd.Count.ToString()), new KeyValuePair <string, string>("removed", registriesToRemove.Count.ToString()) }, "Project Manifest Modified"); } else { analytics.Report("registry_manifest/write/failed", "Project Manifest Write Failed"); } } return(successful); }
/// <summary> /// Apply registry changes to the modifier. /// </summary> /// <param name="manifestModifier">Object that modifies the project's manifest.</param> /// <param name="registriesToAdd">Registries added to the modifier.</param> /// <param name="registriesToRemove">Registries removed from the modifier.</param> /// <param name="availableRegistries">Registries that are available in the /// configuration.</param> /// <param name="manifestRegistries">Registries that are present in the manifest.</param> /// <param name="selectedRegistryUrls">URLs of selected registries, these should be items in /// availableRegistries.</param> /// <param name="addRegistries">Whether to add selected registries to the manifest.</param> /// <param name="removeRegistries">Whether to remove unselected registries from the /// manifest.</param> /// <param name="invertSelection">If false, adds the selected registries and removes the /// unselected registries. If true, removes the selected registries and adds the unselected /// registries.</param> /// <returns>true if the manifest is modified.</returns> private static bool SyncRegistriesToModifier( PackageManifestModifier manifestModifier, out List <PackageManagerRegistry> registriesToAdd, out List <PackageManagerRegistry> registriesToRemove, Dictionary <string, PackageManagerRegistry> availableRegistries, Dictionary <string, List <PackageManagerRegistry> > manifestRegistries, HashSet <string> selectedRegistryUrls, bool addRegistries = true, bool removeRegistries = true, bool invertSelection = false) { // Build a list of registries to add to and remove from the modifier. registriesToAdd = new List <PackageManagerRegistry>(); registriesToRemove = new List <PackageManagerRegistry>(); foreach (var availableRegistry in availableRegistries.Values) { var url = availableRegistry.Url; bool isSelected = selectedRegistryUrls.Contains(url); if (invertSelection) { isSelected = !isSelected; } bool currentlyInManifest = manifestRegistries.ContainsKey(url); if (isSelected) { if (addRegistries && !currentlyInManifest) { registriesToAdd.Add(availableRegistry); } } else { if (removeRegistries && currentlyInManifest) { registriesToRemove.Add(availableRegistry); } } } bool manifestModified = false; if (registriesToAdd.Count > 0) { manifestModifier.AddRegistries(registriesToAdd); manifestModified = true; } if (registriesToRemove.Count > 0) { manifestModifier.RemoveRegistries(registriesToRemove); manifestModified = true; } return(manifestModified); }
/// <summary> /// Construct an object based on another modifier. /// </summary> public PackageManifestModifier(PackageManifestModifier other) { Logger = other.Logger; try { manifestDict = Json.Deserialize(other.GetManifestJson()) as Dictionary <string, object>; } catch (Exception e) { Logger.Log(String.Format("Failed to clone PackageManifestModifier. \nException:{1}", MANIFEST_FILE_PATH, e.ToString()), LogLevel.Error); } }
/// <summary> /// Update manifest file based on the settings. /// </summary> /// <param name="mode">Manifest modification mode being applied.</param> /// <param name="promptBeforeAction">Whether to display a window that prompts the user for /// confirmation before applying changes.</param> /// <param name="showDisableButton">Whether to show a button to disable auto-registry /// addition.</param> /// <param name="scopePrefixFilter">List of scope prefixes used to filter the set of registries /// being operated on.</param> internal static void UpdateManifest(ManifestModificationMode mode, bool promptBeforeAction = true, bool showDisableButton = false, IEnumerable <string> scopePrefixFilter = null) { if (!ScopedRegistriesSupported) { logger.Log(String.Format("Scoped registries not supported in this version of Unity."), level: LogLevel.Verbose); return; } PackageManifestModifier modifier = new PackageManifestModifier() { Logger = logger }; Dictionary <string, List <PackageManagerRegistry> > manifestRegistries = modifier.ReadManifest() ? modifier.PackageManagerRegistries : null; if (manifestRegistries == null) { PackageManagerResolver.analytics.Report( "registry_manifest/read/failed", "Update Manifest failed: Read/Parse manifest failed"); return; } var xmlRegistries = ReadRegistriesFromXml(); // Filter registries using the scope prefixes. if (scopePrefixFilter != null) { foreach (var registry in new List <PackageManagerRegistry>(xmlRegistries.Values)) { bool removeRegistry = true; foreach (var scope in registry.Scopes) { foreach (var scopePrefix in scopePrefixFilter) { if (scope.StartsWith(scopePrefix)) { removeRegistry = false; } } } if (removeRegistry) { xmlRegistries.Remove(registry.Url); } } } // Filter the set of considered registries based upon the modification mode. HashSet <string> selectedRegistryUrls = null; switch (mode) { case ManifestModificationMode.Add: // Remove all items from the XML loaded registries that are present in the manifest. foreach (var url in manifestRegistries.Keys) { xmlRegistries.Remove(url); } selectedRegistryUrls = new HashSet <string>(xmlRegistries.Keys); break; case ManifestModificationMode.Remove: // Remove all items from the XML loaded registries that are not present in the // manifest. foreach (var url in new List <string>(xmlRegistries.Keys)) { if (!manifestRegistries.ContainsKey(url)) { xmlRegistries.Remove(url); } } selectedRegistryUrls = new HashSet <string>(xmlRegistries.Keys); break; case ManifestModificationMode.Modify: selectedRegistryUrls = new HashSet <string>(); // Keep all XML loaded registries and select the items in the manifest. foreach (var url in xmlRegistries.Keys) { if (manifestRegistries.ContainsKey(url)) { selectedRegistryUrls.Add(url); } } break; } // Applies the manifest modification based upon the modification mode. Action <HashSet <string> > syncRegistriesToManifest = (urlSelectionToApply) => { var addedRegistries = new List <PackageManagerRegistry>(); SyncRegistriesToManifest(modifier, xmlRegistries, manifestRegistries, urlSelectionToApply, addRegistries: (mode == ManifestModificationMode.Add || mode == ManifestModificationMode.Modify), removeRegistries: (mode == ManifestModificationMode.Remove || mode == ManifestModificationMode.Modify), invertSelection: mode == ManifestModificationMode.Remove, addedRegistries: addedRegistries); // If any registries were added try migration if enabled. if (addedRegistries.Count > 0 && PromptToMigratePackages) { PackageMigrator.MigratePackages(); } }; // Get the manifest json string based on the current selection and mode. Func <HashSet <string>, string> getManifestJsonAfterChange = (urlSelectionToApply) => { PackageManifestModifier clonedModifier = new PackageManifestModifier(modifier); List <PackageManagerRegistry> toAdd; List <PackageManagerRegistry> toRemove; SyncRegistriesToModifier(clonedModifier, out toAdd, out toRemove, xmlRegistries, manifestRegistries, urlSelectionToApply, addRegistries: (mode == ManifestModificationMode.Add || mode == ManifestModificationMode.Modify), removeRegistries: (mode == ManifestModificationMode.Remove || mode == ManifestModificationMode.Modify), invertSelection: mode == ManifestModificationMode.Remove); return(clonedModifier.GetManifestJson()); }; if (xmlRegistries.Count > 0) { if (promptBeforeAction) { // Build a list of items to display. var registryItems = new List <KeyValuePair <string, string> >(); foreach (var kv in xmlRegistries) { registryItems.Add(new KeyValuePair <string, string>(kv.Key, kv.Value.Name)); } // Optional when prompting is enabled or forced. var window = MultiSelectWindow.CreateMultiSelectWindow <PackageManagerResolverWindow>( PLUGIN_NAME); window.minSize = new Vector2(1024, 500); window.AvailableItems = registryItems; window.Sort(1); window.SelectedItems = selectedRegistryUrls; switch (mode) { case ManifestModificationMode.Add: window.Caption = String.Format("{0}\n\n{1}\n\n{2}", ADD_REGISTRIES_QUESTION, ADD_REGISTRIES_DESCRIPTION, MODIFY_MENU_ITEM_DESCRIPTION); window.ApplyLabel = "Add Selected Registries"; break; case ManifestModificationMode.Remove: window.Caption = String.Format("{0}\n\n{1}{2}", REMOVE_REGISTRIES_QUESTION, REMOVE_REGISTRIES_DESCRIPTION, MODIFY_MENU_ITEM_DESCRIPTION); window.ApplyLabel = "Remove Selected Registries"; break; case ManifestModificationMode.Modify: window.Caption = String.Format("{0}\n\n{1} {2}", ADD_OR_REMOVE_REGISTRIES_QUESTION, ADD_REGISTRIES_DESCRIPTION, REMOVE_REGISTRIES_DESCRIPTION); window.ApplyLabel = "Modify Registries"; break; } window.RenderItem = (item) => { var registry = xmlRegistries[item.Key]; var termsOfService = registry.TermsOfService; if (!String.IsNullOrEmpty(termsOfService)) { if (GUILayout.Button("View Terms of Service")) { Application.OpenURL(termsOfService); } } var privacyPolicy = registry.PrivacyPolicy; if (!String.IsNullOrEmpty(privacyPolicy)) { if (GUILayout.Button("View Privacy Policy")) { Application.OpenURL(privacyPolicy); } } }; // Set the scroll position to the bottom since "scopedRegistry" section is most // likely at the bottom of the file. scrollManifestViewLeft = new Vector2(0.0f, float.PositiveInfinity); scrollManifestViewRight = new Vector2(0.0f, float.PositiveInfinity); // Render the change in manifest.json dynamically. window.RenderAfterItems = () => { GUILayout.Label("Changes to Packages/manifest.json"); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); GUILayout.Label("Before", EditorStyles.boldLabel); EditorGUILayout.Space(); scrollManifestViewLeft = EditorGUILayout.BeginScrollView(scrollManifestViewLeft, GUILayout.MaxWidth(window.position.width / 2.0f)); EditorGUILayout.TextArea(modifier.GetManifestJson()); EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical(); GUILayout.Label("After", EditorStyles.boldLabel); EditorGUILayout.Space(); scrollManifestViewRight = EditorGUILayout.BeginScrollView(scrollManifestViewRight, GUILayout.MaxWidth(window.position.width / 2.0f)); EditorGUILayout.TextArea(getManifestJsonAfterChange(window.SelectedItems)); EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); }; if (showDisableButton) { window.RenderBeforeCancelApply = () => { if (GUILayout.Button("Disable Registry Addition")) { Enable = false; window.Close(); } }; } window.OnApply = () => { syncRegistriesToManifest(window.SelectedItems); }; window.Show(); } else { syncRegistriesToManifest(selectedRegistryUrls); } } }
/// <summary> /// Update manifest file based on the settings. /// </summary> /// <param name="mode">Manifest modification mode being applied.</param> /// <param name="promptBeforeAction">Whether to display a window that prompts the user for /// confirmation before applying changes.</param> /// <param name="showDisableButton">Whether to show a button to disable auto-registry /// addition.</param> /// <param name="scopePrefixFilter">List of scope prefixes used to filter the set of registries /// being operated on.</param> private static void UpdateManifest(ManifestModificationMode mode, bool promptBeforeAction = true, bool showDisableButton = false, IEnumerable <string> scopePrefixFilter = null) { if (!ScopedRegistriesSupported) { logger.Log(String.Format("Scoped registries not supported in this version of Unity."), level: LogLevel.Verbose); return; } PackageManifestModifier modifier = new PackageManifestModifier() { Logger = logger }; Dictionary <string, List <UnityPackageManagerRegistry> > manifestRegistries = modifier.ReadManifest() ? modifier.UnityPackageManagerRegistries : null; if (manifestRegistries == null) { UnityPackageManagerResolver.analytics.Report( "registry_manifest/read/failed", "Update Manifest failed: Read/Parse manifest failed"); return; } var xmlRegistries = ReadRegistriesFromXml(); // Filter registries using the scope prefixes. if (scopePrefixFilter != null) { foreach (var registry in new List <UnityPackageManagerRegistry>(xmlRegistries.Values)) { bool removeRegistry = true; foreach (var scope in registry.Scopes) { foreach (var scopePrefix in scopePrefixFilter) { if (scope.StartsWith(scopePrefix)) { removeRegistry = false; } } } if (removeRegistry) { xmlRegistries.Remove(registry.Url); } } } // Filter the set of considered registries based upon the modification mode. HashSet <string> selectedRegistryUrls = null; switch (mode) { case ManifestModificationMode.Add: // Remove all items from the XML loaded registries that are present in the manifest. foreach (var url in manifestRegistries.Keys) { xmlRegistries.Remove(url); } selectedRegistryUrls = new HashSet <string>(xmlRegistries.Keys); break; case ManifestModificationMode.Remove: // Remove all items from the XML loaded registries that are not present in the // manifest. foreach (var url in new List <string>(xmlRegistries.Keys)) { if (!manifestRegistries.ContainsKey(url)) { xmlRegistries.Remove(url); } } selectedRegistryUrls = new HashSet <string>(xmlRegistries.Keys); break; case ManifestModificationMode.Modify: selectedRegistryUrls = new HashSet <string>(); // Keep all XML loaded registries and select the items in the manifest. foreach (var url in xmlRegistries.Keys) { if (manifestRegistries.ContainsKey(url)) { selectedRegistryUrls.Add(url); } } break; } // Applies the manifest modification based upon the modification mode. Action <HashSet <string> > syncRegistriesToManifest = (urlSelectionToApply) => { SyncRegistriesToManifest(modifier, xmlRegistries, manifestRegistries, urlSelectionToApply, addRegistries: (mode == ManifestModificationMode.Add || mode == ManifestModificationMode.Modify), removeRegistries: (mode == ManifestModificationMode.Remove || mode == ManifestModificationMode.Modify), invertSelection: mode == ManifestModificationMode.Remove); }; if (xmlRegistries.Count > 0) { if (promptBeforeAction) { // Build a list of items to display. var registryItems = new List <KeyValuePair <string, string> >(); foreach (var kv in xmlRegistries) { registryItems.Add(new KeyValuePair <string, string>(kv.Key, kv.Value.Name)); } // Optional when prompting is enabled or forced. var window = MultiSelectWindow.CreateMultiSelectWindow(PLUGIN_NAME); window.minSize = new Vector2(1024, 500); window.AvailableItems = registryItems; window.Sort(1); window.SelectedItems = selectedRegistryUrls; switch (mode) { case ManifestModificationMode.Add: window.Caption = String.Format("{0}\n\n{1}\n\n{2}", ADD_REGISTRIES_QUESTION, ADD_REGISTRIES_DESCRIPTION, MODIFY_MENU_ITEM_DESCRIPTION); window.ApplyLabel = "Add Selected Registries"; break; case ManifestModificationMode.Remove: window.Caption = String.Format("{0}\n\n{1}{2}", REMOVE_REGISTRIES_QUESTION, REMOVE_REGISTRIES_DESCRIPTION, MODIFY_MENU_ITEM_DESCRIPTION); window.ApplyLabel = "Remove Selected Registries"; break; case ManifestModificationMode.Modify: window.Caption = String.Format("{0}\n\n{1} {2}", ADD_OR_REMOVE_REGISTRIES_QUESTION, ADD_REGISTRIES_DESCRIPTION, REMOVE_REGISTRIES_DESCRIPTION); window.ApplyLabel = "Modify Registries"; break; } window.RenderItem = (item) => { var registry = xmlRegistries[item.Key]; var termsOfService = registry.TermsOfService; if (!String.IsNullOrEmpty(termsOfService)) { if (GUILayout.Button("View Terms of Service")) { Application.OpenURL(termsOfService); } } var privacyPolicy = registry.PrivacyPolicy; if (!String.IsNullOrEmpty(privacyPolicy)) { if (GUILayout.Button("View Privacy Policy")) { Application.OpenURL(privacyPolicy); } } }; if (showDisableButton) { window.RenderBeforeCancelApply = () => { if (GUILayout.Button("Disable Registry Addition")) { Enable = false; window.Close(); } }; } window.OnApply = () => { syncRegistriesToManifest(window.SelectedItems); }; window.Show(); } else { syncRegistriesToManifest(selectedRegistryUrls); } } }
/// <summary> /// Apply registry changes to the projects manifest. /// </summary> /// <param name="manifestModifier">Object that modifies the project's manifest.</param> /// <param name="availableRegistries">Registries that are available in the /// configuration.</param> /// <param name="manifestRegistries">Registries that are present in the manifest.</param> /// <param name="selectedRegistryUrls">URLs of selected registries, these should be items in /// availableRegistries.</param> /// <param name="addRegistries">Whether to add selected registries to the manifest.</param> /// <param name="removeRegistries">Whether to remove unselected registries from the /// manifest.</param> /// <param name="invertSelection">If false, adds the selected registries and removes the /// unselected registries. If true, removes the selected registries and adds the unselected /// registries.</param> /// <returns>true if successful, false otherwise.</returns> private static bool SyncRegistriesToManifest( PackageManifestModifier manifestModifier, Dictionary <string, UnityPackageManagerRegistry> availableRegistries, Dictionary <string, List <UnityPackageManagerRegistry> > manifestRegistries, HashSet <string> selectedRegistryUrls, bool addRegistries = true, bool removeRegistries = true, bool invertSelection = false) { // Build a list of registries to add to and remove from the manifest. var registriesToAdd = new List <UnityPackageManagerRegistry>(); var registriesToRemove = new List <UnityPackageManagerRegistry>(); foreach (var availableRegistry in availableRegistries.Values) { var url = availableRegistry.Url; bool isSelected = selectedRegistryUrls.Contains(url); if (invertSelection) { isSelected = !isSelected; } bool currentlyInManifest = manifestRegistries.ContainsKey(url); if (isSelected) { if (addRegistries && !currentlyInManifest) { registriesToAdd.Add(availableRegistry); } } else { if (removeRegistries && currentlyInManifest) { registriesToRemove.Add(availableRegistry); } } } bool manifestModified = false; if (registriesToAdd.Count > 0) { manifestModifier.AddRegistries(registriesToAdd); manifestModified = true; } if (registriesToRemove.Count > 0) { manifestModifier.RemoveRegistries(registriesToRemove); manifestModified = true; } bool successful = true; if (manifestModified) { successful = manifestModifier.WriteManifest(); if (successful) { if (registriesToAdd.Count > 0) { logger.Log(String.Format( "Added registries to {0}:\n{1}", PackageManifestModifier.MANIFEST_FILE_PATH, UnityPackageManagerRegistry.ToString(registriesToAdd))); } if (registriesToRemove.Count > 0) { logger.Log(String.Format( "Removed registries from {0}:\n{1}", PackageManifestModifier.MANIFEST_FILE_PATH, UnityPackageManagerRegistry.ToString(registriesToRemove))); } analytics.Report( "registry_manifest/write/success", new KeyValuePair <string, string>[] { new KeyValuePair <string, string>("added", registriesToAdd.Count.ToString()), new KeyValuePair <string, string>("removed", registriesToRemove.Count.ToString()) }, "Project Manifest Modified"); } else { analytics.Report("registry_manifest/write/failed", "Project Manifest Write Failed"); } } return(successful); }