Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
702f681
Initial plan
Copilot Feb 1, 2026
a8853bc
Add system-wide config feature to disable update checks
Copilot Feb 1, 2026
83dc909
Add documentation for system-wide config feature
Copilot Feb 1, 2026
7dc158c
Fix documentation formatting in config README
Copilot Feb 1, 2026
36e595c
Rename ConfigManager to PolicyManager to avoid naming conflict
Copilot Feb 1, 2026
506e6bf
Add config.json.example to installation and UI for policy-managed set…
Copilot Feb 7, 2026
1b12a86
Add localization string for policy-managed setting message
Copilot Feb 7, 2026
9ed5c23
Improve accessibility with icon and add documentation to config example
Copilot Feb 7, 2026
509ede8
Chore: Move config file to settings namespace
BornToBeRoot Feb 8, 2026
247c4c5
Rename policy property to Update_CheckForUpdatesAtStartup and allow e…
Copilot Feb 8, 2026
5180ca0
Chore: Minor color adjustments
BornToBeRoot Feb 8, 2026
388c828
Merge branch 'main' into copilot/disable-update-check-all-users
BornToBeRoot Feb 8, 2026
84434ef
Use direct pattern for policy check and remove helper property
Copilot Feb 8, 2026
a83ee1d
Add Docusaurus documentation for system-wide policies and remove old …
Copilot Feb 8, 2026
65134b0
Fix changelog PR number placeholder
Copilot Feb 8, 2026
dbe56fe
Update system-wide-policies.md
BornToBeRoot Feb 8, 2026
3100ee9
Move policy property details to settings/update.md to avoid duplication
Copilot Feb 8, 2026
c5840c6
Feature: System wide policy
BornToBeRoot Feb 8, 2026
f02c7be
Fix Docusaurus build error with react-image-gallery CSS import
Copilot Feb 8, 2026
bafdcf3
Update browserslist database to latest version
Copilot Feb 8, 2026
6bd67c0
Add null-safety checks for PolicyManager deserialization
Copilot Feb 8, 2026
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3960,4 +3960,7 @@ If you click Cancel, the profile file will remain unencrypted.</value>
<data name="HelpMessage_MaximumNumberOfBackups" xml:space="preserve">
<value>Number of backups that are retained before the oldest one is deleted.</value>
</data>
<data name="SettingManagedByAdministrator" xml:space="preserve">
<value>This setting is managed by your administrator.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions Source/NETworkManager.Settings/NETworkManager.Settings.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<Compile Include="..\GlobalAssemblyInfo.cs" Link="Properties\GlobalAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="config.json.example">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Themes\Dark.Accent1.xaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType>
Expand Down
13 changes: 13 additions & 0 deletions Source/NETworkManager.Settings/PolicyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;

namespace NETworkManager.Settings;

/// <summary>
/// Class that represents system-wide policies that override user settings.
/// This configuration is loaded from a config.json file in the application directory.
/// </summary>
public class PolicyInfo
{
[JsonPropertyName("Update_CheckForUpdatesAtStartup")]
public bool? Update_CheckForUpdatesAtStartup { get; set; }
}
102 changes: 102 additions & 0 deletions Source/NETworkManager.Settings/PolicyManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using log4net;
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NETworkManager.Settings;

