Skip to content

Commit 2383273

Browse files
authored
Feature: HostsFileEditor delete entry added (#3094)
* Feature: HostsFileEditor delete entry added * Docs: #3094
1 parent 2177b60 commit 2383273

File tree

11 files changed

+233
-32
lines changed

11 files changed

+233
-32
lines changed

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3933,4 +3933,9 @@ Right-click for more options.</value>
39333933
<data name="Entries" xml:space="preserve">
39343934
<value>Entries</value>
39353935
</data>
3936+
<data name="DeleteHostsFileEntryMessage" xml:space="preserve">
3937+
<value>The selected entry is permanently deleted:
3938+
3939+
{0} {1} {2}</value>
3940+
</data>
39363941
</root>

Source/NETworkManager.Models/HostsFileEditor/HostsFileEditor.cs

Lines changed: 120 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,27 +164,52 @@ private static IEnumerable<HostsFileEntry> GetHostsFileEntries()
164164
return entries;
165165
}
166166

167-
public static Task<bool> EnableEntryAsync(HostsFileEntry entry)
167+
/// <summary>
168+
/// Enable a hosts file entry asynchronously.
169+
/// </summary>
170+
/// <param name="entry">Entry to enable.</param>
171+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>
172+
public static Task<HostsFileEntryModifyResult> EnableEntryAsync(HostsFileEntry entry)
168173
{
169174
return Task.Run(() => EnableEntry(entry));
170175
}
171176

