/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertTo-Html.cs
761 строка
24 KB
xtqqczze
Seal internal types in `Microsoft.PowerShell.Commands.Utility` (#25892)
25 авг 2025, 08:22
Не верифицирован
25 авг 2025, 08:22
5b72eb5
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using System.Management.Automation.Internal; using System.Net; using System.Text; using Microsoft.PowerShell.Commands.Internal.Format; namespace Microsoft.PowerShell.Commands { /// <summary> /// Class comment. /// </summary> [Cmdlet(VerbsData.ConvertTo, "Html", DefaultParameterSetName = "Page", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096595", RemotingCapability = RemotingCapability.None)] [OutputType(typeof(string))] public sealed class ConvertToHtmlCommand : PSCmdlet { /// <summary>The incoming object</summary> /// <value></value> [Parameter(ValueFromPipeline = true)] public PSObject InputObject { get { return _inputObject; } set { _inputObject = value; } } private PSObject _inputObject; /// <summary> /// The list of properties to display. /// These take the form of a PSPropertyExpression. /// </summary> /// <value></value> [Parameter(Position = 0)] public object[] Property { get { return _property; } set { _property = value; } } private object[] _property; /// <summary> /// Text to go after the opening body tag and before the table. /// </summary> /// <value></value> [Parameter(ParameterSetName = "Page", Position = 3)] public string[] Body { get { return _body; } set { _body = value; } } private string[] _body; /// <summary> /// Text to go into the head section of the html doc. /// </summary> /// <value></value> [Parameter(ParameterSetName = "Page", Position = 1)] public string[] Head { get { return _head; } set { _head = value; } } private string[] _head; /// <summary> /// The string for the title tag /// The title is also placed in the body of the document /// before the table between h3 tags /// If the -Head parameter is used, this parameter has no /// effect. /// </summary> /// <value></value> [Parameter(ParameterSetName = "Page", Position = 2)] [ValidateNotNullOrEmpty] public string Title { get { return _title; } set { _title = value; } } private string _title = "HTML TABLE"; /// <summary> /// This specifies whether the objects should /// be rendered as an HTML TABLE or /// HTML LIST. /// </summary> /// <value></value> [Parameter] [ValidateNotNullOrEmpty] [ValidateSet("Table", "List")] public string As { get { return _as; } set { _as = value; } } private string _as = "Table"; /// <summary> /// This specifies a full or partial URI /// for the CSS information. /// The HTML should reference the CSS file specified. /// </summary> [Parameter(ParameterSetName = "Page")] [Alias("cu", "uri")] [ValidateNotNullOrEmpty] public Uri CssUri { get { return _cssuri; } set { _cssuri = value; _cssuriSpecified = true; } } private Uri _cssuri; private bool _cssuriSpecified; /// <summary> /// When this switch is specified generate only the /// HTML representation of the incoming object /// without the HTML,HEAD,TITLE,BODY,etc tags. /// </summary> [Parameter(ParameterSetName = "Fragment")] [ValidateNotNullOrEmpty] public SwitchParameter Fragment { get { return _fragment; } set { _fragment = value; } } private SwitchParameter _fragment; /// <summary> /// Specifies the text to include prior the closing body tag of the HTML output. /// </summary> [Parameter] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] PostContent { get { return _postContent; } set { _postContent = value; } } private string[] _postContent; /// <summary> /// Specifies the text to include after the body tag of the HTML output. /// </summary> [Parameter] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] PreContent { get { return _preContent; } set { _preContent = value; } } private string[] _preContent; /// <summary> /// Sets and Gets the meta property of the HTML head. /// </summary> /// <returns></returns> [Parameter(ParameterSetName = "Page")] [ValidateNotNullOrEmpty] public Hashtable Meta { get { return _meta; } set { _meta = value; _metaSpecified = true; } } private Hashtable _meta; private bool _metaSpecified = false; /// <summary> /// Specifies the charset encoding for the HTML document. /// </summary> [Parameter(ParameterSetName = "Page")] [ValidateNotNullOrEmpty] [ValidatePattern("^[A-Za-z0-9]\\w+\\S+[A-Za-z0-9]$")] public string Charset { get { return _charset; } set { _charset = value; _charsetSpecified = true; } } private string _charset; private bool _charsetSpecified = false; /// <summary> /// When this switch statement is specified, /// it will change the DOCTYPE to XHTML Transitional DTD. /// </summary> /// <returns></returns> [Parameter(ParameterSetName = "Page")] [ValidateNotNullOrEmpty] public SwitchParameter Transitional { get { return _transitional; } set { _transitional = true; } } private bool _transitional = false; /// <summary> /// Definitions for hash table keys. /// </summary> internal static class ConvertHTMLParameterDefinitionKeys { internal const string LabelEntryKey = "label"; internal const string AlignmentEntryKey = "alignment"; internal const string WidthEntryKey = "width"; } /// <summary> /// This allows for @{e='foo';label='bar';alignment='center';width='20'}. /// </summary> internal sealed class ConvertHTMLExpressionParameterDefinition : CommandParameterDefinition { protected override void SetEntries() { this.hashEntries.Add(new ExpressionEntryDefinition()); this.hashEntries.Add(new LabelEntryDefinition()); this.hashEntries.Add(new HashtableEntryDefinition(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey, new[] { typeof(string) })); // Note: We accept "width" as either string or int. this.hashEntries.Add(new HashtableEntryDefinition(ConvertHTMLParameterDefinitionKeys.WidthEntryKey, new[] { typeof(string), typeof(int) })); } } /// <summary> /// Create a list of MshParameter from properties. /// </summary> /// <param name="properties">Can be a string, ScriptBlock, or Hashtable.</param> /// <returns></returns> private List<MshParameter> ProcessParameter(object[] properties) { TerminatingErrorContext invocationContext = new(this); ParameterProcessor processor = new(new ConvertHTMLExpressionParameterDefinition()); properties ??= new object[] { "*" }; return processor.ProcessParameters(properties, invocationContext); } /// <summary> /// Resolve all wildcards in user input Property into resolvedNameMshParameters. /// </summary> private void InitializeResolvedNameMshParameters() { // temp list of properties with wildcards resolved var resolvedNameProperty = new List<object>(); foreach (MshParameter p in _propertyMshParameterList) { string label = p.GetEntry(ConvertHTMLParameterDefinitionKeys.LabelEntryKey) as string; string alignment = p.GetEntry(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey) as string; // Accept the width both as a string and as an int. string width; int? widthNum = p.GetEntry(ConvertHTMLParameterDefinitionKeys.WidthEntryKey) as int?; width = widthNum != null ? widthNum.Value.ToString() : p.GetEntry(ConvertHTMLParameterDefinitionKeys.WidthEntryKey) as string; PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression; List<PSPropertyExpression> resolvedNames = ex.ResolveNames(_inputObject); foreach (PSPropertyExpression resolvedName in resolvedNames) { Hashtable ht = CreateAuxPropertyHT(label, alignment, width); if (resolvedName.Script != null) { // The argument is a calculated property whose value is calculated by a script block. ht.Add(FormatParameterDefinitionKeys.ExpressionEntryKey, resolvedName.Script); } else { ht.Add(FormatParameterDefinitionKeys.ExpressionEntryKey, resolvedName.ToString()); } resolvedNameProperty.Add(ht); } } _resolvedNameMshParameters = ProcessParameter(resolvedNameProperty.ToArray()); } private static Hashtable CreateAuxPropertyHT( string label, string alignment, string width) { Hashtable ht = new(); if (label != null) { ht.Add(ConvertHTMLParameterDefinitionKeys.LabelEntryKey, label); } if (alignment != null) { ht.Add(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey, alignment); } if (width != null) { ht.Add(ConvertHTMLParameterDefinitionKeys.WidthEntryKey, width); } return ht; } /// <summary> /// Calls ToString. If an exception occurs, eats it and return string.Empty. /// </summary> /// <param name="obj"></param> /// <returns></returns> private static string SafeToString(object obj) { if (obj == null) { return string.Empty; } try { return obj.ToString(); } catch (Exception) { // eats exception if safe } return string.Empty; } /// <summary> /// </summary> protected override void BeginProcessing() { // ValidateNotNullOrEmpty attribute is not working for System.Uri datatype, so handling it here if ((_cssuriSpecified) && (string.IsNullOrEmpty(_cssuri.OriginalString.Trim()))) { ArgumentException ex = new(StringUtil.Format(UtilityCommonStrings.EmptyCSSUri, "CSSUri")); ErrorRecord er = new(ex, "ArgumentException", ErrorCategory.InvalidArgument, "CSSUri"); ThrowTerminatingError(er); } _propertyMshParameterList = ProcessParameter(_property); if (!string.IsNullOrEmpty(_title)) { WebUtility.HtmlEncode(_title); } // This first line ensures w3c validation will succeed. However we are not specifying // an encoding in the HTML because we don't know where the text will be written and // if a particular encoding will be used. if (!_fragment) { if (!_transitional) { WriteObject("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); } else { WriteObject("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"); } WriteObject("<html xmlns=\"http://www.w3.org/1999/xhtml\">"); WriteObject("<head>"); if (_charsetSpecified) { WriteObject("<meta charset=\"" + _charset + "\">"); } if (_metaSpecified) { List<string> useditems = new(); foreach (string s in _meta.Keys) { if (!useditems.Contains(s)) { switch (s.ToLower()) { case "content-type": case "default-style": case "x-ua-compatible": WriteObject("<meta http-equiv=\"" + s + "\" content=\"" + _meta[s] + "\">"); break; case "application-name": case "author": case "description": case "generator": case "keywords": case "viewport": WriteObject("<meta name=\"" + s + "\" content=\"" + _meta[s] + "\">"); break; default: MshCommandRuntime mshCommandRuntime = this.CommandRuntime as MshCommandRuntime; string Message = StringUtil.Format(ConvertHTMLStrings.MetaPropertyNotFound, s, _meta[s]); WarningRecord record = new(Message); if (GetVariableValue(SpecialVariables.MyInvocation) is InvocationInfo invocationInfo) { record.SetInvocationInfo(invocationInfo); } mshCommandRuntime.WriteWarning(record); WriteObject("<meta name=\"" + s + "\" content=\"" + _meta[s] + "\">"); break; } useditems.Add(s); } } } WriteObject(_head ?? new string[] { "<title>" + _title + "</title>" }, true); if (_cssuriSpecified) { WriteObject("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + _cssuri + "\" />"); } WriteObject("</head><body>"); if (_body != null) { WriteObject(_body, true); } } if (_preContent != null) { WriteObject(_preContent, true); } WriteObject("<table>"); _isTHWritten = false; } /// <summary> /// Reads Width and Alignment from Property and write Col tags. /// </summary> /// <param name="mshParams"></param> private void WriteColumns(List<MshParameter> mshParams) { StringBuilder COLTag = new(); COLTag.Append("<colgroup>"); foreach (MshParameter p in mshParams) { COLTag.Append("<col"); if (p.GetEntry(ConvertHTMLParameterDefinitionKeys.WidthEntryKey) is string width) { COLTag.Append(" width = \""); COLTag.Append(width); COLTag.Append('"'); } if (p.GetEntry(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey) is string alignment) { COLTag.Append(" align = \""); COLTag.Append(alignment); COLTag.Append('"'); } COLTag.Append("/>"); } COLTag.Append("</colgroup>"); // The columngroup and col nodes will be printed in a single line. WriteObject(COLTag.ToString()); } /// <summary> /// Writes the list entries when the As parameter has value List. /// </summary> private void WriteListEntry() { foreach (MshParameter p in _resolvedNameMshParameters) { StringBuilder Listtag = new(); Listtag.Append("<tr><td>"); // for writing the property name WritePropertyName(Listtag, p); Listtag.Append(':'); Listtag.Append("</td>"); // for writing the property value Listtag.Append("<td>"); WritePropertyValue(Listtag, p); Listtag.Append("</td></tr>"); WriteObject(Listtag.ToString()); } } /// <summary> /// To write the Property name. /// </summary> private static void WritePropertyName(StringBuilder Listtag, MshParameter p) { // for writing the property name if (p.GetEntry(ConvertHTMLParameterDefinitionKeys.LabelEntryKey) is string label) { Listtag.Append(label); } else { PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression; Listtag.Append(ex.ToString()); } } /// <summary> /// To write the Property value. /// </summary> private void WritePropertyValue(StringBuilder Listtag, MshParameter p) { PSPropertyExpression exValue = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression; // get the value of the property List<PSPropertyExpressionResult> resultList = exValue.GetValues(_inputObject); foreach (PSPropertyExpressionResult result in resultList) { // create comma sep list for multiple results if (result.Result != null) { string htmlEncodedResult = WebUtility.HtmlEncode(SafeToString(result.Result)); Listtag.Append(htmlEncodedResult); } Listtag.Append(", "); } if (Listtag.ToString().EndsWith(", ", StringComparison.Ordinal)) { Listtag.Remove(Listtag.Length - 2, 2); } } /// <summary> /// To write the Table header for the object property names. /// </summary> private static void WriteTableHeader(StringBuilder THtag, List<MshParameter> resolvedNameMshParameters) { // write the property names foreach (MshParameter p in resolvedNameMshParameters) { THtag.Append("<th>"); WritePropertyName(THtag, p); THtag.Append("</th>"); } } /// <summary> /// To write the Table row for the object property values. /// </summary> private void WriteTableRow(StringBuilder TRtag, List<MshParameter> resolvedNameMshParameters) { // write the property values foreach (MshParameter p in resolvedNameMshParameters) { TRtag.Append("<td>"); WritePropertyValue(TRtag, p); TRtag.Append("</td>"); } } // count of the objects private int _numberObjects = 0; /// <summary> /// </summary> protected override void ProcessRecord() { // writes the table headers // it is not in BeginProcessing because the first inputObject is needed for // the number of columns and column name if (_inputObject == null || _inputObject == AutomationNull.Value) { return; } _numberObjects++; if (!_isTHWritten) { InitializeResolvedNameMshParameters(); if (_resolvedNameMshParameters == null || _resolvedNameMshParameters.Count == 0) { return; } // if the As parameter is given as List if (_as.Equals("List", StringComparison.OrdinalIgnoreCase)) { // if more than one object,write the horizontal rule to put visual separator if (_numberObjects > 1) WriteObject("<tr><td><hr></td></tr>"); WriteListEntry(); } else // if the As parameter is Table, first we have to write the property names { WriteColumns(_resolvedNameMshParameters); StringBuilder THtag = new("<tr>"); // write the table header WriteTableHeader(THtag, _resolvedNameMshParameters); THtag.Append("</tr>"); WriteObject(THtag.ToString()); _isTHWritten = true; } } // if the As parameter is Table, write the property values if (_as.Equals("Table", StringComparison.OrdinalIgnoreCase)) { StringBuilder TRtag = new("<tr>"); // write the table row WriteTableRow(TRtag, _resolvedNameMshParameters); TRtag.Append("</tr>"); WriteObject(TRtag.ToString()); } } /// <summary> /// </summary> protected override void EndProcessing() { // if fragment,end with table WriteObject("</table>"); if (_postContent != null) WriteObject(_postContent, true); // if not fragment end with body and html also if (!_fragment) { WriteObject("</body></html>"); } } #region private /// <summary> /// List of incoming objects to compare. /// </summary> private bool _isTHWritten; private List<MshParameter> _propertyMshParameterList; private List<MshParameter> _resolvedNameMshParameters; // private string ResourcesBaseName = "ConvertHTMLStrings"; #endregion private } }