/// <summary>
/// Manager for system-wide policies that are loaded from a config.json file
/// in the application directory. These policies override user settings.
/// </summary>
public static class PolicyManager
{
#region Variables

/// <summary>
/// Logger for logging.
/// </summary>
private static readonly ILog Log = LogManager.GetLogger(typeof(PolicyManager));

/// <summary>
/// Config file name.
/// </summary>
private static string ConfigFileName => "config.json";

/// <summary>
/// System-wide policies that are currently loaded.
/// </summary>
public static PolicyInfo Current { get; private set; }

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

#endregion

#region Methods

/// <summary>
/// Method to get the config file path in the application directory.
/// </summary>
/// <returns>Config file path.</returns>
private static string GetConfigFilePath()
{
return Path.Combine(AssemblyManager.Current.Location, ConfigFileName);
}

/// <summary>
/// Method to load the system-wide policies from config.json file in the application directory.
/// </summary>
public static void Load()
{
var filePath = GetConfigFilePath();

// Check if config file exists
if (File.Exists(filePath))
{
try
{
Log.Info($"Loading system-wide policies from: {filePath}");

var jsonString = File.ReadAllText(filePath);

// Treat empty or JSON "null" as "no policies" instead of crashing
if (string.IsNullOrWhiteSpace(jsonString))
{
Current = new PolicyInfo();
Log.Info("Config file is empty, no system-wide policies loaded.");
}
else
{
Current = JsonSerializer.Deserialize<PolicyInfo>(jsonString, JsonOptions) ?? new PolicyInfo();

Log.Info("System-wide policies loaded successfully.");

// Log enabled settings
Log.Info($"System-wide policy - Update_CheckForUpdatesAtStartup: {Current.Update_CheckForUpdatesAtStartup?.ToString() ?? "Not set"}");
}
}
catch (Exception ex)
{
Log.Error($"Failed to load system-wide policies from: {filePath}", ex);
Current = new PolicyInfo();
}
}
else
{
Log.Debug($"No system-wide policy file found at: {filePath}");
Current = new PolicyInfo();
}
}

#endregion
}
3 changes: 3 additions & 0 deletions Source/NETworkManager.Settings/SettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ public static void Initialize()
/// </summary>
public static void Load()
{
// Load system-wide policies first (from app directory)
PolicyManager.Load();

var filePath = GetSettingsFilePath();
var legacyFilePath = GetLegacySettingsFilePath();

Expand Down
3 changes: 3 additions & 0 deletions Source/NETworkManager.Settings/config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Update_CheckForUpdatesAtStartup": false
}
3 changes: 2 additions & 1 deletion Source/NETworkManager/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ private void Load()
NetworkChange.NetworkAddressChanged += (_, _) => OnNetworkHasChanged();

// Search for updates...
if (SettingsManager.Current.Update_CheckForUpdatesAtStartup)
if (PolicyManager.Current?.Update_CheckForUpdatesAtStartup
?? SettingsManager.Current.Update_CheckForUpdatesAtStartup)
CheckForUpdates();
}

Expand Down
9 changes: 8 additions & 1 deletion Source/NETworkManager/ViewModels/SettingsUpdateViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public bool CheckForUpdatesAtStartup
}
}

/// <summary>
/// Gets whether the "Check for updates at startup" setting is managed by system-wide policy.
/// </summary>
public bool IsUpdateCheckManagedByPolicy => PolicyManager.Current?.Update_CheckForUpdatesAtStartup.HasValue == true;

private bool _checkForPreReleases;

public bool CheckForPreReleases
Expand Down Expand Up @@ -78,7 +83,9 @@ public SettingsUpdateViewModel()

