Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,12 @@ public static bool IsStaSupported
// Gets the location for cache and config folders.
internal static readonly string CacheDirectory = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
internal static readonly string ConfigDirectory = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CONFIG);
internal static readonly string DefaultPSContentDirectory = Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.DATA), "PowerShell");
#else
// Gets the location for cache and config folders.
internal static readonly string CacheDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerShell";
internal static readonly string ConfigDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\PowerShell";

internal static readonly string DefaultPSContentDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\PowerShell";
private static readonly Lazy<bool> _isStaSupported = new Lazy<bool>(() =>
{
int result = Interop.Windows.CoInitializeEx(IntPtr.Zero, Interop.Windows.COINIT_APARTMENTTHREADED);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Configuration;
using System.Management.Automation.Internal;

namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Implements Get-PSContentPath cmdlet.
/// </summary>
/// TODO:: Add helpURI
[Cmdlet(VerbsCommon.Get, "PSContentPath", HelpUri = "https://go.microsoft.com/fwlink/?linkid=")]
[Experimental(ExperimentalFeature.PSContentPath, ExperimentAction.Show)]
public class GetPSContentPathCommand : PSCmdlet
{
/// <summary>
/// ProcessRecord method of this cmdlet.
/// </summary>
protected override void ProcessRecord()
{
try
{
var psContentPath = Utils.GetPSContentPath();
WriteObject(psContentPath);
}
catch (Exception ex)
{
WriteError(new ErrorRecord(
ex,
"GetPSContentPathFailed",
ErrorCategory.ReadError,
null));
}
}
}

/// <summary>
/// Implements Set-PSContentPath cmdlet.
/// </summary>
/// TODO:: Add helpURI
[Cmdlet(VerbsCommon.Set, "PSContentPath", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?linkid=")]
[Experimental(ExperimentalFeature.PSContentPath, ExperimentAction.Show)]
public class SetPSContentPathCommand : PSCmdlet
{
/// <summary>
/// Gets or sets the PSContentPath to configure.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
public string Path { get; set; }

/// <summary>
/// ProcessRecord method of this cmdlet.
/// </summary>
protected override void ProcessRecord()
{
// Validate the path before processing
if (!ValidatePath(Path))
{
return; // Error already written in ValidatePath
}

if (ShouldProcess($"PSContentPath = {Path}", "Set PSContentPath"))
{
try
{
PowerShellConfig.Instance.SetPSContentPath(Path);
WriteVerbose($"Successfully set PSContentPath to '{Path}'");
WriteWarning("PSContentPath changes will take effect after restarting PowerShell.");
}
catch (Exception ex)
{
WriteError(new ErrorRecord(
ex,
"SetPSContentPathFailed",
ErrorCategory.WriteError,
Path));
}
}
}

/// <summary>
/// Validates that the provided path is a valid directory path.
/// </summary>
/// <param name="path">The path to validate.</param>
/// <returns>True if the path is valid, false otherwise.</returns>
private bool ValidatePath(string path)
{
try
{
// Expand environment variables if present
string expandedPath = Environment.ExpandEnvironmentVariables(path);

// Check if the path contains invalid characters using PowerShell's existing utility
if (PathUtils.ContainsInvalidPathChars(expandedPath))
{
WriteError(new ErrorRecord(
new ArgumentException($"The path '{path}' contains invalid characters."),
"InvalidPathCharacters",
ErrorCategory.InvalidArgument,
path));
return false;
}

// Check if the path is rooted (absolute path)
if (!System.IO.Path.IsPathRooted(expandedPath))
{
WriteError(new ErrorRecord(
new ArgumentException($"The path '{path}' must be an absolute path."),
"RelativePathNotAllowed",
ErrorCategory.InvalidArgument,
path));
return false;
}

// Try to get the full path to validate format
string fullPath = System.IO.Path.GetFullPath(expandedPath);

// Warn if the directory doesn't exist, but don't fail
if (!Directory.Exists(fullPath))
{
WriteWarning($"The directory '{fullPath}' does not exist. It will be created when needed.");
}

return true;
}
catch (ArgumentException ex)
{
WriteError(new ErrorRecord(
ex,
"InvalidPathFormat",
ErrorCategory.InvalidArgument,
path));
return false;
}
catch (System.Security.SecurityException ex)
{
WriteError(new ErrorRecord(
ex,
"PathAccessDenied",
ErrorCategory.PermissionDenied,
path));
return false;
}
catch (NotSupportedException ex)
{
WriteError(new ErrorRecord(
ex,
"PathNotSupported",
ErrorCategory.InvalidArgument,
path));
return false;
}
catch (PathTooLongException ex)
{
WriteError(new ErrorRecord(
ex,
"PathTooLong",
ErrorCategory.InvalidArgument,
path));
return false;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class ExperimentalFeature
internal const string PSNativeWindowsTildeExpansion = nameof(PSNativeWindowsTildeExpansion);
internal const string PSRedirectToVariable = "PSRedirectToVariable";
internal const string PSSerializeJSONLongEnumAsNumber = nameof(PSSerializeJSONLongEnumAsNumber);
internal const string PSContentPath = "PSContentPath";

#endregion

Expand Down Expand Up @@ -124,7 +125,10 @@ static ExperimentalFeature()
description: "Add support for redirecting to the variable drive"),
new ExperimentalFeature(
name: PSSerializeJSONLongEnumAsNumber,
description: "Serialize enums based on long or ulong as an numeric value rather than the string representation when using ConvertTo-Json."
description: "Serialize enums based on long or ulong as an numeric value rather than the string representation when using ConvertTo-Json."),
new ExperimentalFeature(
name: PSContentPath,
description: "Moves PS content to the new default location in LocalAppData/PowerShell and allows users to specify the content path."
)
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5468,6 +5468,7 @@ private static void InitializeCoreCmdletsAndProviders(
{ "Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) },
{ "Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) },
{ "Get-Module", new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), helpFile) },
{ "Get-PSContentPath", new SessionStateCmdletEntry("Get-PSContentPath", typeof(GetPSContentPathCommand), helpFile) },
{ "Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) },
{ "Get-PSSession", new SessionStateCmdletEntry("Get-PSSession", typeof(GetPSSessionCommand), helpFile) },
{ "Import-Module", new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), helpFile) },
Expand All @@ -5489,6 +5490,7 @@ private static void InitializeCoreCmdletsAndProviders(
{ "Remove-Module", new SessionStateCmdletEntry("Remove-Module", typeof(RemoveModuleCommand), helpFile) },
{ "Remove-PSSession", new SessionStateCmdletEntry("Remove-PSSession", typeof(RemovePSSessionCommand), helpFile) },
{ "Save-Help", new SessionStateCmdletEntry("Save-Help", typeof(SaveHelpCommand), helpFile) },
{ "Set-PSContentPath", new SessionStateCmdletEntry("Set-PSContentPath", typeof(SetPSContentPathCommand), helpFile) },
{ "Set-PSDebug", new SessionStateCmdletEntry("Set-PSDebug", typeof(SetPSDebugCommand), helpFile) },
{ "Set-StrictMode", new SessionStateCmdletEntry("Set-StrictMode", typeof(SetStrictModeCommand), helpFile) },
{ "Start-Job", new SessionStateCmdletEntry("Start-Job", typeof(StartJobCommand), helpFile) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace System.Management.Automation
internal static class Constants
{
public const string PSModulePathEnvVar = "PSModulePath";
public const string PSUserContentPathEnvVar = "PSUserContentPath";
}

/// <summary>
Expand Down Expand Up @@ -490,7 +491,7 @@ internal static bool IsModuleMatchingConstraints(
/// <param name="requiredVersion">The required version of the expected module.</param>
/// <param name="minimumVersion">The minimum required version of the expected module.</param>
/// <param name="maximumVersion">The maximum required version of the expected module.</param>
/// <returns>True if the module info object matches all given constraints, false otherwise.</returns>
/// <returns>True if the module info object matches all the constraints on the module specification, false otherwise.</returns>
internal static bool IsModuleMatchingConstraints(
out ModuleMatchFailure matchFailureReason,
PSModuleInfo moduleInfo,
Expand Down Expand Up @@ -964,12 +965,7 @@ internal static string GetModuleName(string path)
/// <returns>Personal module path.</returns>
internal static string GetPersonalModulePath()
{
#if UNIX
return Platform.SelectProductNameForDirectory(Platform.XDG_Type.USER_MODULES);
#else
string myDocumentsPath = InternalTestHooks.SetMyDocumentsSpecialFolderToBlank ? string.Empty : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
return string.IsNullOrEmpty(myDocumentsPath) ? null : Path.Combine(myDocumentsPath, Utils.ModuleDirectory);
#endif
return Path.Combine(Utils.GetPSContentPath(), "Modules");
}

/// <summary>
Expand Down Expand Up @@ -1366,9 +1362,14 @@ private static string SetModulePath()
}
#endif
string allUsersModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.AllUsers);
string personalModulePath = PowerShellConfig.Instance.GetModulePath(ConfigScope.CurrentUser);
string personalModulePath = Utils.GetPSContentPath(true) ?? string.Empty;
string newModulePathString = GetModulePath(currentModulePath, allUsersModulePath, personalModulePath);

if (!string.IsNullOrEmpty(personalModulePath))
{
Environment.SetEnvironmentVariable(Constants.PSUserContentPathEnvVar, personalModulePath);
}

if (!string.IsNullOrEmpty(newModulePathString))
{
Environment.SetEnvironmentVariable(Constants.PSModulePathEnvVar, newModulePathString);
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.