Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Source/NETworkManager.Settings/SettingsInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
using System.Text.Json.Serialization;

// ReSharper disable InconsistentNaming

Expand Down Expand Up @@ -42,7 +42,7 @@ private void OnPropertyChanged([CallerMemberName] string propertyName = null)

#region Variables

[XmlIgnore] public bool SettingsChanged { get; set; }
[JsonIgnore] public bool SettingsChanged { get; set; }

/// <summary>
/// Private field for the <see cref="WelcomeDialog_Show" /> property.
Expand Down
125 changes: 118 additions & 7 deletions Source/NETworkManager.Settings/SettingsManager.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using log4net;
using NETworkManager.Models;
using NETworkManager.Models.Network;
using NETworkManager.Utilities;
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml.Serialization;

namespace NETworkManager.Settings;
Expand All @@ -22,6 +25,11 @@ public static class SettingsManager
/// </summary>
private static string SettingsFolderName => "Settings";

/// <summary>
/// Settings backups directory name.
/// </summary>
private static string BackupFolderName => "Backups";

/// <summary>
/// Settings file name.
/// </summary>
Expand All @@ -30,7 +38,13 @@ public static class SettingsManager
/// <summary>
/// Settings file extension.
/// </summary>
private static string SettingsFileExtension => ".xml";
private static string SettingsFileExtension => ".json";

/// <summary>
/// Legacy XML settings file extension.
/// </summary>
[Obsolete("Legacy XML settings are no longer used, but the extension is kept for migration purposes.")]
private static string LegacySettingsFileExtension => ".xml";

/// <summary>
/// Settings that are currently loaded.
Expand All @@ -42,6 +56,17 @@ public static class SettingsManager
/// </summary>
public static bool HotKeysChanged { get; set; }

/// <summary>
/// JSON serializer options for consistent serialization/deserialization.
/// </summary>
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Converters = { new JsonStringEnumConverter() }
};

#endregion

#region Settings location, default paths and file names
Expand All @@ -58,6 +83,15 @@ public static string GetSettingsFolderLocation()
AssemblyManager.Current.Name, SettingsFolderName);
}

/// <summary>
/// Method to get the path of the settings backup folder.
/// </summary>
/// <returns>Path to the settings backup folder.</returns>
public static string GetSettingsBackupFolderLocation()
{
return Path.Combine(GetSettingsFolderLocation(), BackupFolderName);
}

/// <summary>
/// Method to get the settings file name.
/// </summary>
Expand All @@ -67,6 +101,16 @@ public static string GetSettingsFileName()
return $"{SettingsFileName}{SettingsFileExtension}";
}

/// <summary>
/// Method to get the legacy settings file name.
/// </summary>
/// <returns>Legacy settings file name.</returns>
[Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
public static string GetLegacySettingsFileName()
{
return $"{SettingsFileName}{LegacySettingsFileExtension}";
}

/// <summary>
/// Method to get the settings file path.
/// </summary>
Expand All @@ -76,6 +120,16 @@ public static string GetSettingsFilePath()
return Path.Combine(GetSettingsFolderLocation(), GetSettingsFileName());
}

/// <summary>
/// Method to get the legacy XML settings file path.
/// </summary>
/// <returns>Legacy XML settings file path.</returns>
[Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
private static string GetLegacySettingsFilePath()
{
return Path.Combine(GetSettingsFolderLocation(), GetLegacySettingsFileName());
}

#endregion

#region Initialize, load and save
Expand All @@ -99,7 +153,9 @@ public static void Initialize()
public static void Load()
{
var filePath = GetSettingsFilePath();
var legacyFilePath = GetLegacySettingsFilePath();

// Check if JSON file exists
if (File.Exists(filePath))
{
Current = DeserializeFromFile(filePath);
Expand All @@ -109,22 +165,66 @@ public static void Load()
return;
}

// Check if legacy XML file exists and migrate it
if (File.Exists(legacyFilePath))
{
Log.Info("Legacy XML settings file found. Migrating to JSON format...");

Current = DeserializeFromXmlFile(legacyFilePath);

Current.SettingsChanged = false;

// Save in new JSON format
Save();

// Create a backup of the legacy XML file and delete the original
Directory.CreateDirectory(GetSettingsBackupFolderLocation());

var backupFilePath = Path.Combine(GetSettingsBackupFolderLocation(),
$"{TimestampHelper.GetTimestamp()}_{GetLegacySettingsFileName()}");

File.Copy(legacyFilePath, backupFilePath, true);

File.Delete(legacyFilePath);

Log.Info($"Legacy XML settings file backed up to: {backupFilePath}");

Log.Info("Settings migration from XML to JSON completed successfully.");

return;
}

// Initialize the default settings if there is no settings file.
Initialize();
}

/// <summary>
/// Method to deserialize the settings from a file.
/// Method to deserialize the settings from a JSON file.
/// </summary>
/// <param name="filePath">Path to the settings file.</param>
/// <returns>Settings as <see cref="SettingsInfo" />.</returns>
private static SettingsInfo DeserializeFromFile(string filePath)
{
var jsonString = File.ReadAllText(filePath);

var settingsInfo = JsonSerializer.Deserialize<SettingsInfo>(jsonString, JsonOptions);

return settingsInfo;
}

/// <summary>
/// Method to deserialize the settings from a legacy XML file.
/// </summary>
/// <param name="filePath">Path to the XML settings file.</param>
/// <returns>Settings as <see cref="SettingsInfo" />.</returns>
[Obsolete("Legacy XML settings are no longer used, but the method is kept for migration purposes.")]
private static SettingsInfo DeserializeFromXmlFile(string filePath)
{
var xmlSerializer = new XmlSerializer(typeof(SettingsInfo));

using var fileStream = new FileStream(filePath, FileMode.Open);

var settingsInfo = (SettingsInfo)xmlSerializer.Deserialize(fileStream);
var settingsInfo = xmlSerializer.Deserialize(fileStream) as SettingsInfo;

return settingsInfo;
}
Expand All @@ -145,17 +245,28 @@ public static void Save()
}