private void LoadSettings()
{
CheckForUpdatesAtStartup = SettingsManager.Current.Update_CheckForUpdatesAtStartup;
// If policy is set, show the policy value; otherwise show the user's setting
CheckForUpdatesAtStartup = PolicyManager.Current?.Update_CheckForUpdatesAtStartup
?? SettingsManager.Current.Update_CheckForUpdatesAtStartup;
CheckForPreReleases = SettingsManager.Current.Update_CheckForPreReleases;
EnableExperimentalFeatures = SettingsManager.Current.Experimental_EnableExperimentalFeatures;
}
Expand Down
20 changes: 19 additions & 1 deletion Source/NETworkManager/Views/SettingsUpdateView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:localization="clr-namespace:NETworkManager.Localization.Resources;assembly=NETworkManager.Localization"
xmlns:viewModels="clr-namespace:NETworkManager.ViewModels"
xmlns:converters="clr-namespace:NETworkManager.Converters;assembly=NETworkManager.Converters"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
mc:Ignorable="d" d:DataContext="{d:DesignInstance viewModels:SettingsUpdateViewModel}">
<UserControl.Resources>
<converters:BooleanReverseConverter x:Key="BooleanReverseConverter" />
<converters:BooleanToVisibilityCollapsedConverter x:Key="BooleanToVisibilityCollapsedConverter" />
</UserControl.Resources>
<StackPanel>
<TextBlock Style="{StaticResource HeaderTextBlock}"
Text="{x:Static localization:Strings.Update}" />
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<mah:ToggleSwitch Header="{x:Static localization:Strings.CheckForUpdatesAtStartup}"
IsOn="{Binding CheckForUpdatesAtStartup}" />
IsOn="{Binding CheckForUpdatesAtStartup}"
IsEnabled="{Binding IsUpdateCheckManagedByPolicy, Converter={StaticResource BooleanReverseConverter}}" />
<Rectangle Width="24" Height="24"
VerticalAlignment="Top"
ToolTip="{x:Static localization:Strings.HelpMessage_CheckForUpdatesAtStartup}"
Expand All @@ -24,6 +31,17 @@
</Rectangle.Resources>
</Rectangle>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10"
Visibility="{Binding IsUpdateCheckManagedByPolicy, Converter={StaticResource BooleanToVisibilityCollapsedConverter}}">
<Rectangle Width="24" Height="24" Fill="{DynamicResource MahApps.Brushes.Accent}" VerticalAlignment="Center" Margin="0,0,5,0">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Uniform" Visual="{iconPacks:Material Kind=ShieldLock}" />
</Rectangle.OpacityMask>
</Rectangle>
<TextBlock Text="{x:Static localization:Strings.SettingManagedByAdministrator}"
Style="{StaticResource AccentTextBlock}"
VerticalAlignment="Center" />
</StackPanel>
<mah:ToggleSwitch Header="{x:Static localization:Strings.CheckForPreReleases}"
IsOn="{Binding CheckForPreReleases}"
Margin="0,0,0,20" />
Expand Down
1 change: 1 addition & 0 deletions Website/docs/changelog/next-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Release date: **xx.xx.2025**

