fix(form,skd,init,meta-edit): развести экранирование атрибута и текста

esc_xml был один на две задачи, и в атрибутном контексте применялся текстовый
вариант без ". form-compile с choiceParameters, имя которого содержит
кавычку, выдавал <app:item name="Отбор.Наименование "кавычка"">: lxml падает с
attributes construct error, то есть файл невалиден как XML. Затронуто 9 мест в
form-compile и 3 в skd-compile, одинаково в обоих портах.

Раундтрип через базу показал границу: в ТЕКСТЕ элемента платформа экранирует
только & < > (кавычка и апостроф возвращаются сырыми байт-в-байт), а в ЗНАЧЕНИИ
АТРИБУТА пишет &quot; — внутри "..." литеральная кавычка невалидна. Поэтому две
функции с говорящими именами, а не одна с флагом:
  esc_xml      — значение атрибута: & < > "
  esc_xml_text — текст элемента:    & < >

Заодно закрыто расхождение портов в init-навыках: PY экранировал текст с &quot;,
PS1 — через SecurityElement::Escape (ещё и &apos;), а epf-init/erf-init в PS1 не
экранировали вовсе, из-за чего амперсанд в синониме давал невалидный XML.

Кейсы: form-compile/attr-value-escaping, skd-compile/additional-properties-escaping,
cf-init/synonym-escaping — все три проверены негативным прогоном на обоих портах
и верификацией снэпшотов на платформе.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-08 20:53:12 +03:00
co-authored by Claude Opus 5
parent 3c50d3caa0
commit 974ca3aac9
43 changed files with 1818 additions and 882 deletions
+72 -66
View File
@@ -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('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
}
function Esc-XmlText {
param([string]$s)
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
@@ -1008,16 +1014,16 @@ function Build-ValueTypeXml {
}
if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.') {
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-Xml $typeStr)</v8:Type>"
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-XmlText $typeStr)</v8:Type>"
return $lines -join "`n"
}
if ($typeStr.Contains('.')) {
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-Xml $typeStr)</v8:Type>"
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-XmlText $typeStr)</v8:Type>"
return $lines -join "`n"
}
$lines += "$indent<v8:Type>$(Esc-Xml $typeStr)</v8:Type>"
$lines += "$indent<v8:Type>$(Esc-XmlText $typeStr)</v8:Type>"
return $lines -join "`n"
}
@@ -1073,7 +1079,7 @@ function Build-MLTextXml {
$lines += "$indent<$tag xsi:type=`"v8:LocalStringType`">"
$lines += "$indent`t<v8:item>"
$lines += "$indent`t`t<v8:lang>ru</v8:lang>"
$lines += "$indent`t`t<v8:content>$(Esc-Xml $text)</v8:content>"
$lines += "$indent`t`t<v8:content>$(Esc-XmlText $text)</v8:content>"
$lines += "$indent`t</v8:item>"
$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 = '(<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>)[^<]*(</v8:content>\s*</v8:item>)'
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<field xsi:type=`"DataSetFieldField`">"
$lines += "$i`t<dataPath>$(Esc-Xml $parsed.dataPath)</dataPath>"
$lines += "$i`t<field>$(Esc-Xml $parsed.field)</field>"
$lines += "$i`t<dataPath>$(Esc-XmlText $parsed.dataPath)</dataPath>"
$lines += "$i`t<field>$(Esc-XmlText $parsed.field)</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<totalField>"
$lines += "$i`t<dataPath>$(Esc-Xml $parsed.dataPath)</dataPath>"
$lines += "$i`t<expression>$(Esc-Xml $parsed.expression)</expression>"
$lines += "$i`t<dataPath>$(Esc-XmlText $parsed.dataPath)</dataPath>"
$lines += "$i`t<expression>$(Esc-XmlText $parsed.expression)</expression>"
$lines += "$i</totalField>"
return $lines -join "`n"
}
@@ -1211,8 +1217,8 @@ function Build-CalcFieldFragment {
$i = $indent
$lines = @()
$lines += "$i<calculatedField>"
$lines += "$i`t<dataPath>$(Esc-Xml $parsed.dataPath)</dataPath>"
$lines += "$i`t<expression>$(Esc-Xml $parsed.expression)</expression>"
$lines += "$i`t<dataPath>$(Esc-XmlText $parsed.dataPath)</dataPath>"
$lines += "$i`t<expression>$(Esc-XmlText $parsed.expression)</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<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-Xml $valStr)</v8:variant>"
$lines += "$i`t<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-XmlText $valStr)</v8:variant>"
$lines += "$i`t<v8:startDate>0001-01-01T00:00:00</v8:startDate>"
$lines += "$i`t<v8:endDate>0001-01-01T00:00:00</v8:endDate>"
$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<presentation xsi:type=`"v8:LocalStringType`">"
$lines += "$indent`t`t<v8:item>"
$lines += "$indent`t`t`t<v8:lang>ru</v8:lang>"
$lines += "$indent`t`t`t<v8:content>$(Esc-Xml $item.presentation)</v8:content>"
$lines += "$indent`t`t`t<v8:content>$(Esc-XmlText $item.presentation)</v8:content>"
$lines += "$indent`t`t</v8:item>"
$lines += "$indent`t</presentation>"
}
@@ -1307,7 +1313,7 @@ function Build-ParamFragment {
$lines = @()
$lines += "$i<parameter>"
$lines += "$i`t<name>$(Esc-Xml $parsed.name)</name>"
$lines += "$i`t<name>$(Esc-XmlText $parsed.name)</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</valueType>"
$bLines += "$i`t<value xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</value>"
$bLines += "$i`t<useRestriction>true</useRestriction>"
$bLines += "$i`t<expression>$(Esc-Xml "&$paramName.ДатаНачала")</expression>"
$bLines += "$i`t<expression>$(Esc-XmlText "&$paramName.ДатаНачала")</expression>"
$bLines += "$i</parameter>"
$fragments += ($bLines -join "`n")
@@ -1386,7 +1392,7 @@ function Build-ParamFragment {
$eLines += "$i`t</valueType>"
$eLines += "$i`t<value xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</value>"
$eLines += "$i`t<useRestriction>true</useRestriction>"
$eLines += "$i`t<expression>$(Esc-Xml "&$paramName.ДатаОкончания")</expression>"
$eLines += "$i`t<expression>$(Esc-XmlText "&$paramName.ДатаОкончания")</expression>"
$eLines += "$i</parameter>"
$fragments += ($eLines -join "`n")
}
@@ -1405,21 +1411,21 @@ function Build-FilterItemFragment {
$lines += "$i`t<dcsset:use>false</dcsset:use>"
}
$lines += "$i`t<dcsset:left xsi:type=`"dcscor:Field`">$(Esc-Xml $parsed.field)</dcsset:left>"
$lines += "$i`t<dcsset:comparisonType>$(Esc-Xml $parsed.op)</dcsset:comparisonType>"
$lines += "$i`t<dcsset:left xsi:type=`"dcscor:Field`">$(Esc-XmlText $parsed.field)</dcsset:left>"
$lines += "$i`t<dcsset:comparisonType>$(Esc-XmlText $parsed.op)</dcsset:comparisonType>"
if ($null -ne $parsed.value) {
$vt = if ($parsed["valueType"]) { $parsed["valueType"] } else { "xs:string" }
$lines += "$i`t<dcsset:right xsi:type=`"$vt`">$(Esc-Xml "$($parsed.value)")</dcsset:right>"
$lines += "$i`t<dcsset:right xsi:type=`"$vt`">$(Esc-XmlText "$($parsed.value)")</dcsset:right>"
}
if ($parsed.viewMode) {
$lines += "$i`t<dcsset:viewMode>$(Esc-Xml $parsed.viewMode)</dcsset:viewMode>"
$lines += "$i`t<dcsset:viewMode>$(Esc-XmlText $parsed.viewMode)</dcsset:viewMode>"
}
if ($parsed.userSettingID) {
$uid = if ($parsed.userSettingID -eq "auto") { [System.Guid]::NewGuid().ToString() } else { $parsed.userSettingID }
$lines += "$i`t<dcsset:userSettingID>$(Esc-Xml $uid)</dcsset:userSettingID>"
$lines += "$i`t<dcsset:userSettingID>$(Esc-XmlText $uid)</dcsset:userSettingID>"
}
$lines += "$i</dcsset:item>"
@@ -1448,20 +1454,20 @@ function Build-SelectionItemFragment {
$lines += "$i`t<dcsset:lwsTitle>"
$lines += "$i`t`t<v8:item>"
$lines += "$i`t`t`t<v8:lang>ru</v8:lang>"
$lines += "$i`t`t`t<v8:content>$(Esc-Xml $title)</v8:content>"
$lines += "$i`t`t`t<v8:content>$(Esc-XmlText $title)</v8:content>"
$lines += "$i`t`t</v8:item>"
$lines += "$i`t</dcsset:lwsTitle>"
}
foreach ($item in $items) {
$lines += "$i`t<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
$lines += "$i`t`t<dcsset:field>$(Esc-Xml $item)</dcsset:field>"
$lines += "$i`t`t<dcsset:field>$(Esc-XmlText $item)</dcsset:field>"
$lines += "$i`t</dcsset:item>"
}
$lines += "$i`t<dcsset:placement>Auto</dcsset:placement>"
$lines += "$i</dcsset:item>"
} else {
$lines += "$i<dcsset:item xsi:type=`"dcsset:SelectedItemField`">"
$lines += "$i`t<dcsset:field>$(Esc-Xml $fieldName)</dcsset:field>"
$lines += "$i`t<dcsset:field>$(Esc-XmlText $fieldName)</dcsset:field>"
$lines += "$i</dcsset:item>"
}
return $lines -join "`n"
@@ -1478,33 +1484,33 @@ function Build-DataParamFragment {
$lines += "$i`t<dcscor:use>false</dcscor:use>"
}
$lines += "$i`t<dcscor:parameter>$(Esc-Xml $parsed.parameter)</dcscor:parameter>"
$lines += "$i`t<dcscor:parameter>$(Esc-XmlText $parsed.parameter)</dcscor:parameter>"
if ($null -ne $parsed.value) {
if ($parsed.value -is [hashtable] -and $parsed.value.variant) {
$lines += "$i`t<dcscor:value xsi:type=`"v8:StandardPeriod`">"
$lines += "$i`t`t<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-Xml $parsed.value.variant)</v8:variant>"
$lines += "$i`t`t<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-XmlText $parsed.value.variant)</v8:variant>"
$lines += "$i`t`t<v8:startDate>0001-01-01T00:00:00</v8:startDate>"
$lines += "$i`t`t<v8:endDate>0001-01-01T00:00:00</v8:endDate>"
$lines += "$i`t</dcscor:value>"
} elseif (Test-EmptyValue $parsed.value) {
$lines += "$i`t<dcscor:value xsi:nil=`"true`"/>"
} elseif ("$($parsed.value)" -match '^\d{4}-\d{2}-\d{2}T') {
$lines += "$i`t<dcscor:value xsi:type=`"xs:dateTime`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$lines += "$i`t<dcscor:value xsi:type=`"xs:dateTime`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
} elseif ("$($parsed.value)" -eq "true" -or "$($parsed.value)" -eq "false") {
$lines += "$i`t<dcscor:value xsi:type=`"xs:boolean`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$lines += "$i`t<dcscor:value xsi:type=`"xs:boolean`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
} else {
$lines += "$i`t<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$lines += "$i`t<dcscor:value xsi:type=`"xs:string`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
}
}
if ($parsed.viewMode) {
$lines += "$i`t<dcsset:viewMode>$(Esc-Xml $parsed.viewMode)</dcsset:viewMode>"
$lines += "$i`t<dcsset:viewMode>$(Esc-XmlText $parsed.viewMode)</dcsset:viewMode>"
}
if ($parsed.userSettingID) {
$uid = if ($parsed.userSettingID -eq "auto") { [System.Guid]::NewGuid().ToString() } else { $parsed.userSettingID }
$lines += "$i`t<dcsset:userSettingID>$(Esc-Xml $uid)</dcsset:userSettingID>"
$lines += "$i`t<dcsset:userSettingID>$(Esc-XmlText $uid)</dcsset:userSettingID>"
}
$lines += "$i</dcscor:item>"
@@ -1520,7 +1526,7 @@ function Build-OrderItemFragment {
$lines += "$i<dcsset:item xsi:type=`"dcsset:OrderItemAuto`"/>"
} else {
$lines += "$i<dcsset:item xsi:type=`"dcsset:OrderItemField`">"
$lines += "$i`t<dcsset:field>$(Esc-Xml $parsed.field)</dcsset:field>"
$lines += "$i`t<dcsset:field>$(Esc-XmlText $parsed.field)</dcsset:field>"
$lines += "$i`t<dcsset:orderType>$($parsed.direction)</dcsset:orderType>"
$lines += "$i</dcsset:item>"
}
@@ -1533,12 +1539,12 @@ function Build-DataSetLinkFragment {
$i = $indent
$lines = @()
$lines += "$i<dataSetLink>"
$lines += "$i`t<sourceDataSet>$(Esc-Xml $parsed.source)</sourceDataSet>"
$lines += "$i`t<destinationDataSet>$(Esc-Xml $parsed.dest)</destinationDataSet>"
$lines += "$i`t<sourceExpression>$(Esc-Xml $parsed.sourceExpr)</sourceExpression>"
$lines += "$i`t<destinationExpression>$(Esc-Xml $parsed.destExpr)</destinationExpression>"
$lines += "$i`t<sourceDataSet>$(Esc-XmlText $parsed.source)</sourceDataSet>"
$lines += "$i`t<destinationDataSet>$(Esc-XmlText $parsed.dest)</destinationDataSet>"
$lines += "$i`t<sourceExpression>$(Esc-XmlText $parsed.sourceExpr)</sourceExpression>"
$lines += "$i`t<destinationExpression>$(Esc-XmlText $parsed.destExpr)</destinationExpression>"
if ($parsed.parameter) {
$lines += "$i`t<parameter>$(Esc-Xml $parsed.parameter)</parameter>"
$lines += "$i`t<parameter>$(Esc-XmlText $parsed.parameter)</parameter>"
}
$lines += "$i</dataSetLink>"
return $lines -join "`n"
@@ -1550,9 +1556,9 @@ function Build-DataSetQueryFragment {
$i = $indent
$lines = @()
$lines += "$i<dataSet xsi:type=`"DataSetQuery`">"
$lines += "$i`t<name>$(Esc-Xml $parsed.name)</name>"
$lines += "$i`t<dataSource>$(Esc-Xml $parsed.dataSource)</dataSource>"
$lines += "$i`t<query>$(Esc-Xml $parsed.query)</query>"
$lines += "$i`t<name>$(Esc-XmlText $parsed.name)</name>"
$lines += "$i`t<dataSource>$(Esc-XmlText $parsed.dataSource)</dataSource>"
$lines += "$i`t<query>$(Esc-XmlText $parsed.query)</query>"
$lines += "$i</dataSet>"
return $lines -join "`n"
}
@@ -1563,7 +1569,7 @@ function Build-VariantFragment {
$i = $indent
$lines = @()
$lines += "$i<settingsVariant>"
$lines += "$i`t<dcsset:name>$(Esc-Xml $parsed.name)</dcsset:name>"
$lines += "$i`t<dcsset:name>$(Esc-XmlText $parsed.name)</dcsset:name>"
$lines += (Build-MLTextXml -tag "dcsset:presentation" -text $parsed.presentation -indent "$i`t")
$lines += "$i`t<dcsset:settings xmlns:style=`"http://v8.1c.ru/8.1/data/ui/style`" xmlns:sys=`"http://v8.1c.ru/8.1/data/ui/fonts/system`" xmlns:web=`"http://v8.1c.ru/8.1/data/ui/colors/web`" xmlns:win=`"http://v8.1c.ru/8.1/data/ui/colors/windows`">"
$lines += "$i`t`t<dcsset:selection>"
@@ -1587,11 +1593,11 @@ function Emit-FilterComparison {
param($f, [string]$indent)
$lines = @()
$lines += "$indent<dcsset:item xsi:type=`"dcsset:FilterItemComparison`">"
$lines += "$indent`t<dcsset:left xsi:type=`"dcscor:Field`">$(Esc-Xml $f.field)</dcsset:left>"
$lines += "$indent`t<dcsset:comparisonType>$(Esc-Xml $f.op)</dcsset:comparisonType>"
$lines += "$indent`t<dcsset:left xsi:type=`"dcscor:Field`">$(Esc-XmlText $f.field)</dcsset:left>"
$lines += "$indent`t<dcsset:comparisonType>$(Esc-XmlText $f.op)</dcsset:comparisonType>"
if ($null -ne $f.value) {
$vt = if ($f["valueType"]) { $f["valueType"] } else { "xs:string" }
$lines += "$indent`t<dcsset:right xsi:type=`"$vt`">$(Esc-Xml "$($f.value)")</dcsset:right>"
$lines += "$indent`t<dcsset:right xsi:type=`"$vt`">$(Esc-XmlText "$($f.value)")</dcsset:right>"
}
$lines += "$indent</dcsset:item>"
return $lines
@@ -1609,7 +1615,7 @@ function Build-ConditionalAppearanceItemFragment {
$lines += "$i`t<dcsset:selection>"
foreach ($fld in $parsed.fields) {
$lines += "$i`t`t<dcsset:item>"
$lines += "$i`t`t`t<dcsset:field>$(Esc-Xml $fld)</dcsset:field>"
$lines += "$i`t`t`t<dcsset:field>$(Esc-XmlText $fld)</dcsset:field>"
$lines += "$i`t`t</dcsset:item>"
}
$lines += "$i`t</dcsset:selection>"
@@ -1641,21 +1647,21 @@ function Build-ConditionalAppearanceItemFragment {
$val = $parsed.value
$lines += "$i`t`t<dcscor:item xsi:type=`"dcsset:SettingsParameterValue`">"
$lines += "$i`t`t`t<dcscor:parameter>$(Esc-Xml $parsed.param)</dcscor:parameter>"
$lines += "$i`t`t`t<dcscor:parameter>$(Esc-XmlText $parsed.param)</dcscor:parameter>"
if ($val -match '^(web|style|win):') {
$lines += "$i`t`t`t<dcscor:value xsi:type=`"v8ui:Color`">$(Esc-Xml $val)</dcscor:value>"
$lines += "$i`t`t`t<dcscor:value xsi:type=`"v8ui:Color`">$(Esc-XmlText $val)</dcscor:value>"
} elseif ($val -eq "true" -or $val -eq "false") {
$lines += "$i`t`t`t<dcscor:value xsi:type=`"xs:boolean`">$(Esc-Xml $val)</dcscor:value>"
$lines += "$i`t`t`t<dcscor:value xsi:type=`"xs:boolean`">$(Esc-XmlText $val)</dcscor:value>"
} elseif ($parsed.param -eq "Формат" -or $parsed.param -eq "Текст" -or $parsed.param -eq "Заголовок") {
$lines += "$i`t`t`t<dcscor:value xsi:type=`"v8:LocalStringType`">"
$lines += "$i`t`t`t`t<v8:item>"
$lines += "$i`t`t`t`t`t<v8:lang>ru</v8:lang>"
$lines += "$i`t`t`t`t`t<v8:content>$(Esc-Xml $val)</v8:content>"
$lines += "$i`t`t`t`t`t<v8:content>$(Esc-XmlText $val)</v8:content>"
$lines += "$i`t`t`t`t</v8:item>"
$lines += "$i`t`t`t</dcscor:value>"
} else {
$lines += "$i`t`t`t<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml $val)</dcscor:value>"
$lines += "$i`t`t`t<dcscor:value xsi:type=`"xs:string`">$(Esc-XmlText $val)</dcscor:value>"
}
$lines += "$i`t`t</dcscor:item>"
@@ -1674,7 +1680,7 @@ function Build-StructureItemFragment {
# name
if ($item["name"]) {
$lines += "$i`t<dcsset:name>$(Esc-Xml $item["name"])</dcsset:name>"
$lines += "$i`t<dcsset:name>$(Esc-XmlText $item["name"])</dcsset:name>"
}
# groupItems
@@ -1685,7 +1691,7 @@ function Build-StructureItemFragment {
$lines += "$i`t<dcsset:groupItems>"
foreach ($field in $groupBy) {
$lines += "$i`t`t<dcsset:item xsi:type=`"dcsset:GroupItemField`">"
$lines += "$i`t`t`t<dcsset:field>$(Esc-Xml $field)</dcsset:field>"
$lines += "$i`t`t`t<dcsset:field>$(Esc-XmlText $field)</dcsset:field>"
$lines += "$i`t`t`t<dcsset:groupType>Items</dcsset:groupType>"
$lines += "$i`t`t`t<dcsset:periodAdditionType>None</dcsset:periodAdditionType>"
$lines += "$i`t`t`t<dcsset:periodAdditionBegin xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</dcsset:periodAdditionBegin>"
@@ -1728,17 +1734,17 @@ function Build-OutputParamFragment {
$lines = @()
$lines += "$i<dcscor:item xsi:type=`"dcsset:SettingsParameterValue`">"
$lines += "$i`t<dcscor:parameter>$(Esc-Xml $key)</dcscor:parameter>"
$lines += "$i`t<dcscor:parameter>$(Esc-XmlText $key)</dcscor:parameter>"
if ($ptype -eq "mltext") {
$lines += "$i`t<dcscor:value xsi:type=`"v8:LocalStringType`">"
$lines += "$i`t`t<v8:item>"
$lines += "$i`t`t`t<v8:lang>ru</v8:lang>"
$lines += "$i`t`t`t<v8:content>$(Esc-Xml $val)</v8:content>"
$lines += "$i`t`t`t<v8:content>$(Esc-XmlText $val)</v8:content>"
$lines += "$i`t`t</v8:item>"
$lines += "$i`t</dcscor:value>"
} else {
$lines += "$i`t<dcscor:value xsi:type=`"$ptype`">$(Esc-Xml $val)</dcscor:value>"
$lines += "$i`t<dcscor:value xsi:type=`"$ptype`">$(Esc-XmlText $val)</dcscor:value>"
}
$lines += "$i</dcscor:item>"
@@ -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<dcsset:item xsi:type=`"dcsset:GroupItemField`">"
$lines += "$itemIndent`t<dcsset:field>$(Esc-Xml $field)</dcsset:field>"
$lines += "$itemIndent`t<dcsset:field>$(Esc-XmlText $field)</dcsset:field>"
$lines += "$itemIndent`t<dcsset:groupType>Items</dcsset:groupType>"
$lines += "$itemIndent`t<dcsset:periodAdditionType>None</dcsset:periodAdditionType>"
$lines += "$itemIndent`t<dcsset:periodAdditionBegin xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</dcsset:periodAdditionBegin>"
@@ -3563,18 +3569,18 @@ switch ($Operation) {
$valLines = @()
if ($parsed.value -is [hashtable] -and $parsed.value.variant) {
$valLines += "$itemIndent<dcscor:value xsi:type=`"v8:StandardPeriod`">"
$valLines += "$itemIndent`t<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-Xml $parsed.value.variant)</v8:variant>"
$valLines += "$itemIndent`t<v8:variant xsi:type=`"v8:StandardPeriodVariant`">$(Esc-XmlText $parsed.value.variant)</v8:variant>"
$valLines += "$itemIndent`t<v8:startDate>0001-01-01T00:00:00</v8:startDate>"
$valLines += "$itemIndent`t<v8:endDate>0001-01-01T00:00:00</v8:endDate>"
$valLines += "$itemIndent</dcscor:value>"
} elseif (Test-EmptyValue $parsed.value) {
$valLines += "$itemIndent<dcscor:value xsi:nil=`"true`"/>"
} elseif ("$($parsed.value)" -match '^\d{4}-\d{2}-\d{2}T') {
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:dateTime`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:dateTime`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
} elseif ("$($parsed.value)" -eq "true" -or "$($parsed.value)" -eq "false") {
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:boolean`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:boolean`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
} else {
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:string`">$(Esc-XmlText "$($parsed.value)")</dcscor:value>"
}
$valXml = $valLines -join "`n"
$valNodes = Import-Fragment $xmlDoc $valXml
@@ -3748,7 +3754,7 @@ switch ($Operation) {
}
}
foreach ($k in $kv.Keys) {
$lines += "$fieldIndent`t<dcscom:$k>$(Esc-Xml $kv[$k])</dcscom:$k>"
$lines += "$fieldIndent`t<dcscom:$k>$(Esc-XmlText $kv[$k])</dcscom:$k>"
}
foreach ($raw in $preservedRoleChildren) {
$lines += "$fieldIndent`t" + $raw
+71 -66
View File
@@ -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('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
def esc_xml_text(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
@@ -932,7 +937,7 @@ def build_available_value_fragment(item, declared_type, indent):
lines.append(f'{indent}\t<presentation xsi:type="v8:LocalStringType">')
lines.append(f"{indent}\t\t<v8:item>")
lines.append(f"{indent}\t\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{indent}\t\t\t<v8:content>{esc_xml(item['presentation'])}</v8:content>")
lines.append(f"{indent}\t\t\t<v8:content>{esc_xml_text(item['presentation'])}</v8:content>")
lines.append(f"{indent}\t\t</v8:item>")
lines.append(f"{indent}\t</presentation>")
lines.append(f"{indent}</availableValue>")
@@ -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}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml(type_str)}</v8:Type>')
lines.append(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml_text(type_str)}</v8:Type>')
return "\n".join(lines)
if "." in type_str:
lines.append(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml(type_str)}</v8:Type>')
lines.append(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml_text(type_str)}</v8:Type>')
return "\n".join(lines)
lines.append(f"{indent}<v8:Type>{esc_xml(type_str)}</v8:Type>")
lines.append(f"{indent}<v8:Type>{esc_xml_text(type_str)}</v8:Type>")
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<v8:item>",
f"{indent}\t\t<v8:lang>ru</v8:lang>",
f"{indent}\t\t<v8:content>{esc_xml(text)}</v8:content>",
f"{indent}\t\t<v8:content>{esc_xml_text(text)}</v8:content>",
f"{indent}\t</v8:item>",
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 <v8:content> 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"(<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>)[^<]*(</v8:content>\s*</v8:item>)"
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}<field xsi:type="DataSetFieldField">']
lines.append(f"{i}\t<dataPath>{esc_xml(parsed['dataPath'])}</dataPath>")
lines.append(f"{i}\t<field>{esc_xml(parsed['field'])}</field>")
lines.append(f"{i}\t<dataPath>{esc_xml_text(parsed['dataPath'])}</dataPath>")
lines.append(f"{i}\t<field>{esc_xml_text(parsed['field'])}</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}<totalField>",
f"{i}\t<dataPath>{esc_xml(parsed['dataPath'])}</dataPath>",
f"{i}\t<expression>{esc_xml(parsed['expression'])}</expression>",
f"{i}\t<dataPath>{esc_xml_text(parsed['dataPath'])}</dataPath>",
f"{i}\t<expression>{esc_xml_text(parsed['expression'])}</expression>",
f"{i}</totalField>",
]
return "\n".join(lines)
@@ -1170,8 +1175,8 @@ def build_calc_field_fragment(parsed, indent):
i = indent
lines = [
f"{i}<calculatedField>",
f"{i}\t<dataPath>{esc_xml(parsed['dataPath'])}</dataPath>",
f"{i}\t<expression>{esc_xml(parsed['expression'])}</expression>",
f"{i}\t<dataPath>{esc_xml_text(parsed['dataPath'])}</dataPath>",
f"{i}\t<expression>{esc_xml_text(parsed['expression'])}</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<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml(val_str)}</v8:variant>')
lines.append(f'{indent}\t<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml_text(val_str)}</v8:variant>')
lines.append(f"{indent}\t<v8:startDate>0001-01-01T00:00:00</v8:startDate>")
lines.append(f"{indent}\t<v8:endDate>0001-01-01T00:00:00</v8:endDate>")
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}<parameter>", f"{i}\t<name>{esc_xml(parsed['name'])}</name>"]
lines = [f"{i}<parameter>", f"{i}\t<name>{esc_xml_text(parsed['name'])}</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}<parameter>",
f"{i}\t<name>\u0414\u0430\u0442\u0430\u041d\u0430\u0447\u0430\u043b\u0430</name>",
@@ -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}<parameter>",
f"{i}\t<name>\u0414\u0430\u0442\u0430\u041e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f</name>",
@@ -1317,19 +1322,19 @@ def build_filter_item_fragment(parsed, indent):
if parsed.get("use") is False:
lines.append(f"{i}\t<dcsset:use>false</dcsset:use>")
lines.append(f'{i}\t<dcsset:left xsi:type="dcscor:Field">{esc_xml(parsed["field"])}</dcsset:left>')
lines.append(f"{i}\t<dcsset:comparisonType>{esc_xml(parsed['op'])}</dcsset:comparisonType>")
lines.append(f'{i}\t<dcsset:left xsi:type="dcscor:Field">{esc_xml_text(parsed["field"])}</dcsset:left>')
lines.append(f"{i}\t<dcsset:comparisonType>{esc_xml_text(parsed['op'])}</dcsset:comparisonType>")
if parsed.get("value") is not None:
vt = parsed.get("valueType", "xs:string")
lines.append(f'{i}\t<dcsset:right xsi:type="{vt}">{esc_xml(str(parsed["value"]))}</dcsset:right>')
lines.append(f'{i}\t<dcsset:right xsi:type="{vt}">{esc_xml_text(str(parsed["value"]))}</dcsset:right>')
if parsed.get("viewMode"):
lines.append(f"{i}\t<dcsset:viewMode>{esc_xml(parsed['viewMode'])}</dcsset:viewMode>")
lines.append(f"{i}\t<dcsset:viewMode>{esc_xml_text(parsed['viewMode'])}</dcsset:viewMode>")
if parsed.get("userSettingID"):
uid = new_uuid() if parsed["userSettingID"] == "auto" else parsed["userSettingID"]
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml(uid)}</dcsset:userSettingID>")
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml_text(uid)}</dcsset:userSettingID>")
lines.append(f"{i}</dcsset:item>")
return "\n".join(lines)
@@ -1354,19 +1359,19 @@ def build_selection_item_fragment(field_name, indent):
lines.append(f"{i}\t<dcsset:lwsTitle>")
lines.append(f"{i}\t\t<v8:item>")
lines.append(f"{i}\t\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{i}\t\t\t<v8:content>{esc_xml(title)}</v8:content>")
lines.append(f"{i}\t\t\t<v8:content>{esc_xml_text(title)}</v8:content>")
lines.append(f"{i}\t\t</v8:item>")
lines.append(f"{i}\t</dcsset:lwsTitle>")
for item in items:
lines.append(f'{i}\t<dcsset:item xsi:type="dcsset:SelectedItemField">')
lines.append(f"{i}\t\t<dcsset:field>{esc_xml(item)}</dcsset:field>")
lines.append(f"{i}\t\t<dcsset:field>{esc_xml_text(item)}</dcsset:field>")
lines.append(f"{i}\t</dcsset:item>")
lines.append(f"{i}\t<dcsset:placement>Auto</dcsset:placement>")
lines.append(f"{i}</dcsset:item>")
return "\n".join(lines)
lines = [
f'{i}<dcsset:item xsi:type="dcsset:SelectedItemField">',
f"{i}\t<dcsset:field>{esc_xml(field_name)}</dcsset:field>",
f"{i}\t<dcsset:field>{esc_xml_text(field_name)}</dcsset:field>",
f"{i}</dcsset:item>",
]
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}\t<dcscor:use>false</dcscor:use>")
lines.append(f"{i}\t<dcscor:parameter>{esc_xml(parsed['parameter'])}</dcscor:parameter>")
lines.append(f"{i}\t<dcscor:parameter>{esc_xml_text(parsed['parameter'])}</dcscor:parameter>")
if parsed.get("value") is not None:
val = parsed["value"]
if isinstance(val, dict) and val.get("variant"):
lines.append(f'{i}\t<dcscor:value xsi:type="v8:StandardPeriod">')
lines.append(f'{i}\t\t<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml(val["variant"])}</v8:variant>')
lines.append(f'{i}\t\t<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml_text(val["variant"])}</v8:variant>')
lines.append(f"{i}\t\t<v8:startDate>0001-01-01T00:00:00</v8:startDate>")
lines.append(f"{i}\t\t<v8:endDate>0001-01-01T00:00:00</v8:endDate>")
lines.append(f"{i}\t</dcscor:value>")
elif is_empty_value(val):
lines.append(f'{i}\t<dcscor:value xsi:nil="true"/>')
elif re.match(r'^\d{4}-\d{2}-\d{2}T', str(val)):
lines.append(f'{i}\t<dcscor:value xsi:type="xs:dateTime">{esc_xml(str(val))}</dcscor:value>')
lines.append(f'{i}\t<dcscor:value xsi:type="xs:dateTime">{esc_xml_text(str(val))}</dcscor:value>')
elif str(val) in ("true", "false"):
lines.append(f'{i}\t<dcscor:value xsi:type="xs:boolean">{esc_xml(str(val))}</dcscor:value>')
lines.append(f'{i}\t<dcscor:value xsi:type="xs:boolean">{esc_xml_text(str(val))}</dcscor:value>')
else:
lines.append(f'{i}\t<dcscor:value xsi:type="xs:string">{esc_xml(str(val))}</dcscor:value>')
lines.append(f'{i}\t<dcscor:value xsi:type="xs:string">{esc_xml_text(str(val))}</dcscor:value>')
if parsed.get("viewMode"):
lines.append(f"{i}\t<dcsset:viewMode>{esc_xml(parsed['viewMode'])}</dcsset:viewMode>")
lines.append(f"{i}\t<dcsset:viewMode>{esc_xml_text(parsed['viewMode'])}</dcsset:viewMode>")
if parsed.get("userSettingID"):
uid = new_uuid() if parsed["userSettingID"] == "auto" else parsed["userSettingID"]
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml(uid)}</dcsset:userSettingID>")
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml_text(uid)}</dcsset:userSettingID>")
lines.append(f"{i}</dcscor:item>")
return "\n".join(lines)
@@ -1415,7 +1420,7 @@ def build_order_item_fragment(parsed, indent):
return f'{i}<dcsset:item xsi:type="dcsset:OrderItemAuto"/>'
lines = [
f'{i}<dcsset:item xsi:type="dcsset:OrderItemField">',
f"{i}\t<dcsset:field>{esc_xml(parsed['field'])}</dcsset:field>",
f"{i}\t<dcsset:field>{esc_xml_text(parsed['field'])}</dcsset:field>",
f"{i}\t<dcsset:orderType>{parsed['direction']}</dcsset:orderType>",
f"{i}</dcsset:item>",
]
@@ -1426,13 +1431,13 @@ def build_data_set_link_fragment(parsed, indent):
i = indent
lines = [
f"{i}<dataSetLink>",
f"{i}\t<sourceDataSet>{esc_xml(parsed['source'])}</sourceDataSet>",
f"{i}\t<destinationDataSet>{esc_xml(parsed['dest'])}</destinationDataSet>",
f"{i}\t<sourceExpression>{esc_xml(parsed['sourceExpr'])}</sourceExpression>",
f"{i}\t<destinationExpression>{esc_xml(parsed['destExpr'])}</destinationExpression>",
f"{i}\t<sourceDataSet>{esc_xml_text(parsed['source'])}</sourceDataSet>",
f"{i}\t<destinationDataSet>{esc_xml_text(parsed['dest'])}</destinationDataSet>",
f"{i}\t<sourceExpression>{esc_xml_text(parsed['sourceExpr'])}</sourceExpression>",
f"{i}\t<destinationExpression>{esc_xml_text(parsed['destExpr'])}</destinationExpression>",
]
if parsed.get("parameter"):
lines.append(f"{i}\t<parameter>{esc_xml(parsed['parameter'])}</parameter>")
lines.append(f"{i}\t<parameter>{esc_xml_text(parsed['parameter'])}</parameter>")
lines.append(f"{i}</dataSetLink>")
return "\n".join(lines)
@@ -1441,9 +1446,9 @@ def build_data_set_query_fragment(parsed, indent):
i = indent
lines = [
f'{i}<dataSet xsi:type="DataSetQuery">',
f"{i}\t<name>{esc_xml(parsed['name'])}</name>",
f"{i}\t<dataSource>{esc_xml(parsed['dataSource'])}</dataSource>",
f"{i}\t<query>{esc_xml(parsed['query'])}</query>",
f"{i}\t<name>{esc_xml_text(parsed['name'])}</name>",
f"{i}\t<dataSource>{esc_xml_text(parsed['dataSource'])}</dataSource>",
f"{i}\t<query>{esc_xml_text(parsed['query'])}</query>",
f"{i}</dataSet>",
]
return "\n".join(lines)
@@ -1453,7 +1458,7 @@ def build_variant_fragment(parsed, indent):
i = indent
lines = [
f"{i}<settingsVariant>",
f"{i}\t<dcsset:name>{esc_xml(parsed['name'])}</dcsset:name>",
f"{i}\t<dcsset:name>{esc_xml_text(parsed['name'])}</dcsset:name>",
build_mltext_xml("dcsset:presentation", parsed["presentation"], f"{i}\t"),
f'{i}\t<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">',
f"{i}\t\t<dcsset:selection>",
@@ -1476,11 +1481,11 @@ def build_variant_fragment(parsed, indent):
def _emit_filter_comparison(lines, f, indent):
lines.append(f'{indent}<dcsset:item xsi:type="dcsset:FilterItemComparison">')
lines.append(f'{indent}\t<dcsset:left xsi:type="dcscor:Field">{esc_xml(f["field"])}</dcsset:left>')
lines.append(f"{indent}\t<dcsset:comparisonType>{esc_xml(f['op'])}</dcsset:comparisonType>")
lines.append(f'{indent}\t<dcsset:left xsi:type="dcscor:Field">{esc_xml_text(f["field"])}</dcsset:left>')
lines.append(f"{indent}\t<dcsset:comparisonType>{esc_xml_text(f['op'])}</dcsset:comparisonType>")
if f.get("value") is not None:
vt = f.get("valueType", "xs:string")
lines.append(f'{indent}\t<dcsset:right xsi:type="{vt}">{esc_xml(str(f["value"]))}</dcsset:right>')
lines.append(f'{indent}\t<dcsset:right xsi:type="{vt}">{esc_xml_text(str(f["value"]))}</dcsset:right>')
lines.append(f"{indent}</dcsset:item>")
@@ -1492,7 +1497,7 @@ def build_conditional_appearance_item_fragment(parsed, indent):
lines.append(f"{i}\t<dcsset:selection>")
for fld in parsed["fields"]:
lines.append(f"{i}\t\t<dcsset:item>")
lines.append(f"{i}\t\t\t<dcsset:field>{esc_xml(fld)}</dcsset:field>")
lines.append(f"{i}\t\t\t<dcsset:field>{esc_xml_text(fld)}</dcsset:field>")
lines.append(f"{i}\t\t</dcsset:item>")
lines.append(f"{i}\t</dcsset:selection>")
else:
@@ -1518,21 +1523,21 @@ def build_conditional_appearance_item_fragment(parsed, indent):
lines.append(f"{i}\t<dcsset:appearance>")
val = parsed["value"]
lines.append(f'{i}\t\t<dcscor:item xsi:type="dcsset:SettingsParameterValue">')
lines.append(f"{i}\t\t\t<dcscor:parameter>{esc_xml(parsed['param'])}</dcscor:parameter>")
lines.append(f"{i}\t\t\t<dcscor:parameter>{esc_xml_text(parsed['param'])}</dcscor:parameter>")
if re.match(r'^(web|style|win):', val):
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="v8ui:Color">{esc_xml(val)}</dcscor:value>')
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="v8ui:Color">{esc_xml_text(val)}</dcscor:value>')
elif val in ("true", "false"):
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="xs:boolean">{esc_xml(val)}</dcscor:value>')
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="xs:boolean">{esc_xml_text(val)}</dcscor:value>')
elif parsed["param"] in ("Формат", "Текст", "Заголовок"):
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="v8:LocalStringType">')
lines.append(f"{i}\t\t\t\t<v8:item>")
lines.append(f"{i}\t\t\t\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{i}\t\t\t\t\t<v8:content>{esc_xml(val)}</v8:content>")
lines.append(f"{i}\t\t\t\t\t<v8:content>{esc_xml_text(val)}</v8:content>")
lines.append(f"{i}\t\t\t\t</v8:item>")
lines.append(f"{i}\t\t\t</dcscor:value>")
else:
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="xs:string">{esc_xml(val)}</dcscor:value>')
lines.append(f'{i}\t\t\t<dcscor:value xsi:type="xs:string">{esc_xml_text(val)}</dcscor:value>')
lines.append(f"{i}\t\t</dcscor:item>")
lines.append(f"{i}\t</dcsset:appearance>")
@@ -1546,7 +1551,7 @@ def build_structure_item_fragment(item, indent):
lines = [f'{i}<dcsset:item xsi:type="dcsset:StructureItemGroup">']
if item.get("name"):
lines.append(f"{i}\t<dcsset:name>{esc_xml(item['name'])}</dcsset:name>")
lines.append(f"{i}\t<dcsset:name>{esc_xml_text(item['name'])}</dcsset: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<dcsset:groupItems>")
for field in group_by:
lines.append(f'{i}\t\t<dcsset:item xsi:type="dcsset:GroupItemField">')
lines.append(f"{i}\t\t\t<dcsset:field>{esc_xml(field)}</dcsset:field>")
lines.append(f"{i}\t\t\t<dcsset:field>{esc_xml_text(field)}</dcsset:field>")
lines.append(f"{i}\t\t\t<dcsset:groupType>Items</dcsset:groupType>")
lines.append(f"{i}\t\t\t<dcsset:periodAdditionType>None</dcsset:periodAdditionType>")
lines.append(f'{i}\t\t\t<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>')
@@ -1585,17 +1590,17 @@ def build_output_param_fragment(parsed, indent):
ptype = output_param_types.get(key, "xs:string")
lines = [f'{i}<dcscor:item xsi:type="dcsset:SettingsParameterValue">']
lines.append(f"{i}\t<dcscor:parameter>{esc_xml(key)}</dcscor:parameter>")
lines.append(f"{i}\t<dcscor:parameter>{esc_xml_text(key)}</dcscor:parameter>")
if ptype == "mltext":
lines.append(f'{i}\t<dcscor:value xsi:type="v8:LocalStringType">')
lines.append(f"{i}\t\t<v8:item>")
lines.append(f"{i}\t\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{i}\t\t\t<v8:content>{esc_xml(val)}</v8:content>")
lines.append(f"{i}\t\t\t<v8:content>{esc_xml_text(val)}</v8:content>")
lines.append(f"{i}\t\t</v8:item>")
lines.append(f"{i}\t</dcscor:value>")
else:
lines.append(f'{i}\t<dcscor:value xsi:type="{ptype}">{esc_xml(val)}</dcscor:value>')
lines.append(f'{i}\t<dcscor:value xsi:type="{ptype}">{esc_xml_text(val)}</dcscor:value>')
lines.append(f"{i}</dcscor:item>")
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}<dcsset:item xsi:type="dcsset:GroupItemField">',
f'{item_indent}\t<dcsset:field>{esc_xml(field)}</dcsset:field>',
f'{item_indent}\t<dcsset:field>{esc_xml_text(field)}</dcsset:field>',
f'{item_indent}\t<dcsset:groupType>Items</dcsset:groupType>',
f'{item_indent}\t<dcsset:periodAdditionType>None</dcsset:periodAdditionType>',
f'{item_indent}\t<dcsset:periodAdditionBegin xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionBegin>',
@@ -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}<dcscor:value xsi:type="v8:StandardPeriod">')
val_lines.append(f'{item_indent}\t<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml(pv["variant"])}</v8:variant>')
val_lines.append(f'{item_indent}\t<v8:variant xsi:type="v8:StandardPeriodVariant">{esc_xml_text(pv["variant"])}</v8:variant>')
val_lines.append(f"{item_indent}\t<v8:startDate>0001-01-01T00:00:00</v8:startDate>")
val_lines.append(f"{item_indent}\t<v8:endDate>0001-01-01T00:00:00</v8:endDate>")
val_lines.append(f"{item_indent}</dcscor:value>")
elif is_empty_value(pv):
val_lines.append(f'{item_indent}<dcscor:value xsi:nil="true"/>')
elif re.match(r'^\d{4}-\d{2}-\d{2}T', str(pv)):
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:dateTime">{esc_xml(str(pv))}</dcscor:value>')
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:dateTime">{esc_xml_text(str(pv))}</dcscor:value>')
elif str(pv) in ("true", "false"):
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:boolean">{esc_xml(str(pv))}</dcscor:value>')
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:boolean">{esc_xml_text(str(pv))}</dcscor:value>')
else:
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:string">{esc_xml(str(pv))}</dcscor:value>')
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:string">{esc_xml_text(str(pv))}</dcscor:value>')
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}\t<dcscom:{flag}>true</dcscom:{flag}>")
for k, v in kv:
lines.append(f"{field_indent}\t<dcscom:{k}>{esc_xml(v)}</dcscom:{k}>")
lines.append(f"{field_indent}\t<dcscom:{k}>{esc_xml_text(v)}</dcscom:{k}>")
for raw in preserved_role_children:
lines.append(f"{field_indent}\t" + raw)
lines.append(f"{field_indent}</role>")