Merge pull request #1528 from jlewin/1.6.0

Refactor ProfileManager to prefer isolated instances
This commit is contained in:
Lars Brubaker 2016-10-21 14:18:49 -07:00 committed by GitHub
commit ebfcbcee3e
6 changed files with 112 additions and 129 deletions

View file

@ -388,24 +388,6 @@ namespace MatterHackers.MatterControl
} }
} }
private static string MakeValidFileName(string name)
{
if (string.IsNullOrEmpty(name))
{
return name;
}
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
return Regex.Replace(name, invalidRegStr, "_");
}
public string GetSessionUsernameForFileSystem()
{
return MakeValidFileName(AuthenticationData.Instance.ActiveSessionUsername);
}
bool pendingReloadRequest = false; bool pendingReloadRequest = false;
public void ReloadAll(object sender, EventArgs e) public void ReloadAll(object sender, EventArgs e)
{ {
@ -476,8 +458,10 @@ namespace MatterHackers.MatterControl
// set the colors // set the colors
LoadUITheme(); LoadUITheme();
// This will initialize the theme for the first printer to load
ProfileManager.Reload(); // We previously made a call to Reload, which fired the method twice due to it being in the static constructor. Accessing
// any property will run the static constructor and perform the Reload behavior without the overhead of duplicate calls
bool na = ProfileManager.Instance.IsGuestProfile;
if (UserSettings.Instance.DisplayMode == ApplicationDisplayType.Touchscreen) if (UserSettings.Instance.DisplayMode == ApplicationDisplayType.Touchscreen)
{ {
@ -558,10 +542,10 @@ namespace MatterHackers.MatterControl
// Ensure SQLite printers are imported // Ensure SQLite printers are imported
profileManager.EnsurePrintersImported(); profileManager.EnsurePrintersImported();
var guestDB = ProfileManager.LoadGuestDB(); var guest = ProfileManager.LoadGuestProfiles();
// If profiles.json was created, run the import wizard to pull in any SQLite printers // If profiles.json was created, run the import wizard to pull in any SQLite printers
if (guestDB?.Profiles != null && guestDB.Profiles.Any() && !profileManager.IsGuestProfile && !profileManager.PrintersImported) if (guest?.Profiles != null && guest.Profiles.Any() && !profileManager.IsGuestProfile && !profileManager.PrintersImported)
{ {
var wizardPage = new CopyGuestProfilesToUser(() => var wizardPage = new CopyGuestProfilesToUser(() =>
{ {

View file

@ -105,7 +105,7 @@ namespace MatterHackers.MatterControl.DataStorage.ClassicDB
if (string.IsNullOrEmpty(ProfileManager.Instance.LastProfileID)) if (string.IsNullOrEmpty(ProfileManager.Instance.LastProfileID))
{ {
ProfileManager.Instance.SetLastProfile(printer.Id.ToString()); ProfileManager.Instance.LastProfileID = printer.Id.ToString();
} }
printerSettings.UserLayer[SettingsKey.active_theme_name] = UserSettings.Instance.get(UserSettingsKey.ActiveThemeName); printerSettings.UserLayer[SettingsKey.active_theme_name] = UserSettings.Instance.get(UserSettingsKey.ActiveThemeName);

View file

@ -68,8 +68,8 @@ namespace MatterHackers.MatterControl
var byCheckbox = new Dictionary<CheckBox, PrinterInfo>(); var byCheckbox = new Dictionary<CheckBox, PrinterInfo>();
var guestProfileManager = ProfileManager.LoadGuestDB(); var guest = ProfileManager.LoadGuestProfiles();
if (guestProfileManager?.Profiles.Count > 0) if (guest?.Profiles.Count > 0)
{ {
container.AddChild(new TextWidget("Printers to Copy:".Localize()) container.AddChild(new TextWidget("Printers to Copy:".Localize())
{ {
@ -77,7 +77,7 @@ namespace MatterHackers.MatterControl
Margin = new BorderDouble(0, 3, 0, 15), Margin = new BorderDouble(0, 3, 0, 15),
}); });
foreach (var printerInfo in guestProfileManager.Profiles) foreach (var printerInfo in guest.Profiles)
{ {
var checkBox = new CheckBox(printerInfo.Name) var checkBox = new CheckBox(printerInfo.Name)
{ {
@ -104,20 +104,23 @@ namespace MatterHackers.MatterControl
// import the printer // import the printer
var printerInfo = byCheckbox[checkBox]; var printerInfo = byCheckbox[checkBox];
string existingPath = Path.Combine(ProfileManager.GuestDBDirectory, printerInfo.ID + ProfileManager.ProfileExtension); ; string existingPath = guest.ProfilePath(printerInfo);
ProfileManager.Instance.Profiles.Add(printerInfo);
guestProfileManager.Profiles.Remove(printerInfo);
// PrinterSettings files must actually be copied to the users profile directory // PrinterSettings files must actually be copied to the users profile directory
if (File.Exists(existingPath)) if (File.Exists(existingPath))
{ {
File.Copy(existingPath, printerInfo.ProfilePath); File.Copy(existingPath, printerInfo.ProfilePath);
// Only add if copy succeeds
ProfileManager.Instance.Profiles.Add(printerInfo);
// TODO: Do we copy or migrate. This looks a lot like migrate which is not the current expected behavior
// guestProfileManager.Profiles.Remove(printerInfo);
} }
} }
} }
guestProfileManager.Save(); guest.Save();
// close the window // close the window
UiThread.RunOnIdle(() => UiThread.RunOnIdle(() =>

View file

@ -141,12 +141,12 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
static ActiveSliceSettings() static ActiveSliceSettings()
{ {
// Load last profile or fall back to empty // Load last profile or fall back to empty
Instance = ProfileManager.Instance?.LoadLastProfileWithoutRecovery() ?? ProfileManager.LoadEmptyProfile(); Instance = ProfileManager.Instance?.LoadWithoutRecovery(ProfileManager.Instance.LastProfileID) ?? ProfileManager.LoadEmptyProfile();
} }
internal static async Task SwitchToProfile(string printerID) internal static async Task SwitchToProfile(string printerID)
{ {
ProfileManager.Instance.SetLastProfile(printerID); ProfileManager.Instance.LastProfileID = printerID;
Instance = (await ProfileManager.LoadProfileAsync(printerID)) ?? ProfileManager.LoadEmptyProfile(); Instance = (await ProfileManager.LoadProfileAsync(printerID)) ?? ProfileManager.LoadEmptyProfile();
} }

View file

@ -47,104 +47,95 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{ {
public static RootedObjectEventHandler ProfilesListChanged = new RootedObjectEventHandler(); public static RootedObjectEventHandler ProfilesListChanged = new RootedObjectEventHandler();
public static ProfileManager Instance { get; set; } public static ProfileManager Instance { get; private set; }
private static EventHandler unregisterEvents;
public const string ProfileExtension = ".printer"; public const string ProfileExtension = ".printer";
public const string ConfigFileExtension = ".slice"; public const string ConfigFileExtension = ".slice";
public const string ProfileDocExtension = ".profiles";
private static object writeLock = new object(); private object writeLock = new object();
private static EventHandler unregisterEvents;
private static readonly string userDataPath = ApplicationDataStorage.ApplicationUserDataPath;
/// <summary>
/// The user specific path to the Profiles directory
/// </summary>
private static string ProfilesPath
{
get
{
// Determine username
string username = ApplicationController.Instance.GetSessionUsernameForFileSystem();
if (string.IsNullOrEmpty(username))
{
username = "guest";
}
else
{
username = ApplicationController.EnvironmentName + username;
}
string path = Path.Combine(userDataPath, "Profiles", username);
// Ensure directory exists
Directory.CreateDirectory(path);
return path;
}
}
private const string userDBExtension = ".profiles";
private const string guestDBFileName = "guest" + userDBExtension;
internal static string GuestDBDirectory => Path.Combine(userDataPath, "Profiles", "guest");
private static string GuestDBPath => Path.Combine(GuestDBDirectory, guestDBFileName);
internal static string ProfilesDBPath
{
get
{
string username = ApplicationController.Instance.GetSessionUsernameForFileSystem();
if (string.IsNullOrEmpty(username))
{
username = GuestDBPath;
}
else
{
username = Path.Combine(ProfilesPath, $"{username}{userDBExtension}");
}
return username;
}
}
static ProfileManager() static ProfileManager()
{ {
SliceSettingsWidget.SettingChanged.RegisterEvent(SettingsChanged, ref unregisterEvents); SliceSettingsWidget.SettingChanged.RegisterEvent(SettingsChanged, ref unregisterEvents);
// Ensure the profiles directory exists
Directory.CreateDirectory(ProfilesPath);
Reload(); Reload();
} }
public ProfileManager() public ProfileManager(string userName)
{ {
this.UserName = userName;
}
public string UserName { get; set; }
/// <summary>
/// The user specific path to the Profiles directory
/// </summary>
[JsonIgnore]
private string UserProfilesDirectory => GetProfilesDirectoryForUser(this.UserName);
/// <summary>
/// The user specific path to the Profiles document
/// </summary>
[JsonIgnore]
public string ProfilesDocPath => GetProfilesDocPathForUser(this.UserName);
private static string GetProfilesDocPathForUser(string userName)
{
return Path.Combine(GetProfilesDirectoryForUser(userName), $"{userName}{ProfileDocExtension}");
}
private static string GetProfilesDirectoryForUser(string userName)
{
string userProfilesDirectory = Path.Combine(ApplicationDataStorage.ApplicationUserDataPath, "Profiles", userName);
// Ensure directory exists
Directory.CreateDirectory(userProfilesDirectory);
return userProfilesDirectory;
} }
[JsonIgnore] [JsonIgnore]
public bool IsGuestProfile => Path.GetFileName(ProfilesDBPath) == guestDBFileName; public bool IsGuestProfile { get; private set; } = false;
public static void Reload() public static void Reload()
{ {
string userName = AuthenticationData.Instance.FileSystemSafeUserName;
if (string.IsNullOrEmpty(userName))
{
userName = "guest";
}
if (Instance?.UserName == userName)
{
return;
}
if (Instance?.Profiles != null) if (Instance?.Profiles != null)
{ {
// Release event registration // Release event registration
Instance.Profiles.CollectionChanged -= Profiles_CollectionChanged; Instance.Profiles.CollectionChanged -= Profiles_CollectionChanged;
} }
// Load the profiles document string profilesDocPath = GetProfilesDocPathForUser(userName);
if (File.Exists(ProfilesDBPath))
// Reassign the active instance based on the logged in user
if (File.Exists(profilesDocPath))
{ {
string json = File.ReadAllText(ProfilesDBPath); string json = File.ReadAllText(profilesDocPath);
Instance = JsonConvert.DeserializeObject<ProfileManager>(json); Instance = JsonConvert.DeserializeObject<ProfileManager>(json);
Instance.UserName = userName;
} }
else else
{ {
Instance = new ProfileManager(); Instance = new ProfileManager(userName);
} }
if (ActiveSliceSettings.Instance?.ID != Instance.LastProfileID) if (ActiveSliceSettings.Instance?.ID != Instance.LastProfileID)
{ {
// async so we can safely wait for LoadProfileAsync to complete
Task.Run(async () => Task.Run(async () =>
{ {
// Load or download on a background thread // Load or download on a background thread
@ -152,6 +143,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
if (MatterControlApplication.IsLoading) if (MatterControlApplication.IsLoading)
{ {
// TODO: Not true - we're on a background thread in an async lambda... what is the intent of this?
// Assign on the UI thread // Assign on the UI thread
ActiveSliceSettings.Instance = lastProfile ?? LoadEmptyProfile(); ActiveSliceSettings.Instance = lastProfile ?? LoadEmptyProfile();
} }
@ -170,15 +162,9 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
Instance.Profiles.CollectionChanged += Profiles_CollectionChanged; Instance.Profiles.CollectionChanged += Profiles_CollectionChanged;
} }
internal static ProfileManager LoadGuestDB() internal static ProfileManager LoadGuestProfiles()
{ {
if (File.Exists(GuestDBPath)) return new ProfileManager("guest") { IsGuestProfile = true };
{
string json = File.ReadAllText(GuestDBPath);
return JsonConvert.DeserializeObject<ProfileManager>(json);
}
return null;
} }
internal static void SettingsChanged(object sender, EventArgs e) internal static void SettingsChanged(object sender, EventArgs e)
@ -227,35 +213,27 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{ {
get get
{ {
string activeUserName = ApplicationController.Instance.GetSessionUsernameForFileSystem(); return UserSettings.Instance.get($"ActiveProfileID-{UserName}");
return UserSettings.Instance.get($"ActiveProfileID-{activeUserName}"); }
set
{
UserSettings.Instance.set($"ActiveProfileID-{UserName}", value);
} }
} }
public bool PrintersImported { get; set; } = false; public bool PrintersImported { get; set; } = false;
public PrinterSettings LoadLastProfileWithoutRecovery()
{
return LoadWithoutRecovery(this.LastProfileID);
}
public void SetLastProfile(string printerID)
{
string activeUserName = ApplicationController.Instance.GetSessionUsernameForFileSystem();
UserSettings.Instance.set($"ActiveProfileID-{activeUserName}", printerID);
}
public string ProfilePath(PrinterInfo printer)
{
return Path.Combine(ProfileManager.ProfilesPath, printer.ID + ProfileExtension);
}
public string ProfilePath(string printerID) public string ProfilePath(string printerID)
{ {
return ProfilePath(this[printerID]); return ProfilePath(this[printerID]);
} }
public static PrinterSettings LoadWithoutRecovery(string profileID) public string ProfilePath(PrinterInfo printer)
{
return Path.Combine(UserProfilesDirectory, printer.ID + ProfileExtension);
}
public PrinterSettings LoadWithoutRecovery(string profileID)
{ {
var printerInfo = Instance[profileID]; var printerInfo = Instance[profileID];
@ -291,14 +269,14 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
} }
// Only load profiles by ID that are defined in the profiles document // Only load profiles by ID that are defined in the profiles document
var printerInfo = ProfileManager.Instance[profileID]; var printerInfo = Instance[profileID];
if (printerInfo == null) if (printerInfo == null)
{ {
return null; return null;
} }
// Attempt to load from disk, pull from the web or fall back using recovery logic // Attempt to load from disk, pull from the web or fall back using recovery logic
PrinterSettings printerSettings = LoadWithoutRecovery(profileID); PrinterSettings printerSettings = Instance.LoadWithoutRecovery(profileID);
if (printerSettings != null) if (printerSettings != null)
{ {
return printerSettings; return printerSettings;
@ -553,7 +531,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
if (IsGuestProfile && !PrintersImported) if (IsGuestProfile && !PrintersImported)
{ {
// Import Sqlite printer profiles into local json files // Import Sqlite printer profiles into local json files
DataStorage.ClassicDB.ClassicSqlitePrinterProfiles.ImportPrinters(Instance, ProfilesPath); DataStorage.ClassicDB.ClassicSqlitePrinterProfiles.ImportPrinters(Instance, UserProfilesDirectory);
PrintersImported = true; PrintersImported = true;
Save(); Save();
} }
@ -574,7 +552,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{ {
lock(writeLock) lock(writeLock)
{ {
File.WriteAllText(ProfilesDBPath, JsonConvert.SerializeObject(this, Formatting.Indented)); File.WriteAllText(ProfilesDocPath, JsonConvert.SerializeObject(this, Formatting.Indented));
} }
} }
} }

View file

@ -7,6 +7,8 @@ using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Binary;
using MatterHackers.Localizations; using MatterHackers.Localizations;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace MatterHackers.MatterControl namespace MatterHackers.MatterControl
{ {
@ -156,5 +158,21 @@ namespace MatterHackers.MatterControl
ApplicationSettings.Instance.set($"{ApplicationController.EnvironmentName}LastSessionUsername", value); ApplicationSettings.Instance.set($"{ApplicationController.EnvironmentName}LastSessionUsername", value);
} }
} }
[JsonIgnore]
public string FileSystemSafeUserName => MakeValidFileName(this.ActiveSessionUsername);
private static string MakeValidFileName(string name)
{
if (string.IsNullOrEmpty(name))
{
return name;
}
string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
return Regex.Replace(name, invalidRegStr, "_");
}
} }
} }