- New language Ukrainian (`uk-UA`) has been added. Thanks to [@vadickkt](https://github.com/vadickkt) [#3240](https://github.com/BornToBeRoot/NETworkManager/pull/3240)
- Migrated all dialogs to child windows for improved usability and accessibility. [#3271](https://github.com/BornToBeRoot/NETworkManager/pull/3271)
- System-wide policies can now be configured via a `config.json` file in the application directory to control settings for all users. Currently supports controlling the "Check for updates at startup" setting. This is useful for enterprise deployments where administrators need centralized control over update behavior. See [System-Wide Policies](../system-wide-policies.md) documentation for more information. [#3313](https://github.com/BornToBeRoot/NETworkManager/pull/3313)

**DNS Lookup**

Expand Down
Binary file added Website/docs/img/system-wide-policy-indicator.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions Website/docs/settings/update.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@ Check for new program versions on GitHub when the application is launched.

**Default:** `Enabled`

:::info System-Wide Policy

This setting can be controlled by administrators using a system-wide policy. See [System-Wide Policies](../system-wide-policies.md) for more information.

**Policy Property:** `Update_CheckForUpdatesAtStartup`

**Values:**
- `true` - Force enable automatic update checks at startup for all users
- `false` - Force disable automatic update checks at startup for all users
- Omit the property - Allow users to control this setting themselves

**Example:**

```json
{
"Update_CheckForUpdatesAtStartup": false
}
```

:::

:::note

The URL `https://api.github.com/` must be reachable to check for updates.
Expand Down
102 changes: 102 additions & 0 deletions Website/docs/system-wide-policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
sidebar_position: 6
---

# System-Wide Policies

System-wide policies allow administrators to enforce specific settings for all users on a machine. These policies override user-specific settings and provide centralized control over application behavior in enterprise environments.

## Overview

Policies are defined in a `config.json` file placed in the application installation directory (the same folder as `NETworkManager.exe`). When this file exists, the application loads the policies at startup and applies them with precedence over user settings.

Users will see a visual indicator in the Settings UI when a setting is controlled by a system-wide policy, showing them the administrator-enforced value and preventing them from changing it.

![System-wide policy indicator](./img/system-wide-policy-indicator.png)

## Configuration File

The `config.json` file uses a simple JSON structure to define policy values. An example file (`config.json.example`) is included in the application installation directory for reference.

**File Location:**
- **Installed version**: `C:\Program Files\NETworkManager\config.json` (or your custom installation path)
- **Portable version**: Same directory as `NETworkManager.exe`

**File Format:**

```json
{
"Policy_Name1": true,
"Policy_Name2": "ExampleValue"
}
```


**Example:**

```json
{
"Update_CheckForUpdatesAtStartup": false
}
```

Property names generally follow the pattern `Section_SettingName` (see each setting's documentation). Ensure values use the correct JSON type (boolean, string, number, etc.).

:::note

- The file must be named exactly `config.json`
- The file must contain valid JSON syntax
- Changes to the file require restarting the application to take effect
- If the file doesn't exist or contains invalid JSON, it will be ignored and user settings will apply

:::

## Deployment

For enterprise deployments:

1. **Create the configuration file**:
- Use the `config.json.example` as a template
- Rename it to `config.json`
- Set your desired policy values (you find them in the corresponding setting's documentation)

2. **Deploy to installation directory**:
- Place the `config.json` file in the same directory as `NETworkManager.exe`
- For MSI installations, this is typically `C:\Program Files\NETworkManager\`
- For portable installations, place it next to the executable

3. **Deploy methods**:
- Group Policy — copy the `config.json` file to the installation directory (use Group Policy preferences or a startup script)
- Configuration management tools — SCCM/ConfigMgr, Microsoft Intune, Ansible, etc.
- Scripts and deployment toolkits — PowerShell scripts, PSAppDeployToolkit (recommended for scripted MSI/App deployments)
- Manual deployment — hand-copy for small-scale rollouts

4. **Verification**:
- Launch the application
- Navigate to Settings > Update (e.g., "Check for updates at startup")
- Verify the shield icon and the administrator message appear and that the control is disabled
- Confirm the displayed value matches the policy

:::warning

Ensure the `config.json` file has appropriate permissions so that regular users cannot modify it. On standard installations in `Program Files`, this is automatically enforced by Windows file permissions.

:::

## Troubleshooting

**Policy not being applied:**
- Verify the file is named exactly `config.json` (not `config.json.txt`)
- Check that the JSON syntax is valid (use a JSON validator)
- Confirm the file is in the correct directory (same as `NETworkManager.exe`)
- Restart the application after creating or modifying the file
- Check the application logs for any error messages related to policy loading

**Policy values not showing in UI:**
- Ensure the property name matches exactly (see the corresponding setting's documentation for the property name)
- Verify the value is a boolean (`true` or `false`), not a string (`"true"` or `"false"`)
- Check that there are no syntax errors in the JSON file

## Future Policies

Additional policy options will be added in future releases to provide more granular control over application behavior. If you have specific requirements for system-wide policies in your organization, please submit a feature request via the [GitHub issue tracker](https://github.com/BornToBeRoot/NETworkManager/issues/new/choose).
3 changes: 3 additions & 0 deletions Website/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* work well for content-centric websites.
*/

/* Import react-image-gallery styles */
@import 'react-image-gallery/styles/css/image-gallery.css';

/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #00d4aa;
Expand Down
1 change: 0 additions & 1 deletion Website/src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Layout from "@theme/Layout";
import HomepageFeatures from "@site/src/components/HomepageFeatures";
import ImageGallery from "react-image-gallery";
import "react-image-gallery/styles/css/image-gallery.css";

import ImageGalleryDashboard from "@site/docs/img/dashboard.png";
import ImageGalleryNetworkInterfaceInformation from "@site/docs/img/network-interface--information.png";
Expand Down
Loading
Loading