diff --git a/.claude/skills/cf-init/scripts/cf-init.ps1 b/.claude/skills/cf-init/scripts/cf-init.ps1 index 0834c0ad..8fd50976 100644 --- a/.claude/skills/cf-init/scripts/cf-init.ps1 +++ b/.claude/skills/cf-init/scripts/cf-init.ps1 @@ -1,4 +1,4 @@ -# cf-init v1.8 — Create empty 1C configuration scaffold +# cf-init v1.9 — Create empty 1C configuration scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -16,6 +16,12 @@ param( ) $ErrorActionPreference = "Stop" + +function Esc-XmlText { + param([string]$s) + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return $s.Replace('&','&').Replace('<','<').Replace('>','>') +} [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # --- Resolve output dir --- @@ -74,14 +80,14 @@ foreach ($mf in $mobileFuncs) { # --- Synonym XML --- $synonymXml = "" if ($Synonym) { - $synonymXml = "`r`n`t`t`t`t`r`n`t`t`t`t`tru`r`n`t`t`t`t`t$([System.Security.SecurityElement]::Escape($Synonym))`r`n`t`t`t`t`r`n`t`t`t" + $synonymXml = "`r`n`t`t`t`t`r`n`t`t`t`t`tru`r`n`t`t`t`t`t$(Esc-XmlText ($Synonym))`r`n`t`t`t`t`r`n`t`t`t" } # --- Optional properties --- # Элемент целиком, а не значение внутри пары: при пустом значении Конфигуратор # пишет , а не . -$vendorEl = if ($Vendor) { "$([System.Security.SecurityElement]::Escape($Vendor))" } else { "" } -$versionEl = if ($Version) { "$([System.Security.SecurityElement]::Escape($Version))" } else { "" } +$vendorEl = if ($Vendor) { "$(Esc-XmlText ($Vendor))" } else { "" } +$versionEl = if ($Version) { "$(Esc-XmlText ($Version))" } else { "" } # --- Свойства и пространство имён формата 2.21 (платформа 8.5) --- # Значения и ПОЗИЦИИ сняты с выгрузки 8.5.1 (debug/fmt221/dump_v851): те же исходники, @@ -143,7 +149,7 @@ $cfgXml = @" - $([System.Security.SecurityElement]::Escape($Name)) + $(Esc-XmlText ($Name)) $synonymXml diff --git a/.claude/skills/cf-init/scripts/cf-init.py b/.claude/skills/cf-init/scripts/cf-init.py index 7d5249a6..616fec34 100644 --- a/.claude/skills/cf-init/scripts/cf-init.py +++ b/.claude/skills/cf-init/scripts/cf-init.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 -# cf-init v1.8 — Create empty 1C configuration scaffold +# cf-init v1.9 — Create empty 1C configuration scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Generates minimal XML source files for a 1C configuration.""" import sys, os, argparse, re, uuid -def esc_xml(s): - return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"') +def esc_xml_text(s): + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return s.replace('&', '&').replace('<', '<').replace('>', '>') def new_uuid(): return str(uuid.uuid4()) @@ -96,12 +97,12 @@ def main(): # --- Synonym XML --- synonym_xml = "" if synonym: - synonym_xml = f"\r\n\t\t\t\t\r\n\t\t\t\t\tru\r\n\t\t\t\t\t{esc_xml(synonym)}\r\n\t\t\t\t\r\n\t\t\t" + synonym_xml = f"\r\n\t\t\t\t\r\n\t\t\t\t\tru\r\n\t\t\t\t\t{esc_xml_text(synonym)}\r\n\t\t\t\t\r\n\t\t\t" # Элемент целиком, а не значение внутри пары: при пустом значении Конфигуратор # пишет , а не . - vendor_el = f"{esc_xml(vendor)}" if vendor else "" - version_el = f"{esc_xml(version)}" if version else "" + vendor_el = f"{esc_xml_text(vendor)}" if vendor else "" + version_el = f"{esc_xml_text(version)}" if version else "" class_ids = [ "9cd510cd-abfc-11d4-9434-004095e12fc7", @@ -148,7 +149,7 @@ def main(): \t\t {contained_objects}\t\t \t\t -\t\t\t{esc_xml(name)} +\t\t\t{esc_xml_text(name)} \t\t\t{synonym_xml} \t\t\t \t\t\t diff --git a/.claude/skills/cfe-init/scripts/cfe-init.ps1 b/.claude/skills/cfe-init/scripts/cfe-init.ps1 index 7b1d3437..edb186c5 100644 --- a/.claude/skills/cfe-init/scripts/cfe-init.ps1 +++ b/.claude/skills/cfe-init/scripts/cfe-init.ps1 @@ -1,4 +1,4 @@ -# cfe-init v1.7 — Create 1C configuration extension scaffold (CFE) +# cfe-init v1.8 — Create 1C configuration extension scaffold (CFE) (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -16,6 +16,12 @@ param( ) $ErrorActionPreference = "Stop" + +function Esc-XmlText { + param([string]$s) + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return $s.Replace('&','&').Replace('<','<').Replace('>','>') +} [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # --- Default NamePrefix --- @@ -121,14 +127,14 @@ $co7 = [guid]::NewGuid().ToString() # --- Synonym XML --- $synonymXml = "" if ($Synonym) { - $synonymXml = "`r`n`t`t`t`t`r`n`t`t`t`t`tru`r`n`t`t`t`t`t$([System.Security.SecurityElement]::Escape($Synonym))`r`n`t`t`t`t`r`n`t`t`t" + $synonymXml = "`r`n`t`t`t`t`r`n`t`t`t`t`tru`r`n`t`t`t`t`t$(Esc-XmlText ($Synonym))`r`n`t`t`t`t`r`n`t`t`t" } # --- Optional properties --- # Элемент целиком, а не значение внутри пары: при пустом значении Конфигуратор # пишет , а не . -$vendorEl = if ($Vendor) { "$([System.Security.SecurityElement]::Escape($Vendor))" } else { "" } -$versionEl = if ($Version) { "$([System.Security.SecurityElement]::Escape($Version))" } else { "" } +$vendorEl = if ($Vendor) { "$(Esc-XmlText ($Vendor))" } else { "" } +$versionEl = if ($Version) { "$(Esc-XmlText ($Version))" } else { "" } # --- Role name --- $roleName = "${NamePrefix}ОсновнаяРоль" @@ -206,12 +212,12 @@ $cfgXml = @" Adopted - $([System.Security.SecurityElement]::Escape($Name)) + $(Esc-XmlText ($Name)) $synonymXml $Purpose true - $([System.Security.SecurityElement]::Escape($NamePrefix)) + $(Esc-XmlText ($NamePrefix)) $CompatibilityMode ManagedApplication @@ -257,7 +263,7 @@ $roleXml = @" - $([System.Security.SecurityElement]::Escape($roleName)) + $(Esc-XmlText ($roleName)) diff --git a/.claude/skills/cfe-init/scripts/cfe-init.py b/.claude/skills/cfe-init/scripts/cfe-init.py index 32ede231..b7e496e9 100644 --- a/.claude/skills/cfe-init/scripts/cfe-init.py +++ b/.claude/skills/cfe-init/scripts/cfe-init.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 -# cfe-init v1.7 — Create 1C configuration extension scaffold (CFE) +# cfe-init v1.8 — Create 1C configuration extension scaffold (CFE) (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Generates minimal XML source files for a 1C configuration extension.""" import sys, os, re, argparse, uuid from xml.etree import ElementTree as ET -def esc_xml(s): - return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"') +def esc_xml_text(s): + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return s.replace('&', '&').replace('<', '<').replace('>', '>') def new_uuid(): return str(uuid.uuid4()) @@ -142,12 +143,12 @@ def main(): # --- Synonym XML --- synonym_xml = "" if synonym: - synonym_xml = f"\r\n\t\t\t\t\r\n\t\t\t\t\tru\r\n\t\t\t\t\t{esc_xml(synonym)}\r\n\t\t\t\t\r\n\t\t\t" + synonym_xml = f"\r\n\t\t\t\t\r\n\t\t\t\t\tru\r\n\t\t\t\t\t{esc_xml_text(synonym)}\r\n\t\t\t\t\r\n\t\t\t" # Элемент целиком, а не значение внутри пары: при пустом значении Конфигуратор # пишет , а не . - vendor_el = f"{esc_xml(vendor)}" if vendor else "" - version_el = f"{esc_xml(version)}" if version else "" + vendor_el = f"{esc_xml_text(vendor)}" if vendor else "" + version_el = f"{esc_xml_text(version)}" if version else "" # --- Role name --- role_name = f"{name_prefix}ОсновнаяРоль" @@ -224,12 +225,12 @@ def main(): {contained_objects}\t\t \t\t \t\t\tAdopted -\t\t\t{esc_xml(name)} +\t\t\t{esc_xml_text(name)} \t\t\t{synonym_xml} \t\t\t \t\t\t{purpose} \t\t\ttrue -\t\t\t{esc_xml(name_prefix)} +\t\t\t{esc_xml_text(name_prefix)} \t\t\t{compat} \t\t\tManagedApplication \t\t\t @@ -271,7 +272,7 @@ def main(): \t \t\t -\t\t\t{esc_xml(role_name)} +\t\t\t{esc_xml_text(role_name)} \t\t\t \t\t\t \t\t diff --git a/.claude/skills/epf-init/scripts/init.ps1 b/.claude/skills/epf-init/scripts/init.ps1 index 6c2b1ee9..f8c96e84 100644 --- a/.claude/skills/epf-init/scripts/init.ps1 +++ b/.claude/skills/epf-init/scripts/init.ps1 @@ -1,4 +1,4 @@ -# epf-init v1.4 — Init 1C external data processor scaffold +# epf-init v1.5 — Init 1C external data processor scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -16,6 +16,12 @@ param( ) $ErrorActionPreference = "Stop" + +function Esc-XmlText { + param([string]$s) + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return $s.Replace('&','&').Replace('<','<').Replace('>','>') +} [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::InputEncoding = [System.Text.Encoding]::UTF8 @@ -47,11 +53,11 @@ $xml = @" - $Name + $(Esc-XmlText $Name) ru - $Synonym + $(Esc-XmlText $Synonym) diff --git a/.claude/skills/epf-init/scripts/init.py b/.claude/skills/epf-init/scripts/init.py index 4d7a4d72..05c48004 100644 --- a/.claude/skills/epf-init/scripts/init.py +++ b/.claude/skills/epf-init/scripts/init.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 -# epf-init v1.4 — Init 1C external data processor scaffold +# epf-init v1.5 — Init 1C external data processor scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Generates minimal XML source files for a 1C external data processor.""" import sys, os, re, argparse, uuid -def esc_xml(s): - return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"') +def esc_xml_text(s): + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return s.replace('&', '&').replace('<', '<').replace('>', '>') def new_uuid(): return str(uuid.uuid4()) @@ -95,11 +96,11 @@ def main(): \t\t\t \t\t \t\t -\t\t\t{esc_xml(name)} +\t\t\t{esc_xml_text(name)} \t\t\t \t\t\t\t \t\t\t\t\tru -\t\t\t\t\t{esc_xml(synonym)} +\t\t\t\t\t{esc_xml_text(synonym)} \t\t\t\t \t\t\t \t\t\t diff --git a/.claude/skills/erf-init/scripts/init.ps1 b/.claude/skills/erf-init/scripts/init.ps1 index 264f5452..bcd5ed65 100644 --- a/.claude/skills/erf-init/scripts/init.ps1 +++ b/.claude/skills/erf-init/scripts/init.ps1 @@ -1,4 +1,4 @@ -# erf-init v1.4 — Init 1C external report scaffold +# erf-init v1.5 — Init 1C external report scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -18,6 +18,12 @@ param( ) $ErrorActionPreference = "Stop" + +function Esc-XmlText { + param([string]$s) + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return $s.Replace('&','&').Replace('<','<').Replace('>','>') +} [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::InputEncoding = [System.Text.Encoding]::UTF8 @@ -75,11 +81,11 @@ $xml = @" - $Name + $(Esc-XmlText $Name) ru - $Synonym + $(Esc-XmlText $Synonym) diff --git a/.claude/skills/erf-init/scripts/init.py b/.claude/skills/erf-init/scripts/init.py index 08757972..7a6ac948 100644 --- a/.claude/skills/erf-init/scripts/init.py +++ b/.claude/skills/erf-init/scripts/init.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 -# erf-init v1.4 — Init 1C external report scaffold +# erf-init v1.5 — Init 1C external report scaffold (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Generates minimal XML source files for a 1C external report.""" import sys, os, re, argparse, uuid -def esc_xml(s): - return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"') +def esc_xml_text(s): + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return s.replace('&', '&').replace('<', '<').replace('>', '>') def new_uuid(): return str(uuid.uuid4()) @@ -107,11 +108,11 @@ def main(): \t\t\t \t\t \t\t -\t\t\t{esc_xml(name)} +\t\t\t{esc_xml_text(name)} \t\t\t \t\t\t\t \t\t\t\t\tru -\t\t\t\t\t{esc_xml(synonym)} +\t\t\t\t\t{esc_xml_text(synonym)} \t\t\t\t \t\t\t \t\t\t diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 9de22daf..5493bd61 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -1,4 +1,4 @@ -# form-compile v1.187 — Compile 1C managed form from JSON or object metadata (+resolve_type_str: срезание префикса cfg:/d5p1:) +# form-compile v1.188 — Compile 1C managed form from JSON or object metadata (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -1705,6 +1705,12 @@ function X { } function Esc-Xml { + param([string]$s) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +function Esc-XmlText { # Экранирование ТЕКСТА элемента (, ): только & < > . # Кавычки/апострофы в тексте экранировать НЕ нужно (1С их не экранирует — пишет литерально); # " ломал бы раундтрип. Кавычки спецсимвольны лишь в значениях атрибутов. @@ -1719,14 +1725,14 @@ function Emit-MLItems { param($val, [string]$indent) if ($val -is [System.Collections.IDictionary]) { foreach ($k in $val.Keys) { - X "$indent"; X "$indent`t$k"; X "$indent`t$(Esc-Xml "$($val[$k])")"; X "$indent" + X "$indent"; X "$indent`t$k"; X "$indent`t$(Esc-XmlText "$($val[$k])")"; X "$indent" } } elseif ($val -is [System.Management.Automation.PSCustomObject]) { foreach ($p in $val.PSObject.Properties) { - X "$indent"; X "$indent`t$($p.Name)"; X "$indent`t$(Esc-Xml "$($p.Value)")"; X "$indent" + X "$indent"; X "$indent`t$($p.Name)"; X "$indent`t$(Esc-XmlText "$($p.Value)")"; X "$indent" } } else { - X "$indent"; X "$indent`tru"; X "$indent`t$(Esc-Xml "$val")"; X "$indent" + X "$indent"; X "$indent`tru"; X "$indent`t$(Esc-XmlText "$val")"; X "$indent" } } @@ -1745,7 +1751,7 @@ function Emit-USPresentation { param($val, [string]$tag, [string]$indent) if ($null -eq $val) { return } if ($val -is [string]) { - X "$indent<$tag xsi:type=`"xs:string`">$(Esc-Xml $val)" + X "$indent<$tag xsi:type=`"xs:string`">$(Esc-XmlText $val)" } else { Emit-MLText -tag $tag -text $val -indent $indent -xsiType "v8:LocalStringType" } @@ -1874,10 +1880,10 @@ function Emit-FilterItem { } } if ($item.presentation) { Emit-USPresentation -val $item.presentation -tag "dcsset:presentation" -indent "$indent`t" } - if ($item.viewMode) { X "$indent`t$(Esc-Xml "$($item.viewMode)")" } + if ($item.viewMode) { X "$indent`t$(Esc-XmlText "$($item.viewMode)")" } if ($item.userSettingID) { $guid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" } - X "$indent`t$(Esc-Xml $guid)" + X "$indent`t$(Esc-XmlText $guid)" } if ($item.userSettingPresentation) { Emit-USPresentation -val $item.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" @@ -1885,10 +1891,10 @@ function Emit-FilterItem { } X "$indent" if ($item.use -eq $false) { X "$indent`tfalse" } - X "$indent`t$(Esc-Xml "$($item.field)")" + X "$indent`t$(Esc-XmlText "$($item.field)")" $compType = $script:comparisonTypes["$($item.op)"] if (-not $compType) { $compType = "$($item.op)" } - X "$indent`t$(Esc-Xml $compType)" + X "$indent`t$(Esc-XmlText $compType)" $valIsArray = ($item.value -is [array]) -or ($item.value -is [System.Collections.IList] -and $item.value -isnot [string]) if ($valIsArray) { if (@($item.value).Count -eq 0) { @@ -1907,7 +1913,7 @@ function Emit-FilterItem { elseif ("$v" -match '^(Перечисление|Справочник|ПланСчетов|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена|Catalog|Enum|Document|ChartOfAccounts|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { $vt = 'dcscor:DesignTimeValue' } else { $vt = 'xs:string' } } - $vStr = if ($v -is [bool]) { "$v".ToLower() } else { Esc-Xml "$v" } + $vStr = if ($v -is [bool]) { "$v".ToLower() } else { Esc-XmlText "$v" } $nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$v" X "$indent`t$vStr" } @@ -1933,8 +1939,8 @@ function Emit-FilterItem { $variant = "$sv"; $hasDate = $false; $dateV = $null } X "$indent`t" - X "$indent`t`t$(Esc-Xml $variant)" - if ($hasDate) { X "$indent`t`t$(Esc-Xml $dateV)" } + X "$indent`t`t$(Esc-XmlText $variant)" + if ($hasDate) { X "$indent`t`t$(Esc-XmlText $dateV)" } X "$indent`t" } elseif ("$($item.value)" -eq '_') { # "_" — маркер пустого значения: платформа эмитит пустой self-closing @@ -1952,15 +1958,15 @@ function Emit-FilterItem { elseif ("$v" -match '^(Перечисление|Справочник|ПланСчетов|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена|Catalog|Enum|Document|ChartOfAccounts|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { $vt = "dcscor:DesignTimeValue" } else { $vt = "xs:string" } } - $vStr = if ($item.value -is [bool]) { "$($item.value)".ToLower() } else { Esc-Xml "$($item.value)" } + $vStr = if ($item.value -is [bool]) { "$($item.value)".ToLower() } else { Esc-XmlText "$($item.value)" } $nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$($item.value)" X "$indent`t$vStr" } if ($item.presentation) { Emit-USPresentation -val $item.presentation -tag "dcsset:presentation" -indent "$indent`t" } - if ($item.viewMode) { X "$indent`t$(Esc-Xml "$($item.viewMode)")" } + if ($item.viewMode) { X "$indent`t$(Esc-XmlText "$($item.viewMode)")" } if ($item.userSettingID) { $uid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" } - X "$indent`t$(Esc-Xml $uid)" + X "$indent`t$(Esc-XmlText $uid)" } if ($item.userSettingPresentation) { Emit-USPresentation -val $item.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" @@ -1984,10 +1990,10 @@ function Emit-Filter { Emit-FilterItem -item ([pscustomobject]$obj) -indent "$indent`t" } else { Emit-FilterItem -item $item -indent "$indent`t" } } - if ($null -ne $blockViewMode) { X "$indent`t$(Esc-Xml "$blockViewMode")" } + if ($null -ne $blockViewMode) { X "$indent`t$(Esc-XmlText "$blockViewMode")" } if ($null -ne $blockUserSettingID) { $uid = if ("$blockUserSettingID" -eq 'auto') { New-Guid-String } else { "$blockUserSettingID" } - X "$indent`t$(Esc-Xml $uid)" + X "$indent`t$(Esc-XmlText $uid)" } if ($null -ne $blockUserSettingPresentation) { Emit-USPresentation -val $blockUserSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" @@ -2009,7 +2015,7 @@ function Emit-Order { if ($parts.Count -gt 1 -and $parts[1] -match '^(?i)(desc|убыв)') { $dir = "Desc" } elseif ($parts.Count -gt 1 -and $parts[1] -match '^(?i)(asc|возр)') { $dir = "Asc" } X "$indent`t" - X "$indent`t`t$(Esc-Xml $field)" + X "$indent`t`t$(Esc-XmlText $field)" X "$indent`t`t$dir" X "$indent`t" } @@ -2019,16 +2025,16 @@ function Emit-Order { if ($dir -match '^(?i)(desc|убыв)') { $dir = "Desc" } elseif ($dir -match '^(?i)(asc|возр)') { $dir = "Asc" } X "$indent`t" if ($item.use -eq $false) { X "$indent`t`tfalse" } - X "$indent`t`t$(Esc-Xml "$($item.field)")" + X "$indent`t`t$(Esc-XmlText "$($item.field)")" X "$indent`t`t$dir" - if ($item.viewMode) { X "$indent`t`t$(Esc-Xml "$($item.viewMode)")" } + if ($item.viewMode) { X "$indent`t`t$(Esc-XmlText "$($item.viewMode)")" } X "$indent`t" } } - if ($null -ne $blockViewMode) { X "$indent`t$(Esc-Xml "$blockViewMode")" } + if ($null -ne $blockViewMode) { X "$indent`t$(Esc-XmlText "$blockViewMode")" } if ($null -ne $blockUserSettingID) { $uid = if ("$blockUserSettingID" -eq 'auto') { New-Guid-String } else { "$blockUserSettingID" } - X "$indent`t$(Esc-Xml $uid)" + X "$indent`t$(Esc-XmlText $uid)" } if ($null -ne $blockUserSettingPresentation) { Emit-USPresentation -val $blockUserSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" @@ -2060,7 +2066,7 @@ function Emit-AppearanceValue { if (_HasKey $val 'items') { $nestedItems = (_Get $val 'items') } } if ($useWrapper) { X "$indent`tfalse" } - X "$indent`t$(Esc-Xml $key)" + X "$indent`t$(Esc-XmlText $key)" $isFontDict = $false if ($innerVal -is [PSCustomObject]) { $tProp = $innerVal.PSObject.Properties['@type'] @@ -2076,7 +2082,7 @@ function Emit-AppearanceValue { $lg = if (_HasKey $innerVal 'gap') { if ((_Get $innerVal 'gap')) { 'true' } else { 'false' } } else { 'false' } $ls = if (_HasKey $innerVal 'style') { "$(_Get $innerVal 'style')" } else { 'None' } X "$indent`t" - X "$indent`t`t$(Esc-Xml $ls)" + X "$indent`t`t$(Esc-XmlText $ls)" X "$indent`t" } elseif ($isFontDict) { $attrParts = @() @@ -2089,7 +2095,7 @@ function Emit-AppearanceValue { X "$indent`t" } elseif ($isDict -and (_HasKey $innerVal 'field')) { # Ссылка на поле (dcscor:Field) — значение параметра оформления = поле компоновки - X "$indent`t$(Esc-Xml "$(_Get $innerVal 'field')")" + X "$indent`t$(Esc-XmlText "$(_Get $innerVal 'field')")" } elseif ($isDict) { # Локализуемый текст параметра оформления: платформа объявляет xsi:type на dcscor:value Emit-MLText -tag "dcscor:value" -text $innerVal -indent "$indent`t" -xsiType "v8:LocalStringType" @@ -2104,19 +2110,19 @@ function Emit-AppearanceValue { 'ТипМакета' = 'dcsset:DataCompositionGroupTemplateType' } $keyType = $keyTypeMap[$key] - if ($keyType) { X "$indent`t$(Esc-Xml $actualVal)" } - elseif ($actualVal -match '^(style|web|win):') { X "$indent`t$(Esc-Xml $actualVal)" } + if ($keyType) { X "$indent`t$(Esc-XmlText $actualVal)" } + elseif ($actualVal -match '^(style|web|win):') { X "$indent`t$(Esc-XmlText $actualVal)" } elseif ($actualVal -eq "true" -or $actualVal -eq "false") { X "$indent`t$actualVal" } elseif ($key -eq "Текст" -or $key -eq "Заголовок" -or $key -eq "Формат") { # Текст/Заголовок/Формат: голая строка = плоский xs:string (так платформа хранит # нелокализованный литерал). Локализуемый текст → объект {ru,en} (ветка isDict выше). # Пустая строка → самозакрывающийся тег (как у платформы). if ($actualVal -eq '') { X "$indent`t" } - else { X "$indent`t$(Esc-Xml $actualVal)" } + else { X "$indent`t$(Esc-XmlText $actualVal)" } } elseif ($actualVal -match '^-?\d+(\.\d+)?$') { X "$indent`t$actualVal" } - elseif ($key -eq 'ЦветТекста' -or $key -eq 'ЦветФона' -or $key -eq 'ЦветГраницы') { X "$indent`t$(Esc-Xml $actualVal)" } - else { X "$indent`t$(Esc-Xml $actualVal)" } + elseif ($key -eq 'ЦветТекста' -or $key -eq 'ЦветФона' -or $key -eq 'ЦветГраницы') { X "$indent`t$(Esc-XmlText $actualVal)" } + else { X "$indent`t$(Esc-XmlText $actualVal)" } } if ($nestedItems) { $niProps = if ($nestedItems -is [PSCustomObject]) { $nestedItems.PSObject.Properties } else { $null } @@ -2139,7 +2145,7 @@ function Emit-ConditionalAppearance { X "$indent`t`t" foreach ($sel in $ca.selection) { X "$indent`t`t`t" - X "$indent`t`t`t`t$(Esc-Xml "$sel")" + X "$indent`t`t`t`t$(Esc-XmlText "$sel")" X "$indent`t`t`t" } X "$indent`t`t" @@ -2158,12 +2164,12 @@ function Emit-ConditionalAppearance { Emit-MLItems -val $ca.presentation -indent "$indent`t`t`t" X "$indent`t`t" } - else { X "$indent`t`t$(Esc-Xml "$($ca.presentation)")" } + else { X "$indent`t`t$(Esc-XmlText "$($ca.presentation)")" } } - if ($ca.viewMode) { X "$indent`t`t$(Esc-Xml "$($ca.viewMode)")" } + if ($ca.viewMode) { X "$indent`t`t$(Esc-XmlText "$($ca.viewMode)")" } if ($ca.userSettingID) { $uid = if ("$($ca.userSettingID)" -eq "auto") { New-Guid-String } else { "$($ca.userSettingID)" } - X "$indent`t`t$(Esc-Xml $uid)" + X "$indent`t`t$(Esc-XmlText $uid)" } if ($ca.userSettingPresentation) { Emit-USPresentation -val $ca.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t`t" } if ($ca.useInDontUse -and $ca.useInDontUse.Count -gt 0) { @@ -2179,10 +2185,10 @@ function Emit-ConditionalAppearance { } X "$indent`t" } - if ($null -ne $blockViewMode) { X "$indent`t$(Esc-Xml "$blockViewMode")" } + if ($null -ne $blockViewMode) { X "$indent`t$(Esc-XmlText "$blockViewMode")" } if ($null -ne $blockUserSettingID) { $uid = if ("$blockUserSettingID" -eq 'auto') { New-Guid-String } else { "$blockUserSettingID" } - X "$indent`t$(Esc-Xml $uid)" + X "$indent`t$(Esc-XmlText $uid)" } if ($null -ne $blockUserSettingPresentation) { Emit-USPresentation -val $blockUserSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" @@ -2221,14 +2227,14 @@ function Emit-GroupItemField { $pae = if ($level.periodAdditionEnd) { "$($level.periodAdditionEnd)" } else { '0001-01-01T00:00:00' } } X "$indent" - X "$indent`t$(Esc-Xml $field)" - X "$indent`t$(Esc-Xml $gt)" - X "$indent`t$(Esc-Xml $pat)" + X "$indent`t$(Esc-XmlText $field)" + X "$indent`t$(Esc-XmlText $gt)" + X "$indent`t$(Esc-XmlText $pat)" # Авто-детект: ISO-дата → xs:dateTime, иначе путь → dcscor:Field. $pabT = if ($pab -match '^\d{4}-\d{2}-\d{2}T') { 'xs:dateTime' } else { 'dcscor:Field' } $paeT = if ($pae -match '^\d{4}-\d{2}-\d{2}T') { 'xs:dateTime' } else { 'dcscor:Field' } - X "$indent`t$(Esc-Xml $pab)" - X "$indent`t$(Esc-Xml $pae)" + X "$indent`t$(Esc-XmlText $pab)" + X "$indent`t$(Esc-XmlText $pae)" X "$indent" } @@ -2297,22 +2303,22 @@ function Emit-CalcFields { } $ci = "$indent`t" X "$indent" - X "$ci$(Esc-Xml $dataPath)" - X "$ci$(Esc-Xml $expression)" + X "$ci$(Esc-XmlText $dataPath)" + X "$ci$(Esc-XmlText $expression)" if ($title) { Emit-MLText -tag 'dcssch:title' -text $title -indent $ci -xsiType 'v8:LocalStringType' } if ($restrict.Count -gt 0) { X "$ci" foreach ($r in @('field','condition','group','order')) { if ($restrict -contains $r) { X "$ci`ttrue" } } X "$ci" } - if ($pres) { X "$ci$(Esc-Xml "$pres")" } + if ($pres) { X "$ci$(Esc-XmlText "$pres")" } if ($orderExpr) { $oeList = if ($orderExpr -is [System.Collections.IList]) { $orderExpr } else { @($orderExpr) } foreach ($oe in $oeList) { if ($oe -is [string]) { $exprV = $oe; $oType = 'Asc'; $auto = 'false' } else { $exprV = "$($oe.expression)"; $oType = if ($oe.orderType) { "$($oe.orderType)" } else { 'Asc' }; $auto = if ($oe.autoOrder) { 'true' } else { 'false' } } X "$ci" - X "$ci`t$(Esc-Xml $exprV)" + X "$ci`t$(Esc-XmlText $exprV)" X "$ci`t$oType" X "$ci`t$auto" X "$ci" @@ -3259,7 +3265,7 @@ function Emit-CommonElementProps { if ($null -ne $el.($p[0])) { X "$indent<$($p[1])>$(if ($el.($p[0])){'true'}else{'false'})" } } # Динамический заголовок колонки-группы из данных (HeaderDataPath) — перед HeaderHorizontalAlign (порядок XSD) - if ($el.headerDataPath) { X "$indent$(Esc-Xml "$($el.headerDataPath)")" } + if ($el.headerDataPath) { X "$indent$(Esc-XmlText "$($el.headerDataPath)")" } if ($el.footerHorizontalAlign) { X "$indent$($el.footerHorizontalAlign)" } if ($el.headerHorizontalAlign) { X "$indent$($el.headerHorizontalAlign)" } # Формат заголовка колонки-группы (ML-текст) — после HeaderHorizontalAlign (порядок XSD) @@ -3279,8 +3285,8 @@ function Emit-PictureRef { if (-not $src) { return } $srcStr = "$src" X "$indent<$picTag>" - if ($srcStr -match '^abs:(.*)$') { X "$indent`t$(Esc-Xml $matches[1])" } - else { X "$indent`t$(Esc-Xml $srcStr)" } + if ($srcStr -match '^abs:(.*)$') { X "$indent`t$(Esc-XmlText $matches[1])" } + else { X "$indent`t$(Esc-XmlText $srcStr)" } X "$indent`t$(if ($lt) { 'true' } else { 'false' })" if ($tpx) { X "$indent`t" } X "$indent" @@ -3309,8 +3315,8 @@ function Emit-CommandPicture { if ($null -eq $lt -and $null -ne $elemLt) { $lt = [bool]$elemLt } $srcStr = "$src" X "$indent" - if ($srcStr -match '^abs:(.*)$') { X "$indent`t$(Esc-Xml $matches[1])" } - else { X "$indent`t$(Esc-Xml $srcStr)" } + if ($srcStr -match '^abs:(.*)$') { X "$indent`t$(Esc-XmlText $matches[1])" } + else { X "$indent`t$(Esc-XmlText $srcStr)" } X "$indent`t$(if ($lt -eq $false) { 'false' } else { 'true' })" if ($tpx) { X "$indent`t" } X "$indent" @@ -3472,7 +3478,7 @@ function Emit-GenericScalars { X "$indent<$($s.Tag)>$(if ($p.Value){'true'}else{'false'})" } else { $v = "$($p.Value)"; if ($v -eq '') { continue } - X "$indent<$($s.Tag)>$(Esc-Xml $v)" + X "$indent<$($s.Tag)>$(Esc-XmlText $v)" } } } @@ -3519,7 +3525,7 @@ function Emit-BorderTag { $width = if ($val.PSObject.Properties['width'] -and $null -ne $val.width) { $val.width } else { 1 } $style = if ($val.PSObject.Properties['style']) { "$($val.style)" } else { $null } X "$indent" - if ($style) { X "$indent`t$(Esc-Xml $style)" } + if ($style) { X "$indent`t$(Esc-XmlText $style)" } X "$indent" } @@ -3546,13 +3552,13 @@ function PL-Bool { } function Emit-PlannerColor { param([string]$tag, $o, [string]$key, [string]$ind) - X "$ind$(Esc-Xml "$(PL-Get $o $key 'auto')")" + X "$ind$(Esc-XmlText "$(PL-Get $o $key 'auto')")" } # /… — пустое → самозакрывающийся тег (как в выгрузке платформы). function Emit-PlannerText { param([string]$tag, $v, [string]$ind) if ([string]::IsNullOrEmpty("$v")) { X "$ind" } - else { X "$ind$(Esc-Xml "$v")" } + else { X "$ind$(Esc-XmlText "$v")" } } # Признак ссылочного значения (объект разреза/элемент-ссылка) → xsi:type="xr:DesignTimeRef"; # иначе xs:string. Покрывает англ. (Enum.X.EnumValue.Y) и рус. (Справочник.X) метатипы. @@ -3567,7 +3573,7 @@ function Emit-PlannerValue { param($v, [string]$ind) if ($null -eq $v -or "$v" -eq '') { X "$ind"; return } $t = if (Test-PlannerRef "$v") { 'xr:DesignTimeRef' } else { 'xs:string' } - X "$ind$(Esc-Xml "$v")" + X "$ind$(Esc-XmlText "$v")" } function Emit-PlannerFont { param($o, [string]$ind) @@ -3581,14 +3587,14 @@ function Emit-PlannerBorder { $bw = if ($b) { PL-Get $b 'width' 1 } else { 1 } $bs = if ($b) { PL-Get $b 'style' 'Single' } else { 'Single' } X "$ind" - X "$ind`t$(Esc-Xml "$bs")" + X "$ind`t$(Esc-XmlText "$bs")" X "$ind" } function Emit-PlannerLevel { param($lv, [string]$cns, [string]$ind) $li = "$ind`t" X "$ind" - X "$li$(Esc-Xml "$(PL-Get $lv 'measure' 'Hour')")" + X "$li$(Esc-XmlText "$(PL-Get $lv 'measure' 'Hour')")" X "$li$(PL-Get $lv 'interval' 1)" X "$li$(PL-Bool (PL-Get $lv 'show' $true))" $line = PL-Get $lv 'line' $null @@ -3596,10 +3602,10 @@ function Emit-PlannerLevel { $lg = if ($line) { PL-Get $line 'gap' $false } else { $false } $lst = if ($line) { PL-Get $line 'style' 'Solid' } else { 'Solid' } X "$li" - X "$li`t$(Esc-Xml "$lst")" + X "$li`t$(Esc-XmlText "$lst")" X "$li" - X "$li$(Esc-Xml "$(PL-Get $lv 'scaleColor' 'auto')")" - X "$li$(Esc-Xml "$(PL-Get $lv 'dayFormatRule' 'MonthDayWeekDay')")" + X "$li$(Esc-XmlText "$(PL-Get $lv 'scaleColor' 'auto')")" + X "$li$(Esc-XmlText "$(PL-Get $lv 'dayFormatRule' 'MonthDayWeekDay')")" $fmt = PL-Get $lv 'format' $null if ($null -eq $fmt) { $fmt = [ordered]@{ '#' = 'DF="HH:mm"'; 'ru' = 'DF="HH:mm"' } } X "$li" @@ -3610,8 +3616,8 @@ function Emit-PlannerLevel { X "$li" X "$li`t$ticks" X "$li" - X "$li$(Esc-Xml "$(PL-Get $lv 'backColor' 'auto')")" - X "$li$(Esc-Xml "$(PL-Get $lv 'textColor' 'auto')")" + X "$li$(Esc-XmlText "$(PL-Get $lv 'backColor' 'auto')")" + X "$li$(Esc-XmlText "$(PL-Get $lv 'textColor' 'auto')")" X "$li$(PL-Bool (PL-Get $lv 'showPereodicalLabels' $true))" X "$ind" } @@ -3620,14 +3626,14 @@ function Emit-PlannerTimeScale { $cns = $script:CHART_NS $ci = "$ind`t" X "$ind" - X "$ci$(Esc-Xml "$(if ($ts) { PL-Get $ts 'placement' 'Left' } else { 'Left' })")" + X "$ci$(Esc-XmlText "$(if ($ts) { PL-Get $ts 'placement' 'Left' } else { 'Left' })")" $levels = if ($ts) { @(PL-Get $ts 'levels' @()) } else { @() } if (@($levels).Count -eq 0) { $levels = @($null) } # один уровень-дефолт foreach ($lv in $levels) { Emit-PlannerLevel $lv $cns $ci } $transp = if ($ts) { PL-Get $ts 'transparent' $false } else { $false } X "$ci$(PL-Bool $transp)" - X "$ci$(Esc-Xml "$(if ($ts) { PL-Get $ts 'backColor' 'auto' } else { 'auto' })")" - X "$ci$(Esc-Xml "$(if ($ts) { PL-Get $ts 'textColor' 'auto' } else { 'auto' })")" + X "$ci$(Esc-XmlText "$(if ($ts) { PL-Get $ts 'backColor' 'auto' } else { 'auto' })")" + X "$ci$(Esc-XmlText "$(if ($ts) { PL-Get $ts 'textColor' 'auto' } else { 'auto' })")" X "$ci$(if ($ts) { PL-Get $ts 'currentLevel' 0 } else { 0 })" X "$ind" } @@ -3652,7 +3658,7 @@ function Emit-PlannerItem { X "$ii$id" X "$ii$(PL-Bool (PL-Get $it 'textFormatted' $false))" Emit-PlannerBorder $it $ii 'border' - X "$ii$(Esc-Xml "$(PL-Get $it 'editMode' 'EnableEdit')")" + X "$ii$(Esc-XmlText "$(PL-Get $it 'editMode' 'EnableEdit')")" X "$ind" } # Элемент измерения ( внутри ) — рекурсивен: может нести вложенные @@ -3707,7 +3713,7 @@ function Emit-PlannerSettings { $wfmt = PL-Get $pl 'timeScaleWrapHeadersFormat' $null if ($null -eq $wfmt) { $wfmt = [ordered]@{ '#' = 'DLF="DD"'; 'ru' = 'DLF="DD"' } } Emit-MLText -tag 'pl:timeScaleWrapHeadersFormat' -text $wfmt -indent $si - X "$si$(Esc-Xml "$(PL-Get $pl 'periodicVariantUnit' 'Day')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'periodicVariantUnit' 'Day')")" X "$si$(PL-Get $pl 'periodicVariantRepetition' 1)" X "$si$(PL-Get $pl 'timeScaleWrapBeginIndent' 0)" X "$si$(PL-Get $pl 'timeScaleWrapEndIndent' 0)" @@ -3720,16 +3726,16 @@ function Emit-PlannerSettings { X "$si" } X "$si$(PL-Bool (PL-Get $pl 'displayCurrentDate' $true))" - X "$si$(Esc-Xml "$(PL-Get $pl 'itemsTimeRepresentation' 'BeginTime')")" - X "$si$(Esc-Xml "$(PL-Get $pl 'itemsBehaviorWhenSpaceInsufficient' 'CollapseItems')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'itemsTimeRepresentation' 'BeginTime')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'itemsBehaviorWhenSpaceInsufficient' 'CollapseItems')")" X "$si$(PL-Bool (PL-Get $pl 'autoMinColumnWidth' $true))" X "$si$(PL-Bool (PL-Get $pl 'autoMinRowHeight' $true))" X "$si$(PL-Get $pl 'minColumnWidth' 0)" X "$si$(PL-Get $pl 'minRowHeight' 0)" - X "$si$(Esc-Xml "$(PL-Get $pl 'fixDimensionsHeader' 'auto')")" - X "$si$(Esc-Xml "$(PL-Get $pl 'fixTimeScaleHeader' 'auto')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'fixDimensionsHeader' 'auto')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'fixTimeScaleHeader' 'auto')")" Emit-PlannerBorder $pl $si 'border' - X "$si$(Esc-Xml "$(PL-Get $pl 'newItemsTextType' 'String')")" + X "$si$(Esc-XmlText "$(PL-Get $pl 'newItemsTextType' 'String')")" X "$ind" } @@ -3762,13 +3768,13 @@ function Emit-ChartNode { if ($keys -contains 'gap') { $w = Get-Prop $val 'width'; $g = Get-Prop $val 'gap'; $st = Get-Prop $val 'style' X "$ind" - X "$ind`t$(Esc-Xml "$st")" + X "$ind`t$(Esc-XmlText "$st")" X "$ind"; return } if (($keys -contains 'style') -and ($keys -contains 'width')) { $w = Get-Prop $val 'width'; $st = Get-Prop $val 'style' X "$ind" - X "$ind`t$(Esc-Xml "$st")" + X "$ind`t$(Esc-XmlText "$st")" X "$ind"; return } $isFont = $false; foreach ($fk in $script:CHART_FONT_KEYS) { if ($keys -contains $fk) { $isFont = $true; break } } @@ -3784,7 +3790,7 @@ function Emit-ChartNode { } if ($null -eq $val -or "$val" -eq '') { X "$ind"; return } if ($val -is [bool]) { X "$ind$(PL-Bool $val)"; return } - X "$ind$(Esc-Xml "$val")" + X "$ind$(Esc-XmlText "$val")" } function Emit-ChartSettings { param($chart, [string]$ind, [string]$ctype = 'd4p1:Chart') @@ -3806,7 +3812,7 @@ function Emit-Appearance { if ($null -eq $val -or ($val -is [string] -and $val -eq '')) { continue } $spec = $script:appearanceSpec[$key] switch ($spec.kind) { - 'color' { X "$indent<$($spec.tag)>$(Esc-Xml "$val")" } + 'color' { X "$indent<$($spec.tag)>$(Esc-XmlText "$val")" } 'font' { Emit-FontTag -tag $spec.tag -val $val -indent $indent } 'border' { Emit-BorderTag -val $val -indent $indent } } @@ -4091,13 +4097,13 @@ function Emit-Input { @('choiceForm','ChoiceForm'), @('choiceHistoryOnInput','ChoiceHistoryOnInput'), @('choiceFoldersAndItems','ChoiceFoldersAndItems'), @('footerDataPath','FooterDataPath') )) { - if ($el.($p[0])) { X "$inner<$($p[1])>$(Esc-Xml "$($el.($p[0]))")" } + if ($el.($p[0])) { X "$inner<$($p[1])>$(Esc-XmlText "$($el.($p[0]))")" } } # MinValue/MaxValue — типизированное. JSON-число → xs:decimal, строка → xs:string (тип сохранён декомпилятором). foreach ($p in @(@('minValue','MinValue'), @('maxValue','MaxValue'))) { if ($null -ne $el.($p[0])) { $mvt = if ($el.($p[0]) -is [string]) { 'xs:string' } else { 'xs:decimal' } - X "$inner<$($p[1]) xsi:type=`"$mvt`">$(Esc-Xml "$($el.($p[0]))")" + X "$inner<$($p[1]) xsi:type=`"$mvt`">$(Esc-XmlText "$($el.($p[0]))")" } } if ($el.choiceButtonRepresentation) { X "$inner$($el.choiceButtonRepresentation)" } @@ -4160,7 +4166,7 @@ function Emit-Check { if ($null -ne $el.warningOnEdit) { Emit-MLText -tag "WarningOnEdit" -text $el.warningOnEdit -indent $inner } # FooterDataPath / FooterText — общие cell-свойства колонки (как у input/labelField) - if ($el.footerDataPath) { X "$inner$(Esc-Xml "$($el.footerDataPath)")" } + if ($el.footerDataPath) { X "$inner$(Esc-XmlText "$($el.footerDataPath)")" } if ($null -ne $el.footerText) { Emit-MLText -tag "FooterText" -text $el.footerText -indent $inner } # Формат / формат редактирования (LocalStringType — строка или {ru,en}) @@ -4317,7 +4323,7 @@ function Emit-ChoicePresentation { foreach ($pair in $pairs) { X "$indent`t" X "$indent`t`t$($pair[0])" - X "$indent`t`t$(Esc-Xml $pair[1])" + X "$indent`t`t$(Esc-XmlText $pair[1])" X "$indent`t" } X "$indent" @@ -4327,7 +4333,7 @@ function Emit-ChoicePresentation { function Get-ChoiceValueTag { param($norm) if ([string]::IsNullOrEmpty($norm.Text)) { return "" } - return "$(Esc-Xml $norm.Text)" + return "$(Esc-XmlText $norm.Text)" } # Emit (список выбора) — у RadioButtonField и InputField. @@ -4540,8 +4546,8 @@ function Emit-ChoiceParameterLinks { } } X "$indent`t" - X "$indent`t`t$(Esc-Xml "$name")" - X "$indent`t`t$(Esc-Xml "$dp")" + X "$indent`t`t$(Esc-XmlText "$name")" + X "$indent`t`t$(Esc-XmlText "$dp")" X "$indent`t`t$vc" X "$indent`t" } @@ -4558,7 +4564,7 @@ function Emit-TypeLink { $li = Get-ElProp $tl @('linkItem','элементСвязи') if ($null -eq $li) { $li = 0 } X "$indent" - X "$indent`t$(Esc-Xml "$dp")" + X "$indent`t$(Esc-XmlText "$dp")" X "$indent`t$li" X "$indent" } @@ -4665,7 +4671,7 @@ function Emit-LabelField { if ($el.titleLocation) { X "$inner$(Map-TitleLoc "$($el.titleLocation)")" } if ($el.editMode) { X "$inner$($el.editMode)" } # FooterDataPath — путь данных подвала колонки (общий cell-prop, как у input); после EditMode - if ($el.footerDataPath) { X "$inner$(Esc-Xml "$($el.footerDataPath)")" } + if ($el.footerDataPath) { X "$inner$(Esc-XmlText "$($el.footerDataPath)")" } # PasswordMode на LabelField — платформа эмитит явный false (редко); факт. значение if ($null -ne $el.passwordMode) { X "$inner$(if ($el.passwordMode){'true'}else{'false'})" } Emit-ColumnPics -el $el -indent $inner @@ -4988,7 +4994,7 @@ function Emit-Button { if (($btnParam -is [System.Management.Automation.PSCustomObject] -or $btnParam -is [hashtable]) -and $btnParam.type) { Emit-Type -typeStr "$($btnParam.type)" -indent $inner -tag "Parameter" -tagAttrs ' xsi:type="v8:TypeDescription"' } else { - X "$inner$(Esc-Xml "$btnParam")" + X "$inner$(Esc-XmlText "$btnParam")" } } # DataPath — привязка команды кнопки к контексту (Объект.Ref, Items.X.CurrentData.Поле) @@ -5043,8 +5049,8 @@ function Emit-PictureDecoration { $srcStr = "$($el.src)" $lt = if ($el.loadTransparent -eq $true) { "true" } else { "false" } X "$inner" - if ($srcStr -match '^abs:(.*)$') { X "$inner`t$(Esc-Xml $matches[1])" } - else { X "$inner`t$(Esc-Xml $srcStr)" } + if ($srcStr -match '^abs:(.*)$') { X "$inner`t$(Esc-XmlText $matches[1])" } + else { X "$inner`t$(Esc-XmlText $srcStr)" } X "$inner`t$lt" if ($el.transparentPixel) { X "$inner`t" } X "$inner" @@ -5088,7 +5094,7 @@ function Emit-PictureField { if ($null -ne $el.enableDrag) { X "$inner$(if ($el.enableDrag){'true'}else{'false'})" } # FooterDataPath / FooterText — общие cell-свойства колонки (как у input/labelField) - if ($el.footerDataPath) { X "$inner$(Esc-Xml "$($el.footerDataPath)")" } + if ($el.footerDataPath) { X "$inner$(Esc-XmlText "$($el.footerDataPath)")" } if ($null -ne $el.footerText) { Emit-MLText -tag "FooterText" -text $el.footerText -indent $inner } # ValuesPicture — picture (collection) used to render the field's value. @@ -5444,18 +5450,18 @@ function Emit-DLValue { return } $valStr = if ($val -is [bool]) { if ($val) { 'true' } else { 'false' } } else { "$val" } - if ($type -match '^(date|dateTime|time)') { X "$indent$(Esc-Xml $valStr)" } - elseif ($type -eq "boolean") { X "$indent$(Esc-Xml $valStr)" } - elseif ($type -eq 'v8:Type') { $nsAttr = Get-ValueTypeNsAttr -valueType 'v8:Type' -value $valStr; X "$indent$(Esc-Xml $valStr)" } - elseif ($type -match '^ent:') { X "$indent$(Esc-Xml $valStr)" } # системное перечисление (ent:X) — value несёт тот же xsi:type - elseif ($type -match '^decimal') { X "$indent$(Esc-Xml $valStr)" } - elseif ($type -match '^string') { X "$indent$(Esc-Xml $valStr)" } - elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent$(Esc-Xml $valStr)" } + if ($type -match '^(date|dateTime|time)') { X "$indent$(Esc-XmlText $valStr)" } + elseif ($type -eq "boolean") { X "$indent$(Esc-XmlText $valStr)" } + elseif ($type -eq 'v8:Type') { $nsAttr = Get-ValueTypeNsAttr -valueType 'v8:Type' -value $valStr; X "$indent$(Esc-XmlText $valStr)" } + elseif ($type -match '^ent:') { X "$indent$(Esc-XmlText $valStr)" } # системное перечисление (ent:X) — value несёт тот же xsi:type + elseif ($type -match '^decimal') { X "$indent$(Esc-XmlText $valStr)" } + elseif ($type -match '^string') { X "$indent$(Esc-XmlText $valStr)" } + elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent$(Esc-XmlText $valStr)" } else { - if ($valStr -match '^\d{4}-\d{2}-\d{2}T') { X "$indent$(Esc-Xml $valStr)" } - elseif ($valStr -eq "true" -or $valStr -eq "false") { X "$indent$(Esc-Xml $valStr)" } - elseif ($valStr -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or $valStr -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { X "$indent$(Esc-Xml $valStr)" } - else { X "$indent$(Esc-Xml $valStr)" } + if ($valStr -match '^\d{4}-\d{2}-\d{2}T') { X "$indent$(Esc-XmlText $valStr)" } + elseif ($valStr -eq "true" -or $valStr -eq "false") { X "$indent$(Esc-XmlText $valStr)" } + elseif ($valStr -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or $valStr -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { X "$indent$(Esc-XmlText $valStr)" } + else { X "$indent$(Esc-XmlText $valStr)" } } } @@ -5489,7 +5495,7 @@ function Emit-DLInputParameters { foreach ($item in $items) { X "$indent`t" if ((Has-DLProp $item 'use') -and $null -ne $item.use -and -not $item.use) { X "$indent`t`tfalse" } - X "$indent`t`t$(Esc-Xml "$($item.parameter)")" + X "$indent`t`t$(Esc-XmlText "$($item.parameter)")" if (Has-DLProp $item 'choiceParameters') { $cpItems = if ($null -ne $item.choiceParameters) { @($item.choiceParameters) } else { @() } if ($cpItems.Count -eq 0) { X "$indent`t`t" } @@ -5497,11 +5503,11 @@ function Emit-DLInputParameters { X "$indent`t`t" foreach ($cpItem in $cpItems) { X "$indent`t`t`t" - X "$indent`t`t`t`t$(Esc-Xml "$($cpItem.name)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cpItem.name)")" foreach ($v in @($cpItem.values)) { if ($v -is [bool]) { X "$indent`t`t`t`t$(if ($v) { 'true' } else { 'false' })" } elseif ($v -is [int] -or $v -is [long] -or $v -is [double] -or $v -is [decimal]) { X "$indent`t`t`t`t$v" } - else { X "$indent`t`t`t`t$(Esc-Xml "$v")" } + else { X "$indent`t`t`t`t$(Esc-XmlText "$v")" } } X "$indent`t`t`t" } @@ -5514,8 +5520,8 @@ function Emit-DLInputParameters { X "$indent`t`t" foreach ($cplItem in $cplItems) { X "$indent`t`t`t" - X "$indent`t`t`t`t$(Esc-Xml "$($cplItem.name)")" - X "$indent`t`t`t`t$(Esc-Xml "$($cplItem.value)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cplItem.name)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cplItem.value)")" $mode = if ($cplItem.mode) { "$($cplItem.mode)" } else { 'Auto' } X "$indent`t`t`t`t$mode" X "$indent`t`t`t" @@ -5526,15 +5532,15 @@ function Emit-DLInputParameters { # Связь по типу (dcscor:TypeLink) — field + linkItem (структурное значение параметра). $tl = $item.typeLink X "$indent`t`t" - $tlf = Get-Prop $tl 'field'; if ($null -ne $tlf) { X "$indent`t`t`t$(Esc-Xml "$tlf")" } - $tli = Get-Prop $tl 'linkItem'; if ($null -ne $tli) { X "$indent`t`t`t$(Esc-Xml "$tli")" } + $tlf = Get-Prop $tl 'field'; if ($null -ne $tlf) { X "$indent`t`t`t$(Esc-XmlText "$tlf")" } + $tli = Get-Prop $tl 'linkItem'; if ($null -ne $tli) { X "$indent`t`t`t$(Esc-XmlText "$tli")" } X "$indent`t`t" } elseif (Has-DLProp $item 'value') { $val = $item.value if ($val -is [bool]) { X "$indent`t`t$(if ($val) { 'true' } else { 'false' })" } elseif ($val -is [int] -or $val -is [long] -or $val -is [double] -or $val -is [decimal]) { X "$indent`t`t$val" } elseif ($val -is [hashtable] -or $val -is [System.Collections.IDictionary] -or $val -is [PSCustomObject]) { Emit-DLMLText -tag "dcscor:value" -text $val -indent "$indent`t`t" } - else { X "$indent`t`t$(Esc-Xml "$val")" } + else { X "$indent`t`t$(Esc-XmlText "$val")" } } X "$indent`t" } @@ -5609,16 +5615,16 @@ function Emit-DataParameters { } X "$indent`t" if ($dp.use -eq $false) { X "$indent`t`tfalse" } - X "$indent`t`t$(Esc-Xml "$($dp.parameter)")" + X "$indent`t`t$(Esc-XmlText "$($dp.parameter)")" $dpValIsArr = ($dp.value -is [array]) -or ($dp.value -is [System.Collections.IList] -and $dp.value -isnot [string]) if ($dpValIsArr) { # Список значений параметра (valueListAllowed) — отдельный на каждое. $avtype = "$($dp.valueType)" foreach ($v in @($dp.value)) { $vStr = if ($v -is [bool]) { "$v".ToLower() } else { "$v" } - if ($avtype -match '^[a-zA-Z]+:') { X "$indent`t`t$(Esc-Xml $vStr)" } - elseif ("$vStr" -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or "$vStr" -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { X "$indent`t`t$(Esc-Xml $vStr)" } - else { X "$indent`t`t$(Esc-Xml $vStr)" } + if ($avtype -match '^[a-zA-Z]+:') { X "$indent`t`t$(Esc-XmlText $vStr)" } + elseif ("$vStr" -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or "$vStr" -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { X "$indent`t`t$(Esc-XmlText $vStr)" } + else { X "$indent`t`t$(Esc-XmlText $vStr)" } } } elseif ($dp.nilValue -eq $true) { X "$indent`t`t" @@ -5641,41 +5647,41 @@ function Emit-DataParameters { if ($dp.value -is [PSCustomObject] -and $dp.value.PSObject.Properties['date']) { $_d = "$($dp.value.date)" } elseif (($dp.value -is [System.Collections.IDictionary]) -and $dp.value.Contains('date')) { $_d = "$($dp.value['date'])" } X "$indent`t`t" - X "$indent`t`t`t$(Esc-Xml $_variantStr)" - if ($_variantStr -eq 'Custom') { if (-not $_d) { $_d = '0001-01-01T00:00:00' }; X "$indent`t`t`t$(Esc-Xml $_d)" } + X "$indent`t`t`t$(Esc-XmlText $_variantStr)" + if ($_variantStr -eq 'Custom') { if (-not $_d) { $_d = '0001-01-01T00:00:00' }; X "$indent`t`t`t$(Esc-XmlText $_d)" } X "$indent`t`t" } else { $_sd = $null; $_ed = $null if ($dp.value -is [PSCustomObject]) { if ($dp.value.PSObject.Properties['startDate']) { $_sd = "$($dp.value.startDate)" }; if ($dp.value.PSObject.Properties['endDate']) { $_ed = "$($dp.value.endDate)" } } else { if ($dp.value.Contains('startDate')) { $_sd = "$($dp.value['startDate'])" }; if ($dp.value.Contains('endDate')) { $_ed = "$($dp.value['endDate'])" } } X "$indent`t`t" - X "$indent`t`t`t$(Esc-Xml $_variantStr)" - if ($_variantStr -eq 'Custom') { if (-not $_sd) { $_sd = '0001-01-01T00:00:00' }; if (-not $_ed) { $_ed = '0001-01-01T00:00:00' }; X "$indent`t`t`t$(Esc-Xml $_sd)"; X "$indent`t`t`t$(Esc-Xml $_ed)" } + X "$indent`t`t`t$(Esc-XmlText $_variantStr)" + if ($_variantStr -eq 'Custom') { if (-not $_sd) { $_sd = '0001-01-01T00:00:00' }; if (-not $_ed) { $_ed = '0001-01-01T00:00:00' }; X "$indent`t`t`t$(Esc-XmlText $_sd)"; X "$indent`t`t`t$(Esc-XmlText $_ed)" } X "$indent`t`t" } } elseif ($vtype -match '^[a-zA-Z]+:') { $vStr = if ($dp.value -is [bool]) { "$($dp.value)".ToLower() } else { "$($dp.value)" } - X "$indent`t`t$(Esc-Xml $vStr)" + X "$indent`t`t$(Esc-XmlText $vStr)" } elseif ($vtype -eq 'boolean' -or $dp.value -is [bool]) { - X "$indent`t`t$(Esc-Xml ("$($dp.value)".ToLower()))" + X "$indent`t`t$(Esc-XmlText ("$($dp.value)".ToLower()))" } elseif ($vtype -match '^date' -or "$($dp.value)" -match '^\d{4}-\d{2}-\d{2}T') { - X "$indent`t`t$(Esc-Xml "$($dp.value)")" + X "$indent`t`t$(Esc-XmlText "$($dp.value)")" } elseif ($vtype -match '^decimal') { - X "$indent`t`t$(Esc-Xml "$($dp.value)")" + X "$indent`t`t$(Esc-XmlText "$($dp.value)")" } elseif ($vtype -match '^string') { - X "$indent`t`t$(Esc-Xml "$($dp.value)")" + X "$indent`t`t$(Esc-XmlText "$($dp.value)")" } elseif ("$($dp.value)" -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or "$($dp.value)" -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { - X "$indent`t`t$(Esc-Xml "$($dp.value)")" + X "$indent`t`t$(Esc-XmlText "$($dp.value)")" } else { - X "$indent`t`t$(Esc-Xml "$($dp.value)")" + X "$indent`t`t$(Esc-XmlText "$($dp.value)")" } } - if ($dp.viewMode) { X "$indent`t`t$(Esc-Xml "$($dp.viewMode)")" } - if ($dp.userSettingID) { $uid = if ("$($dp.userSettingID)" -eq "auto") { New-Guid-String } else { "$($dp.userSettingID)" }; X "$indent`t`t$(Esc-Xml $uid)" } + if ($dp.viewMode) { X "$indent`t`t$(Esc-XmlText "$($dp.viewMode)")" } + if ($dp.userSettingID) { $uid = if ("$($dp.userSettingID)" -eq "auto") { New-Guid-String } else { "$($dp.userSettingID)" }; X "$indent`t`t$(Esc-XmlText $uid)" } if ($dp.userSettingPresentation) { Emit-USPresentation -val $dp.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t`t" } X "$indent`t" } - if ($null -ne $blockViewMode) { X "$indent`t$(Esc-Xml "$blockViewMode")" } + if ($null -ne $blockViewMode) { X "$indent`t$(Esc-XmlText "$blockViewMode")" } X "$indent" } @@ -5683,7 +5689,7 @@ function Emit-DLParameter { param($p, $parsed, [string]$indent) X "$indent" $ci = "$indent`t" - X "$ci$(Esc-Xml $parsed.name)" + X "$ci$(Esc-XmlText $parsed.name)" # Title: явный override (shorthand [..] / объект title/presentation) или авто из имени. $title = $null if ($parsed.title) { $title = $parsed.title } @@ -5717,7 +5723,7 @@ function Emit-DLParameter { # expression $expr = $null if ($p -isnot [string] -and (Has-DLProp $p 'expression') -and $p.expression) { $expr = "$($p.expression)" } - if ($expr) { X "$ci$(Esc-Xml $expr)" } + if ($expr) { X "$ci$(Esc-XmlText $expr)" } # availableValues if ($p -isnot [string] -and (Has-DLProp $p 'availableValues') -and $p.availableValues) { foreach ($av in @($p.availableValues)) { Emit-DLAvailableValue -av $av -type $parsed.type -indent $ci } @@ -5736,7 +5742,7 @@ function Emit-DLParameter { # use $useVal = $null if ($p -isnot [string] -and (Has-DLProp $p 'use') -and $p.use) { $useVal = "$($p.use)" } - if ($useVal) { X "$ci$(Esc-Xml $useVal)" } + if ($useVal) { X "$ci$(Esc-XmlText $useVal)" } X "$indent" } @@ -5856,7 +5862,7 @@ function Emit-Attributes { } if ($saveFields.Count -gt 0) { X "$inner" - foreach ($f in $saveFields) { X "$inner`t$(Esc-Xml $f)" } + foreach ($f in $saveFields) { X "$inner`t$(Esc-XmlText $f)" } X "$inner" } } @@ -5957,7 +5963,7 @@ function Emit-Attributes { X "$si$ddr" if ($hasQuery) { $qtext = Resolve-QueryValue "$($st.query)" $script:queryBaseDir - X "$si$(Esc-Xml $qtext)" + X "$si$(Esc-XmlText $qtext)" } # Явные поля набора (редко): override title/dataPath if ($st.fields) { @@ -5972,8 +5978,8 @@ function Emit-Attributes { if ($null -ne (Get-Prop $fld 'dataPath')) { $dp = "$($fld.dataPath)" } elseif ($isFolder) { $dp = "" } else { $dp = "$($fld.field)" } - if ($dp -eq "") { X "$si`t" } else { X "$si`t$(Esc-Xml "$dp")" } - if (-not $isFolder) { X "$si`t$(Esc-Xml "$($fld.field)")" } + if ($dp -eq "") { X "$si`t" } else { X "$si`t$(Esc-XmlText "$dp")" } + if (-not $isFolder) { X "$si`t$(Esc-XmlText "$($fld.field)")" } if ($fld.title) { X "$si`t" Emit-MLItems -val $fld.title -indent "$si`t`t" @@ -5983,7 +5989,7 @@ function Emit-Attributes { Emit-RestrictBlock 'useRestriction' $fld.useRestriction "$si`t" Emit-RestrictBlock 'attributeUseRestriction' $fld.attributeUseRestriction "$si`t" # presentationExpression поля — перед valueType (порядок исходника) - if ($fld.presentationExpression) { X "$si`t$(Esc-Xml "$($fld.presentationExpression)")" } + if ($fld.presentationExpression) { X "$si`t$(Esc-XmlText "$($fld.presentationExpression)")" } # valueType поля набора (тип значения; вычисляемые/кастомные поля) if ($fld.valueType) { Emit-DLValueType -typeStr "$($fld.valueType)" -indent "$si`t" } # appearance поля (формат/оформление) — после valueType (порядок исходника) @@ -6003,8 +6009,8 @@ function Emit-Attributes { Emit-DLParameters -params $st.parameters -indent $si # Ключ набора (query-based список без MainTable): KeyType (RowNumber/FieldValue/RowKey) # + KeyField* — после Parameter*, до MainTable. Захват/эмит факт. значений. - if ($st.keyType) { X "$si$(Esc-Xml "$($st.keyType)")" } - if ($st.keyFields) { foreach ($kf in @($st.keyFields)) { X "$si$(Esc-Xml "$kf")" } } + if ($st.keyType) { X "$si$(Esc-XmlText "$($st.keyType)")" } + if ($st.keyFields) { foreach ($kf in @($st.keyFields)) { X "$si$(Esc-XmlText "$kf")" } } if ($st.mainTable) { X "$si$(Normalize-MetaTypeRef "$($st.mainTable)")" } # GetInvisibleFieldPresentations — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении false). if ($null -ne $st.getInvisibleFieldPresentations) { X "$si$(if ($st.getInvisibleFieldPresentations){'true'}else{'false'})" } @@ -6141,7 +6147,7 @@ function Emit-Commands { if (-not $cmdTable) { $cmdTable = $cmd.associatedTableElementId } if (-not $cmdTable) { $cmdTable = $cmd.используемаяТаблица } if ($cmdTable) { - X "$inner$(Esc-Xml "$cmdTable")" + X "$inner$(Esc-XmlText "$cmdTable")" } if ($cmd.shortcut) { @@ -6233,10 +6239,10 @@ function Emit-CommandInterface { # group из дерева побеждает (если задан и непустой); явный group элемента — фолбэк if ($treeGroup) { $grp = $treeGroup } X "$inner`t" - X "$inner`t`t$(Esc-Xml "$cmd")" + X "$inner`t`t$(Esc-XmlText "$cmd")" X "$inner`t`t$type" - if ($attr) { X "$inner`t`t$(Esc-Xml "$attr")" } - if ($grp) { X "$inner`t`t$(Esc-Xml "$grp")" } + if ($attr) { X "$inner`t`t$(Esc-XmlText "$attr")" } + if ($grp) { X "$inner`t`t$(Esc-XmlText "$grp")" } if ($null -ne $idx) { X "$inner`t`t$idx" } if ($null -ne $dv) { X "$inner`t`t$(if ($dv){'true'}else{'false'})" } if ($null -ne $vis) { Emit-XrFlag -tag 'Visible' -val $vis -indent "$inner`t`t" } @@ -6573,7 +6579,7 @@ if ($null -ne $def.mobileCommandBarContent -and @($def.mobileCommandBarContent). X "`t`t`t0" # пустое значение → самозакрывающийся тег (зеркало платформы) if ([string]::IsNullOrEmpty("$nm")) { X "`t`t`t" } - else { X "`t`t`t$(Esc-Xml "$nm")" } + else { X "`t`t`t$(Esc-XmlText "$nm")" } X "`t`t" } X "`t" diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index e0918a45..066f1531 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-compile v1.187 — Compile 1C managed form from JSON or object metadata (+resolve_type_str: срезание префикса cfg:/d5p1:) +# form-compile v1.188 — Compile 1C managed form from JSON or object metadata (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -1431,6 +1431,11 @@ def generate_chart_of_accounts_choice_dsl(meta, preset_data): def esc_xml(s): + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') + + +def esc_xml_text(s): # Экранирование ТЕКСТА элемента (, ): только & < > . # Кавычки/апострофы в тексте 1С не экранирует (пишет литерально) — " ломал бы раундтрип. return s.replace('&', '&').replace('<', '<').replace('>', '>') @@ -1469,12 +1474,12 @@ def emit_ml_items(lines, indent, val): for k, v in val.items(): lines.append(f"{indent}") lines.append(f"{indent}\t{k}") - lines.append(f"{indent}\t{esc_xml(str(v))}") + lines.append(f"{indent}\t{esc_xml_text(str(v))}") lines.append(f"{indent}") else: lines.append(f"{indent}") lines.append(f"{indent}\tru") - lines.append(f"{indent}\t{esc_xml(str(val))}") + lines.append(f"{indent}\t{esc_xml_text(str(val))}") lines.append(f"{indent}") @@ -1493,7 +1498,7 @@ def emit_us_presentation(lines, indent, tag, val): if val is None: return if isinstance(val, str): - lines.append(f'{indent}<{tag} xsi:type="xs:string">{esc_xml(val)}') + lines.append(f'{indent}<{tag} xsi:type="xs:string">{esc_xml_text(val)}') else: emit_mltext(lines, indent, tag, val, xsi_type='v8:LocalStringType') @@ -1642,10 +1647,10 @@ def emit_filter_item(lines, item, indent): if item.get('presentation'): emit_us_presentation(lines, f'{indent}\t', 'dcsset:presentation', item['presentation']) if item.get('viewMode'): - lines.append(f'{indent}\t{esc_xml(str(item["viewMode"]))}') + lines.append(f'{indent}\t{esc_xml_text(str(item["viewMode"]))}') if item.get('userSettingID'): guid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID']) - lines.append(f'{indent}\t{esc_xml(guid)}') + lines.append(f'{indent}\t{esc_xml_text(guid)}') if item.get('userSettingPresentation'): emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) lines.append(f'{indent}') @@ -1654,12 +1659,12 @@ def emit_filter_item(lines, item, indent): lines.append(f'{indent}') if item.get('use') is False: lines.append(f'{indent}\tfalse') - lines.append(f'{indent}\t{esc_xml(str(item.get("field", "")))}') + lines.append(f'{indent}\t{esc_xml_text(str(item.get("field", "")))}') # Регистронезависимый лукап (зеркало PS): Like/LIKE/ПОДОБНО → канон; иначе — как есть comp_type = _COMPARISON_TYPES_CI.get(str(item.get('op')).lower()) if not comp_type: comp_type = str(item.get('op')) - lines.append(f'{indent}\t{esc_xml(comp_type)}') + lines.append(f'{indent}\t{esc_xml_text(comp_type)}') val = item.get('value') if isinstance(val, list): if len(val) == 0: @@ -1670,7 +1675,7 @@ def emit_filter_item(lines, item, indent): else: for v in val: vt = _value_type_for(v, item.get('valueType')) - v_str = str(v).lower() if isinstance(v, bool) else esc_xml(str(v)) + v_str = str(v).lower() if isinstance(v, bool) else esc_xml_text(str(v)) ns_attr = _value_type_ns_attr(vt, v) lines.append(f'{indent}\t{v_str}') elif val is not None and ( @@ -1687,9 +1692,9 @@ def emit_filter_item(lines, item, indent): else: variant = str(val); date_v = None lines.append(f'{indent}\t') - lines.append(f'{indent}\t\t{esc_xml(variant)}') + lines.append(f'{indent}\t\t{esc_xml_text(variant)}') if date_v is not None: - lines.append(f'{indent}\t\t{esc_xml(str(date_v))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(date_v))}') lines.append(f'{indent}\t') elif str(val) == '_': # "_" — маркер пустого значения: платформа эмитит пустой self-closing @@ -1698,16 +1703,16 @@ def emit_filter_item(lines, item, indent): lines.append(f'{indent}\t') elif val is not None: vt = _value_type_for(val, item.get('valueType')) - v_str = str(val).lower() if isinstance(val, bool) else esc_xml(str(val)) + v_str = str(val).lower() if isinstance(val, bool) else esc_xml_text(str(val)) ns_attr = _value_type_ns_attr(vt, val) lines.append(f'{indent}\t{v_str}') if item.get('presentation'): emit_us_presentation(lines, f'{indent}\t', 'dcsset:presentation', item['presentation']) if item.get('viewMode'): - lines.append(f'{indent}\t{esc_xml(str(item["viewMode"]))}') + lines.append(f'{indent}\t{esc_xml_text(str(item["viewMode"]))}') if item.get('userSettingID'): uid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID']) - lines.append(f'{indent}\t{esc_xml(uid)}') + lines.append(f'{indent}\t{esc_xml_text(uid)}') if item.get('userSettingPresentation'): emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) lines.append(f'{indent}') @@ -1737,10 +1742,10 @@ def emit_filter(lines, items, indent, block_view_mode=None, block_user_setting_i else: emit_filter_item(lines, item, f'{indent}\t') if block_view_mode is not None: - lines.append(f'{indent}\t{esc_xml(str(block_view_mode))}') + lines.append(f'{indent}\t{esc_xml_text(str(block_view_mode))}') if block_user_setting_id is not None: uid = new_uuid() if str(block_user_setting_id) == 'auto' else str(block_user_setting_id) - lines.append(f'{indent}\t{esc_xml(uid)}') + lines.append(f'{indent}\t{esc_xml_text(uid)}') if block_user_setting_presentation is not None: emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', block_user_setting_presentation) lines.append(f'{indent}') @@ -1766,7 +1771,7 @@ def emit_order(lines, items, indent, skip_auto=False, block_view_mode=None, bloc elif len(parts) > 1 and re.match(r'(?i)^(asc|возр)', parts[1]): direction = 'Asc' lines.append(f'{indent}\t') - lines.append(f'{indent}\t\t{esc_xml(field)}') + lines.append(f'{indent}\t\t{esc_xml_text(field)}') lines.append(f'{indent}\t\t{direction}') lines.append(f'{indent}\t') else: @@ -1782,16 +1787,16 @@ def emit_order(lines, items, indent, skip_auto=False, block_view_mode=None, bloc lines.append(f'{indent}\t') if item.get('use') is False: lines.append(f'{indent}\t\tfalse') - lines.append(f'{indent}\t\t{esc_xml(str(item.get("field", "")))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(item.get("field", "")))}') lines.append(f'{indent}\t\t{direction}') if item.get('viewMode'): - lines.append(f'{indent}\t\t{esc_xml(str(item["viewMode"]))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(item["viewMode"]))}') lines.append(f'{indent}\t') if block_view_mode is not None: - lines.append(f'{indent}\t{esc_xml(str(block_view_mode))}') + lines.append(f'{indent}\t{esc_xml_text(str(block_view_mode))}') if block_user_setting_id is not None: uid = new_uuid() if str(block_user_setting_id) == 'auto' else str(block_user_setting_id) - lines.append(f'{indent}\t{esc_xml(uid)}') + lines.append(f'{indent}\t{esc_xml_text(uid)}') if block_user_setting_presentation is not None: emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', block_user_setting_presentation) lines.append(f'{indent}') @@ -1823,7 +1828,7 @@ def emit_appearance_value(lines, key, val, indent): nested_items = _get(val, 'items') if use_wrapper: lines.append(f'{indent}\tfalse') - lines.append(f'{indent}\t{esc_xml(key)}') + lines.append(f'{indent}\t{esc_xml_text(key)}') is_font_dict = isinstance(inner_val, dict) and inner_val.get('@type') is not None and str(inner_val.get('@type')) == 'Font' is_line_dict = _has_key(inner_val, '@type') and (str(_get(inner_val, '@type')) == 'Line') @@ -1833,7 +1838,7 @@ def emit_appearance_value(lines, key, val, indent): lg = ('true' if _get(inner_val, 'gap') else 'false') if _has_key(inner_val, 'gap') else 'false' ls = str(_get(inner_val, 'style')) if _has_key(inner_val, 'style') else 'None' lines.append(f'{indent}\t') - lines.append(f'{indent}\t\t{esc_xml(ls)}') + lines.append(f'{indent}\t\t{esc_xml_text(ls)}') lines.append(f'{indent}\t') elif is_font_dict: attr_parts = [] @@ -1845,7 +1850,7 @@ def emit_appearance_value(lines, key, val, indent): lines.append(f'{indent}\t') elif is_dict and _has_key(inner_val, 'field'): # Ссылка на поле (dcscor:Field) — значение параметра оформления = поле компоновки - lines.append(f'{indent}\t{esc_xml(str(_get(inner_val, "field")))}') + lines.append(f'{indent}\t{esc_xml_text(str(_get(inner_val, "field")))}') elif is_dict: # Локализуемый текст параметра оформления: платформа объявляет xsi:type на dcscor:value emit_mltext(lines, f'{indent}\t', 'dcscor:value', inner_val, xsi_type='v8:LocalStringType') @@ -1861,9 +1866,9 @@ def emit_appearance_value(lines, key, val, indent): } key_type = key_type_map.get(key) if key_type: - lines.append(f'{indent}\t{esc_xml(actual_val)}') + lines.append(f'{indent}\t{esc_xml_text(actual_val)}') elif re.match(r'^(style|web|win):', actual_val): - lines.append(f'{indent}\t{esc_xml(actual_val)}') + lines.append(f'{indent}\t{esc_xml_text(actual_val)}') elif actual_val == 'true' or actual_val == 'false': lines.append(f'{indent}\t{actual_val}') elif key == 'Текст' or key == 'Заголовок' or key == 'Формат': @@ -1872,13 +1877,13 @@ def emit_appearance_value(lines, key, val, indent): if actual_val == '': lines.append(f'{indent}\t') else: - lines.append(f'{indent}\t{esc_xml(actual_val)}') + lines.append(f'{indent}\t{esc_xml_text(actual_val)}') elif re.match(r'^-?\d+(\.\d+)?$', actual_val): lines.append(f'{indent}\t{actual_val}') elif key == 'ЦветТекста' or key == 'ЦветФона' or key == 'ЦветГраницы': - lines.append(f'{indent}\t{esc_xml(actual_val)}') + lines.append(f'{indent}\t{esc_xml_text(actual_val)}') else: - lines.append(f'{indent}\t{esc_xml(actual_val)}') + lines.append(f'{indent}\t{esc_xml_text(actual_val)}') if nested_items: if isinstance(nested_items, dict): for nk, nv in nested_items.items(): @@ -1916,14 +1921,14 @@ def emit_group_item_field(lines, level, indent): pab = str(level.get('periodAdditionBegin') or '0001-01-01T00:00:00') pae = str(level.get('periodAdditionEnd') or '0001-01-01T00:00:00') lines.append(f'{indent}') - lines.append(f'{indent}\t{esc_xml(field)}') - lines.append(f'{indent}\t{esc_xml(gt)}') - lines.append(f'{indent}\t{esc_xml(pat)}') + lines.append(f'{indent}\t{esc_xml_text(field)}') + lines.append(f'{indent}\t{esc_xml_text(gt)}') + lines.append(f'{indent}\t{esc_xml_text(pat)}') # Авто-детект: ISO-дата → xs:dateTime, иначе путь → dcscor:Field. pab_t = 'xs:dateTime' if re.match(r'^\d{4}-\d{2}-\d{2}T', pab) else 'dcscor:Field' pae_t = 'xs:dateTime' if re.match(r'^\d{4}-\d{2}-\d{2}T', pae) else 'dcscor:Field' - lines.append(f'{indent}\t{esc_xml(pab)}') - lines.append(f'{indent}\t{esc_xml(pae)}') + lines.append(f'{indent}\t{esc_xml_text(pab)}') + lines.append(f'{indent}\t{esc_xml_text(pae)}') lines.append(f'{indent}') @@ -1999,8 +2004,8 @@ def emit_calc_fields(lines, calc_fields, indent): order_expr = cf.get('orderExpression') ci = f'{indent}\t' lines.append(f'{indent}') - lines.append(f'{ci}{esc_xml(data_path)}') - lines.append(f'{ci}{esc_xml(expression)}') + lines.append(f'{ci}{esc_xml_text(data_path)}') + lines.append(f'{ci}{esc_xml_text(expression)}') if title: emit_mltext(lines, ci, 'dcssch:title', title, xsi_type='v8:LocalStringType') if restrict: @@ -2010,7 +2015,7 @@ def emit_calc_fields(lines, calc_fields, indent): lines.append(f'{ci}\ttrue') lines.append(f'{ci}') if pres_expr: - lines.append(f'{ci}{esc_xml(str(pres_expr))}') + lines.append(f'{ci}{esc_xml_text(str(pres_expr))}') if order_expr: for oe in (order_expr if isinstance(order_expr, list) else [order_expr]): if isinstance(oe, str): @@ -2020,7 +2025,7 @@ def emit_calc_fields(lines, calc_fields, indent): otype = str(oe.get('orderType', 'Asc')) auto = 'true' if oe.get('autoOrder') else 'false' lines.append(f'{ci}') - lines.append(f'{ci}\t{esc_xml(expr_v)}') + lines.append(f'{ci}\t{esc_xml_text(expr_v)}') lines.append(f'{ci}\t{otype}') lines.append(f'{ci}\t{auto}') lines.append(f'{ci}') @@ -2068,7 +2073,7 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc lines.append(f'{indent}\t\t') for sel in ca['selection']: lines.append(f'{indent}\t\t\t') - lines.append(f'{indent}\t\t\t\t{esc_xml(str(sel))}') + lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(sel))}') lines.append(f'{indent}\t\t\t') lines.append(f'{indent}\t\t') else: @@ -2089,12 +2094,12 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc emit_ml_items(lines, f'{indent}\t\t\t', ca['presentation']) lines.append(f'{indent}\t\t') else: - lines.append(f'{indent}\t\t{esc_xml(str(ca["presentation"]))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(ca["presentation"]))}') if ca.get('viewMode'): - lines.append(f'{indent}\t\t{esc_xml(str(ca["viewMode"]))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(ca["viewMode"]))}') if ca.get('userSettingID'): uid = new_uuid() if str(ca['userSettingID']) == 'auto' else str(ca['userSettingID']) - lines.append(f'{indent}\t\t{esc_xml(uid)}') + lines.append(f'{indent}\t\t{esc_xml_text(uid)}') if ca.get('userSettingPresentation'): emit_us_presentation(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', ca['userSettingPresentation']) if ca.get('useInDontUse') and len(ca['useInDontUse']) > 0: @@ -2108,10 +2113,10 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc lines.append(f'{indent}\t\tDontUse') lines.append(f'{indent}\t') if block_view_mode is not None: - lines.append(f'{indent}\t{esc_xml(str(block_view_mode))}') + lines.append(f'{indent}\t{esc_xml_text(str(block_view_mode))}') if block_user_setting_id is not None: uid = new_uuid() if str(block_user_setting_id) == 'auto' else str(block_user_setting_id) - lines.append(f'{indent}\t{esc_xml(uid)}') + lines.append(f'{indent}\t{esc_xml_text(uid)}') if block_user_setting_presentation is not None: emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', block_user_setting_presentation) lines.append(f'{indent}') @@ -2482,7 +2487,7 @@ def emit_choice_presentation(lines, pres, indent): for lang, content in pairs: lines.append(f"{indent}\t") lines.append(f"{indent}\t\t{lang}") - lines.append(f"{indent}\t\t{esc_xml(content)}") + lines.append(f"{indent}\t\t{esc_xml_text(content)}") lines.append(f"{indent}\t") lines.append(f"{indent}") @@ -2491,7 +2496,7 @@ def choice_value_tag(norm): # для choiceList/choiceParameters: пустой текст → самозакрывающийся тег (зеркало платформы). if not norm["text"]: return f'' - return f'{esc_xml(norm["text"])}' + return f'{esc_xml_text(norm["text"])}' def emit_choice_list(lines, el, indent): @@ -2668,8 +2673,8 @@ def emit_choice_parameter_links(lines, el, indent): name_s = '' if name is None else str(name) dp_s = '' if dp is None else str(dp) lines.append(f'{indent}\t') - lines.append(f'{indent}\t\t{esc_xml(name_s)}') - lines.append(f'{indent}\t\t{esc_xml(dp_s)}') + lines.append(f'{indent}\t\t{esc_xml_text(name_s)}') + lines.append(f'{indent}\t\t{esc_xml_text(dp_s)}') lines.append(f'{indent}\t\t{vc}') lines.append(f'{indent}\t') lines.append(f'{indent}') @@ -2688,7 +2693,7 @@ def emit_type_link(lines, el, indent): li = 0 dp_s = '' if dp is None else str(dp) lines.append(f'{indent}') - lines.append(f'{indent}\t{esc_xml(dp_s)}') + lines.append(f'{indent}\t{esc_xml_text(dp_s)}') lines.append(f'{indent}\t{li}') lines.append(f'{indent}') @@ -3011,7 +3016,7 @@ def emit_common_element_props(lines, el, indent): lines.append(f'{indent}<{tag}>{"true" if el[key] else "false"}') # Динамический заголовок колонки-группы из данных (HeaderDataPath) — перед HeaderHorizontalAlign (порядок XSD) if el.get('headerDataPath'): - lines.append(f"{indent}{esc_xml(str(el['headerDataPath']))}") + lines.append(f"{indent}{esc_xml_text(str(el['headerDataPath']))}") if el.get('footerHorizontalAlign'): lines.append(f"{indent}{el['footerHorizontalAlign']}") if el.get('headerHorizontalAlign'): @@ -3040,9 +3045,9 @@ def emit_picture_ref(lines, val, pic_tag, indent): src_str = str(src) lines.append(f"{indent}<{pic_tag}>") if src_str.startswith('abs:'): - lines.append(f"{indent}\t{esc_xml(src_str[4:])}") + lines.append(f"{indent}\t{esc_xml_text(src_str[4:])}") else: - lines.append(f"{indent}\t{esc_xml(src_str)}") + lines.append(f"{indent}\t{esc_xml_text(src_str)}") lines.append(f'{indent}\t{"true" if lt else "false"}') if tpx: lines.append(f'{indent}\t') @@ -3080,9 +3085,9 @@ def emit_command_picture(lines, pic, elem_lt, indent): src_str = str(src) lines.append(f'{indent}') if src_str.startswith('abs:'): - lines.append(f'{indent}\t{esc_xml(src_str[4:])}') + lines.append(f'{indent}\t{esc_xml_text(src_str[4:])}') else: - lines.append(f'{indent}\t{esc_xml(src_str)}') + lines.append(f'{indent}\t{esc_xml_text(src_str)}') lines.append(f'{indent}\t{"false" if lt is False else "true"}') if tpx: lines.append(f'{indent}\t') @@ -3172,7 +3177,7 @@ def emit_border_tag(lines, val, indent): style = str(val['style']) if 'style' in val else None lines.append(f'{indent}') if style: - lines.append(f'{indent}\t{esc_xml(style)}') + lines.append(f'{indent}\t{esc_xml_text(style)}') lines.append(f'{indent}') @@ -3199,14 +3204,14 @@ def _pl_bool(v): def emit_planner_color(lines, tag, o, key, ind): - lines.append(f'{ind}{esc_xml(str(_pl_get(o, key, "auto")))}') + lines.append(f'{ind}{esc_xml_text(str(_pl_get(o, key, "auto")))}') def emit_planner_text(lines, tag, v, ind): if v is None or str(v) == '': lines.append(f'{ind}') else: - lines.append(f'{ind}{esc_xml(str(v))}') + lines.append(f'{ind}{esc_xml_text(str(v))}') _PLANNER_REF_RE = re.compile( @@ -3224,7 +3229,7 @@ def emit_planner_value(lines, v, ind): lines.append(f'{ind}') return t = 'xr:DesignTimeRef' if test_planner_ref(v) else 'xs:string' - lines.append(f'{ind}{esc_xml(str(v))}') + lines.append(f'{ind}{esc_xml_text(str(v))}') def emit_planner_font(lines, o, ind): @@ -3240,14 +3245,14 @@ def emit_planner_border(lines, o, ind, key='border'): bw = _pl_get(b, 'width', 1) if b else 1 bs = _pl_get(b, 'style', 'Single') if b else 'Single' lines.append(f'{ind}') - lines.append(f'{ind}\t{esc_xml(str(bs))}') + lines.append(f'{ind}\t{esc_xml_text(str(bs))}') lines.append(f'{ind}') def emit_planner_level(lines, lv, cns, ind): li = f'{ind}\t' lines.append(f'{ind}') - lines.append(f'{li}{esc_xml(str(_pl_get(lv, "measure", "Hour")))}') + lines.append(f'{li}{esc_xml_text(str(_pl_get(lv, "measure", "Hour")))}') lines.append(f'{li}{_pl_get(lv, "interval", 1)}') lines.append(f'{li}{_pl_bool(_pl_get(lv, "show", True))}') line = _pl_get(lv, 'line') @@ -3255,10 +3260,10 @@ def emit_planner_level(lines, lv, cns, ind): lg = _pl_get(line, 'gap', False) if line else False lst = _pl_get(line, 'style', 'Solid') if line else 'Solid' lines.append(f'{li}') - lines.append(f'{li}\t{esc_xml(str(lst))}') + lines.append(f'{li}\t{esc_xml_text(str(lst))}') lines.append(f'{li}') - lines.append(f'{li}{esc_xml(str(_pl_get(lv, "scaleColor", "auto")))}') - lines.append(f'{li}{esc_xml(str(_pl_get(lv, "dayFormatRule", "MonthDayWeekDay")))}') + lines.append(f'{li}{esc_xml_text(str(_pl_get(lv, "scaleColor", "auto")))}') + lines.append(f'{li}{esc_xml_text(str(_pl_get(lv, "dayFormatRule", "MonthDayWeekDay")))}') fmt = _pl_get(lv, 'format') if fmt is None: fmt = {'#': 'DF="HH:mm"', 'ru': 'DF="HH:mm"'} @@ -3270,8 +3275,8 @@ def emit_planner_level(lines, lv, cns, ind): lines.append(f'{li}') lines.append(f'{li}\t{ticks}') lines.append(f'{li}') - lines.append(f'{li}{esc_xml(str(_pl_get(lv, "backColor", "auto")))}') - lines.append(f'{li}{esc_xml(str(_pl_get(lv, "textColor", "auto")))}') + lines.append(f'{li}{esc_xml_text(str(_pl_get(lv, "backColor", "auto")))}') + lines.append(f'{li}{esc_xml_text(str(_pl_get(lv, "textColor", "auto")))}') lines.append(f'{li}{_pl_bool(_pl_get(lv, "showPereodicalLabels", True))}') lines.append(f'{ind}') @@ -3281,7 +3286,7 @@ def emit_planner_timescale(lines, ts, ind): ci = f'{ind}\t' lines.append(f'{ind}') placement = _pl_get(ts, 'placement', 'Left') if ts else 'Left' - lines.append(f'{ci}{esc_xml(str(placement))}') + lines.append(f'{ci}{esc_xml_text(str(placement))}') levels = _pl_get(ts, 'levels', []) if ts else [] if not levels: levels = [None] @@ -3292,8 +3297,8 @@ def emit_planner_timescale(lines, ts, ind): tbc = _pl_get(ts, 'backColor', 'auto') if ts else 'auto' ttc = _pl_get(ts, 'textColor', 'auto') if ts else 'auto' tcl = _pl_get(ts, 'currentLevel', 0) if ts else 0 - lines.append(f'{ci}{esc_xml(str(tbc))}') - lines.append(f'{ci}{esc_xml(str(ttc))}') + lines.append(f'{ci}{esc_xml_text(str(tbc))}') + lines.append(f'{ci}{esc_xml_text(str(ttc))}') lines.append(f'{ci}{tcl}') lines.append(f'{ind}') @@ -3320,7 +3325,7 @@ def emit_planner_item(lines, it, ind): lines.append(f'{ii}{iid}') lines.append(f'{ii}{_pl_bool(_pl_get(it, "textFormatted", False))}') emit_planner_border(lines, it, ii, 'border') - lines.append(f'{ii}{esc_xml(str(_pl_get(it, "editMode", "EnableEdit")))}') + lines.append(f'{ii}{esc_xml_text(str(_pl_get(it, "editMode", "EnableEdit")))}') lines.append(f'{ind}') @@ -3376,7 +3381,7 @@ def emit_planner_settings(lines, pl, ind): if wfmt is None: wfmt = {'#': 'DLF="DD"', 'ru': 'DLF="DD"'} emit_mltext(lines, si, 'pl:timeScaleWrapHeadersFormat', wfmt) - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "periodicVariantUnit", "Day")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "periodicVariantUnit", "Day")))}') lines.append(f'{si}{_pl_get(pl, "periodicVariantRepetition", 1)}') lines.append(f'{si}{_pl_get(pl, "timeScaleWrapBeginIndent", 0)}') lines.append(f'{si}{_pl_get(pl, "timeScaleWrapEndIndent", 0)}') @@ -3388,16 +3393,16 @@ def emit_planner_settings(lines, pl, ind): lines.append(f'{si}\t{_pl_get(period, "end", "0001-01-01T00:00:00")}') lines.append(f'{si}') lines.append(f'{si}{_pl_bool(_pl_get(pl, "displayCurrentDate", True))}') - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "itemsTimeRepresentation", "BeginTime")))}') - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "itemsBehaviorWhenSpaceInsufficient", "CollapseItems")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "itemsTimeRepresentation", "BeginTime")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "itemsBehaviorWhenSpaceInsufficient", "CollapseItems")))}') lines.append(f'{si}{_pl_bool(_pl_get(pl, "autoMinColumnWidth", True))}') lines.append(f'{si}{_pl_bool(_pl_get(pl, "autoMinRowHeight", True))}') lines.append(f'{si}{_pl_get(pl, "minColumnWidth", 0)}') lines.append(f'{si}{_pl_get(pl, "minRowHeight", 0)}') - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "fixDimensionsHeader", "auto")))}') - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "fixTimeScaleHeader", "auto")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "fixDimensionsHeader", "auto")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "fixTimeScaleHeader", "auto")))}') emit_planner_border(lines, pl, si, 'border') - lines.append(f'{si}{esc_xml(str(_pl_get(pl, "newItemsTextType", "String")))}') + lines.append(f'{si}{esc_xml_text(str(_pl_get(pl, "newItemsTextType", "String")))}') lines.append(f'{ind}') @@ -3430,12 +3435,12 @@ def emit_chart_node(lines, name, val, ind): return if 'gap' in val: lines.append(f'{ind}') - lines.append(f'{ind}\t{esc_xml(str(val.get("style")))}') + lines.append(f'{ind}\t{esc_xml_text(str(val.get("style")))}') lines.append(f'{ind}') return if 'style' in val and 'width' in val: lines.append(f'{ind}') - lines.append(f'{ind}\t{esc_xml(str(val.get("style")))}') + lines.append(f'{ind}\t{esc_xml_text(str(val.get("style")))}') lines.append(f'{ind}') return if any(fk in val for fk in CHART_FONT_KEYS): @@ -3456,7 +3461,7 @@ def emit_chart_node(lines, name, val, ind): if isinstance(val, bool): lines.append(f'{ind}{_pl_bool(val)}') return - lines.append(f'{ind}{esc_xml(str(val))}') + lines.append(f'{ind}{esc_xml_text(str(val))}') def emit_chart_settings(lines, chart, ind, ctype='d4p1:Chart'): @@ -3476,7 +3481,7 @@ def emit_appearance(lines, el, indent, profile='field'): continue tag, kind = APPEARANCE_SPEC[key] if kind == 'color': - lines.append(f'{indent}<{tag}>{esc_xml(str(val))}') + lines.append(f'{indent}<{tag}>{esc_xml_text(str(val))}') elif kind == 'font': emit_font_tag(lines, tag, val, indent) else: @@ -3588,7 +3593,7 @@ def emit_generic_scalars(lines, el, indent): v = str(el[key]) if v == '': continue - lines.append(f'{indent}<{tag}>{esc_xml(v)}') + lines.append(f'{indent}<{tag}>{esc_xml_text(v)}') def emit_layout(lines, el, indent, skip_height=False, multi_line_default=False): @@ -4214,12 +4219,12 @@ def emit_input(lines, el, name, eid, indent): for key, tag in (('choiceForm', 'ChoiceForm'), ('choiceHistoryOnInput', 'ChoiceHistoryOnInput'), ('choiceFoldersAndItems', 'ChoiceFoldersAndItems'), ('footerDataPath', 'FooterDataPath')): if el.get(key): - lines.append(f'{inner}<{tag}>{esc_xml(str(el[key]))}') + lines.append(f'{inner}<{tag}>{esc_xml_text(str(el[key]))}') # MinValue/MaxValue — типизированное. JSON-число → xs:decimal, строка → xs:string (тип сохранён декомпилятором). for key, tag in (('minValue', 'MinValue'), ('maxValue', 'MaxValue')): if el.get(key) is not None: mvt = 'xs:string' if isinstance(el[key], str) else 'xs:decimal' - lines.append(f'{inner}<{tag} xsi:type="{mvt}">{esc_xml(str(el[key]))}') + lines.append(f'{inner}<{tag} xsi:type="{mvt}">{esc_xml_text(str(el[key]))}') if el.get('choiceButtonRepresentation'): lines.append(f'{inner}{el["choiceButtonRepresentation"]}') emit_picture_ref(lines, el.get('choiceButtonPicture'), 'ChoiceButtonPicture', inner) @@ -4286,7 +4291,7 @@ def emit_check(lines, el, name, eid, indent): emit_mltext(lines, inner, 'WarningOnEdit', el['warningOnEdit']) # FooterDataPath / FooterText — общие cell-свойства колонки (как у input/labelField) if el.get('footerDataPath'): - lines.append(f'{inner}{esc_xml(str(el["footerDataPath"]))}') + lines.append(f'{inner}{esc_xml_text(str(el["footerDataPath"]))}') if el.get('footerText') is not None: emit_mltext(lines, inner, 'FooterText', el['footerText']) @@ -4403,7 +4408,7 @@ def emit_label_field(lines, el, name, eid, indent): lines.append(f'{inner}{el["editMode"]}') # FooterDataPath — путь данных подвала колонки (общий cell-prop, как у input); после EditMode if el.get('footerDataPath'): - lines.append(f'{inner}{esc_xml(str(el["footerDataPath"]))}') + lines.append(f'{inner}{esc_xml_text(str(el["footerDataPath"]))}') # PasswordMode на LabelField — платформа эмитит явный false (редко); факт. значение if el.get('passwordMode') is not None: lines.append(f'{inner}{"true" if el["passwordMode"] else "false"}') @@ -4734,7 +4739,7 @@ def emit_button(lines, el, name, eid, indent, in_cmd_bar=False): if isinstance(btn_param, dict) and btn_param.get('type'): emit_type(lines, str(btn_param['type']), inner, tag='Parameter', tag_attrs=' xsi:type="v8:TypeDescription"') else: - lines.append(f'{inner}{esc_xml(str(btn_param))}') + lines.append(f'{inner}{esc_xml_text(str(btn_param))}') # DataPath — привязка команды кнопки к контексту (Объект.Ref, Items.X.CurrentData.Поле) if el.get('path'): lines.append(f'{inner}{el["path"]}') @@ -4787,9 +4792,9 @@ def emit_picture_decoration(lines, el, name, eid, indent): lt = 'true' if el.get('loadTransparent') is True else 'false' lines.append(f'{inner}') if src_str.startswith('abs:'): - lines.append(f'{inner}\t{esc_xml(src_str[4:])}') + lines.append(f'{inner}\t{esc_xml_text(src_str[4:])}') else: - lines.append(f'{inner}\t{esc_xml(src_str)}') + lines.append(f'{inner}\t{esc_xml_text(src_str)}') lines.append(f'{inner}\t{lt}') tpx = el.get('transparentPixel') if tpx: @@ -4840,7 +4845,7 @@ def emit_picture_field(lines, el, name, eid, indent): # FooterDataPath / FooterText — общие cell-свойства колонки (как у input/labelField) if el.get('footerDataPath'): - lines.append(f'{inner}{esc_xml(str(el["footerDataPath"]))}') + lines.append(f'{inner}{esc_xml_text(str(el["footerDataPath"]))}') if el.get('footerText') is not None: emit_mltext(lines, inner, 'FooterText', el['footerText']) @@ -5198,30 +5203,30 @@ def emit_dl_value(lines, type_str, val, indent, value_list_allowed=False): val_str = str(val) t = type_str or '' if re.match(r'^(date|dateTime|time)', t): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif t == 'boolean': - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif t == 'v8:Type': ns_attr = _value_type_ns_attr('v8:Type', val_str) - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif re.match(r'^ent:', t): # системное перечисление (ent:X) — value несёт тот же xsi:type - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif re.match(r'^decimal', t): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif re.match(r'^string', t): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.', t): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') else: if re.match(r'^\d{4}-\d{2}-\d{2}T', val_str): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif val_str in ('true', 'false'): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') elif re.match(r'^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.', val_str) or re.match(r'^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.', val_str): - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') else: - lines.append(f'{indent}{esc_xml(val_str)}') + lines.append(f'{indent}{esc_xml_text(val_str)}') def emit_dl_value_type(lines, type_str, indent): @@ -5254,7 +5259,7 @@ def emit_dl_input_parameters(lines, ip, indent): lines.append(f'{indent}\t') if 'use' in item and item.get('use') is not None and not item.get('use'): lines.append(f'{indent}\t\tfalse') - lines.append(f'{indent}\t\t{esc_xml(str(item.get("parameter", "")))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(item.get("parameter", "")))}') if 'choiceParameters' in item: cp_items = item.get('choiceParameters') or [] if len(cp_items) == 0: @@ -5263,14 +5268,14 @@ def emit_dl_input_parameters(lines, ip, indent): lines.append(f'{indent}\t\t') for cp in cp_items: lines.append(f'{indent}\t\t\t') - lines.append(f'{indent}\t\t\t\t{esc_xml(str(cp.get("name", "")))}') + lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(cp.get("name", "")))}') for v in (cp.get('values') or []): if isinstance(v, bool): lines.append(f'{indent}\t\t\t\t{"true" if v else "false"}') elif isinstance(v, (int, float)): lines.append(f'{indent}\t\t\t\t{v}') else: - lines.append(f'{indent}\t\t\t\t{esc_xml(str(v))}') + lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(v))}') lines.append(f'{indent}\t\t\t') lines.append(f'{indent}\t\t') elif 'choiceParameterLinks' in item: @@ -5281,8 +5286,8 @@ def emit_dl_input_parameters(lines, ip, indent): lines.append(f'{indent}\t\t') for cpl in cpl_items: lines.append(f'{indent}\t\t\t') - lines.append(f'{indent}\t\t\t\t{esc_xml(str(cpl.get("name", "")))}') - lines.append(f'{indent}\t\t\t\t{esc_xml(str(cpl.get("value", "")))}') + lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(cpl.get("name", "")))}') + lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(cpl.get("value", "")))}') mode = str(cpl.get('mode') or 'Auto') lines.append(f'{indent}\t\t\t\t{mode}') lines.append(f'{indent}\t\t\t') @@ -5292,9 +5297,9 @@ def emit_dl_input_parameters(lines, ip, indent): tl = item.get('typeLink') or {} lines.append(f'{indent}\t\t') if tl.get('field') is not None: - lines.append(f'{indent}\t\t\t{esc_xml(str(tl.get("field")))}') + lines.append(f'{indent}\t\t\t{esc_xml_text(str(tl.get("field")))}') if tl.get('linkItem') is not None: - lines.append(f'{indent}\t\t\t{esc_xml(str(tl.get("linkItem")))}') + lines.append(f'{indent}\t\t\t{esc_xml_text(str(tl.get("linkItem")))}') lines.append(f'{indent}\t\t') elif 'value' in item: val = item.get('value') @@ -5305,7 +5310,7 @@ def emit_dl_input_parameters(lines, ip, indent): elif isinstance(val, dict): emit_dl_mltext(lines, f'{indent}\t\t', 'dcscor:value', val) else: - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') lines.append(f'{indent}\t') lines.append(f'{indent}') @@ -5394,7 +5399,7 @@ def emit_data_parameters(lines, items, indent, block_view_mode=None): lines.append(f'{indent}\t') if dp.get('use') is False: lines.append(f'{indent}\t\tfalse') - lines.append(f'{indent}\t\t{esc_xml(str(dp.get("parameter", "")))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(dp.get("parameter", "")))}') vtype = str(dp.get('valueType') or '') val = dp.get('value') if isinstance(val, list): @@ -5403,11 +5408,11 @@ def emit_data_parameters(lines, items, indent, block_view_mode=None): for v in val: v_str = ('true' if v else 'false') if isinstance(v, bool) else str(v) if re.match(r'^[a-zA-Z]+:', avtype): - lines.append(f'{indent}\t\t{esc_xml(v_str)}') + lines.append(f'{indent}\t\t{esc_xml_text(v_str)}') elif re.match(r'^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.', v_str) or re.match(r'^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.', v_str): - lines.append(f'{indent}\t\t{esc_xml(v_str)}') + lines.append(f'{indent}\t\t{esc_xml_text(v_str)}') else: - lines.append(f'{indent}\t\t{esc_xml(v_str)}') + lines.append(f'{indent}\t\t{esc_xml_text(v_str)}') elif dp.get('nilValue') is True: lines.append(f'{indent}\t\t') elif _test_empty_value(val) and vtype: @@ -5422,45 +5427,45 @@ def emit_data_parameters(lines, items, indent, block_view_mode=None): is_sbd = has_date or (not has_sd and variant.startswith('BeginningOf')) if is_sbd: lines.append(f'{indent}\t\t') - lines.append(f'{indent}\t\t\t{esc_xml(variant)}') + lines.append(f'{indent}\t\t\t{esc_xml_text(variant)}') if variant == 'Custom': d = str(val.get('date') or '0001-01-01T00:00:00') - lines.append(f'{indent}\t\t\t{esc_xml(d)}') + lines.append(f'{indent}\t\t\t{esc_xml_text(d)}') lines.append(f'{indent}\t\t') else: lines.append(f'{indent}\t\t') - lines.append(f'{indent}\t\t\t{esc_xml(variant)}') + lines.append(f'{indent}\t\t\t{esc_xml_text(variant)}') if variant == 'Custom': sd = str(val.get('startDate') or '0001-01-01T00:00:00') ed = str(val.get('endDate') or '0001-01-01T00:00:00') - lines.append(f'{indent}\t\t\t{esc_xml(sd)}') - lines.append(f'{indent}\t\t\t{esc_xml(ed)}') + lines.append(f'{indent}\t\t\t{esc_xml_text(sd)}') + lines.append(f'{indent}\t\t\t{esc_xml_text(ed)}') lines.append(f'{indent}\t\t') elif re.match(r'^[a-zA-Z]+:', vtype): v_str = str(val).lower() if isinstance(val, bool) else str(val) - lines.append(f'{indent}\t\t{esc_xml(v_str)}') + lines.append(f'{indent}\t\t{esc_xml_text(v_str)}') elif vtype == 'boolean' or isinstance(val, bool): - lines.append(f'{indent}\t\t{esc_xml(str(val).lower())}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val).lower())}') elif re.match(r'^date', vtype) or re.match(r'^\d{4}-\d{2}-\d{2}T', str(val)): - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') elif re.match(r'^decimal', vtype): - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') elif re.match(r'^string', vtype): - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') elif re.match(r'^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.', str(val)) or re.match(r'^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.', str(val)): - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') else: - lines.append(f'{indent}\t\t{esc_xml(str(val))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(val))}') if dp.get('viewMode'): - lines.append(f'{indent}\t\t{esc_xml(str(dp["viewMode"]))}') + lines.append(f'{indent}\t\t{esc_xml_text(str(dp["viewMode"]))}') if dp.get('userSettingID'): uid = new_uuid() if str(dp['userSettingID']) == 'auto' else str(dp['userSettingID']) - lines.append(f'{indent}\t\t{esc_xml(uid)}') + lines.append(f'{indent}\t\t{esc_xml_text(uid)}') if dp.get('userSettingPresentation'): emit_us_presentation(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', dp['userSettingPresentation']) lines.append(f'{indent}\t') if block_view_mode is not None: - lines.append(f'{indent}\t{esc_xml(str(block_view_mode))}') + lines.append(f'{indent}\t{esc_xml_text(str(block_view_mode))}') lines.append(f'{indent}') @@ -5468,7 +5473,7 @@ def emit_dl_parameter(lines, p, parsed, indent): is_obj = not isinstance(p, str) lines.append(f'{indent}') ci = f'{indent}\t' - lines.append(f'{ci}{esc_xml(parsed["name"])}') + lines.append(f'{ci}{esc_xml_text(parsed["name"])}') # Title: явный override (shorthand [..] / объект title/presentation) или авто из имени. title = None if parsed.get('title'): @@ -5508,7 +5513,7 @@ def emit_dl_parameter(lines, p, parsed, indent): # expression expr = str(p['expression']) if (is_obj and p.get('expression')) else None if expr: - lines.append(f'{ci}{esc_xml(expr)}') + lines.append(f'{ci}{esc_xml_text(expr)}') # availableValues if is_obj and p.get('availableValues'): for av in p['availableValues']: @@ -5532,7 +5537,7 @@ def emit_dl_parameter(lines, p, parsed, indent): lines.append(f'{ci}true') # use if is_obj and p.get('use'): - lines.append(f'{ci}{esc_xml(str(p["use"]))}') + lines.append(f'{ci}{esc_xml_text(str(p["use"]))}') lines.append(f'{indent}') @@ -5653,7 +5658,7 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): if save_fields: lines.append(f'{inner}') for f in save_fields: - lines.append(f'{inner}\t{esc_xml(f)}') + lines.append(f'{inner}\t{esc_xml_text(f)}') lines.append(f'{inner}') # Проверка заполнения → (реальный тег; в схеме нет). # bool true → ShowError; строка → verbatim. Синоним fillChecking. @@ -5742,7 +5747,7 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): lines.append(f'{si}{ddr}') if has_query: qtext = resolve_query_value(str(s['query']), QUERY_BASE_DIR) - lines.append(f'{si}{esc_xml(qtext)}') + lines.append(f'{si}{esc_xml_text(qtext)}') # Явные поля набора (редко): override title/dataPath if s.get('fields'): for fld in s['fields']: @@ -5762,9 +5767,9 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): if dp == '': lines.append(f'{si}\t') else: - lines.append(f'{si}\t{esc_xml(dp)}') + lines.append(f'{si}\t{esc_xml_text(dp)}') if not is_folder: - lines.append(f'{si}\t{esc_xml(str(fld.get("field", "")))}') + lines.append(f'{si}\t{esc_xml_text(str(fld.get("field", "")))}') if fld.get('title'): lines.append(f'{si}\t') emit_ml_items(lines, f'{si}\t\t', fld['title']) @@ -5774,7 +5779,7 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): emit_restrict_block(lines, 'attributeUseRestriction', fld.get('attributeUseRestriction'), f'{si}\t') # presentationExpression поля — перед valueType (порядок исходника) if fld.get('presentationExpression'): - lines.append(f'{si}\t{esc_xml(str(fld["presentationExpression"]))}') + lines.append(f'{si}\t{esc_xml_text(str(fld["presentationExpression"]))}') # valueType поля набора (тип значения; вычисляемые/кастомные поля) if fld.get('valueType'): emit_dl_value_type(lines, fld['valueType'], f'{si}\t') @@ -5795,10 +5800,10 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None): # Ключ набора (query-based список без MainTable): KeyType (RowNumber/FieldValue/RowKey) # + KeyField* — после Parameter*, до MainTable. Захват/эмит факт. значений. if s.get('keyType'): - lines.append(f'{si}{esc_xml(str(s["keyType"]))}') + lines.append(f'{si}{esc_xml_text(str(s["keyType"]))}') if s.get('keyFields'): for kf in s['keyFields']: - lines.append(f'{si}{esc_xml(str(kf))}') + lines.append(f'{si}{esc_xml_text(str(kf))}') if s.get('mainTable'): lines.append(f'{si}{normalize_meta_type_ref(str(s["mainTable"]))}') # GetInvisibleFieldPresentations — после MainTable (дефолт true; эмитим только при заданном ключе = отклонении false). @@ -5937,7 +5942,7 @@ def emit_commands(lines, cmds, indent): cmd_table = (_cmd_norm.get('table') or _cmd_norm.get('associatedtableelementid') or _cmd_norm.get('используемаятаблица')) if cmd_table: - lines.append(f'{inner}{esc_xml(str(cmd_table))}') + lines.append(f'{inner}{esc_xml_text(str(cmd_table))}') if cmd.get('shortcut'): lines.append(f'{inner}{cmd["shortcut"]}') @@ -6014,12 +6019,12 @@ def emit_command_interface(lines, ci, indent): if tree_group: grp = tree_group lines.append(f'{inner}\t') - lines.append(f'{inner}\t\t{esc_xml(str(cmd))}') + lines.append(f'{inner}\t\t{esc_xml_text(str(cmd))}') lines.append(f'{inner}\t\t{typ}') if attr: - lines.append(f'{inner}\t\t{esc_xml(str(attr))}') + lines.append(f'{inner}\t\t{esc_xml_text(str(attr))}') if grp: - lines.append(f'{inner}\t\t{esc_xml(str(grp))}') + lines.append(f'{inner}\t\t{esc_xml_text(str(grp))}') if idx is not None: lines.append(f'{inner}\t\t{idx}') if dv is not None: @@ -6573,7 +6578,7 @@ def main(): if not str(nm): lines.append('\t\t\t') else: - lines.append(f'\t\t\t{esc_xml(str(nm))}') + lines.append(f'\t\t\t{esc_xml_text(str(nm))}') lines.append('\t\t') lines.append('\t') diff --git a/.claude/skills/form-edit/scripts/form-edit.ps1 b/.claude/skills/form-edit/scripts/form-edit.ps1 index f81e8f8d..b4d7f812 100644 --- a/.claude/skills/form-edit/scripts/form-edit.ps1 +++ b/.claude/skills/form-edit/scripts/form-edit.ps1 @@ -1,4 +1,4 @@ -# form-edit v1.11 — Edit 1C managed form elements (+resolve_type_str: срезание префикса cfg:/d5p1:) +# form-edit v1.12 — Edit 1C managed form elements (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -270,6 +270,12 @@ function X { } function Esc-Xml { + param([string]$s) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +function Esc-XmlText { # Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует — # пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной "). " платформа # принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе. @@ -282,7 +288,7 @@ function Emit-MLText { X "$indent<$tag>" X "$indent`t" X "$indent`t`tru" - X "$indent`t`t$(Esc-Xml $text)" + X "$indent`t`t$(Esc-XmlText $text)" X "$indent`t" X "$indent" } @@ -617,7 +623,7 @@ function Emit-Label { X "$inner" X "$inner`t<v8:item>" X "$inner`t`t<v8:lang>ru</v8:lang>" - X "$inner`t`t<v8:content>$(Esc-Xml "$($el.title)")</v8:content>" + X "$inner`t`t<v8:content>$(Esc-XmlText "$($el.title)")</v8:content>" X "$inner`t</v8:item>" X "$inner" } diff --git a/.claude/skills/form-edit/scripts/form-edit.py b/.claude/skills/form-edit/scripts/form-edit.py index 205b614b..51bfa4fb 100644 --- a/.claude/skills/form-edit/scripts/form-edit.py +++ b/.claude/skills/form-edit/scripts/form-edit.py @@ -1,4 +1,4 @@ -# form-edit v1.11 — Edit 1C managed form elements (Python port) (+resolve_type_str: срезание префикса cfg:/d5p1:) +# form-edit v1.12 — Edit 1C managed form elements (Python port) (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -226,6 +226,11 @@ def local_name(node): # ── helpers ────────────────────────────────────────────────── def esc_xml(s): + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') + + +def esc_xml_text(s): """Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует (92142 сырых кавычки на корпус, ни одной "); " она принимает, но нормализует обратно.""" return s.replace('&', '&').replace('<', '<').replace('>', '>') @@ -521,7 +526,7 @@ def emit_mltext(tag, text, indent): X(f"{indent}<{tag}>") X(f"{indent}\t") X(f"{indent}\t\tru") - X(f"{indent}\t\t{esc_xml(text)}") + X(f"{indent}\t\t{esc_xml_text(text)}") X(f"{indent}\t") X(f"{indent}") @@ -749,7 +754,7 @@ def emit_label(el, name, _id, indent): X(f'{inner}') X(f"{inner}\t<v8:item>") X(f"{inner}\t\t<v8:lang>ru</v8:lang>") - X(f"{inner}\t\t<v8:content>{esc_xml(str(el['title']))}</v8:content>") + X(f"{inner}\t\t<v8:content>{esc_xml_text(str(el['title']))}</v8:content>") X(f"{inner}\t</v8:item>") X(f"{inner}") emit_common_flags(el, inner) diff --git a/.claude/skills/meta-edit/scripts/meta-edit.ps1 b/.claude/skills/meta-edit/scripts/meta-edit.ps1 index 6093be77..5e26d5ae 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.ps1 +++ b/.claude/skills/meta-edit/scripts/meta-edit.ps1 @@ -1,4 +1,4 @@ -# meta-edit v1.34 — Edit existing 1C metadata object XML (+resolve_type_str: срезание префикса cfg:/d5p1:) +# meta-edit v1.35 — Edit existing 1C metadata object XML (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -2854,7 +2854,11 @@ function Build-ChoiceParametersXml([string]$indent, $cp) { $script:fillBoolTrue = @('true','истина','да') $script:fillBoolFalse = @('false','ложь','нет') -function Esc-XmlText([string]$s) { return $s.Replace('&','&').Replace('<','<').Replace('>','>') } +function Esc-XmlText { + param([string]$s) + # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми. + return $s.Replace('&','&').Replace('<','<').Replace('>','>') +} function Get-FillTypeCategory([string]$typeStr) { if (-not $typeStr) { return 'String' } diff --git a/.claude/skills/meta-edit/scripts/meta-edit.py b/.claude/skills/meta-edit/scripts/meta-edit.py index 9dd34205..5da17060 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.py +++ b/.claude/skills/meta-edit/scripts/meta-edit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-edit v1.34 — Edit existing 1C metadata object XML (+resolve_type_str: срезание префикса cfg:/d5p1:) +# meta-edit v1.35 — Edit existing 1C metadata object XML (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -258,7 +258,8 @@ def localname(el): def esc_xml(s): - return s.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') # ============================================================ diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 index 5114175c..696cde37 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.ps1 @@ -1,4 +1,4 @@ -# mxl-compile v1.10 — Compile 1C spreadsheet from JSON +# mxl-compile v1.11 — Compile 1C spreadsheet from JSON (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -477,6 +477,12 @@ foreach ($col in ($colWidthMap.Keys | Sort-Object)) { # Helper: escape XML special characters function Esc-Xml { + param([string]$s) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +function Esc-XmlText { # Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует — # пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной "). " платформа # принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе. @@ -760,7 +766,7 @@ foreach ($area in $def.areas) { X "`t`t`t`t`t" X "`t`t`t`t`t`t" X "`t`t`t`t`t`t`tru" - X "`t`t`t`t`t`t`t$(Esc-Xml $cellInfo.Text)" + X "`t`t`t`t`t`t`t$(Esc-XmlText $cellInfo.Text)" X "`t`t`t`t`t`t" X "`t`t`t`t`t" } @@ -769,7 +775,7 @@ foreach ($area in $def.areas) { X "`t`t`t`t`t" X "`t`t`t`t`t`t" X "`t`t`t`t`t`t`tru" - X "`t`t`t`t`t`t`t$(Esc-Xml $cellInfo.Template)" + X "`t`t`t`t`t`t`t$(Esc-XmlText $cellInfo.Template)" X "`t`t`t`t`t`t" X "`t`t`t`t`t" } @@ -885,7 +891,7 @@ foreach ($key in $formatRegistry.Keys) { X "`t`t" X "`t`t`t" X "`t`t`t`tru" - X "`t`t`t`t$(Esc-Xml $fmt.NumberFormat)" + X "`t`t`t`t$(Esc-XmlText $fmt.NumberFormat)" X "`t`t`t" X "`t`t" } diff --git a/.claude/skills/mxl-compile/scripts/mxl-compile.py b/.claude/skills/mxl-compile/scripts/mxl-compile.py index 5d0b6629..18945141 100644 --- a/.claude/skills/mxl-compile/scripts/mxl-compile.py +++ b/.claude/skills/mxl-compile/scripts/mxl-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# mxl-compile v1.10 — Compile 1C spreadsheet from JSON +# mxl-compile v1.11 — Compile 1C spreadsheet from JSON (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -188,6 +188,11 @@ def assert_edit_allowed(target_path, require): def esc_xml(s): + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') + + +def esc_xml_text(s): """Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует (92142 сырых кавычки на корпус, ни одной "); " она принимает, но нормализует обратно.""" return s.replace('&', '&').replace('<', '<').replace('>', '>') @@ -725,7 +730,7 @@ def main(): lines.append('\t\t\t\t\t') lines.append('\t\t\t\t\t\t') lines.append('\t\t\t\t\t\t\tru') - lines.append(f'\t\t\t\t\t\t\t{esc_xml(cell_info["Text"])}') + lines.append(f'\t\t\t\t\t\t\t{esc_xml_text(cell_info["Text"])}') lines.append('\t\t\t\t\t\t') lines.append('\t\t\t\t\t') @@ -733,7 +738,7 @@ def main(): lines.append('\t\t\t\t\t') lines.append('\t\t\t\t\t\t') lines.append('\t\t\t\t\t\t\tru') - lines.append(f'\t\t\t\t\t\t\t{esc_xml(cell_info["Template"])}') + lines.append(f'\t\t\t\t\t\t\t{esc_xml_text(cell_info["Template"])}') lines.append('\t\t\t\t\t\t') lines.append('\t\t\t\t\t') @@ -829,7 +834,7 @@ def main(): lines.append('\t\t') lines.append('\t\t\t') lines.append('\t\t\t\tru') - lines.append(f'\t\t\t\t{esc_xml(fmt["NumberFormat"])}') + lines.append(f'\t\t\t\t{esc_xml_text(fmt["NumberFormat"])}') lines.append('\t\t\t') lines.append('\t\t') diff --git a/.claude/skills/role-compile/scripts/role-compile.ps1 b/.claude/skills/role-compile/scripts/role-compile.ps1 index 21567d56..2e426ea5 100644 --- a/.claude/skills/role-compile/scripts/role-compile.ps1 +++ b/.claude/skills/role-compile/scripts/role-compile.ps1 @@ -1,4 +1,4 @@ -# role-compile v1.19 — Compile 1C role from JSON (+detect_format_version: ветка автономной EPF/ERF) +# role-compile v1.20 — Compile 1C role from JSON (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -171,6 +171,12 @@ function X { } function Esc-Xml { + param([string]$s) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +function Esc-XmlText { # Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует — # пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной "). " платформа # принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе. @@ -701,11 +707,11 @@ X "`t`t`t$roleName" X "`t`t`t" X "`t`t`t`t" X "`t`t`t`t`tru" -X "`t`t`t`t`t$(Esc-Xml $synonym)" +X "`t`t`t`t`t$(Esc-XmlText $synonym)" X "`t`t`t`t" X "`t`t`t" if ($comment) { - X "`t`t`t$(Esc-Xml $comment)" + X "`t`t`t$(Esc-XmlText $comment)" } else { X "`t`t`t" } @@ -742,7 +748,7 @@ foreach ($obj in $parsedObjects) { X "`t`t`t$($right.Value)" if ($right.Condition) { X "`t`t`t" - X "`t`t`t`t$(Esc-Xml $right.Condition)" + X "`t`t`t`t$(Esc-XmlText $right.Condition)" X "`t`t`t" } X "`t`t" @@ -756,8 +762,8 @@ $templateCount = 0 if ($def.templates) { foreach ($tpl in $def.templates) { X "`t" - X "`t`t$(Esc-Xml "$($tpl.name)")" - X "`t`t$(Esc-Xml "$($tpl.condition)")" + X "`t`t$(Esc-XmlText "$($tpl.name)")" + X "`t`t$(Esc-XmlText "$($tpl.condition)")" X "`t" $templateCount++ } diff --git a/.claude/skills/role-compile/scripts/role-compile.py b/.claude/skills/role-compile/scripts/role-compile.py index 87949750..8333c92f 100644 --- a/.claude/skills/role-compile/scripts/role-compile.py +++ b/.claude/skills/role-compile/scripts/role-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# role-compile v1.19 — Compile 1C role from JSON (+detect_format_version: ветка автономной EPF/ERF) +# role-compile v1.20 — Compile 1C role from JSON (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -228,6 +228,11 @@ def detect_eol(text): return '\r\n' if '\r\n' in text else '\n' def esc_xml(s): + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') + + +def esc_xml_text(s): """Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует (92142 сырых кавычки на корпус, ни одной "); " она принимает, но нормализует обратно.""" return s.replace('&', '&').replace('<', '<').replace('>', '>') @@ -240,7 +245,7 @@ def emit_mltext(lines, indent, tag, text): lines.append(f"{indent}<{tag}>") lines.append(f"{indent}\t") lines.append(f"{indent}\t\tru") - lines.append(f"{indent}\t\t{esc_xml(text)}") + lines.append(f"{indent}\t\t{esc_xml_text(text)}") lines.append(f"{indent}\t") lines.append(f"{indent}") @@ -712,11 +717,11 @@ def main(): lines.append('\t\t\t') lines.append('\t\t\t\t') lines.append('\t\t\t\t\tru') - lines.append(f'\t\t\t\t\t{esc_xml(synonym)}') + lines.append(f'\t\t\t\t\t{esc_xml_text(synonym)}') lines.append('\t\t\t\t') lines.append('\t\t\t') if comment: - lines.append(f'\t\t\t{esc_xml(comment)}') + lines.append(f'\t\t\t{esc_xml_text(comment)}') else: lines.append('\t\t\t') lines.append('\t\t') @@ -752,7 +757,7 @@ def main(): lines.append(f'\t\t\t{right["Value"]}') if right['Condition']: lines.append('\t\t\t') - lines.append(f'\t\t\t\t{esc_xml(right["Condition"])}') + lines.append(f'\t\t\t\t{esc_xml_text(right["Condition"])}') lines.append('\t\t\t') lines.append('\t\t') total_rights += 1 @@ -763,8 +768,8 @@ def main(): if defn.get('templates'): for tpl in defn['templates']: lines.append('\t') - lines.append(f'\t\t{esc_xml(str(tpl["name"]))}') - lines.append(f'\t\t{esc_xml(str(tpl["condition"]))}') + lines.append(f'\t\t{esc_xml_text(str(tpl["name"]))}') + lines.append(f'\t\t{esc_xml_text(str(tpl["condition"]))}') lines.append('\t') template_count += 1 diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1 index 29cb7adb..409a114e 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.ps1 +++ b/.claude/skills/skd-compile/scripts/skd-compile.ps1 @@ -1,4 +1,4 @@ -# skd-compile v1.113 — Compile 1C DCS from JSON (+resolve_type_str: срезание префикса cfg:/d5p1:) +# skd-compile v1.114 — Compile 1C DCS from JSON (+esc_xml/esc_xml_text: разное экранирование атрибута и текста) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -226,6 +226,12 @@ function X { } function Esc-Xml { + param([string]$s) + # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна. + return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') +} + +function Esc-XmlText { param([string]$s) return $s.Replace('&','&').Replace('<','<').Replace('>','>') } @@ -274,14 +280,14 @@ function Emit-MLText { $lang = if ($p -is [hashtable]) { $p.Name } else { $p.Name } $content = if ($p -is [hashtable]) { $p.Value } else { $p.Value } X "$indent`t" - X "$indent`t`t$(Esc-Xml "$lang")" - X "$indent`t`t$(Esc-Xml "$content")" + X "$indent`t`t$(Esc-XmlText "$lang")" + X "$indent`t`t$(Esc-XmlText "$content")" X "$indent`t" } } else { X "$indent`t" X "$indent`t`tru" - X "$indent`t`t$(Esc-Xml "$text")" + X "$indent`t`t$(Esc-XmlText "$text")" X "$indent`t" } X "$indent" @@ -472,7 +478,7 @@ function Emit-SingleValueType { # Reference types: CatalogRef.XXX, DocumentRef.XXX, EnumRef.XXX, etc. # Real DCS files use inline namespace d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config" if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.') { - X "$indentd5p1:$(Esc-Xml $typeStr)" + X "$indentd5p1:$(Esc-XmlText $typeStr)" return } @@ -480,17 +486,17 @@ function Emit-SingleValueType { # EnumRef / ChartOfAccountsRef / etc. (все ссылки указанного класса). # Эмитим вместо . if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef|InformationRegisterRef|AnyRef)$') { - X "$indentd5p1:$(Esc-Xml $typeStr)" + X "$indentd5p1:$(Esc-XmlText $typeStr)" return } # Fallback — assume dot-qualified types are also config references if ($typeStr.Contains('.')) { - X "$indentd5p1:$(Esc-Xml $typeStr)" + X "$indentd5p1:$(Esc-XmlText $typeStr)" return } - X "$indent$(Esc-Xml $typeStr)" + X "$indent$(Esc-XmlText $typeStr)" } # --- 5. Field shorthand parser --- @@ -916,8 +922,8 @@ $script:outputParamTypes = @{ function Emit-DataSources { foreach ($ds in $dataSources) { X "`t" - X "`t`t$(Esc-Xml $ds.name)" - X "`t`t$(Esc-Xml $ds.type)" + X "`t`t$(Esc-XmlText $ds.name)" + X "`t`t$(Esc-XmlText $ds.type)" X "`t" } } @@ -944,7 +950,7 @@ function Emit-InputParameters { if ((Has-JsonProp $item 'use') -and $null -ne $item.use -and -not $item.use) { X "$indent`t`tfalse" } - X "$indent`t`t$(Esc-Xml "$($item.parameter)")" + X "$indent`t`t$(Esc-XmlText "$($item.parameter)")" if (Has-JsonProp $item 'choiceParameters') { $cp = $item.choiceParameters $cpItems = if ($null -ne $cp) { @($cp) } else { @() } @@ -954,7 +960,7 @@ function Emit-InputParameters { X "$indent`t`t" foreach ($cpItem in $cpItems) { X "$indent`t`t`t" - X "$indent`t`t`t`t$(Esc-Xml "$($cpItem.name)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cpItem.name)")" foreach ($v in @($cpItem.values)) { if ($v -is [bool]) { $vStr = if ($v) { 'true' } else { 'false' } @@ -962,7 +968,7 @@ function Emit-InputParameters { } elseif ($v -is [int] -or $v -is [long] -or $v -is [double] -or $v -is [decimal]) { X "$indent`t`t`t`t$v" } else { - X "$indent`t`t`t`t$(Esc-Xml "$v")" + X "$indent`t`t`t`t$(Esc-XmlText "$v")" } } X "$indent`t`t`t" @@ -978,8 +984,8 @@ function Emit-InputParameters { X "$indent`t`t" foreach ($cplItem in $cplItems) { X "$indent`t`t`t" - X "$indent`t`t`t`t$(Esc-Xml "$($cplItem.name)")" - X "$indent`t`t`t`t$(Esc-Xml "$($cplItem.value)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cplItem.name)")" + X "$indent`t`t`t`t$(Esc-XmlText "$($cplItem.value)")" $mode = if ($cplItem.mode) { "$($cplItem.mode)" } else { 'Auto' } X "$indent`t`t`t`t$mode" X "$indent`t`t`t" @@ -1004,7 +1010,7 @@ function Emit-InputParameters { if ($uri -and $tName) { $customType = @{ uri = $uri; name = $tName } } } if ($customType) { - X "$indent`t`t$(Esc-Xml "$val")" + X "$indent`t`t$(Esc-XmlText "$val")" } elseif ($val -is [bool]) { $vStr = if ($val) { 'true' } else { 'false' } X "$indent`t`t$vStr" @@ -1014,7 +1020,7 @@ function Emit-InputParameters { # Multilang dict {ru, en, ...} → LocalStringType Emit-MLText -tag "dcscor:value" -text $val -indent "$indent`t`t" } else { - X "$indent`t`t$(Esc-Xml "$val")" + X "$indent`t`t$(Esc-XmlText "$val")" } } X "$indent`t" @@ -1088,15 +1094,15 @@ function Emit-Field { # DataSetFieldFolder — только dataPath + title (для UI-группировки полей в композиторе) if ($f["folder"]) { X "$indent" - X "$indent`t$(Esc-Xml $f.dataPath)" + X "$indent`t$(Esc-XmlText $f.dataPath)" if ($f.title) { Emit-MLText -tag "title" -text $f.title -indent "$indent`t" } X "$indent" return } X "$indent" - X "$indent`t$(Esc-Xml $f.dataPath)" - X "$indent`t$(Esc-Xml $f.field)" + X "$indent`t$(Esc-XmlText $f.dataPath)" + X "$indent`t$(Esc-XmlText $f.field)" # Title if ($f.title) { @@ -1148,7 +1154,7 @@ function Emit-Field { } if ($hasExtras) { foreach ($k in $f["roleExtras"].Keys) { - X "$indent`t`t$(Esc-Xml "$($f["roleExtras"][$k])")" + X "$indent`t`t$(Esc-XmlText "$($f["roleExtras"][$k])")" } } X "$indent`t" @@ -1169,7 +1175,7 @@ function Emit-Field { $oType = if ($oe.orderType) { "$($oe.orderType)" } else { 'Asc' } $autoOrder = if ($null -ne $oe.autoOrder) { $(if ($oe.autoOrder) { 'true' } else { 'false' }) } else { 'false' } X "$indent`t" - X "$indent`t`t$(Esc-Xml $expr)" + X "$indent`t`t$(Esc-XmlText $expr)" X "$indent`t`t$oType" X "$indent`t`t$autoOrder" X "$indent`t" @@ -1195,7 +1201,7 @@ function Emit-Field { elseif ("$avVal" -match '^\d{4}-\d{2}-\d{2}T') { $avType = 'xs:dateTime' } else { $avType = 'xs:string' } } - $avStr = if ($avVal -is [bool]) { "$avVal".ToLower() } else { Esc-Xml "$avVal" } + $avStr = if ($avVal -is [bool]) { "$avVal".ToLower() } else { Esc-XmlText "$avVal" } X "$indent`t`t$avStr" if ($av.presentation) { Emit-MLText -tag "presentation" -text $av.presentation -indent "$indent`t`t" @@ -1212,8 +1218,8 @@ function Emit-Field { # ГоризонтальноеПоложение требует специального xsi:type (v8ui:HorizontalAlign), не строка if ($key -eq "ГоризонтальноеПоложение" -and -not ($val -is [hashtable] -or $val -is [System.Collections.IDictionary] -or $val -is [PSCustomObject])) { X "$indent`t`t" - X "$indent`t`t`t$(Esc-Xml $key)" - X "$indent`t`t`t$(Esc-Xml "$val")" + X "$indent`t`t`t$(Esc-XmlText $key)" + X "$indent`t`t`t$(Esc-XmlText "$val")" X "$indent`t`t" } else { Emit-AppearanceValue -key $key -val $val -indent "$indent`t`t" @@ -1224,7 +1230,7 @@ function Emit-Field { # PresentationExpression if ($f["presentationExpression"]) { - X "$indent`t$(Esc-Xml $f["presentationExpression"])" + X "$indent`t$(Esc-XmlText $f["presentationExpression"])" } # InputParameters — в конце field @@ -1249,7 +1255,7 @@ function Emit-DataSet { } X "$indent<$tagName xsi:type=`"$dsType`">" - X "$indent`t$(Esc-Xml "$($ds.name)")" + X "$indent`t$(Esc-XmlText "$($ds.name)")" # Fields if ($ds.fields) { @@ -1261,18 +1267,18 @@ function Emit-DataSet { # DataSource (not for Union) if ($dsType -ne "DataSetUnion") { $src = if ($ds.source) { "$($ds.source)" } else { $defaultSource } - X "$indent`t$(Esc-Xml $src)" + X "$indent`t$(Esc-XmlText $src)" } # Type-specific content if ($dsType -eq "DataSetQuery") { $queryText = Resolve-QueryValue "$($ds.query)" $script:queryBaseDir - X "$indent`t$(Esc-Xml $queryText)" + X "$indent`t$(Esc-XmlText $queryText)" if ($ds.autoFillFields -eq $false) { X "$indent`tfalse" } } elseif ($dsType -eq "DataSetObject") { - X "$indent`t$(Esc-Xml "$($ds.objectName)")" + X "$indent`t$(Esc-XmlText "$($ds.objectName)")" } elseif ($dsType -eq "DataSetUnion") { foreach ($item in $ds.items) { # Union inner items are wrapped as @@ -1298,21 +1304,21 @@ function Emit-DataSetLinks { $dstDS = if ($link.dest) { "$($link.dest)" } elseif ($link.destinationDataSet) { "$($link.destinationDataSet)" } else { "" } $srcEx = if ($link.sourceExpr) { "$($link.sourceExpr)" } elseif ($link.sourceExpression) { "$($link.sourceExpression)" } else { "" } $dstEx = if ($link.destExpr) { "$($link.destExpr)" } elseif ($link.destinationExpression) { "$($link.destinationExpression)" } else { "" } - X "`t`t$(Esc-Xml $srcDS)" - X "`t`t$(Esc-Xml $dstDS)" - X "`t`t$(Esc-Xml $srcEx)" - X "`t`t$(Esc-Xml $dstEx)" + X "`t`t$(Esc-XmlText $srcDS)" + X "`t`t$(Esc-XmlText $dstDS)" + X "`t`t$(Esc-XmlText $srcEx)" + X "`t`t$(Esc-XmlText $dstEx)" if ($link.parameter) { - X "`t`t$(Esc-Xml "$($link.parameter)")" + X "`t`t$(Esc-XmlText "$($link.parameter)")" } if ($link.PSObject.Properties.Match('parameterListAllowed').Count -gt 0 -and $link.parameterListAllowed) { X "`t`ttrue" } if ($link.PSObject.Properties.Match('startExpression').Count -gt 0 -and $null -ne $link.startExpression) { - X "`t`t$(Esc-Xml "$($link.startExpression)")" + X "`t`t$(Esc-XmlText "$($link.startExpression)")" } if ($link.PSObject.Properties.Match('linkConditionExpression').Count -gt 0 -and $null -ne $link.linkConditionExpression) { - X "`t`t$(Esc-Xml "$($link.linkConditionExpression)")" + X "`t`t$(Esc-XmlText "$($link.linkConditionExpression)")" } X "`t" } @@ -1369,8 +1375,8 @@ function Emit-CalcFields { } X "`t" - X "`t`t$(Esc-Xml $dataPath)" - X "`t`t$(Esc-Xml $expression)" + X "`t`t$(Esc-XmlText $dataPath)" + X "`t`t$(Esc-XmlText $expression)" if ($title) { Emit-MLText -tag "title" -text $title -indent "`t`t" @@ -1402,8 +1408,8 @@ function Emit-CalcFields { # ГоризонтальноеПоложение — особый xsi:type (если не multilang) if ($prop.Name -eq "ГоризонтальноеПоложение" -and -not ($prop.Value -is [hashtable] -or $prop.Value -is [System.Collections.IDictionary] -or $prop.Value -is [PSCustomObject])) { X "`t`t`t" - X "`t`t`t`t$(Esc-Xml $prop.Name)" - X "`t`t`t`t$(Esc-Xml "$($prop.Value)")" + X "`t`t`t`t$(Esc-XmlText $prop.Name)" + X "`t`t`t`t$(Esc-XmlText "$($prop.Value)")" X "`t`t`t" } else { Emit-AppearanceValue -key $prop.Name -val $prop.Value -indent "`t`t`t" @@ -1431,11 +1437,11 @@ function Emit-TotalFields { } X "`t" - X "`t`t$(Esc-Xml $parsed.dataPath)" - X "`t`t$(Esc-Xml $parsed.expression)" + X "`t`t$(Esc-XmlText $parsed.dataPath)" + X "`t`t$(Esc-XmlText $parsed.expression)" if ($parsed.groups) { foreach ($g in $parsed.groups) { - X "`t`t$(Esc-Xml "$g")" + X "`t`t$(Esc-XmlText "$g")" } } X "`t" @@ -1448,7 +1454,7 @@ function Emit-SingleParam { param($p, $parsed) X "`t" - X "`t`t$(Esc-Xml $parsed.name)" + X "`t`t$(Esc-XmlText $parsed.name)" # Title (from parsed first, then from object form; accept `presentation` as # a synonym — 1C UI labels a parameter's caption "Представление"). @@ -1508,7 +1514,7 @@ function Emit-SingleParam { # Expression if ($parsed.expression) { - X "`t`t$(Esc-Xml $parsed.expression)" + X "`t`t$(Esc-XmlText $parsed.expression)" } # AvailableAsField @@ -1540,7 +1546,7 @@ function Emit-SingleParam { if ($avVal -match '^(Перечисление|Справочник|ПланСчетов|Документ|ПланВидовХарактеристик|ПланВидовРасчета)\.') { $avType = "dcscor:DesignTimeValue" } - X "`t`t`t$(Esc-Xml $avVal)" + X "`t`t`t$(Esc-XmlText $avVal)" } } # `title` accepted as synonym of `presentation` — both map to the same UI label. @@ -1564,7 +1570,7 @@ function Emit-SingleParam { if ($null -ne $p -and $p -isnot [string] -and $p.use) { $useVal = "$($p.use)" } elseif ($parsed.use) { $useVal = "$($parsed.use)" } if ($useVal) { - X "`t`t$(Esc-Xml $useVal)" + X "`t`t$(Esc-XmlText $useVal)" } # InputParameters на параметре (ФорматРедактирования и т.п.) @@ -1715,34 +1721,34 @@ function Emit-ParamValue { # Platform-pattern: startDate/endDate эмитятся ТОЛЬКО для variant=Custom. # Для всех остальных вариантов (ThisMonth, LastYear, Today, ...) — без дат. X "$indent" - X "$indent`t$(Esc-Xml $valStr)" + X "$indent`t$(Esc-XmlText $valStr)" if ($valStr -eq 'Custom') { $sdOut = if ($sdStr) { $sdStr } else { '0001-01-01T00:00:00' } $edOut = if ($edStr) { $edStr } else { '0001-01-01T00:00:00' } - X "$indent`t$(Esc-Xml $sdOut)" - X "$indent`t$(Esc-Xml $edOut)" + X "$indent`t$(Esc-XmlText $sdOut)" + X "$indent`t$(Esc-XmlText $edOut)" } X "$indent" } elseif ($type -match '^date') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($type -eq "boolean") { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($type -match '^decimal') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($type -match '^string') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } else { # Guess from value if ($valStr -match '^\d{4}-\d{2}-\d{2}T') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($valStr -eq "true" -or $valStr -eq "false") { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } elseif ($valStr -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or $valStr -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } else { - X "$indent$(Esc-Xml $valStr)" + X "$indent$(Esc-XmlText $valStr)" } } } @@ -1840,7 +1846,7 @@ function Emit-ColorValue { return } } - X "$indent$(Esc-Xml $color)" + X "$indent$(Esc-XmlText $color)" } function Emit-CellAppearance { @@ -1904,14 +1910,14 @@ function Emit-CellAppearance { if ($style.hAlign) { X "$ind" X "$ind`tГоризонтальноеПоложение" - X "$ind`t$(Esc-Xml $style.hAlign)" + X "$ind`t$(Esc-XmlText $style.hAlign)" X "$ind" } # Vertical alignment if ($style.vAlign) { X "$ind" X "$ind`tВертикальноеПоложение" - X "$ind`t$(Esc-Xml $style.vAlign)" + X "$ind`t$(Esc-XmlText $style.vAlign)" X "$ind" } # Text placement (wrap) @@ -2037,7 +2043,7 @@ function Emit-AreaTemplateDSL { } X "`t