172-
private static bool EnableEntry(HostsFileEntry entry)
177+
/// <summary>
178+
/// Enable a hosts file entry.
179+
/// </summary>
180+
/// <param name="entry">Entry to enable.</param>
181+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>
182+
private static HostsFileEntryModifyResult EnableEntry(HostsFileEntry entry)
173183
{
174184
// Create a backup of the hosts file before making changes
175185
if (CreateBackup() == false)
176186
{
177187
Log.Error("EnableEntry - Failed to create backup before enabling entry.");
178-
return false;
188+
return HostsFileEntryModifyResult.BackupError;
179189
}
180190

181191
// Replace the entry in the hosts file
182192
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
183193

194+
bool entryFound = false;
195+
184196
for (var i = 0; i < hostsFileLines.Count; i++)
185197
{
186198
if (hostsFileLines[i] == entry.Line)
199+
{
200+
entryFound = true;
201+
187202
hostsFileLines[i] = entry.Line.TrimStart('#', ' ');
203+
204+
break;
205+
}
206+
}
207+
208+
if (!entryFound)
209+
{
210+
Log.Warn($"EnableEntry - Entry not found in hosts file: {entry.Line}");
211+
212+
return HostsFileEntryModifyResult.NotFound;
188213
}
189214

190215
try
@@ -195,34 +220,57 @@ private static bool EnableEntry(HostsFileEntry entry)
195220
catch (Exception ex)
196221
{
197222
Log.Error($"EnableEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
198-
199-
return false;
223+
return HostsFileEntryModifyResult.WriteError;
200224
}
201225

202-
return true;
226+
return HostsFileEntryModifyResult.Success;
203227
}
204228

205-
public static Task<bool> DisableEntryAsync(HostsFileEntry entry)
229+
/// <summary>
230+
/// Disable a hosts file entry asynchronously.
231+
/// </summary>
232+
/// <param name="entry">Entry to disable.</param>
233+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>
234+
public static Task<HostsFileEntryModifyResult> DisableEntryAsync(HostsFileEntry entry)
206235
{
207236
return Task.Run(() => DisableEntry(entry));
208237
}
209238

210-
private static bool DisableEntry(HostsFileEntry entry)
239+
/// <summary>
240+
/// Disable a hosts file entry.
241+
/// </summary>
242+
/// <param name="entry">Entry to disable.</param>
243+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>
244+
private static HostsFileEntryModifyResult DisableEntry(HostsFileEntry entry)
211245
{
212246
// Create a backup of the hosts file before making changes
213247
if (CreateBackup() == false)
214248
{
215249
Log.Error("DisableEntry - Failed to create backup before disabling entry.");
216-
return false;
250+
return HostsFileEntryModifyResult.BackupError;
217251
}
218252

219253
// Replace the entry in the hosts file
220254
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
221255

256+
bool entryFound = false;
257+
222258
for (var i = 0; i < hostsFileLines.Count; i++)
223259
{
224260
if (hostsFileLines[i] == entry.Line)
261+
{
262+
entryFound = true;
263+
225264
hostsFileLines[i] = "# " + entry.Line;
265+
266+
break;
267+
}
268+
}
269+
270+
if (!entryFound)
271+
{
272+
Log.Warn($"DisableEntry - Entry not found in hosts file: {entry.Line}");
273+
return HostsFileEntryModifyResult.NotFound;
226274
}
227275

228276
try
@@ -233,15 +281,75 @@ private static bool DisableEntry(HostsFileEntry entry)
233281
catch (Exception ex)
234282
{
235283
Log.Error($"DisableEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
284+
return HostsFileEntryModifyResult.WriteError;
285+
}
236286

237-
return false;
287+
return HostsFileEntryModifyResult.Success;
288+
}
289+
290+
/// <summary>
291+
/// Delete a hosts file entry asynchronously.
292+
/// </summary>
293+
/// <param name="entry">Entry to delete.</param>"/>
294+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>s
295+
public static Task<HostsFileEntryModifyResult> DeleteEntryAsync(HostsFileEntry entry)
296+
{
297+
return Task.Run(() => DeleteEntry(entry));
298+
}
299+
300+
/// <summary>
301+
/// Delete a hosts file entry.
302+
/// </summary>
303+
/// <param name="entry">Entry to delete.</param>"/>
304+
/// <returns><see cref="HostsFileEntryModifyResult.Success"/> if the entry was enabled successfully, otherwise an error result.</returns>
305+
private static HostsFileEntryModifyResult DeleteEntry(HostsFileEntry entry)
306+
{
307+
// Create a backup of the hosts file before making changes
308+
if (CreateBackup() == false)
309+
{
310+
Log.Error("DeleteEntry - Failed to create backup before deleting entry.");
311+
return HostsFileEntryModifyResult.BackupError;
238312
}
239313

240-
return true;
314+
// Remove the entry from the hosts file
315+
var hostsFileLines = File.ReadAllLines(HostsFilePath).ToList();
316+
317+
bool entryFound = false;
318+
319+
for (var i = 0; i < hostsFileLines.Count; i++)
320+
{
321+
if (hostsFileLines[i] == entry.Line)
322+
{
323+
entryFound = true;
324+
325+
hostsFileLines.RemoveAt(i);
326+
327+
break;
328+
}
329+
}
330+
331+
if (!entryFound)
332+
{
333+
Log.Warn($"DeleteEntry - Entry not found in hosts file: {entry.Line}");
334+
return HostsFileEntryModifyResult.NotFound;
335+
}
336+
337+
try
338+
{
339+
Log.Debug($"DeleteEntry - Writing changes to hosts file: {HostsFilePath}");
340+
File.WriteAllLines(HostsFilePath, hostsFileLines);
341+
}
342+
catch (Exception ex)
343+
{
344+
Log.Error($"DeleteEntry - Failed to write changes to hosts file: {HostsFilePath}", ex);
345+
return HostsFileEntryModifyResult.WriteError;
346+
}
347+
OnHostsFileChanged();
348+
return HostsFileEntryModifyResult.Success;
241349
}
242350

243351
/// <summary>
244-
/// Create a daily backup of the hosts file (before making a change).
352+
/// Create a "daily" backup of the hosts file (before making a change).
245353
/// </summary>
246354
private static bool CreateBackup()
247355
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace NETworkManager.Models.HostsFileEditor
2+
{
3+
/// <summary>
4+
/// Represents the result of an attempt to modify a hosts file entry.
5+
/// </summary>
6+
public enum HostsFileEntryModifyResult
7+
{
8+
/// <summary>
9+
/// The entry was modified successfully and the hosts file was updated.
10+
/// </summary>
11+
Success,
12+
13+
/// <summary>
14+
/// The entry was not found in the hosts file.
15+
/// </summary>
16+
NotFound,
17+
18+
/// <summary>
19+
/// An error occurred while writing to the hosts file.
20+
/// </summary>
21+
WriteError,
22+
23+
/// <summary>
24+
/// An error occurred while backing up the hosts file.
25+
/// </summary>
26+
BackupError,
27+
}
28+
}

Source/NETworkManager/ViewModels/HostsFileEditorViewModel.cs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,21 @@ public IList SelectedResults
9393
}
9494
}
9595

96+
private bool _isModifying;
97+
98+
public bool IsModifying
99+
{
100+
get => _isModifying;
101+
set
102+
{
103+
if (value == _isModifying)
104+
return;
105+
106+
_isModifying = value;
107+
OnPropertyChanged();
108+
}
109+
}
110+
96111
private bool _isRefreshing;
97112

98113
public bool IsRefreshing
@@ -192,7 +207,8 @@ private bool Refresh_CanExecute(object parameter)
192207
return Application.Current.MainWindow != null &&
193208
!((MetroWindow)Application.Current.MainWindow).IsAnyDialogOpen &&
194209
!ConfigurationManager.Current.IsChildWindowOpen &&
195-
!IsRefreshing;
210+
!IsRefreshing &&
211+
!IsModifying;
196212
}
197213