/// <summary>
/// Method to serialize the settings to a file.
/// Method to serialize the settings to a JSON file.
/// </summary>
/// <param name="filePath">Path to the settings file.</param>
private static void SerializeToFile(string filePath)
{
var xmlSerializer = new XmlSerializer(typeof(SettingsInfo));
var jsonString = JsonSerializer.Serialize(Current, JsonOptions);

File.WriteAllText(filePath, jsonString);
}

using var fileStream = new FileStream(filePath, FileMode.Create);
#endregion

#region Backup
/*
private static void Backup()
{
Log.Info("Creating settings backup...");

xmlSerializer.Serialize(fileStream, Current);
// Create the backup directory if it does not exist
Directory.CreateDirectory(GetSettingsBackupFolderLocation());
}
*/

#endregion

Expand Down
46 changes: 31 additions & 15 deletions Source/NETworkManager/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
Expand Down Expand Up @@ -92,21 +93,15 @@ by BornToBeRoot
}
catch (InvalidOperationException ex)
{
Log.Error("Could not load application settings!");
Log.Error(ex.Message + "-" + ex.StackTrace);

// Create backup of corrupted file
var destinationFile =
$"{TimestampHelper.GetTimestamp()}_corrupted_" + SettingsManager.GetSettingsFileName();
File.Copy(SettingsManager.GetSettingsFilePath(),
Path.Combine(SettingsManager.GetSettingsFolderLocation(), destinationFile));
Log.Info($"A backup of the corrupted settings file has been saved under {destinationFile}");

// Initialize default application settings
Log.Info("Initialize default application settings...");
Log.Error("Could not load application settings!", ex);

HandleCorruptedSettingsFile();
}
catch (JsonException ex)
{
Log.Error("Could not load application settings! JSON file is corrupted or invalid.", ex);

SettingsManager.Initialize();
ConfigurationManager.Current.ShowSettingsResetNoteOnStartup = true;
HandleCorruptedSettingsFile();
}

// Upgrade settings if necessary
Expand Down Expand Up @@ -220,6 +215,27 @@ by BornToBeRoot
}
}

/// <summary>
/// Handles a corrupted settings file by creating a backup and initializing default settings.
/// </summary>
private void HandleCorruptedSettingsFile()
{
// Create backup of corrupted file
var destinationFile =
$"{TimestampHelper.GetTimestamp()}_corrupted_" + SettingsManager.GetSettingsFileName();

File.Copy(SettingsManager.GetSettingsFilePath(),
Path.Combine(SettingsManager.GetSettingsFolderLocation(), destinationFile));

Log.Info($"A backup of the corrupted settings file has been saved under {destinationFile}");

// Initialize default application settings
Log.Info("Initialize default application settings...");

SettingsManager.Initialize();
ConfigurationManager.Current.ShowSettingsResetNoteOnStartup = true;
}

private void DispatcherTimer_Tick(object sender, EventArgs e)
{
Log.Info("Run background job...");
Expand Down Expand Up @@ -267,4 +283,4 @@ private void Save()
ProfileManager.Save();
}
}
}
}
4 changes: 4 additions & 0 deletions Website/docs/changelog/next-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Release date: **xx.xx.2025**
- Profile file dialog migrated to a child window to improve usability. [#3227](https://github.com/BornToBeRoot/NETworkManager/pull/3227)
- Credential dialogs migrated to child windows to improve usability. [#3231](https://github.com/BornToBeRoot/NETworkManager/pull/3231)

**Settings**

- Settings format migrated from `XML` to `JSON`. The settings file will be automatically converted on first start after the update. [#3282](https://github.com/BornToBeRoot/NETworkManager/pull/3282)

**DNS Lookup**

- Allow hostname as server address in addition to IP address in the add/edit server dialog. [#3261](https://github.com/BornToBeRoot/NETworkManager/pull/3261)
Expand Down