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)$tag>"
+ X "$indent<$tag xsi:type=`"xs:string`">$(Esc-XmlText $val)$tag>"
} 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$wrapTag>"
@@ -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'})$($p[1])>" }
}
# Динамический заголовок колонки-группы из данных (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$picTag>"
@@ -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'})$($s.Tag)>"
} else {
$v = "$($p.Value)"; if ($v -eq '') { continue }
- X "$indent<$($s.Tag)>$(Esc-Xml $v)$($s.Tag)>"
+ X "$indent<$($s.Tag)>$(Esc-XmlText $v)$($s.Tag)>"
}
}
}
@@ -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")$($spec.tag)>" }
+ 'color' { X "$indent<$($spec.tag)>$(Esc-XmlText "$val")$($spec.tag)>" }
'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]))")$($p[1])>" }
+ if ($el.($p[0])) { X "$inner<$($p[1])>$(Esc-XmlText "$($el.($p[0]))")$($p[1])>" }
}
# 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]))")$($p[1])>"
+ X "$inner<$($p[1]) xsi:type=`"$mvt`">$(Esc-XmlText "$($el.($p[0]))")$($p[1])>"
}
}
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)}{tag}>')
+ lines.append(f'{indent}<{tag} xsi:type="xs:string">{esc_xml_text(val)}{tag}>')
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}{wrap_tag}>')
@@ -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"}{tag}>')
# Динамический заголовок колонки-группы из данных (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))}{tag}>')
+ lines.append(f'{indent}<{tag}>{esc_xml_text(str(val))}{tag}>')
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)}{tag}>')
+ lines.append(f'{indent}<{tag}>{esc_xml_text(v)}{tag}>')
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]))}{tag}>')
+ lines.append(f'{inner}<{tag}>{esc_xml_text(str(el[key]))}{tag}>')
# 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]))}{tag}>')
+ lines.append(f'{inner}<{tag} xsi:type="{mvt}">{esc_xml_text(str(el[key]))}{tag}>')
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$tag>"
}
@@ -617,7 +623,7 @@ function Emit-Label {
X "$inner"
X "$inner`t"
X "$inner`t`tru"
- X "$inner`t`t$(Esc-Xml "$($el.title)")"
+ X "$inner`t`t$(Esc-XmlText "$($el.title)")"
X "$inner`t"
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}{tag}>")
@@ -749,7 +754,7 @@ def emit_label(el, name, _id, indent):
X(f'{inner}')
X(f"{inner}\t")
X(f"{inner}\t\tru")
- X(f"{inner}\t\t{esc_xml(str(el['title']))}")
+ X(f"{inner}\t\t{esc_xml_text(str(el['title']))}")
X(f"{inner}\t")
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}{tag}>")
@@ -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$tag>"
@@ -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"
+ X "`t`t"
}
# 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"
- X "`t`t$(Esc-Xml "$($t.name)")"
+ X "`t`t$(Esc-XmlText "$($t.name)")"
X "`t`t"
for ($r = 0; $r -lt $rows.Count; $r++) {
@@ -2074,7 +2080,7 @@ function Emit-AreaTemplateDSL {
# Parameter reference
$paramName = $Matches[1]
X "`t`t`t`t`t"
- X "`t`t`t`t`t`t$(Esc-Xml $paramName)"
+ X "`t`t`t`t`t`t$(Esc-XmlText $paramName)"
X "`t`t`t`t`t"
# Build drilldown appearance extra items.
# Приоритет: per-cell override (cell={value, drilldown}) → drilldownMap (shortcut form B).
@@ -2094,7 +2100,7 @@ function Emit-AreaTemplateDSL {
if ($ddTarget) {
$cellExtraItems += "`t`t`t`t`t`t"
$cellExtraItems += "`t`t`t`t`t`t`tРасшифровка"
- $cellExtraItems += "`t`t`t`t`t`t`t$(Esc-Xml $ddTarget)"
+ $cellExtraItems += "`t`t`t`t`t`t`t$(Esc-XmlText $ddTarget)"
$cellExtraItems += "`t`t`t`t`t`t"
}
} else {
@@ -2147,28 +2153,28 @@ function Emit-AreaTemplateParameter {
elseif (($dd -is [hashtable] -or $dd -is [System.Collections.IDictionary]) -and $dd.Contains('action')) { $ddActV = "$($dd['action'])" }
$ddAct = if ($ddActV) { $ddActV } else { 'DrillDown' }
X "$indent"
- X "$indent`t$(Esc-Xml "$($tp.name)")"
+ X "$indent`t$(Esc-XmlText "$($tp.name)")"
X "$indent`t"
- X "$indent`t`t$(Esc-Xml $ddField)"
- X "$indent`t`t$(Esc-Xml $ddExpr)"
+ X "$indent`t`t$(Esc-XmlText $ddField)"
+ X "$indent`t`t$(Esc-XmlText $ddExpr)"
X "$indent`t"
- X "$indent`t$(Esc-Xml $ddAct)"
+ X "$indent`t$(Esc-XmlText $ddAct)"
X "$indent"
return
}
# Форма A или B
X "$indent"
- X "$indent`t$(Esc-Xml "$($tp.name)")"
- X "$indent`t$(Esc-Xml "$($tp.expression)")"
+ X "$indent`t$(Esc-XmlText "$($tp.name)")"
+ X "$indent`t$(Esc-XmlText "$($tp.expression)")"
X "$indent"
if ($dd -and ($dd -is [string])) {
# Форма B: shortcut Расшифровка_ + ИмяРесурса + DrillDown
$ddVal = "$dd"
X "$indent"
- X "$indent`tРасшифровка_$(Esc-Xml $ddVal)"
+ X "$indent`tРасшифровка_$(Esc-XmlText $ddVal)"
X "$indent`t"
X "$indent`t`tИмяРесурса"
- X "$indent`t`t`"$(Esc-Xml $ddVal)`""
+ X "$indent`t`t`"$(Esc-XmlText $ddVal)`""
X "$indent`t"
X "$indent`tDrillDown"
X "$indent"
@@ -2185,7 +2191,7 @@ function Emit-Templates {
} else {
# Raw XML mode
X "`t"
- X "`t`t$(Esc-Xml "$($t.name)")"
+ X "`t`t$(Esc-XmlText "$($t.name)")"
if ($t.template) {
X "`t`t$($t.template)"
}
@@ -2206,8 +2212,8 @@ function Emit-FieldTemplates {
if (-not $def.fieldTemplates) { return }
foreach ($ft in $def.fieldTemplates) {
X "`t"
- X "`t`t$(Esc-Xml "$($ft.field)")"
- X "`t`t$(Esc-Xml "$($ft.template)")"
+ X "`t`t$(Esc-XmlText "$($ft.field)")"
+ X "`t`t$(Esc-XmlText "$($ft.template)")"
X "`t"
}
}
@@ -2223,12 +2229,12 @@ function Emit-GroupTemplates {
X "`t<$tag>"
if ($gt.groupName) {
- X "`t`t$(Esc-Xml "$($gt.groupName)")"
+ X "`t`t$(Esc-XmlText "$($gt.groupName)")"
} elseif ($gt.groupField) {
- X "`t`t$(Esc-Xml "$($gt.groupField)")"
+ X "`t`t$(Esc-XmlText "$($gt.groupField)")"
}
- X "`t`t$(Esc-Xml $xmlTType)"
- X "`t`t$(Esc-Xml "$($gt.template)")"
+ X "`t`t$(Esc-XmlText $xmlTType)"
+ X "`t`t$(Esc-XmlText "$($gt.template)")"
X "`t$tag>"
}
}
@@ -2242,7 +2248,7 @@ function Emit-SelectionItem {
X "$indent"
} else {
X "$indent"
- X "$indent`t$(Esc-Xml $item)"
+ X "$indent`t$(Esc-XmlText $item)"
X "$indent"
}
return
@@ -2258,14 +2264,14 @@ function Emit-SelectionItem {
X "$indent"
# Optional на folder (редкий случай, для round-trip-целостности)
if ($item.field) {
- X "$indent`t$(Esc-Xml "$($item.field)")"
+ X "$indent`t$(Esc-XmlText "$($item.field)")"
}
Emit-MLText -tag "dcsset:lwsTitle" -text $item.folder -indent "$indent`t" -NoXsiType
foreach ($sub in $item.items) {
Emit-SelectionItem -item $sub -indent "$indent`t"
}
$pl = if ($item.placement) { "$($item.placement)" } else { 'Auto' }
- X "$indent`t$(Esc-Xml $pl)"
+ X "$indent`t$(Esc-XmlText $pl)"
X "$indent"
return
}
@@ -2274,12 +2280,12 @@ function Emit-SelectionItem {
if ($item.use -eq $false) {
X "$indent`tfalse"
}
- X "$indent`t$(Esc-Xml "$($item.field)")"
+ X "$indent`t$(Esc-XmlText "$($item.field)")"
if ($item.title) {
Emit-MLText -tag "dcsset:lwsTitle" -text $item.title -indent "$indent`t" -NoXsiType
}
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($item.viewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.viewMode)")"
}
X "$indent"
}
@@ -2297,11 +2303,11 @@ function Emit-Selection {
Emit-SelectionItem -item $item -indent "$indent`t"
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$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)"
}
X "$indent"
}
@@ -2338,11 +2344,11 @@ function Emit-FilterItem {
Emit-MLText -tag "dcsset:presentation" -text $item.presentation -indent "$indent`t"
}
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($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-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t"
@@ -2358,11 +2364,11 @@ function Emit-FilterItem {
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)"
# Right value: один, несколько (InList) или ValueListType (пустой list-placeholder)
$valIsArray = ($item.value -is [array]) -or ($item.value -is [System.Collections.IList] -and $item.value -isnot [string])
@@ -2385,7 +2391,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" }
X "$indent`t$vStr"
}
}
@@ -2407,7 +2413,7 @@ function Emit-FilterItem {
$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)" }
X "$indent`t$vStr"
}
@@ -2417,12 +2423,12 @@ function Emit-FilterItem {
# viewMode эмитим только если явно задан — присутствие в XML контекстно
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($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) {
@@ -2468,11 +2474,11 @@ function Emit-Filter {
}
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$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)"
}
X "$indent"
}
@@ -2498,7 +2504,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"
}
@@ -2516,20 +2522,20 @@ function Emit-Order {
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)")"
+ X "$indent`t`t$(Esc-XmlText "$($item.viewMode)")"
}
X "$indent`t"
}
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$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)"
}
X "$indent"
}
@@ -2570,7 +2576,7 @@ function Emit-AppearanceValue {
}
if ($useWrapper) { X "$indent`tfalse" }
- X "$indent`t$(Esc-Xml $key)"
+ X "$indent`t$(Esc-XmlText $key)"
# Font dict ({@type: "Font", ref, faceName, height, bold, ...}) →
$isFontDict = $false
@@ -2589,7 +2595,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 = @()
@@ -2620,11 +2626,11 @@ function Emit-AppearanceValue {
}
$keyType = $keyTypeMap[$key]
if ($keyType) {
- X "$indent`t$(Esc-Xml $actualVal)"
+ X "$indent`t$(Esc-XmlText $actualVal)"
} elseif ($actualVal -match '^(style|web|win):') {
# Внутри префиксы style:/web:/win:/sys: уже объявлены на корне,
# локальный xmlns не нужен — эмитим short form.
- X "$indent`t$(Esc-Xml $actualVal)"
+ 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 "Формат") {
@@ -2635,9 +2641,9 @@ function Emit-AppearanceValue {
X "$indent`t$actualVal"
} elseif ($key -eq 'ЦветТекста' -or $key -eq 'ЦветФона' -or $key -eq 'ЦветГраницы') {
# Color без явного префикса (auto, #FFC8C8)
- X "$indent`t$(Esc-Xml $actualVal)"
+ X "$indent`t$(Esc-XmlText $actualVal)"
} else {
- X "$indent`t$(Esc-Xml $actualVal)"
+ X "$indent`t$(Esc-XmlText $actualVal)"
}
}
# Nested SettingsParameterValue items (например СтильГраницы.Сверху/.Снизу/.Слева/.Справа).
@@ -2678,7 +2684,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"
@@ -2709,18 +2715,18 @@ function Emit-ConditionalAppearance {
if ($ca.presentation -is [hashtable] -or $ca.presentation -is [System.Collections.IDictionary] -or $ca.presentation -is [PSCustomObject]) {
Emit-MLText -tag "dcsset:presentation" -text $ca.presentation -indent "$indent`t`t"
} else {
- X "$indent`t`t$(Esc-Xml "$($ca.presentation)")"
+ X "$indent`t`t$(Esc-XmlText "$($ca.presentation)")"
}
}
if ($ca.viewMode) {
- X "$indent`t`t$(Esc-Xml "$($ca.viewMode)")"
+ X "$indent`t`t$(Esc-XmlText "$($ca.viewMode)")"
}
# UserSettingID
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) {
@@ -2746,11 +2752,11 @@ function Emit-ConditionalAppearance {
X "$indent`t"
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$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)"
}
X "$indent"
}
@@ -2795,11 +2801,11 @@ function Emit-OutputParametersSubItem {
}
X "$indent`t`t"
if ($subUseFalse) { X "$indent`t`t`tfalse" }
- X "$indent`t`t`t$(Esc-Xml $subName)"
+ X "$indent`t`t`t$(Esc-XmlText $subName)"
if ($subUri) {
- X "$indent`t`t`t$(Esc-Xml "$subVal")"
+ X "$indent`t`t`t$(Esc-XmlText "$subVal")"
} else {
- X "$indent`t`t`t$(Esc-Xml "$subVal")"
+ X "$indent`t`t`t$(Esc-XmlText "$subVal")"
}
X "$indent`t`t"
}
@@ -2862,7 +2868,7 @@ function Emit-OutputParameters {
X "$indent`t"
if ($useFalse) { X "$indent`t`tfalse" }
- X "$indent`t`t$(Esc-Xml $key)"
+ X "$indent`t`t$(Esc-XmlText $key)"
if ($isFontDict) {
$attrParts = @()
foreach ($attrName in @('ref','faceName','height','bold','italic','underline','strikeout','kind','scale')) {
@@ -2879,7 +2885,7 @@ function Emit-OutputParameters {
} elseif ($ptype -eq "mltext") {
Emit-MLText -tag "dcscor:value" -text $rawVal -indent "$indent`t`t"
} else {
- X "$indent`t`t$(Esc-Xml "$rawVal")"
+ X "$indent`t`t$(Esc-XmlText "$rawVal")"
}
# Nested sub-параметры (ТипДиаграммы.ВидПодписей и т.п.) — эмитим между value и extras.
# valueType: строка → xsi:type=string, объект {uri, name} → локальный xmlns:dN + xsi:type=dN:name.
@@ -2895,10 +2901,10 @@ function Emit-OutputParameters {
}
}
}
- if ($wrapVM) { X "$indent`t`t$(Esc-Xml $wrapVM)" }
+ if ($wrapVM) { X "$indent`t`t$(Esc-XmlText $wrapVM)" }
if ($wrapUSID) {
$uid = if ("$wrapUSID" -eq 'auto') { New-Guid-String } else { "$wrapUSID" }
- X "$indent`t`t$(Esc-Xml $uid)"
+ X "$indent`t`t$(Esc-XmlText $uid)"
}
if ($wrapUSP) {
Emit-MLText -tag "dcsset:userSettingPresentation" -text $wrapUSP -indent "$indent`t`t"
@@ -2906,7 +2912,7 @@ function Emit-OutputParameters {
X "$indent`t"
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$blockViewMode")"
+ X "$indent`t$(Esc-XmlText "$blockViewMode")"
}
X "$indent"
}
@@ -2944,7 +2950,7 @@ function Emit-DataParameters {
X "$indent`t`tfalse"
}
- X "$indent`t`t$(Esc-Xml "$($dp.parameter)")"
+ X "$indent`t`t$(Esc-XmlText "$($dp.parameter)")"
# Value
if ($dp.nilValue -eq $true) {
@@ -2973,10 +2979,10 @@ 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)"
+ 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-Xml $_d)"
+ X "$indent`t`t`t$(Esc-XmlText $_d)"
}
X "$indent`t`t"
} else {
@@ -2990,42 +2996,42 @@ function Emit-DataParameters {
if ($dp.value.Contains('endDate')) { $_ed = "$($dp.value['endDate'])" }
}
X "$indent`t`t"
- X "$indent`t`t`t$(Esc-Xml $_variantStr)"
+ 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-Xml $_sd)"
- X "$indent`t`t`t$(Esc-Xml $_ed)"
+ 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]+:') {
# Полный xsi:type из decompile (например "xs:boolean", "dcscor:DesignTimeValue").
$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]) {
$bv = "$($dp.value)".ToLower()
- X "$indent`t`t$(Esc-Xml $bv)"
+ X "$indent`t`t$(Esc-XmlText $bv)"
} 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)")"
+ 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-Xml $uid)"
+ X "$indent`t`t$(Esc-XmlText $uid)"
}
if ($dp.userSettingPresentation) {
@@ -3035,7 +3041,7 @@ function Emit-DataParameters {
X "$indent`t"
}
if ($null -ne $blockViewMode) {
- X "$indent`t$(Esc-Xml "$blockViewMode")"
+ X "$indent`t$(Esc-XmlText "$blockViewMode")"
}
X "$indent"
}
@@ -3056,7 +3062,7 @@ function Emit-GroupItems {
continue
}
X "$indent`t"
- X "$indent`t`t$(Esc-Xml $field)"
+ X "$indent`t`t$(Esc-XmlText $field)"
X "$indent`t`tItems"
X "$indent`t`tNone"
X "$indent`t`t0001-01-01T00:00:00"
@@ -3065,18 +3071,18 @@ function Emit-GroupItems {
} else {
# Object form
X "$indent`t"
- X "$indent`t`t$(Esc-Xml "$($field.field)")"
+ X "$indent`t`t$(Esc-XmlText "$($field.field)")"
$gt = if ($field.groupType) { "$($field.groupType)" } else { "Items" }
- X "$indent`t`t$(Esc-Xml $gt)"
+ X "$indent`t`t$(Esc-XmlText $gt)"
$pat = if ($field.periodAdditionType) { "$($field.periodAdditionType)" } else { "None" }
- X "$indent`t`t$(Esc-Xml $pat)"
+ X "$indent`t`t$(Esc-XmlText $pat)"
# Auto-detect: ISO date → xs:dateTime, иначе → dcscor:Field (path).
$pab = if ($field.periodAdditionBegin) { "$($field.periodAdditionBegin)" } else { '0001-01-01T00:00:00' }
$pae = if ($field.periodAdditionEnd) { "$($field.periodAdditionEnd)" } else { '0001-01-01T00:00:00' }
$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`t$(Esc-Xml $pab)"
- X "$indent`t`t$(Esc-Xml $pae)"
+ X "$indent`t`t$(Esc-XmlText $pab)"
+ X "$indent`t`t$(Esc-XmlText $pae)"
X "$indent`t"
}
}
@@ -3132,7 +3138,7 @@ function Emit-UserFields {
$uType = if ($uf.cases) { "UserFieldCase" } else { "UserFieldExpression" }
X "$indent`t"
if ($uf.dataPath) {
- X "$indent`t`t$(Esc-Xml "$($uf.dataPath)")"
+ X "$indent`t`t$(Esc-XmlText "$($uf.dataPath)")"
}
if ($uf.title) {
Emit-MLText -tag "dcsset:lwsTitle" -text $uf.title -indent "$indent`t`t" -NoXsiType
@@ -3141,24 +3147,24 @@ function Emit-UserFields {
if ($uf.detail) {
if ($uf.detail.PSObject.Properties.Match('expression').Count -gt 0) {
$_v = "$($uf.detail.expression)"
- if ($_v) { X "$indent`t`t$(Esc-Xml $_v)" }
+ if ($_v) { X "$indent`t`t$(Esc-XmlText $_v)" }
else { X "$indent`t`t" }
}
if ($uf.detail.PSObject.Properties.Match('presentation').Count -gt 0) {
$_v = "$($uf.detail.presentation)"
- if ($_v) { X "$indent`t`t$(Esc-Xml $_v)" }
+ if ($_v) { X "$indent`t`t$(Esc-XmlText $_v)" }
else { X "$indent`t`t" }
}
}
if ($uf.total) {
if ($uf.total.PSObject.Properties.Match('expression').Count -gt 0) {
$_v = "$($uf.total.expression)"
- if ($_v) { X "$indent`t`t$(Esc-Xml $_v)" }
+ if ($_v) { X "$indent`t`t$(Esc-XmlText $_v)" }
else { X "$indent`t`t" }
}
if ($uf.total.PSObject.Properties.Match('presentation').Count -gt 0) {
$_v = "$($uf.total.presentation)"
- if ($_v) { X "$indent`t`t$(Esc-Xml $_v)" }
+ if ($_v) { X "$indent`t`t$(Esc-XmlText $_v)" }
else { X "$indent`t`t" }
}
}
@@ -3180,7 +3186,7 @@ function Emit-UserFields {
} elseif ($cv -is [int] -or $cv -is [long] -or $cv -is [double]) {
X "$indent`t`t`t`t$cv"
} else {
- X "$indent`t`t`t`t$(Esc-Xml "$cv")"
+ X "$indent`t`t`t`t$(Esc-XmlText "$cv")"
}
}
if ($c.presentation) {
@@ -3204,7 +3210,7 @@ function Emit-UserFields {
function Emit-TableAxisBlock {
param($block, [string]$indent, [bool]$emitName = $true)
if ($emitName -and $block.name) {
- X "$indent$(Esc-Xml "$($block.name)")"
+ X "$indent$(Esc-XmlText "$($block.name)")"
}
$gb = if ($block.groupBy) { $block.groupBy } else { $block.groupFields }
Emit-GroupItems -groupBy $gb -indent $indent
@@ -3234,17 +3240,17 @@ function Emit-TableAxisBlock {
}
}
if ($block.viewMode) {
- X "$indent$(Esc-Xml "$($block.viewMode)")"
+ X "$indent$(Esc-XmlText "$($block.viewMode)")"
}
if ($block.userSettingID) {
$uid = if ("$($block.userSettingID)" -eq "auto") { New-Guid-String } else { "$($block.userSettingID)" }
- X "$indent$(Esc-Xml $uid)"
+ X "$indent$(Esc-XmlText $uid)"
}
if ($block.userSettingPresentation) {
Emit-MLText -tag "dcsset:userSettingPresentation" -text $block.userSettingPresentation -indent $indent
}
if ($block.itemsViewMode) {
- X "$indent$(Esc-Xml "$($block.itemsViewMode)")"
+ X "$indent$(Esc-XmlText "$($block.itemsViewMode)")"
}
}
@@ -3268,7 +3274,7 @@ function Emit-StructureItem {
}
if ($item.name) {
- X "$indent`t$(Esc-Xml "$($item.name)")"
+ X "$indent`t$(Esc-XmlText "$($item.name)")"
}
$gb = if ($item.groupBy) { $item.groupBy } else { $item.groupFields }
@@ -3309,17 +3315,17 @@ function Emit-StructureItem {
# viewMode/itemsViewMode/userSettingID/userSettingPresentation on
# StructureItemGroup are context-dependent — emit only when explicitly set.
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($item.viewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.viewMode)")"
}
if ($item.userSettingID) {
$gid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" }
- X "$indent`t$(Esc-Xml $gid)"
+ X "$indent`t$(Esc-XmlText $gid)"
}
if ($item.userSettingPresentation) {
Emit-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t"
}
if ($item.itemsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.itemsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.itemsViewMode)")"
}
X "$indent"
@@ -3333,7 +3339,7 @@ function Emit-StructureItem {
}
if ($item.name) {
- X "$indent`t$(Esc-Xml "$($item.name)")"
+ X "$indent`t$(Esc-XmlText "$($item.name)")"
}
# Columns
@@ -3366,24 +3372,24 @@ function Emit-StructureItem {
}
# columnsViewMode / rowsViewMode — axis-level режим доступности (после rows/columns)
if ($item.columnsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.columnsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.columnsViewMode)")"
}
if ($item.rowsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.rowsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.rowsViewMode)")"
}
# viewMode / userSettingID / userSettingPresentation / itemsViewMode на самой таблице
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($item.viewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.viewMode)")"
}
if ($item.userSettingID) {
$gid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" }
- X "$indent`t$(Esc-Xml $gid)"
+ X "$indent`t$(Esc-XmlText $gid)"
}
if ($item.userSettingPresentation) {
Emit-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t"
}
if ($item.itemsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.itemsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.itemsViewMode)")"
}
X "$indent"
@@ -3397,7 +3403,7 @@ function Emit-StructureItem {
}
if ($item.name) {
- X "$indent`t$(Esc-Xml "$($item.name)")"
+ X "$indent`t$(Esc-XmlText "$($item.name)")"
}
# Points — single object или массив (multi-series диаграмма)
@@ -3448,31 +3454,31 @@ function Emit-StructureItem {
# pointsViewMode / seriesViewMode — axis-level режим доступности (после points/series)
if ($item.pointsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.pointsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.pointsViewMode)")"
}
if ($item.seriesViewMode) {
- X "$indent`t$(Esc-Xml "$($item.seriesViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.seriesViewMode)")"
}
# viewMode / userSettingID / userSettingPresentation / itemsViewMode на самой диаграмме
if ($item.viewMode) {
- X "$indent`t$(Esc-Xml "$($item.viewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.viewMode)")"
}
if ($item.userSettingID) {
$gid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" }
- X "$indent`t$(Esc-Xml $gid)"
+ X "$indent`t$(Esc-XmlText $gid)"
}
if ($item.userSettingPresentation) {
Emit-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t"
}
if ($item.itemsViewMode) {
- X "$indent`t$(Esc-Xml "$($item.itemsViewMode)")"
+ X "$indent`t$(Esc-XmlText "$($item.itemsViewMode)")"
}
X "$indent"
}
elseif ($type -eq "nestedObject") {
X "$indent"
- if ($item.objectID) { X "$indent`t$(Esc-Xml "$($item.objectID)")" }
+ if ($item.objectID) { X "$indent`t$(Esc-XmlText "$($item.objectID)")" }
X "$indent`t"
$s = $item.settings
if ($s) {
@@ -3523,7 +3529,7 @@ function Emit-SettingsVariants {
foreach ($v in $variants) {
X "`t"
- X "`t`t$(Esc-Xml "$($v.name)")"
+ X "`t`t$(Esc-XmlText "$($v.name)")"
$pres = if ($v.presentation) { $v.presentation } elseif ($v.title) { $v.title } else { "$($v.name)" }
Emit-MLText -tag "dcsset:presentation" -text $pres -indent "`t`t"
@@ -3641,7 +3647,7 @@ function Emit-SettingsVariants {
# on — emit only if explicitly set
if ($s.itemsViewMode) {
- X "`t`t`t$(Esc-Xml "$($s.itemsViewMode)")"
+ X "`t`t`t$(Esc-XmlText "$($s.itemsViewMode)")"
}
# — key/value свойства варианта
@@ -3649,7 +3655,7 @@ function Emit-SettingsVariants {
X "`t`t`t"
foreach ($prop in $s.additionalProperties.PSObject.Properties) {
X "`t`t`t`t"
- X "`t`t`t`t`t$(Esc-Xml "$($prop.Value)")"
+ X "`t`t`t`t`t$(Esc-XmlText "$($prop.Value)")"
X "`t`t`t`t"
}
X "`t`t`t"
diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py
index d11e68db..f737fa62 100644
--- a/.claude/skills/skd-compile/scripts/skd-compile.py
+++ b/.claude/skills/skd-compile/scripts/skd-compile.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# 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
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):
return s.replace('&', '&').replace('<', '<').replace('>', '>')
def fmt_dec(v):
@@ -233,13 +238,13 @@ def emit_mltext(lines, indent, tag, text, no_xsi_type=False):
if isinstance(text, dict):
for lang, content in text.items():
lines.append(f"{indent}\t")
- lines.append(f"{indent}\t\t{esc_xml(str(lang))}")
- lines.append(f"{indent}\t\t{esc_xml(str(content))}")
+ lines.append(f"{indent}\t\t{esc_xml_text(str(lang))}")
+ lines.append(f"{indent}\t\t{esc_xml_text(str(content))}")
lines.append(f"{indent}\t")
else:
lines.append(f"{indent}\t")
lines.append(f"{indent}\t\tru")
- lines.append(f"{indent}\t\t{esc_xml(str(text))}")
+ lines.append(f"{indent}\t\t{esc_xml_text(str(text))}")
lines.append(f"{indent}\t")
lines.append(f"{indent}{tag}>")
@@ -394,20 +399,20 @@ def emit_single_value_type(lines, type_str, indent):
# Reference types: CatalogRef.XXX, DocumentRef.XXX, EnumRef.XXX, etc.
if re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.', type_str):
- lines.append(f'{indent}d5p1:{esc_xml(type_str)}')
+ lines.append(f'{indent}d5p1:{esc_xml_text(type_str)}')
return
# TypeSet (композитный тип-набор): голое имя без точки.
if re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef|InformationRegisterRef|AnyRef)$', type_str):
- lines.append(f'{indent}d5p1:{esc_xml(type_str)}')
+ lines.append(f'{indent}d5p1:{esc_xml_text(type_str)}')
return
# Fallback -- assume dot-qualified types are also config references
if '.' in type_str:
- lines.append(f'{indent}d5p1:{esc_xml(type_str)}')
+ lines.append(f'{indent}d5p1:{esc_xml_text(type_str)}')
return
- lines.append(f'{indent}{esc_xml(type_str)}')
+ lines.append(f'{indent}{esc_xml_text(type_str)}')
# --- Field shorthand parser ---
@@ -787,8 +792,8 @@ OUTPUT_PARAM_TYPES = {
def emit_data_sources(lines, data_sources):
for ds in data_sources:
lines.append('\t')
- lines.append(f'\t\t{esc_xml(ds["name"])}')
- lines.append(f'\t\t{esc_xml(ds["type"])}')
+ lines.append(f'\t\t{esc_xml_text(ds["name"])}')
+ lines.append(f'\t\t{esc_xml_text(ds["type"])}')
lines.append('\t')
@@ -805,7 +810,7 @@ def emit_input_parameters(lines, ip, indent):
lines.append(f'{indent}\t')
if 'use' in item and item['use'] is False:
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 = list(item['choiceParameters']) if item['choiceParameters'] else []
if len(cp_items) == 0:
@@ -814,7 +819,7 @@ def emit_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):
vs = 'true' if v else 'false'
@@ -822,7 +827,7 @@ def emit_input_parameters(lines, ip, indent):
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:
@@ -833,8 +838,8 @@ def emit_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 = cpl.get('mode') or 'Auto'
lines.append(f'{indent}\t\t\t\t{mode}')
lines.append(f'{indent}\t\t\t')
@@ -848,7 +853,7 @@ def emit_input_parameters(lines, ip, indent):
custom_uri = vt_src.get('uri')
custom_name = vt_src.get('name')
if custom_uri and custom_name:
- lines.append(f'{indent}\t\t{esc_xml(str(val))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(val))}')
elif isinstance(val, bool):
vstr = 'true' if val else 'false'
lines.append(f'{indent}\t\t{vstr}')
@@ -858,7 +863,7 @@ def emit_input_parameters(lines, ip, indent):
# Multilang dict {ru, en, ...} → LocalStringType
emit_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}')
@@ -914,15 +919,15 @@ def emit_field(lines, field_def, indent):
# DataSetFieldFolder — только dataPath + title
if f.get('folder'):
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(f["dataPath"])}')
+ lines.append(f'{indent}\t{esc_xml_text(f["dataPath"])}')
if f.get('title'):
emit_mltext(lines, f'{indent}\t', 'title', f['title'])
lines.append(f'{indent}')
return
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(f["dataPath"])}')
- lines.append(f'{indent}\t{esc_xml(f["field"])}')
+ lines.append(f'{indent}\t{esc_xml_text(f["dataPath"])}')
+ lines.append(f'{indent}\t{esc_xml_text(f["field"])}')
# Title
if f.get('title'):
@@ -965,7 +970,7 @@ def emit_field(lines, field_def, indent):
else:
lines.append(f'{indent}\t\ttrue')
for k, v in extras.items():
- lines.append(f'{indent}\t\t{esc_xml(str(v))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(v))}')
lines.append(f'{indent}\t
')
# OrderExpression — после role, до valueType
@@ -978,7 +983,7 @@ def emit_field(lines, field_def, indent):
auto = oe.get('autoOrder', False)
auto_str = 'true' if auto else 'false'
lines.append(f'{indent}\t')
- lines.append(f'{indent}\t\t{esc_xml(expr)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(expr)}')
lines.append(f'{indent}\t\t{o_type}')
lines.append(f'{indent}\t\t{auto_str}')
lines.append(f'{indent}\t')
@@ -1004,7 +1009,7 @@ def emit_field(lines, field_def, indent):
av_type = 'xs:dateTime'
else:
av_type = 'xs:string'
- av_str = str(av_val).lower() if isinstance(av_val, bool) else esc_xml(str(av_val))
+ av_str = str(av_val).lower() if isinstance(av_val, bool) else esc_xml_text(str(av_val))
lines.append(f'{indent}\t\t{av_str}')
if av.get('presentation'):
emit_mltext(lines, f'{indent}\t\t', 'presentation', av['presentation'])
@@ -1017,8 +1022,8 @@ def emit_field(lines, field_def, indent):
# \u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e xsi:type, \u043d\u0435 \u0441\u0442\u0440\u043e\u043a\u0430
if key == '\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435' and not isinstance(val, dict):
lines.append(f'{indent}\t\t')
- lines.append(f'{indent}\t\t\t{esc_xml(key)}')
- lines.append(f'{indent}\t\t\t{esc_xml(str(val))}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(key)}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(str(val))}')
lines.append(f'{indent}\t\t')
else:
emit_appearance_value(lines, key, val, f'{indent}\t\t')
@@ -1026,7 +1031,7 @@ def emit_field(lines, field_def, indent):
# PresentationExpression
if f.get('presentationExpression'):
- lines.append(f'{indent}\t{esc_xml(f["presentationExpression"])}')
+ lines.append(f'{indent}\t{esc_xml_text(f["presentationExpression"])}')
# InputParameters — в конце field
if f.get('inputParameters'):
@@ -1047,7 +1052,7 @@ def emit_data_set(lines, ds, indent, default_source, tag_name='dataSet'):
ds_type = 'DataSetQuery'
lines.append(f'{indent}<{tag_name} xsi:type="{ds_type}">')
- lines.append(f'{indent}\t{esc_xml(str(ds.get("name", "")))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(ds.get("name", "")))}')
# Fields
if ds.get('fields'):
@@ -1057,16 +1062,16 @@ def emit_data_set(lines, ds, indent, default_source, tag_name='dataSet'):
# DataSource (not for Union)
if ds_type != 'DataSetUnion':
src = str(ds['source']) if ds.get('source') else default_source
- lines.append(f'{indent}\t{esc_xml(src)}')
+ lines.append(f'{indent}\t{esc_xml_text(src)}')
# Type-specific content
if ds_type == 'DataSetQuery':
query_text = resolve_query_value(str(ds.get("query", "")), query_base_dir)
- lines.append(f'{indent}\t{esc_xml(query_text)}')
+ lines.append(f'{indent}\t{esc_xml_text(query_text)}')
if ds.get('autoFillFields') is False:
lines.append(f'{indent}\tfalse')
elif ds_type == 'DataSetObject':
- lines.append(f'{indent}\t{esc_xml(str(ds["objectName"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(ds["objectName"]))}')
elif ds_type == 'DataSetUnion':
for item in ds['items']:
# Union inner items are wrapped as -
@@ -1091,18 +1096,18 @@ def emit_data_set_links(lines, defn):
dst_ds = str(link.get('dest') or link.get('destinationDataSet') or '')
src_ex = str(link.get('sourceExpr') or link.get('sourceExpression') or '')
dst_ex = str(link.get('destExpr') or link.get('destinationExpression') or '')
- lines.append(f'\t\t{esc_xml(src_ds)}')
- lines.append(f'\t\t{esc_xml(dst_ds)}')
- lines.append(f'\t\t{esc_xml(src_ex)}')
- lines.append(f'\t\t{esc_xml(dst_ex)}')
+ lines.append(f'\t\t{esc_xml_text(src_ds)}')
+ lines.append(f'\t\t{esc_xml_text(dst_ds)}')
+ lines.append(f'\t\t{esc_xml_text(src_ex)}')
+ lines.append(f'\t\t{esc_xml_text(dst_ex)}')
if link.get('parameter'):
- lines.append(f'\t\t{esc_xml(str(link["parameter"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(link["parameter"]))}')
if link.get('parameterListAllowed'):
lines.append('\t\ttrue')
if link.get('startExpression') is not None:
- lines.append(f'\t\t{esc_xml(str(link["startExpression"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(link["startExpression"]))}')
if link.get('linkConditionExpression') is not None:
- lines.append(f'\t\t{esc_xml(str(link["linkConditionExpression"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(link["linkConditionExpression"]))}')
lines.append('\t')
@@ -1157,8 +1162,8 @@ def emit_calc_fields(lines, defn):
appearance = cf.get('appearance')
lines.append('\t')
- lines.append(f'\t\t{esc_xml(data_path)}')
- lines.append(f'\t\t{esc_xml(expression)}')
+ lines.append(f'\t\t{esc_xml_text(data_path)}')
+ lines.append(f'\t\t{esc_xml_text(expression)}')
if title:
emit_mltext(lines, '\t\t', 'title', title)
@@ -1171,7 +1176,7 @@ def emit_calc_fields(lines, defn):
if restrict_obj:
for xml_name, flag in restrict_obj.items():
if flag:
- lines.append(f'\t\t\t<{esc_xml(str(xml_name))}>true{esc_xml(str(xml_name))}>')
+ lines.append(f'\t\t\t<{esc_xml_text(str(xml_name))}>true{esc_xml_text(str(xml_name))}>')
else:
for r in restrict_tokens:
xml_name = restrict_map.get(str(r))
@@ -1183,8 +1188,8 @@ def emit_calc_fields(lines, defn):
for k, v in appearance.items():
if k == 'ГоризонтальноеПоложение' and not isinstance(v, dict):
lines.append('\t\t\t')
- lines.append(f'\t\t\t\t{esc_xml(k)}')
- lines.append(f'\t\t\t\t{esc_xml(str(v))}')
+ lines.append(f'\t\t\t\t{esc_xml_text(k)}')
+ lines.append(f'\t\t\t\t{esc_xml_text(str(v))}')
lines.append('\t\t\t')
else:
emit_appearance_value(lines, k, v, '\t\t\t')
@@ -1210,14 +1215,14 @@ def emit_total_fields(lines, defn):
groups = tf.get('group')
lines.append('\t')
- lines.append(f'\t\t{esc_xml(parsed["dataPath"])}')
- lines.append(f'\t\t{esc_xml(parsed["expression"])}')
+ lines.append(f'\t\t{esc_xml_text(parsed["dataPath"])}')
+ lines.append(f'\t\t{esc_xml_text(parsed["expression"])}')
if groups:
if isinstance(groups, list):
for g in groups:
- lines.append(f'\t\t{esc_xml(str(g))}')
+ lines.append(f'\t\t{esc_xml_text(str(g))}')
else:
- lines.append(f'\t\t{esc_xml(str(groups))}')
+ lines.append(f'\t\t{esc_xml_text(str(groups))}')
lines.append('\t')
@@ -1283,38 +1288,38 @@ def emit_param_value(lines, type_str, val, indent, value_list_allowed=False):
if type_str == 'StandardPeriod':
# Platform-pattern: startDate/endDate ТОЛЬКО для variant=Custom.
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(val_str)}')
+ lines.append(f'{indent}\t{esc_xml_text(val_str)}')
if val_str == 'Custom':
sd_out = sd_str if sd_str else '0001-01-01T00:00:00'
ed_out = ed_str if ed_str else '0001-01-01T00:00:00'
- lines.append(f'{indent}\t{esc_xml(sd_out)}')
- lines.append(f'{indent}\t{esc_xml(ed_out)}')
+ lines.append(f'{indent}\t{esc_xml_text(sd_out)}')
+ lines.append(f'{indent}\t{esc_xml_text(ed_out)}')
lines.append(f'{indent}')
elif type_str and re.match(r'^date', type_str):
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
elif type_str == 'boolean':
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
elif type_str and re.match(r'^decimal', type_str):
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
elif type_str and re.match(r'^string', type_str):
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
elif type_str and re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.', type_str):
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
else:
# Guess from value
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 == 'true' or val_str == 'false':
- lines.append(f'{indent}{esc_xml(val_str)}')
+ lines.append(f'{indent}{esc_xml_text(val_str)}')
elif 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_single_param(lines, p, parsed):
lines.append('\t')
- lines.append(f'\t\t{esc_xml(parsed["name"])}')
+ lines.append(f'\t\t{esc_xml_text(parsed["name"])}')
# Title (from parsed first, then from object form; accept `presentation` as
# a synonym — 1C UI labels a parameter's caption "Представление").
@@ -1368,7 +1373,7 @@ def emit_single_param(lines, p, parsed):
# Expression
if parsed.get('expression'):
- lines.append(f'\t\t{esc_xml(parsed["expression"])}')
+ lines.append(f'\t\t{esc_xml_text(parsed["expression"])}')
if parsed.get('hidden'):
parsed['availableAsField'] = False
@@ -1397,7 +1402,7 @@ def emit_single_param(lines, p, parsed):
av_type = 'xs:string'
if re.match(r'^(Перечисление|Справочник|ПланСчетов|Документ|ПланВидовХарактеристик|ПланВидовРасчета)\.', av_val):
av_type = 'dcscor:DesignTimeValue'
- lines.append(f'\t\t\t{esc_xml(av_val)}')
+ lines.append(f'\t\t\t{esc_xml_text(av_val)}')
# `title` accepted as synonym of `presentation` — both map to the same UI label.
av_pres = av.get('presentation') or av.get('title') or ''
if av_pres:
@@ -1417,7 +1422,7 @@ def emit_single_param(lines, p, parsed):
elif parsed.get('use'):
use_val = str(parsed['use'])
if use_val:
- lines.append(f'\t\t')
+ lines.append(f'\t\t')
# InputParameters на параметре (ФорматРедактирования и т.п.)
if p is not None and not isinstance(p, str) and p.get('inputParameters'):
@@ -1582,7 +1587,7 @@ def _emit_color_value(lines, color, indent):
name = color[len(pfx):]
lines.append(f'{indent}d8p1:{name}')
return
- lines.append(f'{indent}{esc_xml(color)}')
+ lines.append(f'{indent}{esc_xml_text(color)}')
def _emit_cell_appearance(lines, style, width=0, v_merge=False, h_merge=False, min_height=0, extra_items=None):
@@ -1642,13 +1647,13 @@ def _emit_cell_appearance(lines, style, width=0, v_merge=False, h_merge=False, m
if style.get('hAlign'):
lines.append(f'{ind}')
lines.append(f'{ind}\t\u0413\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435')
- lines.append(f'{ind}\t{esc_xml(style["hAlign"])}')
+ lines.append(f'{ind}\t{esc_xml_text(style["hAlign"])}')
lines.append(f'{ind}')
# Vertical alignment
if style.get('vAlign'):
lines.append(f'{ind}')
lines.append(f'{ind}\t\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0435\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435')
- lines.append(f'{ind}\t{esc_xml(style["vAlign"])}')
+ lines.append(f'{ind}\t{esc_xml_text(style["vAlign"])}')
lines.append(f'{ind}')
# Wrap
if style.get('wrap'):
@@ -1755,7 +1760,7 @@ def _emit_area_template_dsl(lines, t):
drilldown_map[str(tp['name'])] = dd
lines.append('\t')
- lines.append(f'\t\t{esc_xml(str(t["name"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(t["name"]))}')
lines.append('\t\t')
for r in range(len(rows)):
@@ -1790,7 +1795,7 @@ def _emit_area_template_dsl(lines, t):
if m:
param_name = m.group(1)
lines.append('\t\t\t\t\t')
- lines.append(f'\t\t\t\t\t\t{esc_xml(param_name)}')
+ lines.append(f'\t\t\t\t\t\t{esc_xml_text(param_name)}')
lines.append('\t\t\t\t\t')
# Build drilldown appearance extra items.
# \u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: per-cell override (cell={value, drilldown}) \u2192 drilldownMap (shortcut form B).
@@ -1805,7 +1810,7 @@ def _emit_area_template_dsl(lines, t):
if dd_target:
cell_extra_items.append('\t\t\t\t\t\t')
cell_extra_items.append(f'\t\t\t\t\t\t\t\u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430')
- cell_extra_items.append(f'\t\t\t\t\t\t\t{esc_xml(dd_target)}')
+ cell_extra_items.append(f'\t\t\t\t\t\t\t{esc_xml_text(dd_target)}')
cell_extra_items.append('\t\t\t\t\t\t')
else:
lines.append('\t\t\t\t\t')
@@ -1835,27 +1840,27 @@ def _emit_area_template_parameter(lines, tp, indent):
dd_expr = str(dd.get('expression', ''))
dd_act = str(dd.get('action') or 'DrillDown')
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(str(tp["name"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(tp["name"]))}')
lines.append(f'{indent}\t')
- lines.append(f'{indent}\t\t{esc_xml(dd_field)}')
- lines.append(f'{indent}\t\t{esc_xml(dd_expr)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(dd_field)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(dd_expr)}')
lines.append(f'{indent}\t')
- lines.append(f'{indent}\t{esc_xml(dd_act)}')
+ lines.append(f'{indent}\t{esc_xml_text(dd_act)}')
lines.append(f'{indent}')
return
# \u0424\u043e\u0440\u043c\u0430 A \u0438\u043b\u0438 B
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(str(tp["name"]))}')
- lines.append(f'{indent}\t{esc_xml(str(tp.get("expression", "")))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(tp["name"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(tp.get("expression", "")))}')
lines.append(f'{indent}')
if dd and isinstance(dd, str):
# \u0424\u043e\u0440\u043c\u0430 B: shortcut \u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430_ + \u0418\u043c\u044f\u0420\u0435\u0441\u0443\u0440\u0441\u0430 + DrillDown
dd_val = dd
lines.append(f'{indent}')
- lines.append(f'{indent}\t\u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430_{esc_xml(dd_val)}')
+ lines.append(f'{indent}\t\u0420\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u0430_{esc_xml_text(dd_val)}')
lines.append(f'{indent}\t')
lines.append(f'{indent}\t\t\u0418\u043c\u044f\u0420\u0435\u0441\u0443\u0440\u0441\u0430')
- lines.append(f'{indent}\t\t"{esc_xml(dd_val)}"')
+ lines.append(f'{indent}\t\t"{esc_xml_text(dd_val)}"')
lines.append(f'{indent}\t')
lines.append(f'{indent}\tDrillDown')
lines.append(f'{indent}')
@@ -1871,7 +1876,7 @@ def emit_templates(lines, defn):
_emit_area_template_dsl(lines, t)
else:
lines.append('\t')
- lines.append(f'\t\t{esc_xml(str(t["name"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(t["name"]))}')
if t.get('template'):
lines.append(f'\t\t{t["template"]}')
if t.get('parameters'):
@@ -1888,8 +1893,8 @@ def emit_field_templates(lines, defn):
return
for ft in defn['fieldTemplates']:
lines.append('\t')
- lines.append(f'\t\t{esc_xml(str(ft["field"]))}')
- lines.append(f'\t\t{esc_xml(str(ft["template"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(ft["field"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(ft["template"]))}')
lines.append('\t')
@@ -1906,11 +1911,11 @@ def emit_group_templates(lines, defn):
lines.append(f'\t<{tag}>')
if gt.get('groupName'):
- lines.append(f'\t\t{esc_xml(str(gt["groupName"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(gt["groupName"]))}')
elif gt.get('groupField'):
- lines.append(f'\t\t{esc_xml(str(gt["groupField"]))}')
- lines.append(f'\t\t{esc_xml(xml_ttype)}')
- lines.append(f'\t\t{esc_xml(str(gt["template"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(gt["groupField"]))}')
+ lines.append(f'\t\t{esc_xml_text(xml_ttype)}')
+ lines.append(f'\t\t{esc_xml_text(str(gt["template"]))}')
lines.append(f'\t{tag}>')
@@ -1922,7 +1927,7 @@ def emit_selection_item(lines, item, indent):
lines.append(f'{indent}')
else:
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(item)}')
+ lines.append(f'{indent}\t{esc_xml_text(item)}')
lines.append(f'{indent}')
return
# Object form: { auto: true, use: false } — отключённый Auto в selection
@@ -1935,23 +1940,23 @@ def emit_selection_item(lines, item, indent):
if 'folder' in item:
lines.append(f'{indent}')
if item.get('field'):
- lines.append(f'{indent}\t{esc_xml(str(item["field"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["field"]))}')
emit_mltext(lines, f'{indent}\t', 'dcsset:lwsTitle', item['folder'], no_xsi_type=True)
for sub in (item.get('items') or []):
emit_selection_item(lines, sub, f'{indent}\t')
pl = str(item.get('placement') or 'Auto')
- lines.append(f'{indent}\t{esc_xml(pl)}')
+ lines.append(f'{indent}\t{esc_xml_text(pl)}')
lines.append(f'{indent}')
return
# field with optional title / use=false / viewMode
lines.append(f'{indent}')
if item.get('use') is False:
lines.append(f'{indent}\tfalse')
- lines.append(f'{indent}\t{esc_xml(str(item["field"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["field"]))}')
if item.get('title'):
emit_mltext(lines, f'{indent}\t', 'dcsset:lwsTitle', item['title'], no_xsi_type=True)
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"]))}')
lines.append(f'{indent}')
@@ -1966,10 +1971,10 @@ def emit_selection(lines, items, indent, skip_auto=False, block_view_mode=None,
continue
emit_selection_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)}')
lines.append(f'{indent}')
@@ -1999,10 +2004,10 @@ def emit_filter_item(lines, item, indent):
if item.get('presentation'):
emit_mltext(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_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation'])
lines.append(f'{indent}')
@@ -2014,10 +2019,10 @@ def emit_filter_item(lines, item, indent):
if item.get('use') is False:
lines.append(f'{indent}\tfalse')
- lines.append(f'{indent}\t{esc_xml(str(item["field"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["field"]))}')
comp_type = COMPARISON_TYPES.get(str(item.get('op', '')), str(item.get('op', '')))
- lines.append(f'{indent}\t{esc_xml(comp_type)}')
+ lines.append(f'{indent}\t{esc_xml_text(comp_type)}')
# Right value: один, несколько (InList) или ValueListType (пустой list-placeholder)
val = item.get('value')
@@ -2045,7 +2050,7 @@ def emit_filter_item(lines, item, indent):
vt = 'dcscor:DesignTimeValue'
else:
vt = 'xs:string'
- 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))
lines.append(f'{indent}\t{v_str}')
elif val is not None:
vt = str(item.get('valueType', '')) if item.get('valueType') else ''
@@ -2064,18 +2069,18 @@ def emit_filter_item(lines, item, indent):
if isinstance(val, bool):
v_str = str(val).lower()
else:
- v_str = esc_xml(str(val))
+ v_str = esc_xml_text(str(val))
lines.append(f'{indent}\t{v_str}')
if item.get('presentation'):
emit_mltext(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_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item["userSettingPresentation"])
@@ -2111,10 +2116,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)}')
lines.append(f'{indent}')
@@ -2139,7 +2144,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:
@@ -2156,16 +2161,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["field"]))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(item["field"]))}')
lines.append(f'{indent}\t\t{d}')
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)}')
lines.append(f'{indent}')
@@ -2190,7 +2195,7 @@ def emit_appearance_value(lines, key, val, indent):
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)}')
# Line dict ({@type: "Line", width, gap, style}) \u2192
if isinstance(inner_val, dict) and inner_val.get('@type') == 'Line':
@@ -2198,7 +2203,7 @@ def emit_appearance_value(lines, key, val, indent):
lg = 'true' if inner_val.get('gap') else 'false'
ls = str(inner_val.get('style', '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')
# Font dict ({@type: "Font", ref, faceName, height, bold, ...}) \u2192
elif isinstance(inner_val, dict) and inner_val.get('@type') == 'Font':
@@ -2222,11 +2227,11 @@ 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):
# Внутри префиксы style:/web:/win:/sys: уже объявлены на корне,
# локальный xmlns не нужен — эмитим short form.
- 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 in ('\u0422\u0435\u043a\u0441\u0442', '\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a', '\u0424\u043e\u0440\u043c\u0430\u0442'):
@@ -2234,9 +2239,9 @@ def emit_appearance_value(lines, key, val, indent):
elif re.match(r'^-?\d+(\.\d+)?$', actual_val):
lines.append(f'{indent}\t{actual_val}')
elif key in ('\u0426\u0432\u0435\u0442\u0422\u0435\u043a\u0441\u0442\u0430', '\u0426\u0432\u0435\u0442\u0424\u043e\u043d\u0430', '\u0426\u0432\u0435\u0442\u0413\u0440\u0430\u043d\u0438\u0446\u044b'):
- 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)}')
# Nested SettingsParameterValue items (СтильГраницы.Сверху/.Снизу/.Слева/.Справа).
if nested_items and isinstance(nested_items, dict):
for nk, nv in nested_items.items():
@@ -2262,7 +2267,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:
@@ -2288,15 +2293,15 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc
if isinstance(ca['presentation'], dict):
emit_mltext(lines, f'{indent}\t\t', 'dcsset:presentation', ca['presentation'])
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"]))}')
# UserSettingID
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_mltext(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', ca['userSettingPresentation'])
@@ -2314,10 +2319,10 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc
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)}')
lines.append(f'{indent}')
@@ -2354,7 +2359,7 @@ def emit_output_parameters(lines, params, indent):
lines.append(f'{indent}\t')
if use_false:
lines.append(f'{indent}\t\tfalse')
- lines.append(f'{indent}\t\t{esc_xml(key)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(key)}')
if is_font_dict:
attr_parts = []
for attr_name in ('ref', 'faceName', 'height', 'bold', 'italic', 'underline', 'strikeout', 'kind', 'scale'):
@@ -2364,7 +2369,7 @@ def emit_output_parameters(lines, params, indent):
elif ptype == 'mltext':
emit_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))}')
# Nested sub-параметры (ТипДиаграммы.ВидПодписей и т.п.).
# valueType: строка → xsi:type=string, объект {uri, name} → локальный xmlns:dN.
if wrap_items and isinstance(wrap_items, dict):
@@ -2389,17 +2394,17 @@ def emit_output_parameters(lines, params, indent):
lines.append(f'{indent}\t\t')
if sub_use_false:
lines.append(f'{indent}\t\t\tfalse')
- lines.append(f'{indent}\t\t\t{esc_xml(sub_name)}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(sub_name)}')
if sub_uri:
- lines.append(f'{indent}\t\t\t{esc_xml(str(sub_val))}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(str(sub_val))}')
else:
- lines.append(f'{indent}\t\t\t{esc_xml(str(sub_val))}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(str(sub_val))}')
lines.append(f'{indent}\t\t')
if wrap_vm:
- lines.append(f'{indent}\t\t{esc_xml(str(wrap_vm))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(wrap_vm))}')
if wrap_usid:
uid = new_uuid() if str(wrap_usid) == 'auto' else str(wrap_usid)
- lines.append(f'{indent}\t\t{esc_xml(uid)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(uid)}')
if wrap_usp:
emit_mltext(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', wrap_usp)
lines.append(f'{indent}\t')
@@ -2432,7 +2437,7 @@ def emit_data_parameters(lines, items, indent):
if dp.get('use') is False:
lines.append(f'{indent}\t\tfalse')
- lines.append(f'{indent}\t\t{esc_xml(str(dp["parameter"]))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(dp["parameter"]))}')
# Value
if dp.get('nilValue') is True:
@@ -2453,45 +2458,45 @@ def emit_data_parameters(lines, items, indent):
is_sbd = has_date or (not has_sd and variant_str.startswith('BeginningOf'))
if is_sbd:
lines.append(f'{indent}\t\t')
- lines.append(f'{indent}\t\t\t{esc_xml(variant_str)}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(variant_str)}')
if variant_str == '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:
# StandardPeriod — platform-pattern: startDate/endDate ТОЛЬКО для variant=Custom.
lines.append(f'{indent}\t\t')
- lines.append(f'{indent}\t\t\t{esc_xml(variant_str)}')
+ lines.append(f'{indent}\t\t\t{esc_xml_text(variant_str)}')
if variant_str == '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):
# Полный xsi:type из decompile (например "xs:boolean", "dcscor:DesignTimeValue").
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):
bv = str(val).lower()
- lines.append(f'{indent}\t\t{esc_xml(bv)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(bv)}')
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'^(\u041f\u043b\u0430\u043d\u0421\u0447\u0435\u0442\u043e\u0432|\u0421\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a|\u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435|\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u041f\u043b\u0430\u043d\u0412\u0438\u0434\u043e\u0432\u0425\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a|\u041f\u043b\u0430\u043d\u0412\u0438\u0434\u043e\u0432\u0420\u0430\u0441\u0447\u0435\u0442\u0430|\u0411\u0438\u0437\u043d\u0435\u0441\u041f\u0440\u043e\u0446\u0435\u0441\u0441|\u0417\u0430\u0434\u0430\u0447\u0430|\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0421\u0432\u0435\u0434\u0435\u043d\u0438\u0439|\u041f\u043b\u0430\u043d\u041e\u0431\u043c\u0435\u043d\u0430)\.', 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_mltext(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', dp["userSettingPresentation"])
@@ -2513,7 +2518,7 @@ def emit_group_items(lines, group_by, indent):
lines.append(f'{indent}\t')
continue
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\tItems')
lines.append(f'{indent}\t\tNone')
lines.append(f'{indent}\t\t0001-01-01T00:00:00')
@@ -2521,18 +2526,18 @@ def emit_group_items(lines, group_by, indent):
lines.append(f'{indent}\t')
else:
lines.append(f'{indent}\t')
- lines.append(f'{indent}\t\t{esc_xml(str(field["field"]))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(field["field"]))}')
gt = str(field.get('groupType', 'Items'))
- lines.append(f'{indent}\t\t{esc_xml(gt)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(gt)}')
pat = str(field.get('periodAdditionType', 'None'))
- lines.append(f'{indent}\t\t{esc_xml(pat)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(pat)}')
# Auto-detect: ISO date → xs:dateTime, иначе → dcscor:Field (path).
pab = str(field.get('periodAdditionBegin', '0001-01-01T00:00:00'))
pae = str(field.get('periodAdditionEnd', '0001-01-01T00:00:00'))
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\t{esc_xml(pab)}')
- lines.append(f'{indent}\t\t{esc_xml(pae)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(pab)}')
+ lines.append(f'{indent}\t\t{esc_xml_text(pae)}')
lines.append(f'{indent}\t')
lines.append(f'{indent}')
@@ -2577,7 +2582,7 @@ def emit_user_fields(lines, items, indent):
u_type = 'UserFieldCase' if uf.get('cases') is not None else 'UserFieldExpression'
lines.append(f'{indent}\t')
if uf.get('dataPath'):
- lines.append(f'{indent}\t\t{esc_xml(str(uf["dataPath"]))}')
+ lines.append(f'{indent}\t\t{esc_xml_text(str(uf["dataPath"]))}')
if uf.get('title'):
emit_mltext(lines, f'{indent}\t\t', 'dcsset:lwsTitle', uf['title'], no_xsi_type=True)
if u_type == 'UserFieldExpression':
@@ -2585,18 +2590,18 @@ def emit_user_fields(lines, items, indent):
if d is not None:
if 'expression' in d:
v = str(d['expression'])
- lines.append(f'{indent}\t\t{esc_xml(v)}' if v else f'{indent}\t\t')
+ lines.append(f'{indent}\t\t{esc_xml_text(v)}' if v else f'{indent}\t\t')
if 'presentation' in d:
v = str(d['presentation'])
- lines.append(f'{indent}\t\t{esc_xml(v)}' if v else f'{indent}\t\t')
+ lines.append(f'{indent}\t\t{esc_xml_text(v)}' if v else f'{indent}\t\t')
t = uf.get('total')
if t is not None:
if 'expression' in t:
v = str(t['expression'])
- lines.append(f'{indent}\t\t{esc_xml(v)}' if v else f'{indent}\t\t')
+ lines.append(f'{indent}\t\t{esc_xml_text(v)}' if v else f'{indent}\t\t')
if 'presentation' in t:
v = str(t['presentation'])
- lines.append(f'{indent}\t\t{esc_xml(v)}' if v else f'{indent}\t\t')
+ lines.append(f'{indent}\t\t{esc_xml_text(v)}' if v else f'{indent}\t\t')
else:
cases = uf.get('cases') or []
if len(cases) == 0:
@@ -2614,7 +2619,7 @@ def emit_user_fields(lines, items, indent):
elif isinstance(cv, (int, float)):
lines.append(f'{indent}\t\t\t\t{cv}')
else:
- lines.append(f'{indent}\t\t\t\t{esc_xml(str(cv))}')
+ lines.append(f'{indent}\t\t\t\t{esc_xml_text(str(cv))}')
if c.get('presentation'):
emit_mltext(lines, f'{indent}\t\t\t\t', 'dcsset:lwsPresentationValue', c['presentation'], no_xsi_type=True)
lines.append(f'{indent}\t\t\t')
@@ -2631,7 +2636,7 @@ def emit_table_axis_block(lines, block, indent, emit_name=True):
presence in JSON.
"""
if emit_name and block.get('name'):
- lines.append(f'{indent}{esc_xml(str(block["name"]))}')
+ lines.append(f'{indent}{esc_xml_text(str(block["name"]))}')
gb = block.get('groupBy') or block.get('groupFields')
emit_group_items(lines, gb, indent)
if block.get('filter'):
@@ -2653,14 +2658,14 @@ def emit_table_axis_block(lines, block, indent, emit_name=True):
for child in block['children']:
emit_structure_item(lines, child, indent, short_group=True)
if block.get('viewMode'):
- lines.append(f'{indent}{esc_xml(str(block["viewMode"]))}')
+ lines.append(f'{indent}{esc_xml_text(str(block["viewMode"]))}')
if block.get('userSettingID'):
uid = new_uuid() if str(block['userSettingID']) == 'auto' else str(block['userSettingID'])
- lines.append(f'{indent}{esc_xml(uid)}')
+ lines.append(f'{indent}{esc_xml_text(uid)}')
if block.get('userSettingPresentation'):
emit_mltext(lines, indent, 'dcsset:userSettingPresentation', block['userSettingPresentation'])
if block.get('itemsViewMode'):
- lines.append(f'{indent}{esc_xml(str(block["itemsViewMode"]))}')
+ lines.append(f'{indent}{esc_xml_text(str(block["itemsViewMode"]))}')
def emit_structure_item(lines, item, indent, short_group=False):
@@ -2678,7 +2683,7 @@ def emit_structure_item(lines, item, indent, short_group=False):
lines.append(f'{indent}\tfalse')
if item.get('name'):
- lines.append(f'{indent}\t{esc_xml(str(item["name"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["name"]))}')
emit_group_items(lines, item.get('groupBy') or item.get('groupFields'), f'{indent}\t')
@@ -2705,14 +2710,14 @@ def emit_structure_item(lines, item, indent, short_group=False):
# viewMode/itemsViewMode/userSettingID/userSettingPresentation — context-dependent
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'):
gid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID'])
- lines.append(f'{indent}\t{esc_xml(gid)}')
+ lines.append(f'{indent}\t{esc_xml_text(gid)}')
if item.get('userSettingPresentation'):
emit_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation'])
if item.get('itemsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["itemsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["itemsViewMode"]))}')
lines.append(f'{indent}')
@@ -2724,7 +2729,7 @@ def emit_structure_item(lines, item, indent, short_group=False):
lines.append(f'{indent}\tfalse')
if item.get('name'):
- lines.append(f'{indent}\t{esc_xml(str(item["name"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["name"]))}')
# Columns
if item.get('columns'):
@@ -2749,19 +2754,19 @@ def emit_structure_item(lines, item, indent, short_group=False):
emit_output_parameters(lines, item['outputParameters'], f'{indent}\t')
# columnsViewMode / rowsViewMode — axis-level режим доступности
if item.get('columnsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["columnsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["columnsViewMode"]))}')
if item.get('rowsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["rowsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["rowsViewMode"]))}')
# viewMode / userSettingID / userSettingPresentation / itemsViewMode на самой таблице
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'):
gid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID'])
- lines.append(f'{indent}\t{esc_xml(gid)}')
+ lines.append(f'{indent}\t{esc_xml_text(gid)}')
if item.get('userSettingPresentation'):
emit_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation'])
if item.get('itemsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["itemsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["itemsViewMode"]))}')
lines.append(f'{indent}')
@@ -2773,7 +2778,7 @@ def emit_structure_item(lines, item, indent, short_group=False):
lines.append(f'{indent}\tfalse')
if item.get('name'):
- lines.append(f'{indent}\t{esc_xml(str(item["name"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["name"]))}')
# Points — single object или массив (multi-series диаграмма)
pts = item.get('points')
@@ -2803,26 +2808,26 @@ def emit_structure_item(lines, item, indent, short_group=False):
# pointsViewMode / seriesViewMode — axis-level режим доступности
if item.get('pointsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["pointsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["pointsViewMode"]))}')
if item.get('seriesViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["seriesViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["seriesViewMode"]))}')
# viewMode / userSettingID / userSettingPresentation / itemsViewMode на самой диаграмме
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'):
gid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID'])
- lines.append(f'{indent}\t{esc_xml(gid)}')
+ lines.append(f'{indent}\t{esc_xml_text(gid)}')
if item.get('userSettingPresentation'):
emit_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation'])
if item.get('itemsViewMode'):
- lines.append(f'{indent}\t{esc_xml(str(item["itemsViewMode"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["itemsViewMode"]))}')
lines.append(f'{indent}')
elif item_type == 'nestedObject':
lines.append(f'{indent}')
if item.get('objectID'):
- lines.append(f'{indent}\t{esc_xml(str(item["objectID"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(item["objectID"]))}')
lines.append(f'{indent}\t')
s = item.get('settings') or {}
if s.get('selection'): emit_selection(lines, s['selection'], f'{indent}\t\t')
@@ -2854,7 +2859,7 @@ def emit_settings_variants(lines, defn):
for v in variants:
lines.append('\t')
- lines.append(f'\t\t{esc_xml(str(v["name"]))}')
+ lines.append(f'\t\t{esc_xml_text(str(v["name"]))}')
pres = v.get('presentation') or v.get('title') or v['name']
emit_mltext(lines, '\t\t', 'dcsset:presentation', pres)
@@ -2955,14 +2960,14 @@ def emit_settings_variants(lines, defn):
# on settings — emit only if explicitly set
if s.get('itemsViewMode'):
- lines.append(f'\t\t\t{esc_xml(str(s["itemsViewMode"]))}')
+ lines.append(f'\t\t\t{esc_xml_text(str(s["itemsViewMode"]))}')
# — key/value свойства варианта
if s.get('additionalProperties'):
lines.append('\t\t\t')
for k, v in s['additionalProperties'].items():
lines.append(f'\t\t\t\t')
- lines.append(f'\t\t\t\t\t{esc_xml(str(v))}')
+ lines.append(f'\t\t\t\t\t{esc_xml_text(str(v))}')
lines.append('\t\t\t\t')
lines.append('\t\t\t')
diff --git a/.claude/skills/skd-edit/scripts/skd-edit.ps1 b/.claude/skills/skd-edit/scripts/skd-edit.ps1
index 366629f9..7c0d02b8 100644
--- a/.claude/skills/skd-edit/scripts/skd-edit.ps1
+++ b/.claude/skills/skd-edit/scripts/skd-edit.ps1
@@ -1,4 +1,4 @@
-# skd-edit v1.34 — Atomic 1C DCS editor (+resolve_type_str: срезание префикса cfg:/d5p1:)
+# skd-edit v1.35 — Atomic 1C DCS editor (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# NB: парный .py собирает выражения автодат вне f-string ради совместимости с python 3.9 (PEP 701).
param(
@@ -183,6 +183,12 @@ function Assert-EditAllowed([string]$targetPath, [string]$require) {
Assert-EditAllowed $resolvedPath 'editable'
function Esc-Xml {
+ param([string]$s)
+ # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна.
+ return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"')
+}
+
+function Esc-XmlText {
param([string]$s)
return $s.Replace('&','&').Replace('<','<').Replace('>','>')
}
@@ -1008,16 +1014,16 @@ function Build-ValueTypeXml {
}
if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.') {
- $lines += "$indentd5p1:$(Esc-Xml $typeStr)"
+ $lines += "$indentd5p1:$(Esc-XmlText $typeStr)"
return $lines -join "`n"
}
if ($typeStr.Contains('.')) {
- $lines += "$indentd5p1:$(Esc-Xml $typeStr)"
+ $lines += "$indentd5p1:$(Esc-XmlText $typeStr)"
return $lines -join "`n"
}
- $lines += "$indent$(Esc-Xml $typeStr)"
+ $lines += "$indent$(Esc-XmlText $typeStr)"
return $lines -join "`n"
}
@@ -1073,7 +1079,7 @@ function Build-MLTextXml {
$lines += "$indent<$tag xsi:type=`"v8:LocalStringType`">"
$lines += "$indent`t"
$lines += "$indent`t`tru"
- $lines += "$indent`t`t$(Esc-Xml $text)"
+ $lines += "$indent`t`t$(Esc-XmlText $text)"
$lines += "$indent`t"
$lines += "$indent$tag>"
return $lines -join "`n"
@@ -1085,7 +1091,7 @@ function Build-MLTextXml {
# If no ru item exists, one is prepended before the first existing item.
function Patch-MLTextRu {
param([string]$rawOuterXml, [string]$newRuText, [string]$indent)
- $escaped = Esc-Xml $newRuText
+ $escaped = Esc-XmlText $newRuText
$ruItemPat = '(\s*ru\s*)[^<]*(\s*)'
if ([regex]::IsMatch($rawOuterXml, $ruItemPat)) {
return [regex]::Replace($rawOuterXml, $ruItemPat, { param($m) $m.Groups[1].Value + $escaped + $m.Groups[2].Value })
@@ -1148,8 +1154,8 @@ function Build-FieldFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.dataPath)"
- $lines += "$i`t$(Esc-Xml $parsed.field)"
+ $lines += "$i`t$(Esc-XmlText $parsed.dataPath)"
+ $lines += "$i`t$(Esc-XmlText $parsed.field)"
# Title: prefer raw multi-lang title (preserves en/uk/etc.). When shorthand provides
# a new ru text, patch ru content inside the raw title; otherwise emit raw as-is.
@@ -1199,8 +1205,8 @@ function Build-TotalFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.dataPath)"
- $lines += "$i`t$(Esc-Xml $parsed.expression)"
+ $lines += "$i`t$(Esc-XmlText $parsed.dataPath)"
+ $lines += "$i`t$(Esc-XmlText $parsed.expression)"
$lines += "$i"
return $lines -join "`n"
}
@@ -1211,8 +1217,8 @@ function Build-CalcFieldFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.dataPath)"
- $lines += "$i`t$(Esc-Xml $parsed.expression)"
+ $lines += "$i`t$(Esc-XmlText $parsed.dataPath)"
+ $lines += "$i`t$(Esc-XmlText $parsed.expression)"
if ($parsed.title) {
$lines += (Build-MLTextXml -tag "title" -text $parsed.title -indent "$i`t")
@@ -1244,7 +1250,7 @@ function Build-ParamValueXml {
if ($type -eq "StandardPeriod") {
$lines += "$i<$open xsi:type=`"v8:StandardPeriod`">"
- $lines += "$i`t$(Esc-Xml $valStr)"
+ $lines += "$i`t$(Esc-XmlText $valStr)"
$lines += "$i`t0001-01-01T00:00:00"
$lines += "$i`t0001-01-01T00:00:00"
$lines += "$i$open>"
@@ -1270,7 +1276,7 @@ function Build-ParamValueXml {
else { $xsi = "xs:string" }
}
- $lines += "$i<$open xsi:type=`"$xsi`">$(Esc-Xml $valStr)$open>"
+ $lines += "$i<$open xsi:type=`"$xsi`">$(Esc-XmlText $valStr)$open>"
return $lines
}
@@ -1291,7 +1297,7 @@ function Build-AvailableValueFragment {
$lines += "$indent`t"
$lines += "$indent`t`t"
$lines += "$indent`t`t`tru"
- $lines += "$indent`t`t`t$(Esc-Xml $item.presentation)"
+ $lines += "$indent`t`t`t$(Esc-XmlText $item.presentation)"
$lines += "$indent`t`t"
$lines += "$indent`t"
}
@@ -1307,7 +1313,7 @@ function Build-ParamFragment {
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.name)"
+ $lines += "$i`t$(Esc-XmlText $parsed.name)"
if ($parsed.title) {
$lines += (Build-MLTextXml -tag "title" -text $parsed.title -indent "$i`t")
@@ -1373,7 +1379,7 @@ function Build-ParamFragment {
$bLines += "$i`t"
$bLines += "$i`t0001-01-01T00:00:00"
$bLines += "$i`ttrue"
- $bLines += "$i`t$(Esc-Xml "&$paramName.ДатаНачала")"
+ $bLines += "$i`t$(Esc-XmlText "&$paramName.ДатаНачала")"
$bLines += "$i"
$fragments += ($bLines -join "`n")
@@ -1386,7 +1392,7 @@ function Build-ParamFragment {
$eLines += "$i`t"
$eLines += "$i`t0001-01-01T00:00:00"
$eLines += "$i`ttrue"
- $eLines += "$i`t$(Esc-Xml "&$paramName.ДатаОкончания")"
+ $eLines += "$i`t$(Esc-XmlText "&$paramName.ДатаОкончания")"
$eLines += "$i"
$fragments += ($eLines -join "`n")
}
@@ -1405,21 +1411,21 @@ function Build-FilterItemFragment {
$lines += "$i`tfalse"
}
- $lines += "$i`t$(Esc-Xml $parsed.field)"
- $lines += "$i`t$(Esc-Xml $parsed.op)"
+ $lines += "$i`t$(Esc-XmlText $parsed.field)"
+ $lines += "$i`t$(Esc-XmlText $parsed.op)"
if ($null -ne $parsed.value) {
$vt = if ($parsed["valueType"]) { $parsed["valueType"] } else { "xs:string" }
- $lines += "$i`t$(Esc-Xml "$($parsed.value)")"
+ $lines += "$i`t$(Esc-XmlText "$($parsed.value)")"
}
if ($parsed.viewMode) {
- $lines += "$i`t$(Esc-Xml $parsed.viewMode)"
+ $lines += "$i`t$(Esc-XmlText $parsed.viewMode)"
}
if ($parsed.userSettingID) {
$uid = if ($parsed.userSettingID -eq "auto") { [System.Guid]::NewGuid().ToString() } else { $parsed.userSettingID }
- $lines += "$i`t$(Esc-Xml $uid)"
+ $lines += "$i`t$(Esc-XmlText $uid)"
}
$lines += "$i"
@@ -1448,20 +1454,20 @@ function Build-SelectionItemFragment {
$lines += "$i`t"
$lines += "$i`t`t"
$lines += "$i`t`t`tru"
- $lines += "$i`t`t`t$(Esc-Xml $title)"
+ $lines += "$i`t`t`t$(Esc-XmlText $title)"
$lines += "$i`t`t"
$lines += "$i`t"
}
foreach ($item in $items) {
$lines += "$i`t"
- $lines += "$i`t`t$(Esc-Xml $item)"
+ $lines += "$i`t`t$(Esc-XmlText $item)"
$lines += "$i`t"
}
$lines += "$i`tAuto"
$lines += "$i"
} else {
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $fieldName)"
+ $lines += "$i`t$(Esc-XmlText $fieldName)"
$lines += "$i"
}
return $lines -join "`n"
@@ -1478,33 +1484,33 @@ function Build-DataParamFragment {
$lines += "$i`tfalse"
}
- $lines += "$i`t$(Esc-Xml $parsed.parameter)"
+ $lines += "$i`t$(Esc-XmlText $parsed.parameter)"
if ($null -ne $parsed.value) {
if ($parsed.value -is [hashtable] -and $parsed.value.variant) {
$lines += "$i`t"
- $lines += "$i`t`t$(Esc-Xml $parsed.value.variant)"
+ $lines += "$i`t`t$(Esc-XmlText $parsed.value.variant)"
$lines += "$i`t`t0001-01-01T00:00:00"
$lines += "$i`t`t0001-01-01T00:00:00"
$lines += "$i`t"
} elseif (Test-EmptyValue $parsed.value) {
$lines += "$i`t"
} elseif ("$($parsed.value)" -match '^\d{4}-\d{2}-\d{2}T') {
- $lines += "$i`t$(Esc-Xml "$($parsed.value)")"
+ $lines += "$i`t$(Esc-XmlText "$($parsed.value)")"
} elseif ("$($parsed.value)" -eq "true" -or "$($parsed.value)" -eq "false") {
- $lines += "$i`t$(Esc-Xml "$($parsed.value)")"
+ $lines += "$i`t$(Esc-XmlText "$($parsed.value)")"
} else {
- $lines += "$i`t$(Esc-Xml "$($parsed.value)")"
+ $lines += "$i`t$(Esc-XmlText "$($parsed.value)")"
}
}
if ($parsed.viewMode) {
- $lines += "$i`t$(Esc-Xml $parsed.viewMode)"
+ $lines += "$i`t$(Esc-XmlText $parsed.viewMode)"
}
if ($parsed.userSettingID) {
$uid = if ($parsed.userSettingID -eq "auto") { [System.Guid]::NewGuid().ToString() } else { $parsed.userSettingID }
- $lines += "$i`t$(Esc-Xml $uid)"
+ $lines += "$i`t$(Esc-XmlText $uid)"
}
$lines += "$i"
@@ -1520,7 +1526,7 @@ function Build-OrderItemFragment {
$lines += "$i"
} else {
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.field)"
+ $lines += "$i`t$(Esc-XmlText $parsed.field)"
$lines += "$i`t$($parsed.direction)"
$lines += "$i"
}
@@ -1533,12 +1539,12 @@ function Build-DataSetLinkFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.source)"
- $lines += "$i`t$(Esc-Xml $parsed.dest)"
- $lines += "$i`t$(Esc-Xml $parsed.sourceExpr)"
- $lines += "$i`t$(Esc-Xml $parsed.destExpr)"
+ $lines += "$i`t$(Esc-XmlText $parsed.source)"
+ $lines += "$i`t$(Esc-XmlText $parsed.dest)"
+ $lines += "$i`t$(Esc-XmlText $parsed.sourceExpr)"
+ $lines += "$i`t$(Esc-XmlText $parsed.destExpr)"
if ($parsed.parameter) {
- $lines += "$i`t$(Esc-Xml $parsed.parameter)"
+ $lines += "$i`t$(Esc-XmlText $parsed.parameter)"
}
$lines += "$i"
return $lines -join "`n"
@@ -1550,9 +1556,9 @@ function Build-DataSetQueryFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.name)"
- $lines += "$i`t$(Esc-Xml $parsed.dataSource)"
- $lines += "$i`t$(Esc-Xml $parsed.query)"
+ $lines += "$i`t$(Esc-XmlText $parsed.name)"
+ $lines += "$i`t$(Esc-XmlText $parsed.dataSource)"
+ $lines += "$i`t$(Esc-XmlText $parsed.query)"
$lines += "$i"
return $lines -join "`n"
}
@@ -1563,7 +1569,7 @@ function Build-VariantFragment {
$i = $indent
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $parsed.name)"
+ $lines += "$i`t$(Esc-XmlText $parsed.name)"
$lines += (Build-MLTextXml -tag "dcsset:presentation" -text $parsed.presentation -indent "$i`t")
$lines += "$i`t"
$lines += "$i`t`t"
@@ -1587,11 +1593,11 @@ function Emit-FilterComparison {
param($f, [string]$indent)
$lines = @()
$lines += "$indent"
- $lines += "$indent`t$(Esc-Xml $f.field)"
- $lines += "$indent`t$(Esc-Xml $f.op)"
+ $lines += "$indent`t$(Esc-XmlText $f.field)"
+ $lines += "$indent`t$(Esc-XmlText $f.op)"
if ($null -ne $f.value) {
$vt = if ($f["valueType"]) { $f["valueType"] } else { "xs:string" }
- $lines += "$indent`t$(Esc-Xml "$($f.value)")"
+ $lines += "$indent`t$(Esc-XmlText "$($f.value)")"
}
$lines += "$indent"
return $lines
@@ -1609,7 +1615,7 @@ function Build-ConditionalAppearanceItemFragment {
$lines += "$i`t"
foreach ($fld in $parsed.fields) {
$lines += "$i`t`t"
- $lines += "$i`t`t`t$(Esc-Xml $fld)"
+ $lines += "$i`t`t`t$(Esc-XmlText $fld)"
$lines += "$i`t`t"
}
$lines += "$i`t"
@@ -1641,21 +1647,21 @@ function Build-ConditionalAppearanceItemFragment {
$val = $parsed.value
$lines += "$i`t`t"
- $lines += "$i`t`t`t$(Esc-Xml $parsed.param)"
+ $lines += "$i`t`t`t$(Esc-XmlText $parsed.param)"
if ($val -match '^(web|style|win):') {
- $lines += "$i`t`t`t$(Esc-Xml $val)"
+ $lines += "$i`t`t`t$(Esc-XmlText $val)"
} elseif ($val -eq "true" -or $val -eq "false") {
- $lines += "$i`t`t`t$(Esc-Xml $val)"
+ $lines += "$i`t`t`t$(Esc-XmlText $val)"
} elseif ($parsed.param -eq "Формат" -or $parsed.param -eq "Текст" -or $parsed.param -eq "Заголовок") {
$lines += "$i`t`t`t"
$lines += "$i`t`t`t`t"
$lines += "$i`t`t`t`t`tru"
- $lines += "$i`t`t`t`t`t$(Esc-Xml $val)"
+ $lines += "$i`t`t`t`t`t$(Esc-XmlText $val)"
$lines += "$i`t`t`t`t"
$lines += "$i`t`t`t"
} else {
- $lines += "$i`t`t`t$(Esc-Xml $val)"
+ $lines += "$i`t`t`t$(Esc-XmlText $val)"
}
$lines += "$i`t`t"
@@ -1674,7 +1680,7 @@ function Build-StructureItemFragment {
# name
if ($item["name"]) {
- $lines += "$i`t$(Esc-Xml $item["name"])"
+ $lines += "$i`t$(Esc-XmlText $item["name"])"
}
# groupItems
@@ -1685,7 +1691,7 @@ function Build-StructureItemFragment {
$lines += "$i`t"
foreach ($field in $groupBy) {
$lines += "$i`t`t"
- $lines += "$i`t`t`t$(Esc-Xml $field)"
+ $lines += "$i`t`t`t$(Esc-XmlText $field)"
$lines += "$i`t`t`tItems"
$lines += "$i`t`t`tNone"
$lines += "$i`t`t`t0001-01-01T00:00:00"
@@ -1728,17 +1734,17 @@ function Build-OutputParamFragment {
$lines = @()
$lines += "$i"
- $lines += "$i`t$(Esc-Xml $key)"
+ $lines += "$i`t$(Esc-XmlText $key)"
if ($ptype -eq "mltext") {
$lines += "$i`t"
$lines += "$i`t`t"
$lines += "$i`t`t`tru"
- $lines += "$i`t`t`t$(Esc-Xml $val)"
+ $lines += "$i`t`t`t$(Esc-XmlText $val)"
$lines += "$i`t`t"
$lines += "$i`t"
} else {
- $lines += "$i`t$(Esc-Xml $val)"
+ $lines += "$i`t$(Esc-XmlText $val)"
}
$lines += "$i"
@@ -1888,7 +1894,7 @@ function Set-OrCreateChildElement($parent, [string]$localName, [string]$nsUri, [
} else {
$prefix = $parent.GetPrefixOfNamespace($nsUri)
$qualName = if ($prefix) { "${prefix}:$localName" } else { $localName }
- $fragXml = "$indent<$qualName>$(Esc-Xml $value)$qualName>"
+ $fragXml = "$indent<$qualName>$(Esc-XmlText $value)$qualName>"
$nodes = Import-Fragment $xmlDoc $fragXml
foreach ($node in $nodes) {
Insert-BeforeElement $parent $node $null $indent
@@ -1913,7 +1919,7 @@ function Set-OrCreateChildElementWithAttr($parent, [string]$localName, [string]$
$prefix = $parent.GetPrefixOfNamespace($nsUri)
$qualName = if ($prefix) { "${prefix}:$localName" } else { $localName }
$typeAttr = if ($xsiType) { " xsi:type=`"$xsiType`"" } else { "" }
- $fragXml = "$indent<$qualName$typeAttr>$(Esc-Xml $value)$qualName>"
+ $fragXml = "$indent<$qualName$typeAttr>$(Esc-XmlText $value)$qualName>"
$nodes = Import-Fragment $xmlDoc $fragXml
foreach ($node in $nodes) {
Insert-BeforeElement $parent $node $null $indent
@@ -2586,7 +2592,7 @@ switch ($Operation) {
}
}
}
- $fragXml = "$childIndent<$key>$(Esc-Xml $value)$key>"
+ $fragXml = "$childIndent<$key>$(Esc-XmlText $value)$key>"
$nodes = Import-Fragment $xmlDoc $fragXml
foreach ($node in $nodes) {
Insert-BeforeElement $paramEl $node $refNode $childIndent
@@ -3247,7 +3253,7 @@ switch ($Operation) {
foreach ($field in $t.groupBy) {
$lines = @()
$lines += "$itemIndent"
- $lines += "$itemIndent`t$(Esc-Xml $field)"
+ $lines += "$itemIndent`t$(Esc-XmlText $field)"
$lines += "$itemIndent`tItems"
$lines += "$itemIndent`tNone"
$lines += "$itemIndent`t0001-01-01T00:00:00"
@@ -3563,18 +3569,18 @@ switch ($Operation) {
$valLines = @()
if ($parsed.value -is [hashtable] -and $parsed.value.variant) {
$valLines += "$itemIndent"
- $valLines += "$itemIndent`t$(Esc-Xml $parsed.value.variant)"
+ $valLines += "$itemIndent`t$(Esc-XmlText $parsed.value.variant)"
$valLines += "$itemIndent`t0001-01-01T00:00:00"
$valLines += "$itemIndent`t0001-01-01T00:00:00"
$valLines += "$itemIndent"
} elseif (Test-EmptyValue $parsed.value) {
$valLines += "$itemIndent"
} elseif ("$($parsed.value)" -match '^\d{4}-\d{2}-\d{2}T') {
- $valLines += "$itemIndent$(Esc-Xml "$($parsed.value)")"
+ $valLines += "$itemIndent$(Esc-XmlText "$($parsed.value)")"
} elseif ("$($parsed.value)" -eq "true" -or "$($parsed.value)" -eq "false") {
- $valLines += "$itemIndent$(Esc-Xml "$($parsed.value)")"
+ $valLines += "$itemIndent$(Esc-XmlText "$($parsed.value)")"
} else {
- $valLines += "$itemIndent$(Esc-Xml "$($parsed.value)")"
+ $valLines += "$itemIndent$(Esc-XmlText "$($parsed.value)")"
}
$valXml = $valLines -join "`n"
$valNodes = Import-Fragment $xmlDoc $valXml
@@ -3748,7 +3754,7 @@ switch ($Operation) {
}
}
foreach ($k in $kv.Keys) {
- $lines += "$fieldIndent`t$(Esc-Xml $kv[$k])"
+ $lines += "$fieldIndent`t$(Esc-XmlText $kv[$k])"
}
foreach ($raw in $preservedRoleChildren) {
$lines += "$fieldIndent`t" + $raw
diff --git a/.claude/skills/skd-edit/scripts/skd-edit.py b/.claude/skills/skd-edit/scripts/skd-edit.py
index f686015c..448b8062 100644
--- a/.claude/skills/skd-edit/scripts/skd-edit.py
+++ b/.claude/skills/skd-edit/scripts/skd-edit.py
@@ -1,4 +1,4 @@
-# skd-edit v1.34 — Atomic 1C DCS editor (Python port) (+resolve_type_str: срезание префикса cfg:/d5p1:)
+# skd-edit v1.35 — Atomic 1C DCS editor (Python port) (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -81,6 +81,11 @@ def local_name(node):
# ── helpers ──────────────────────────────────────────────────
def esc_xml(s):
+ # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна.
+ return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
+
+
+def esc_xml_text(s):
return s.replace('&', '&').replace('<', '<').replace('>', '>')
@@ -932,7 +937,7 @@ def build_available_value_fragment(item, declared_type, indent):
lines.append(f'{indent}\t')
lines.append(f"{indent}\t\t")
lines.append(f"{indent}\t\t\tru")
- lines.append(f"{indent}\t\t\t{esc_xml(item['presentation'])}")
+ lines.append(f"{indent}\t\t\t{esc_xml_text(item['presentation'])}")
lines.append(f"{indent}\t\t")
lines.append(f"{indent}\t")
lines.append(f"{indent}")
@@ -1005,14 +1010,14 @@ def build_value_type_xml(type_str, indent):
return "\n".join(lines)
if re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.', type_str):
- lines.append(f'{indent}d5p1:{esc_xml(type_str)}')
+ lines.append(f'{indent}d5p1:{esc_xml_text(type_str)}')
return "\n".join(lines)
if "." in type_str:
- lines.append(f'{indent}d5p1:{esc_xml(type_str)}')
+ lines.append(f'{indent}d5p1:{esc_xml_text(type_str)}')
return "\n".join(lines)
- lines.append(f"{indent}{esc_xml(type_str)}")
+ lines.append(f"{indent}{esc_xml_text(type_str)}")
return "\n".join(lines)
@@ -1067,7 +1072,7 @@ def build_mltext_xml(tag, text, indent):
f'{indent}<{tag} xsi:type="v8:LocalStringType">',
f"{indent}\t",
f"{indent}\t\tru",
- f"{indent}\t\t{esc_xml(text)}",
+ f"{indent}\t\t{esc_xml_text(text)}",
f"{indent}\t",
f"{indent}{tag}>",
]
@@ -1077,7 +1082,7 @@ def build_mltext_xml(tag, text, indent):
def patch_mltext_ru(raw_outer_xml, new_ru_text, indent):
"""Patch the ru within an existing multi-lang title OuterXml,
preserving en/uk/etc. siblings. Mirrors PS Patch-MLTextRu."""
- escaped = esc_xml(new_ru_text)
+ escaped = esc_xml_text(new_ru_text)
ru_item_pat = r"(\s*ru\s*)[^<]*(\s*)"
if re.search(ru_item_pat, raw_outer_xml):
return re.sub(ru_item_pat, lambda m: m.group(1) + escaped + m.group(2), raw_outer_xml)
@@ -1117,8 +1122,8 @@ def build_restriction_xml(restrict, indent):
def build_field_fragment(parsed, indent):
i = indent
lines = [f'{i}']
- lines.append(f"{i}\t{esc_xml(parsed['dataPath'])}")
- lines.append(f"{i}\t{esc_xml(parsed['field'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['dataPath'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['field'])}")
# Title: prefer raw multi-lang OuterXml (preserves en/uk/etc.). When shorthand
# provides a new ru text different from existing, patch the ru content. Otherwise
@@ -1159,8 +1164,8 @@ def build_total_fragment(parsed, indent):
i = indent
lines = [
f"{i}",
- f"{i}\t{esc_xml(parsed['dataPath'])}",
- f"{i}\t{esc_xml(parsed['expression'])}",
+ f"{i}\t{esc_xml_text(parsed['dataPath'])}",
+ f"{i}\t{esc_xml_text(parsed['expression'])}",
f"{i}",
]
return "\n".join(lines)
@@ -1170,8 +1175,8 @@ def build_calc_field_fragment(parsed, indent):
i = indent
lines = [
f"{i}",
- f"{i}\t{esc_xml(parsed['dataPath'])}",
- f"{i}\t{esc_xml(parsed['expression'])}",
+ f"{i}\t{esc_xml_text(parsed['dataPath'])}",
+ f"{i}\t{esc_xml_text(parsed['expression'])}",
]
if parsed.get("title"):
lines.append(build_mltext_xml("title", parsed["title"], f"{i}\t"))
@@ -1193,7 +1198,7 @@ def build_param_value_xml(type_str, value, indent, tag_name="value", tag_ns=""):
if type_str == "StandardPeriod":
lines.append(f'{indent}<{open_tag} xsi:type="v8:StandardPeriod">')
- lines.append(f'{indent}\t{esc_xml(val_str)}')
+ lines.append(f'{indent}\t{esc_xml_text(val_str)}')
lines.append(f"{indent}\t0001-01-01T00:00:00")
lines.append(f"{indent}\t0001-01-01T00:00:00")
lines.append(f"{indent}{open_tag}>")
@@ -1222,7 +1227,7 @@ def build_param_value_xml(type_str, value, indent, tag_name="value", tag_ns=""):
else:
xsi = "xs:string"
- lines.append(f'{indent}<{open_tag} xsi:type="{xsi}">{esc_xml(val_str)}{open_tag}>')
+ lines.append(f'{indent}<{open_tag} xsi:type="{xsi}">{esc_xml_text(val_str)}{open_tag}>')
return lines
@@ -1230,7 +1235,7 @@ def build_param_fragment(parsed, indent):
i = indent
fragments = []
- lines = [f"{i}", f"{i}\t{esc_xml(parsed['name'])}"]
+ lines = [f"{i}", f"{i}\t{esc_xml_text(parsed['name'])}"]
if parsed.get("title"):
lines.append(build_mltext_xml("title", parsed["title"], f"{i}\t"))
@@ -1277,7 +1282,7 @@ def build_param_fragment(parsed, indent):
# Canonical БСП pattern: title + valueType + value + useRestriction + expression
# NB: expr автодат собираем в переменную (не в f-string): бэкслеш в \uXXXX
# внутри {} f-строки — SyntaxError на python < 3.12 (PEP 701). Совместимость с 3.9.
- expr_start = esc_xml('&' + param_name + '.\u0414\u0430\u0442\u0430\u041d\u0430\u0447\u0430\u043b\u0430')
+ expr_start = esc_xml_text('&' + param_name + '.\u0414\u0430\u0442\u0430\u041d\u0430\u0447\u0430\u043b\u0430')
b_lines = [
f"{i}",
f"{i}\t\u0414\u0430\u0442\u0430\u041d\u0430\u0447\u0430\u043b\u0430",
@@ -1292,7 +1297,7 @@ def build_param_fragment(parsed, indent):
]
fragments.append("\n".join(b_lines))
- expr_end = esc_xml('&' + param_name + '.\u0414\u0430\u0442\u0430\u041e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f')
+ expr_end = esc_xml_text('&' + param_name + '.\u0414\u0430\u0442\u0430\u041e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f')
e_lines = [
f"{i}",
f"{i}\t\u0414\u0430\u0442\u0430\u041e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f",
@@ -1317,19 +1322,19 @@ def build_filter_item_fragment(parsed, indent):
if parsed.get("use") is False:
lines.append(f"{i}\tfalse")
- lines.append(f'{i}\t{esc_xml(parsed["field"])}')
- lines.append(f"{i}\t{esc_xml(parsed['op'])}")
+ lines.append(f'{i}\t{esc_xml_text(parsed["field"])}')
+ lines.append(f"{i}\t{esc_xml_text(parsed['op'])}")
if parsed.get("value") is not None:
vt = parsed.get("valueType", "xs:string")
- lines.append(f'{i}\t{esc_xml(str(parsed["value"]))}')
+ lines.append(f'{i}\t{esc_xml_text(str(parsed["value"]))}')
if parsed.get("viewMode"):
- lines.append(f"{i}\t{esc_xml(parsed['viewMode'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['viewMode'])}")
if parsed.get("userSettingID"):
uid = new_uuid() if parsed["userSettingID"] == "auto" else parsed["userSettingID"]
- lines.append(f"{i}\t{esc_xml(uid)}")
+ lines.append(f"{i}\t{esc_xml_text(uid)}")
lines.append(f"{i}")
return "\n".join(lines)
@@ -1354,19 +1359,19 @@ def build_selection_item_fragment(field_name, indent):
lines.append(f"{i}\t")
lines.append(f"{i}\t\t")
lines.append(f"{i}\t\t\tru")
- lines.append(f"{i}\t\t\t{esc_xml(title)}")
+ lines.append(f"{i}\t\t\t{esc_xml_text(title)}")
lines.append(f"{i}\t\t")
lines.append(f"{i}\t")
for item in items:
lines.append(f'{i}\t')
- lines.append(f"{i}\t\t{esc_xml(item)}")
+ lines.append(f"{i}\t\t{esc_xml_text(item)}")
lines.append(f"{i}\t")
lines.append(f"{i}\tAuto")
lines.append(f"{i}")
return "\n".join(lines)
lines = [
f'{i}',
- f"{i}\t{esc_xml(field_name)}",
+ f"{i}\t{esc_xml_text(field_name)}",
f"{i}",
]
return "\n".join(lines)
@@ -1379,31 +1384,31 @@ def build_data_param_fragment(parsed, indent):
if parsed.get("use") is False:
lines.append(f"{i}\tfalse")
- lines.append(f"{i}\t{esc_xml(parsed['parameter'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['parameter'])}")
if parsed.get("value") is not None:
val = parsed["value"]
if isinstance(val, dict) and val.get("variant"):
lines.append(f'{i}\t')
- lines.append(f'{i}\t\t{esc_xml(val["variant"])}')
+ lines.append(f'{i}\t\t{esc_xml_text(val["variant"])}')
lines.append(f"{i}\t\t0001-01-01T00:00:00")
lines.append(f"{i}\t\t0001-01-01T00:00:00")
lines.append(f"{i}\t")
elif is_empty_value(val):
lines.append(f'{i}\t')
elif re.match(r'^\d{4}-\d{2}-\d{2}T', str(val)):
- lines.append(f'{i}\t{esc_xml(str(val))}')
+ lines.append(f'{i}\t{esc_xml_text(str(val))}')
elif str(val) in ("true", "false"):
- lines.append(f'{i}\t{esc_xml(str(val))}')
+ lines.append(f'{i}\t{esc_xml_text(str(val))}')
else:
- lines.append(f'{i}\t{esc_xml(str(val))}')
+ lines.append(f'{i}\t{esc_xml_text(str(val))}')
if parsed.get("viewMode"):
- lines.append(f"{i}\t{esc_xml(parsed['viewMode'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['viewMode'])}")
if parsed.get("userSettingID"):
uid = new_uuid() if parsed["userSettingID"] == "auto" else parsed["userSettingID"]
- lines.append(f"{i}\t{esc_xml(uid)}")
+ lines.append(f"{i}\t{esc_xml_text(uid)}")
lines.append(f"{i}")
return "\n".join(lines)
@@ -1415,7 +1420,7 @@ def build_order_item_fragment(parsed, indent):
return f'{i}'
lines = [
f'{i}',
- f"{i}\t{esc_xml(parsed['field'])}",
+ f"{i}\t{esc_xml_text(parsed['field'])}",
f"{i}\t{parsed['direction']}",
f"{i}",
]
@@ -1426,13 +1431,13 @@ def build_data_set_link_fragment(parsed, indent):
i = indent
lines = [
f"{i}",
- f"{i}\t{esc_xml(parsed['source'])}",
- f"{i}\t{esc_xml(parsed['dest'])}",
- f"{i}\t{esc_xml(parsed['sourceExpr'])}",
- f"{i}\t{esc_xml(parsed['destExpr'])}",
+ f"{i}\t{esc_xml_text(parsed['source'])}",
+ f"{i}\t{esc_xml_text(parsed['dest'])}",
+ f"{i}\t{esc_xml_text(parsed['sourceExpr'])}",
+ f"{i}\t{esc_xml_text(parsed['destExpr'])}",
]
if parsed.get("parameter"):
- lines.append(f"{i}\t{esc_xml(parsed['parameter'])}")
+ lines.append(f"{i}\t{esc_xml_text(parsed['parameter'])}")
lines.append(f"{i}")
return "\n".join(lines)
@@ -1441,9 +1446,9 @@ def build_data_set_query_fragment(parsed, indent):
i = indent
lines = [
f'{i}',
- f"{i}\t{esc_xml(parsed['name'])}",
- f"{i}\t{esc_xml(parsed['dataSource'])}",
- f"{i}\t{esc_xml(parsed['query'])}",
+ f"{i}\t{esc_xml_text(parsed['name'])}",
+ f"{i}\t{esc_xml_text(parsed['dataSource'])}",
+ f"{i}\t{esc_xml_text(parsed['query'])}",
f"{i}",
]
return "\n".join(lines)
@@ -1453,7 +1458,7 @@ def build_variant_fragment(parsed, indent):
i = indent
lines = [
f"{i}",
- f"{i}\t{esc_xml(parsed['name'])}",
+ f"{i}\t{esc_xml_text(parsed['name'])}",
build_mltext_xml("dcsset:presentation", parsed["presentation"], f"{i}\t"),
f'{i}\t',
f"{i}\t\t",
@@ -1476,11 +1481,11 @@ def build_variant_fragment(parsed, indent):
def _emit_filter_comparison(lines, f, indent):
lines.append(f'{indent}')
- lines.append(f'{indent}\t{esc_xml(f["field"])}')
- lines.append(f"{indent}\t{esc_xml(f['op'])}")
+ lines.append(f'{indent}\t{esc_xml_text(f["field"])}')
+ lines.append(f"{indent}\t{esc_xml_text(f['op'])}")
if f.get("value") is not None:
vt = f.get("valueType", "xs:string")
- lines.append(f'{indent}\t{esc_xml(str(f["value"]))}')
+ lines.append(f'{indent}\t{esc_xml_text(str(f["value"]))}')
lines.append(f"{indent}")
@@ -1492,7 +1497,7 @@ def build_conditional_appearance_item_fragment(parsed, indent):
lines.append(f"{i}\t")
for fld in parsed["fields"]:
lines.append(f"{i}\t\t")
- lines.append(f"{i}\t\t\t{esc_xml(fld)}")
+ lines.append(f"{i}\t\t\t{esc_xml_text(fld)}")
lines.append(f"{i}\t\t")
lines.append(f"{i}\t")
else:
@@ -1518,21 +1523,21 @@ def build_conditional_appearance_item_fragment(parsed, indent):
lines.append(f"{i}\t")
val = parsed["value"]
lines.append(f'{i}\t\t')
- lines.append(f"{i}\t\t\t{esc_xml(parsed['param'])}")
+ lines.append(f"{i}\t\t\t{esc_xml_text(parsed['param'])}")
if re.match(r'^(web|style|win):', val):
- lines.append(f'{i}\t\t\t{esc_xml(val)}')
+ lines.append(f'{i}\t\t\t{esc_xml_text(val)}')
elif val in ("true", "false"):
- lines.append(f'{i}\t\t\t{esc_xml(val)}')
+ lines.append(f'{i}\t\t\t{esc_xml_text(val)}')
elif parsed["param"] in ("Формат", "Текст", "Заголовок"):
lines.append(f'{i}\t\t\t')
lines.append(f"{i}\t\t\t\t")
lines.append(f"{i}\t\t\t\t\tru")
- lines.append(f"{i}\t\t\t\t\t{esc_xml(val)}")
+ lines.append(f"{i}\t\t\t\t\t{esc_xml_text(val)}")
lines.append(f"{i}\t\t\t\t")
lines.append(f"{i}\t\t\t")
else:
- lines.append(f'{i}\t\t\t{esc_xml(val)}')
+ lines.append(f'{i}\t\t\t{esc_xml_text(val)}')
lines.append(f"{i}\t\t")
lines.append(f"{i}\t")
@@ -1546,7 +1551,7 @@ def build_structure_item_fragment(item, indent):
lines = [f'{i}']
if item.get("name"):
- lines.append(f"{i}\t{esc_xml(item['name'])}")
+ lines.append(f"{i}\t{esc_xml_text(item['name'])}")
group_by = item.get("groupBy", [])
if not group_by:
@@ -1555,7 +1560,7 @@ def build_structure_item_fragment(item, indent):
lines.append(f"{i}\t")
for field in group_by:
lines.append(f'{i}\t\t')
- lines.append(f"{i}\t\t\t{esc_xml(field)}")
+ lines.append(f"{i}\t\t\t{esc_xml_text(field)}")
lines.append(f"{i}\t\t\tItems")
lines.append(f"{i}\t\t\tNone")
lines.append(f'{i}\t\t\t0001-01-01T00:00:00')
@@ -1585,17 +1590,17 @@ def build_output_param_fragment(parsed, indent):
ptype = output_param_types.get(key, "xs:string")
lines = [f'{i}']
- lines.append(f"{i}\t{esc_xml(key)}")
+ lines.append(f"{i}\t{esc_xml_text(key)}")
if ptype == "mltext":
lines.append(f'{i}\t')
lines.append(f"{i}\t\t")
lines.append(f"{i}\t\t\tru")
- lines.append(f"{i}\t\t\t{esc_xml(val)}")
+ lines.append(f"{i}\t\t\t{esc_xml_text(val)}")
lines.append(f"{i}\t\t")
lines.append(f"{i}\t")
else:
- lines.append(f'{i}\t{esc_xml(val)}')
+ lines.append(f'{i}\t{esc_xml_text(val)}')
lines.append(f"{i}")
return "\n".join(lines)
@@ -1732,7 +1737,7 @@ def set_or_create_child_element(parent, ln, ns_uri, value, indent):
prefix = p
break
qual_name = f"{prefix}:{ln}" if prefix else ln
- frag_xml = f"{indent}<{qual_name}>{esc_xml(value)}{qual_name}>"
+ frag_xml = f"{indent}<{qual_name}>{esc_xml_text(value)}{qual_name}>"
nodes = import_fragment(xml_doc, frag_xml)
for node in nodes:
insert_before_element(parent, node, None, indent)
@@ -1756,7 +1761,7 @@ def set_or_create_child_element_with_attr(parent, ln, ns_uri, value, xsi_type, i
break
qual_name = f"{prefix}:{ln}" if prefix else ln
type_attr = f' xsi:type="{xsi_type}"' if xsi_type else ""
- frag_xml = f"{indent}<{qual_name}{type_attr}>{esc_xml(value)}{qual_name}>"
+ frag_xml = f"{indent}<{qual_name}{type_attr}>{esc_xml_text(value)}{qual_name}>"
nodes = import_fragment(xml_doc, frag_xml)
for node in nodes:
insert_before_element(parent, node, None, indent)
@@ -2292,7 +2297,7 @@ elif operation == "modify-parameter":
ref_node = None
if key == "denyIncompleteValues":
ref_node = next((ch for ch in param_el if isinstance(ch.tag, str) and local_name(ch) == "use"), None)
- frag_xml = f"{child_indent}<{key}>{esc_xml(value)}{key}>"
+ frag_xml = f"{child_indent}<{key}>{esc_xml_text(value)}{key}>"
nodes = import_fragment(xml_doc, frag_xml)
for node in nodes:
insert_before_element(param_el, node, ref_node, child_indent)
@@ -2780,7 +2785,7 @@ elif operation == "modify-structure":
for field in t["groupBy"]:
lines = [
f'{item_indent}',
- f'{item_indent}\t{esc_xml(field)}',
+ f'{item_indent}\t{esc_xml_text(field)}',
f'{item_indent}\tItems',
f'{item_indent}\tNone',
f'{item_indent}\t0001-01-01T00:00:00',
@@ -3039,18 +3044,18 @@ elif operation == "modify-dataParameter":
pv = parsed["value"]
if isinstance(pv, dict) and pv.get("variant"):
val_lines.append(f'{item_indent}')
- val_lines.append(f'{item_indent}\t{esc_xml(pv["variant"])}')
+ val_lines.append(f'{item_indent}\t{esc_xml_text(pv["variant"])}')
val_lines.append(f"{item_indent}\t0001-01-01T00:00:00")
val_lines.append(f"{item_indent}\t0001-01-01T00:00:00")
val_lines.append(f"{item_indent}")
elif is_empty_value(pv):
val_lines.append(f'{item_indent}')
elif re.match(r'^\d{4}-\d{2}-\d{2}T', str(pv)):
- val_lines.append(f'{item_indent}{esc_xml(str(pv))}')
+ val_lines.append(f'{item_indent}{esc_xml_text(str(pv))}')
elif str(pv) in ("true", "false"):
- val_lines.append(f'{item_indent}{esc_xml(str(pv))}')
+ val_lines.append(f'{item_indent}{esc_xml_text(str(pv))}')
else:
- val_lines.append(f'{item_indent}{esc_xml(str(pv))}')
+ val_lines.append(f'{item_indent}{esc_xml_text(str(pv))}')
val_xml = "\n".join(val_lines)
val_nodes = import_fragment(xml_doc, val_xml)
@@ -3192,7 +3197,7 @@ elif operation == "set-field-role":
else:
lines.append(f"{field_indent}\ttrue")
for k, v in kv:
- lines.append(f"{field_indent}\t{esc_xml(v)}")
+ lines.append(f"{field_indent}\t{esc_xml_text(v)}")
for raw in preserved_role_children:
lines.append(f"{field_indent}\t" + raw)
lines.append(f"{field_indent}")
diff --git a/.claude/skills/subsystem-compile/scripts/subsystem-compile.ps1 b/.claude/skills/subsystem-compile/scripts/subsystem-compile.ps1
index a8495392..5b6b1616 100644
--- a/.claude/skills/subsystem-compile/scripts/subsystem-compile.ps1
+++ b/.claude/skills/subsystem-compile/scripts/subsystem-compile.ps1
@@ -1,4 +1,4 @@
-# subsystem-compile v1.20 — Create 1C subsystem from JSON definition (+detect_format_version: ветка автономной EPF/ERF)
+# subsystem-compile v1.21 — Create 1C subsystem from JSON definition (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -188,9 +188,15 @@ function X([string]$text) {
$script:xml.AppendLine($text) | Out-Null
}
-function Esc-Xml([string]$s) {
- # Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
- # пишет литерально (92142 сырых кавычки на корпус, ни одной ").
+function Esc-Xml {
+ param([string]$s)
+ # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна.
+ return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"')
+}
+
+function Esc-XmlText {
+ param([string]$s)
+ # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми.
return $s.Replace('&','&').Replace('<','<').Replace('>','>')
}
@@ -211,7 +217,7 @@ function Emit-MLText([string]$indent, [string]$tag, [string]$text) {
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$tag>"
}
@@ -227,7 +233,7 @@ function Write-ChildSubsystemStub([string]$childPath, [string]$childName, [strin
[void]$sb.AppendLine("")
[void]$sb.AppendLine("`t")
[void]$sb.AppendLine("`t`t")
- [void]$sb.AppendLine("`t`t`t$(Esc-Xml $childName)")
+ [void]$sb.AppendLine("`t`t`t$(Esc-XmlText $childName)")
[void]$sb.AppendLine("`t`t`t")
[void]$sb.AppendLine("`t`t`t")
[void]$sb.AppendLine("`t`t`ttrue")
@@ -483,14 +489,14 @@ X "`t"
X "`t`t"
# Name
-X "`t`t`t$(Esc-Xml $objName)"
+X "`t`t`t$(Esc-XmlText $objName)"
# Synonym
Emit-MLText "`t`t`t" "Synonym" $synonym
# Comment
if ($comment) {
- X "`t`t`t$(Esc-Xml $comment)"
+ X "`t`t`t$(Esc-XmlText $comment)"
} else {
X "`t`t`t"
}
@@ -517,7 +523,7 @@ if ($picture) {
if ($contentItems.Count -gt 0) {
X "`t`t`t"
foreach ($item in $contentItems) {
- X "`t`t`t`t$(Esc-Xml $item)"
+ X "`t`t`t`t$(Esc-XmlText $item)"
}
X "`t`t`t"
} else {
@@ -530,7 +536,7 @@ X "`t`t"
if ($children.Count -gt 0) {
X "`t`t"
foreach ($ch in $children) {
- X "`t`t`t$(Esc-Xml $ch)"
+ X "`t`t`t$(Esc-XmlText $ch)"
}
X "`t`t"
} else {
diff --git a/.claude/skills/subsystem-compile/scripts/subsystem-compile.py b/.claude/skills/subsystem-compile/scripts/subsystem-compile.py
index 48cdc8a7..5c8cc80f 100644
--- a/.claude/skills/subsystem-compile/scripts/subsystem-compile.py
+++ b/.claude/skills/subsystem-compile/scripts/subsystem-compile.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# subsystem-compile v1.20 — Create 1C subsystem from JSON definition (+detect_format_version: ветка автономной EPF/ERF)
+# subsystem-compile v1.21 — Create 1C subsystem from JSON definition (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -229,6 +229,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('>', '>')
@@ -241,7 +246,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}{tag}>")
@@ -305,7 +310,7 @@ def write_child_subsystem_stub(child_path, child_name, format_version):
lines.append(f'')
lines.append(f'\t')
lines.append('\t\t')
- lines.append(f'\t\t\t{esc_xml(child_name)}')
+ lines.append(f'\t\t\t{esc_xml_text(child_name)}')
lines.append('\t\t\t')
lines.append('\t\t\t')
lines.append('\t\t\ttrue')
@@ -493,14 +498,14 @@ def main():
lines.append('\t\t')
# Name
- lines.append(f'\t\t\t{esc_xml(obj_name)}')
+ lines.append(f'\t\t\t{esc_xml_text(obj_name)}')
# Synonym
emit_mltext(lines, '\t\t\t', 'Synonym', synonym)
# Comment
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')
@@ -525,7 +530,7 @@ def main():
if len(content_items) > 0:
lines.append('\t\t\t')
for item in content_items:
- lines.append(f'\t\t\t\t{esc_xml(item)}')
+ lines.append(f'\t\t\t\t{esc_xml_text(item)}')
lines.append('\t\t\t')
else:
lines.append('\t\t\t')
@@ -536,7 +541,7 @@ def main():
if len(children) > 0:
lines.append('\t\t')
for ch in children:
- lines.append(f'\t\t\t{esc_xml(ch)}')
+ lines.append(f'\t\t\t{esc_xml_text(ch)}')
lines.append('\t\t')
else:
lines.append('\t\t')
@@ -637,14 +642,14 @@ def main():
if not already_exists:
# Use raw text manipulation to preserve formatting
if '' in raw_text:
- replacement = ('' + eol + f'\t\t\t{esc_xml(obj_name)}' + eol + '\t\t')
+ replacement = ('' + eol + f'\t\t\t{esc_xml_text(obj_name)}' + eol + '\t\t')
raw_text = raw_text.replace('', replacement, 1)
elif '' in raw_text:
# Отступ вставки берём у закрывающего тега +1 уровень: подстановка
# по голому '' удваивала бы уже присутствующий отступ
# строки (получалось 5 табов вместо 3 — PS-порт через DOM даёт 3).
raw_text = re.sub(r'([ \t]*)',
- lambda m: m.group(1) + '\t' + f'{esc_xml(obj_name)}' + eol + m.group(1) + '',
+ lambda m: m.group(1) + '\t' + f'{esc_xml_text(obj_name)}' + eol + m.group(1) + '',
raw_text, count=1)
write_utf8_bom(parent_xml_path, raw_text)
diff --git a/.claude/skills/subsystem-edit/scripts/subsystem-edit.ps1 b/.claude/skills/subsystem-edit/scripts/subsystem-edit.ps1
index ebab6572..5f52b3c1 100644
--- a/.claude/skills/subsystem-edit/scripts/subsystem-edit.ps1
+++ b/.claude/skills/subsystem-edit/scripts/subsystem-edit.ps1
@@ -1,4 +1,4 @@
-# subsystem-edit v1.15 — Edit existing 1C subsystem XML
+# subsystem-edit v1.16 — Edit existing 1C subsystem XML (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath,
@@ -320,9 +320,15 @@ foreach ($child in $script:propsEl.ChildNodes) {
Info "Subsystem: $($script:objName)"
# --- XML manipulation helpers (from meta-edit pattern) ---
-function Esc-Xml([string]$s) {
- # Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
- # пишет литерально (92142 сырых кавычки на корпус, ни одной ").
+function Esc-Xml {
+ param([string]$s)
+ # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна.
+ return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"')
+}
+
+function Esc-XmlText {
+ param([string]$s)
+ # Эскейп ТЕКСТА элемента: только & < > — кавычку и апостроф платформа держит сырыми.
return $s.Replace('&','&').Replace('<','<').Replace('>','>')
}
@@ -337,7 +343,7 @@ function Write-ChildSubsystemStub([string]$childPath, [string]$childName, [strin
[void]$sb.AppendLine("")
[void]$sb.AppendLine("`t")
[void]$sb.AppendLine("`t`t")
- [void]$sb.AppendLine("`t`t`t$(Esc-Xml $childName)")
+ [void]$sb.AppendLine("`t`t`t$(Esc-XmlText $childName)")
[void]$sb.AppendLine("`t`t`t")
[void]$sb.AppendLine("`t`t`t")
[void]$sb.AppendLine("`t`t`ttrue")
diff --git a/.claude/skills/subsystem-edit/scripts/subsystem-edit.py b/.claude/skills/subsystem-edit/scripts/subsystem-edit.py
index 2341f2fa..68389aca 100644
--- a/.claude/skills/subsystem-edit/scripts/subsystem-edit.py
+++ b/.claude/skills/subsystem-edit/scripts/subsystem-edit.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# subsystem-edit v1.15 — Edit existing 1C subsystem XML
+# subsystem-edit v1.16 — Edit existing 1C subsystem XML (+esc_xml/esc_xml_text: разное экранирование атрибута и текста)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -193,6 +193,11 @@ def new_uuid():
def esc_xml(s):
+ # Эскейп ЗНАЧЕНИЯ АТРИБУТА: & < > и кавычка — внутри "..." литеральная " невалидна.
+ return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
+
+
+def esc_xml_text(s):
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной "); " она принимает, но нормализует обратно."""
return s.replace('&', '&').replace('<', '<').replace('>', '>')
@@ -250,7 +255,7 @@ def write_child_subsystem_stub(child_path, child_name, format_version):
lines.append(f'')
lines.append(f'\t')
lines.append('\t\t')
- lines.append(f'\t\t\t{esc_xml(child_name)}')
+ lines.append(f'\t\t\t{esc_xml_text(child_name)}')
lines.append('\t\t\t')
lines.append('\t\t\t')
lines.append('\t\t\ttrue')
diff --git a/tests/skills/cases/cf-init/snapshots/synonym-escaping/Configuration.xml b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Configuration.xml
new file mode 100644
index 00000000..4d59a535
--- /dev/null
+++ b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Configuration.xml
@@ -0,0 +1,251 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+ UUID-006
+ UUID-007
+
+
+ UUID-008
+ UUID-009
+
+
+ UUID-010
+ UUID-011
+
+
+ UUID-012
+ UUID-013
+
+
+ UUID-014
+ UUID-015
+
+
+
+ Экранирование
+
+
+ ru
+ Кавычка " апостроф ' амперсанд & угол <
+
+
+
+
+ Version8_3_24
+ ManagedApplication
+
+ PlatformApplication
+
+ Russian
+
+
+
+
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Biometrics
+ true
+
+
+ Location
+ false
+
+
+ BackgroundLocation
+ false
+
+
+ BluetoothPrinters
+ false
+
+
+ WiFiPrinters
+ false
+
+
+ Contacts
+ false
+
+
+ Calendars
+ false
+
+
+ PushNotifications
+ false
+
+
+ LocalNotifications
+ false
+
+
+ InAppPurchases
+ false
+
+
+ PersonalComputerFileExchange
+ false
+
+
+ Ads
+ false
+
+
+ NumberDialing
+ false
+
+
+ CallProcessing
+ false
+
+
+ CallLog
+ false
+
+
+ AutoSendSMS
+ false
+
+
+ ReceiveSMS
+ false
+
+
+ SMSLog
+ false
+
+
+ Camera
+ false
+
+
+ Microphone
+ false
+
+
+ MusicLibrary
+ false
+
+
+ PictureAndVideoLibraries
+ false
+
+
+ AudioPlaybackAndVibration
+ false
+
+
+ BackgroundAudioPlaybackAndVibration
+ false
+
+
+ InstallPackages
+ false
+
+
+ OSBackup
+ true
+
+
+ ApplicationUsageStatistics
+ false
+
+
+ BarcodeScanning
+ false
+
+
+ BackgroundAudioRecording
+ false
+
+
+ AllFilesAccess
+ false
+
+
+ Videoconferences
+ false
+
+
+ NFC
+ false
+
+
+ DocumentScanning
+ false
+
+
+ SpeechToText
+ false
+
+
+ Geofences
+ false
+
+
+ IncomingShareRequests
+ false
+
+
+ AllIncomingShareRequestsTypesProcessing
+ false
+
+
+
+
+
+ Normal
+
+
+ Language.Русский
+
+
+
+
+
+ Managed
+ NotAutoFree
+ DontUse
+ DontUse
+ TaxiEnableVersion8_2
+ DontUse
+ Version8_3_24
+
+
+
+ Русский
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/cf-init/snapshots/synonym-escaping/Ext/ClientApplicationInterface.xml b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Ext/ClientApplicationInterface.xml
new file mode 100644
index 00000000..3c1161b2
--- /dev/null
+++ b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Ext/ClientApplicationInterface.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ UUID-002
+
+
+
+
+ UUID-004
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/cf-init/snapshots/synonym-escaping/Languages/Русский.xml b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Languages/Русский.xml
new file mode 100644
index 00000000..37c60d78
--- /dev/null
+++ b/tests/skills/cases/cf-init/snapshots/synonym-escaping/Languages/Русский.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Русский
+
+
+ ru
+ Русский
+
+
+
+ ru
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/cf-init/synonym-escaping.json b/tests/skills/cases/cf-init/synonym-escaping.json
new file mode 100644
index 00000000..4b48ab51
--- /dev/null
+++ b/tests/skills/cases/cf-init/synonym-escaping.json
@@ -0,0 +1,17 @@
+{
+ "name": "Спецсимволы в синониме: экранируются только & < >",
+ "params": {
+ "name": "Экранирование"
+ },
+ "args_extra": [
+ "-Synonym",
+ "Кавычка \" апостроф ' амперсанд & угол <"
+ ],
+ "expect": {
+ "files": ["Configuration.xml"],
+ "fileContains": {
+ "file": "Configuration.xml",
+ "text": "Кавычка \" апостроф ' амперсанд & угол <"
+ }
+ }
+}
diff --git a/tests/skills/cases/form-compile/attr-value-escaping.json b/tests/skills/cases/form-compile/attr-value-escaping.json
new file mode 100644
index 00000000..33fa2b31
--- /dev/null
+++ b/tests/skills/cases/form-compile/attr-value-escaping.json
@@ -0,0 +1,34 @@
+{
+ "name": "Кавычка в значении атрибута экранируется ("), иначе XML невалиден",
+ "preRun": [
+ {
+ "script": "meta-compile/scripts/meta-compile",
+ "input": { "type": "DataProcessor", "name": "Экранирование" },
+ "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
+ },
+ {
+ "script": "form-add/scripts/form-add",
+ "args": { "-ObjectPath": "{workDir}/DataProcessors/Экранирование.xml", "-FormName": "Форма" }
+ }
+ ],
+ "params": { "outputPath": "DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml" },
+ "validatePath": "DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml",
+ "input": {
+ "attributes": [ { "name": "Значение", "type": "String" } ],
+ "elements": [
+ {
+ "input": "Поле",
+ "path": "Значение",
+ "choiceParameters": [
+ { "name": "Отбор.Наименование \"в кавычках\" & <угол>", "value": "тест" }
+ ]
+ }
+ ]
+ },
+ "expect": {
+ "fileContains": {
+ "file": "DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml",
+ "text": "name=\"Отбор.Наименование "в кавычках" & <угол>\""
+ }
+ }
+}
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Configuration.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Configuration.xml
new file mode 100644
index 00000000..252d1778
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Configuration.xml
@@ -0,0 +1,252 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+ UUID-006
+ UUID-007
+
+
+ UUID-008
+ UUID-009
+
+
+ UUID-010
+ UUID-011
+
+
+ UUID-012
+ UUID-013
+
+
+ UUID-014
+ UUID-015
+
+
+
+ TestConfig
+
+
+ ru
+ TestConfig
+
+
+
+
+ Version8_3_24
+ ManagedApplication
+
+ PlatformApplication
+
+ Russian
+
+
+
+
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Biometrics
+ true
+
+
+ Location
+ false
+
+
+ BackgroundLocation
+ false
+
+
+ BluetoothPrinters
+ false
+
+
+ WiFiPrinters
+ false
+
+
+ Contacts
+ false
+
+
+ Calendars
+ false
+
+
+ PushNotifications
+ false
+
+
+ LocalNotifications
+ false
+
+
+ InAppPurchases
+ false
+
+
+ PersonalComputerFileExchange
+ false
+
+
+ Ads
+ false
+
+
+ NumberDialing
+ false
+
+
+ CallProcessing
+ false
+
+
+ CallLog
+ false
+
+
+ AutoSendSMS
+ false
+
+
+ ReceiveSMS
+ false
+
+
+ SMSLog
+ false
+
+
+ Camera
+ false
+
+
+ Microphone
+ false
+
+
+ MusicLibrary
+ false
+
+
+ PictureAndVideoLibraries
+ false
+
+
+ AudioPlaybackAndVibration
+ false
+
+
+ BackgroundAudioPlaybackAndVibration
+ false
+
+
+ InstallPackages
+ false
+
+
+ OSBackup
+ true
+
+
+ ApplicationUsageStatistics
+ false
+
+
+ BarcodeScanning
+ false
+
+
+ BackgroundAudioRecording
+ false
+
+
+ AllFilesAccess
+ false
+
+
+ Videoconferences
+ false
+
+
+ NFC
+ false
+
+
+ DocumentScanning
+ false
+
+
+ SpeechToText
+ false
+
+
+ Geofences
+ false
+
+
+ IncomingShareRequests
+ false
+
+
+ AllIncomingShareRequestsTypesProcessing
+ false
+
+
+
+
+
+ Normal
+
+
+ Language.Русский
+
+
+
+
+
+ Managed
+ NotAutoFree
+ DontUse
+ DontUse
+ TaxiEnableVersion8_2
+ DontUse
+ Version8_3_24
+
+
+
+ Русский
+ Экранирование
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование.xml
new file mode 100644
index 00000000..0bc122a3
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ UUID-002
+ UUID-003
+
+
+ UUID-004
+ UUID-005
+
+
+
+ Экранирование
+
+
+ ru
+ Экранирование
+
+
+
+ true
+ DataProcessor.Экранирование.Form.Форма
+
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Ext/ManagerModule.bsl b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Ext/ManagerModule.bsl
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Ext/ObjectModule.bsl b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Ext/ObjectModule.bsl
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма.xml
new file mode 100644
index 00000000..dffeea01
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml
new file mode 100644
index 00000000..b8b1abba
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form.xml
@@ -0,0 +1,36 @@
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form/Module.bsl b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form/Module.bsl
new file mode 100644
index 00000000..8ead4cec
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/DataProcessors/Экранирование/Forms/Форма/Ext/Form/Module.bsl
@@ -0,0 +1,19 @@
+#Область ОбработчикиСобытийФормы
+
+#КонецОбласти
+
+#Область ОбработчикиСобытийЭлементовФормы
+
+#КонецОбласти
+
+#Область ОбработчикиКомандФормы
+
+#КонецОбласти
+
+#Область ОбработчикиОповещений
+
+#КонецОбласти
+
+#Область СлужебныеПроцедурыИФункции
+
+#КонецОбласти
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Ext/ClientApplicationInterface.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Ext/ClientApplicationInterface.xml
new file mode 100644
index 00000000..3c1161b2
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Ext/ClientApplicationInterface.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ UUID-002
+
+
+
+
+ UUID-004
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Languages/Русский.xml b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Languages/Русский.xml
new file mode 100644
index 00000000..37c60d78
--- /dev/null
+++ b/tests/skills/cases/form-compile/snapshots/attr-value-escaping/Languages/Русский.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Русский
+
+
+ ru
+ Русский
+
+
+
+ ru
+
+
+
\ No newline at end of file
diff --git a/tests/skills/cases/skd-compile/additional-properties-escaping.json b/tests/skills/cases/skd-compile/additional-properties-escaping.json
new file mode 100644
index 00000000..df8c2415
--- /dev/null
+++ b/tests/skills/cases/skd-compile/additional-properties-escaping.json
@@ -0,0 +1,36 @@
+{
+ "name": "additionalProperties: ключ идёт в значение атрибута — кавычка экранируется",
+ "params": {
+ "outputPath": "Template.xml"
+ },
+ "input": {
+ "dataSets": [
+ {
+ "name": "Основной",
+ "query": "ВЫБРАТЬ 1 КАК Поле1",
+ "fields": ["Поле1: Число(1,0)"]
+ }
+ ],
+ "settingsVariants": [
+ {
+ "name": "Основной",
+ "presentation": "Основной вариант",
+ "settings": {
+ "additionalProperties": {
+ "Ключ \"в кавычках\" & <угол>": "Значение \"в кавычках\" & <угол>"
+ }
+ }
+ }
+ ]
+ },
+ "validatePath": "Template.xml",
+ "expect": {
+ "fileContains": {
+ "file": "Template.xml",
+ "text": [
+ "",
+ "Значение \"в кавычках\" & <угол>"
+ ]
+ }
+ }
+}
diff --git a/tests/skills/cases/skd-compile/snapshots/additional-properties-escaping/Template.xml b/tests/skills/cases/skd-compile/snapshots/additional-properties-escaping/Template.xml
new file mode 100644
index 00000000..4709dc84
--- /dev/null
+++ b/tests/skills/cases/skd-compile/snapshots/additional-properties-escaping/Template.xml
@@ -0,0 +1,40 @@
+
+
+
+ ИсточникДанных1
+ Local
+
+
+ Основной
+
+ Поле1
+ Поле1
+
+ xs:decimal
+
+ 1
+ 0
+ Any
+
+
+
+ ИсточникДанных1
+ ВЫБРАТЬ 1 КАК Поле1
+
+
+ Основной
+
+
+ ru
+ Основной вариант
+
+
+
+
+
+ Значение "в кавычках" & <угол>
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/skills/check-inline-drift.mjs b/tests/skills/check-inline-drift.mjs
index 6ad1bda8..2cf66913 100644
--- a/tests/skills/check-inline-drift.mjs
+++ b/tests/skills/check-inline-drift.mjs
@@ -3,9 +3,13 @@
// копируются в каждый .ps1/.py — docs/python-porting-guide.md), поэтому нужен гард от расхождения
// копий. Реестр семей держим здесь же: реестр про дрейф не должен дрейфовать относительно проверки.
//
-// Часть расхождений ЗАКОННА (например esc_xml без " в form-* ради раундтрипа), поэтому семья
-// хранит не одно эталонное тело, а список вариантов. У законного варианта обязано быть поле `why`;
-// вариант без `why` — необоснованный, попадает в список долга (WARN).
+// Семья хранит не одно эталонное тело, а список вариантов: расхождение бывает и законным. У такого
+// варианта обязано быть поле `why`; вариант без `why` — необоснованный, идёт в список долга (WARN).
+//
+// Важно не путать «вариант» с «разными задачами под одним именем»: esc_xml и esc_xml_text — это
+// ДВЕ семьи, а не два варианта одной. Платформа в тексте элемента экранирует только & < >, а в
+// значении атрибута добавляет ", поэтому им нужны разные функции с говорящими именами, каждая
+// со своим единственным эталоном. Свести такое в один вариант с флагом — значит спрятать разницу.
//
// Запуск: node tests/skills/check-inline-drift.mjs [--list]
// Выход 1 при ERROR, 0 при WARN. Кандидатов в реестр искать: node debug/inline-utils/scan-dupes.mjs
@@ -119,22 +123,24 @@ const FAMILIES = [
},
// ─── Экранирование XML ───────────────────────────────────────────────────
+ // Платформа в ТЕКСТЕ элемента экранирует только & < > (кавычка и апостроф остаются сырыми —
+ // проверено раундтрипом через базу), а в ЗНАЧЕНИИ АТРИБУТА добавляет ": внутри "..."
+ // литеральная кавычка невалидна. Отсюда две функции, а не одна с переключателем.
{
- name: 'esc_xml', py: 'esc_xml', ps1: 'Esc-Xml',
+ name: 'esc_xml (значение атрибута)', py: 'esc_xml', ps1: 'Esc-Xml',
variants: [
- { id: 'text-no-quot', authority: 'form-compile',
- why: 'экранирование ТЕКСТА элемента: платформа кавычки в тексте не экранирует, " ломает раундтрип',
- consumers: ['form-edit', 'mxl-compile', 'role-compile', 'skd-compile', 'skd-edit'],
- consumersPy: ['subsystem-compile', 'subsystem-edit'] },
- { id: 'text-no-quot-subsystem-ps1', authority: 'subsystem-compile',
- consumers: [], consumersPs1: ['subsystem-edit'], port: 'ps1' },
- { id: 'attr-with-quot', authority: 'cf-init', port: 'py',
- why: 'экранирование ЗНАЧЕНИЯ АТРИБУТА: там " обязателен (init-навыки, PS1-порт функции не имеет)',
- consumers: ['cfe-init', 'epf-init', 'erf-init'] },
- { id: 'meta-attr-with-quot', authority: 'meta-compile',
- why: 'парный esc_xml_text экранирует текст, сам esc_xml применяется только к значениям атрибутов',
- consumers: ['meta-edit'] },
- { id: 'meta-edit-own-py', authority: 'meta-edit', consumers: [], port: 'py' },
+ { id: 'attr-with-quot', authority: 'meta-compile',
+ consumers: ['form-compile', 'form-edit', 'meta-edit', 'mxl-compile', 'role-compile',
+ 'skd-compile', 'skd-edit', 'subsystem-compile', 'subsystem-edit'] },
+ ],
+ },
+ {
+ name: 'esc_xml_text (текст элемента)', py: 'esc_xml_text', ps1: 'Esc-XmlText',
+ variants: [
+ { id: 'text-no-quot', authority: 'meta-compile',
+ consumers: ['cf-init', 'cfe-init', 'epf-init', 'erf-init', 'form-compile', 'form-edit',
+ 'meta-edit', 'mxl-compile', 'role-compile', 'skd-compile', 'skd-edit',
+ 'subsystem-compile', 'subsystem-edit'] },
],
},
// ─── Сохранение стиля XML при round-trip (#44/#46/#47) ───────────────────