198214
private async Task RefreshAction()
@@ -253,14 +269,22 @@ await _dialogCoordinator.ShowMessageAsync(this, Strings.Error,
253269

254270
private async Task EnableEntryAction()
255271
{
272+
IsModifying = true;
273+
256274
await HostsFileEditor.EnableEntryAsync(SelectedResult);
275+
276+
IsModifying = false;
257277
}
258278

259279
public ICommand DisableEntryCommand => new RelayCommand(_ => DisableEntryAction().ConfigureAwait(false), ModifyEntry_CanExecute);
260280

261281
private async Task DisableEntryAction()
262282
{
283+
IsModifying = true;
284+
263285
await HostsFileEditor.DisableEntryAsync(SelectedResult);
286+
287+
IsModifying = false;
264288
}
265289

266290
public ICommand AddEntryCommand => new RelayCommand(_ => AddEntryAction().ConfigureAwait(false), ModifyEntry_CanExecute);
@@ -274,7 +298,33 @@ private async Task AddEntryAction()
274298

275299
private async Task DeleteEntryAction()
276300
{
277-
MessageBox.Show("Delete entry action is not implemented yet.", "Delete Entry", MessageBoxButton.OK, MessageBoxImage.Information);
301+
IsModifying = true;
302+
303+
var childWindow = new OKCancelInfoMessageChildWindow();
304+
305+
var childWindowViewModel = new OKCancelInfoMessageViewModel(async _ =>
306+
{
307+
childWindow.IsOpen = false;
308+
ConfigurationManager.Current.IsChildWindowOpen = false;
309+
310+
await HostsFileEditor.DeleteEntryAsync(SelectedResult);
311+
312+
IsModifying = false;
313+
}, _ =>
314+
{
315+
childWindow.IsOpen = false;
316+
ConfigurationManager.Current.IsChildWindowOpen = false;
317+
318+
IsModifying = false;
319+
}, string.Format(Strings.DeleteHostsFileEntryMessage, SelectedResult.IPAddress, SelectedResult.Hostname, string.IsNullOrEmpty(SelectedResult.Comment) ? "" : $"# {SelectedResult.Comment}"));
320+
321+
childWindow.Title = Strings.DeleteEntry;
322+
323+
childWindow.DataContext = childWindowViewModel;
324+
325+
ConfigurationManager.Current.IsChildWindowOpen = true;
326+
327+
await (Application.Current.MainWindow as MainWindow).ShowChildWindowAsync(childWindow);
278328
}
279329

280330
public ICommand EditEntryCommand => new RelayCommand(_ => EditEntryAction().ConfigureAwait(false), ModifyEntry_CanExecute);
@@ -290,7 +340,8 @@ private bool ModifyEntry_CanExecute(object obj)
290340
Application.Current.MainWindow != null &&
291341
!((MetroWindow)Application.Current.MainWindow).IsAnyDialogOpen &&
292342
!ConfigurationManager.Current.IsChildWindowOpen &&
293-
!IsRefreshing;
343+
!IsRefreshing &&
344+
!IsModifying;
294345
}
295346

296347
public ICommand RestartAsAdminCommand => new RelayCommand(_ => RestartAsAdminAction().ConfigureAwait(false));

Source/NETworkManager/ViewModels/SNTPLookupSettingsViewModel.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
using System.Collections.Generic;
2-
using System.ComponentModel;
3-
using System.Linq;
4-
using System.Threading.Tasks;
5-
using System.Windows;
6-
using System.Windows.Data;
7-
using System.Windows.Input;
8-
using MahApps.Metro.Controls.Dialogs;
1+
using MahApps.Metro.Controls.Dialogs;
92
using MahApps.Metro.SimpleChildWindow;
103
using NETworkManager.Localization.Resources;
114
using NETworkManager.Models.Network;
125
using NETworkManager.Settings;
136
using NETworkManager.Utilities;
147
using NETworkManager.Views;
8+
using System.Collections.Generic;
9+
using System.ComponentModel;
10+
using System.Linq;
11+
using System.Threading.Tasks;
12+
using System.Windows;
13+
using System.Windows.Data;
14+
using System.Windows.Input;
1515

1616
namespace NETworkManager.ViewModels;
1717

@@ -189,7 +189,6 @@ private Task DeleteServer()
189189
{
190190
var childWindow = new OKCancelInfoMessageChildWindow();
191191

192-
193192
var childWindowViewModel = new OKCancelInfoMessageViewModel(_ =>
194193
{
195194
childWindow.IsOpen = false;

Source/NETworkManager/Views/ExportChildWindow.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<Grid Margin="10">
3131
<Grid.RowDefinitions>
3232
<RowDefinition Height="*" />
33-
<RowDefinition Height="10" />
33+
<RowDefinition Height="20" />
3434
<RowDefinition Height="Auto" />
3535
</Grid.RowDefinitions>
3636
<Grid Grid.Row="0">

0 commit comments

Comments
 (0)