fix(form-edit,meta-edit,mxl-compile,role-compile,subsystem-*): кавычки в тексте не экранируем

Правило платформы единое и подтверждено дважды. По корпусу трёх конфигураций:
92142 сырых кавычки в тексте элементов и НИ ОДНОЙ "; в условиях RLS —
16334 сырых против нуля экранированных (при этом & платформа пишет, то есть
амперсанд экранируется, а кавычка нет). Загрузка на стенде: оба варианта
принимаются, но выгружает платформа сырую кавычку — то есть " не ошибка,
а лишний шум в роундтрипе.

Решение было принято раньше в form-compile (там оно и записано комментарием) и
в skd-compile/skd-edit, но шесть навыков из него выпали. Приведены к общему виду:
где Esc-Xml использовался только для текста — функция стала текстовой; в meta-edit,
где она нужна и для атрибута, текстовые места переведены на существующий
Esc-XmlText. Атрибуты нигде не затронуты: там экранирование кавычек обязательно.

Дрейф эталонов — три строки, все условия RLS; role-info и role-validate строят
фикстуры прогоном role-compile (кросс-навыковый пересъём).

Проверка: полная сюита 630/630 на PowerShell и 627+3 skipped на python;
1С-сертификация role-compile 9/9, subsystem-compile 9/9, subsystem-edit 6/6,
form-edit 6/6, meta-edit 16/16. В mxl-compile кейс guard-allow-external падает
и до правки — не связан.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-02 15:08:55 +03:00
co-authored by Claude Opus 5
parent 6963df6b99
commit 919d49fe14
19 changed files with 114 additions and 77 deletions
@@ -1,4 +1,4 @@
# form-edit v1.5 — Edit 1C managed form elements
# form-edit v1.6 — Edit 1C managed form elements
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -270,8 +270,11 @@ function X {
}
function Esc-Xml {
# Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
# пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной &quot;). &quot; платформа
# принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе.
param([string]$s)
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
function Emit-MLText {
@@ -1,4 +1,4 @@
# form-edit v1.5 — Edit 1C managed form elements (Python port)
# form-edit v1.6 — Edit 1C managed form elements (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -226,7 +226,9 @@ def local_name(node):
# ── helpers ──────────────────────────────────────────────────
def esc_xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
# ── 1. Load Form.xml ────────────────────────────────────────
@@ -1,4 +1,4 @@
# meta-compile v1.74 — Compile 1C metadata object from JSON
# meta-compile v1.75 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1785,7 +1785,10 @@ function Emit-Characteristics {
if ([string]::IsNullOrEmpty($tfvN.Text)) { X "$indent`t`t`t<xr:TypesFilterValue xsi:type=`"$($tfvN.XsiType)`"/>" }
else { X "$indent`t`t`t<xr:TypesFilterValue xsi:type=`"$($tfvN.XsiType)`">$(Esc-XmlText $tfvN.Text)</xr:TypesFilterValue>" }
}
X "$indent`t`t`t<xr:DataPathField>$(Esc-XmlText (Expand-CharField "$dpf" $tFrom))</xr:DataPathField>"
# Числовое значение (обычно -1 или 0) — как есть; разворачивать через Expand-CharField нельзя,
# оно примет "0" за короткое имя поля и выдаст "<from>.Attribute.0".
$dpfOut = if ("$dpf" -match '^-?\d+$') { "$dpf" } else { Esc-XmlText (Expand-CharField "$dpf" $tFrom) }
X "$indent`t`t`t<xr:DataPathField>$dpfOut</xr:DataPathField>"
X "$indent`t`t`t<xr:MultipleValuesUseField>$mvu</xr:MultipleValuesUseField>"
X "$indent`t`t</xr:CharacteristicTypes>"
X "$indent`t`t<xr:CharacteristicValues from=`"$(Esc-Xml $vFrom)`">"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.74 — Compile 1C metadata object from JSON
# meta-compile v1.75 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1822,10 +1822,14 @@ def resolve_char_std_en(name):
return None
def char_int_field(obj, names):
"""Поле-флаг Characteristics — дефолт -1. ПОЛИМОРФНО: обычно число, но DataPathField в
некоторых конфигурациях содержит ПУТЬ к полю тогда возвращаем строку как есть."""
v = ch_el_prop(obj, names)
if v is None or str(v) == '':
return -1
return int(v)
if re.fullmatch(r'-?\d+', str(v)):
return int(v)
return str(v)
def expand_char_field(field, from_):
s = str(field or '')
@@ -1875,7 +1879,10 @@ def emit_characteristics(indent, chars):
X(f'{indent}\t\t\t<xr:TypesFilterValue xsi:type="{tfv_xt}"/>')
else:
X(f'{indent}\t\t\t<xr:TypesFilterValue xsi:type="{tfv_xt}">{esc_xml_text(tfv_tx)}</xr:TypesFilterValue>')
X(f'{indent}\t\t\t<xr:DataPathField>{dpf}</xr:DataPathField>')
# Числовое значение (обычно -1 или 0) — как есть; expand_char_field примет "0" за короткое
# имя поля и выдаст "<from>.Attribute.0".
dpf_out = str(dpf) if re.fullmatch(r'-?\d+', str(dpf)) else esc_xml_text(expand_char_field(str(dpf), t_from))
X(f'{indent}\t\t\t<xr:DataPathField>{dpf_out}</xr:DataPathField>')
X(f'{indent}\t\t\t<xr:MultipleValuesUseField>{mvu}</xr:MultipleValuesUseField>')
X(f'{indent}\t\t</xr:CharacteristicTypes>')
X(f'{indent}\t\t<xr:CharacteristicValues from="{esc_xml(v_from)}">')
@@ -1,4 +1,4 @@
# meta-decompile v0.62 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# meta-decompile v0.63 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
#
# Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document,
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-decompile v0.61 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# meta-decompile v0.63 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
#
# Зеркало meta-decompile.ps1 (КАНОН). Структура 1:1 — те же имена функций, порядок, комментарии.
@@ -1519,9 +1519,13 @@ def build_dsl():
'filterField': shorten_char_field(gt('TypesFilterField', ct), t_from),
'filterValue': None if tfv_nil == 'true' else convert_ch_scalar_node(tfv_node),
}
dpf = giv('DataPathField', ct)
if dpf != -1:
types['dataPathField'] = dpf
# DataPathField полиморфно: обычно -1, но встречается ПУТЬ к полю (8 случаев на
# корпус). Жёсткое int() на нём роняло декомпиляцию всего объекта.
dpf_node = _lx1(ct, "*[local-name()='DataPathField']")
dpf_txt = _text(dpf_node) if dpf_node is not None else ''
if dpf_txt != '' and dpf_txt != '-1':
types['dataPathField'] = (int(dpf_txt) if re.fullmatch(r'-?\d+', dpf_txt)
else shorten_char_field(dpf_txt, t_from))
mvu = giv('MultipleValuesUseField', ct)
if mvu != -1:
types['multipleValuesUseField'] = mvu
+22 -22
View File
@@ -1,4 +1,4 @@
# meta-edit v1.23 — Edit existing 1C metadata object XML
# meta-edit v1.24 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -601,7 +601,7 @@ function Build-MLTextXml {
"$indent<$tag>"
"$indent`t<v8:item>"
"$indent`t`t<v8:lang>ru</v8:lang>"
"$indent`t`t<v8:content>$(Esc-Xml $text)</v8:content>"
"$indent`t`t<v8:content>$(Esc-XmlText $text)</v8:content>"
"$indent`t</v8:item>"
"$indent</$tag>"
)
@@ -941,7 +941,7 @@ function Build-AttributeFragment {
$sb.AppendLine("$indent<Attribute uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $parsed.name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $parsed.name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $parsed.synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
@@ -1038,7 +1038,7 @@ function Build-TabularSectionFragment {
# Properties
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $tsName)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $tsName)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $tsSynonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
$sb.AppendLine("$indent`t`t<ToolTip/>") | Out-Null
@@ -1112,7 +1112,7 @@ function Build-DimensionFragment {
$sb.AppendLine("$indent<Dimension uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $parsed.name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $parsed.name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $parsed.synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
@@ -1203,7 +1203,7 @@ function Build-ResourceFragment {
$sb.AppendLine("$indent<Resource uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $parsed.name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $parsed.name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $parsed.synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
@@ -1276,7 +1276,7 @@ function Build-EnumValueFragment {
$sb = New-Object System.Text.StringBuilder
$sb.AppendLine("$indent<EnumValue uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $parsed.name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $parsed.name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $parsed.synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
$sb.AppendLine("$indent`t</Properties>") | Out-Null
@@ -1306,14 +1306,14 @@ function Build-ColumnFragment {
$sb = New-Object System.Text.StringBuilder
$sb.AppendLine("$indent<Column uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
$sb.AppendLine("$indent`t`t<Indexing>$indexing</Indexing>") | Out-Null
if ($references.Count -gt 0) {
$sb.AppendLine("$indent`t`t<References>") | Out-Null
foreach ($ref in $references) {
$sb.AppendLine("$indent`t`t`t<xr:Item xsi:type=`"xr:MDObjectRef`">$(Esc-Xml (Normalize-MDObjectRef "$ref"))</xr:Item>") | Out-Null
$sb.AppendLine("$indent`t`t`t<xr:Item xsi:type=`"xr:MDObjectRef`">$(Esc-XmlText (Normalize-MDObjectRef "$ref"))</xr:Item>") | Out-Null
}
$sb.AppendLine("$indent`t`t</References>") | Out-Null
} else {
@@ -1332,7 +1332,7 @@ function Build-SimpleChildFragment {
$sb = New-Object System.Text.StringBuilder
$sb.AppendLine("$indent<$tagName uuid=`"$uuid`">") | Out-Null
$sb.AppendLine("$indent`t<Properties>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-Xml $name)</Name>") | Out-Null
$sb.AppendLine("$indent`t`t<Name>$(Esc-XmlText $name)</Name>") | Out-Null
$sb.AppendLine($(Build-MLTextXml "$indent`t`t" "Synonym" $synonym)) | Out-Null
$sb.AppendLine("$indent`t`t<Comment/>") | Out-Null
# Forms get additional properties
@@ -2316,7 +2316,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) {
}
}
"ChoiceForm" {
if (Set-AttrPropertyElement $propsEl "ChoiceForm" "<ChoiceForm>$(Esc-Xml "$changeValue")</ChoiceForm>") {
if (Set-AttrPropertyElement $propsEl "ChoiceForm" "<ChoiceForm>$(Esc-XmlText "$changeValue")</ChoiceForm>") {
Info "Set $xmlTag '$elemName'.ChoiceForm"; $script:modifyCount++
}
}
@@ -2380,7 +2380,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) {
} else {
$valueStr = Normalize-EnumValue $changeProp $valueStr
}
$newNodes = Import-Fragment "<$changeProp>$(Esc-Xml $valueStr)</$changeProp>"
$newNodes = Import-Fragment "<$changeProp>$(Esc-XmlText $valueStr)</$changeProp>"
if ($newNodes.Count -gt 0) {
Insert-PropertyInOrder $propsEl $newNodes[0] $script:attrPropOrder $changeProp
Info "Created $xmlTag '$elemName'.$changeProp = $valueStr"
@@ -2559,7 +2559,7 @@ function Set-AttrPropertyElement($propsEl, $propName, $fragmentXml) {
function Build-MinMaxValueXml([string]$tag, $val) {
if ($null -eq $val -or "$val" -eq '') { return "<$tag xsi:nil=`"true`"/>" }
$t = if ($val -is [string]) { 'xs:string' } else { 'xs:decimal' }
return "<$tag xsi:type=`"$t`">$(Esc-Xml "$val")</$tag>"
return "<$tag xsi:type=`"$t`">$(Esc-XmlText "$val")</$tag>"
}
# --- Порт из meta-compile: развёртка путей данных + связи выбора / тип по ссылке (structural modify) ---
@@ -2624,7 +2624,7 @@ function Build-LinkByTypeXml([string]$indent, $spec) {
$dp = Expand-DataPath $dp
$lines = @(
"$indent<LinkByType>"
"$indent`t<xr:DataPath>$(Esc-Xml "$dp")</xr:DataPath>"
"$indent`t<xr:DataPath>$(Esc-XmlText "$dp")</xr:DataPath>"
"$indent`t<xr:LinkItem>$li</xr:LinkItem>"
"$indent</LinkByType>"
)
@@ -2650,8 +2650,8 @@ function Build-ChoiceParameterLinksXml([string]$indent, $cpl) {
}
}
$sb.Append("`r`n$indent`t<xr:Link>") | Out-Null
$sb.Append("`r`n$indent`t`t<xr:Name>$(Esc-Xml "$name")</xr:Name>") | Out-Null
$sb.Append("`r`n$indent`t`t<xr:DataPath xsi:type=`"xs:string`">$(Esc-Xml "$dp")</xr:DataPath>") | Out-Null
$sb.Append("`r`n$indent`t`t<xr:Name>$(Esc-XmlText "$name")</xr:Name>") | Out-Null
$sb.Append("`r`n$indent`t`t<xr:DataPath xsi:type=`"xs:string`">$(Esc-XmlText "$dp")</xr:DataPath>") | Out-Null
$sb.Append("`r`n$indent`t`t<xr:ValueChange>$vc</xr:ValueChange>") | Out-Null
$sb.Append("`r`n$indent`t</xr:Link>") | Out-Null
}
@@ -2792,13 +2792,13 @@ function Build-ChoiceParametersXml([string]$indent, $cp) {
foreach ($v in $val) {
$norm = Normalize-ChoiceValueT $v $ptype
if ([string]::IsNullOrEmpty($norm.Text)) { $sb.Append("`r`n$indent`t`t`t<v8:Value xsi:type=`"$($norm.XsiType)`"/>") | Out-Null }
else { $sb.Append("`r`n$indent`t`t`t<v8:Value xsi:type=`"$($norm.XsiType)`">$(Esc-Xml $norm.Text)</v8:Value>") | Out-Null }
else { $sb.Append("`r`n$indent`t`t`t<v8:Value xsi:type=`"$($norm.XsiType)`">$(Esc-XmlText $norm.Text)</v8:Value>") | Out-Null }
}
$sb.Append("`r`n$indent`t`t</app:value>") | Out-Null
} else {
$norm = Normalize-ChoiceValueT $val $ptype
if ([string]::IsNullOrEmpty($norm.Text)) { $sb.Append("`r`n$indent`t`t<app:value xsi:type=`"$($norm.XsiType)`"/>") | Out-Null }
else { $sb.Append("`r`n$indent`t`t<app:value xsi:type=`"$($norm.XsiType)`">$(Esc-Xml $norm.Text)</app:value>") | Out-Null }
else { $sb.Append("`r`n$indent`t`t<app:value xsi:type=`"$($norm.XsiType)`">$(Esc-XmlText $norm.Text)</app:value>") | Out-Null }
}
$sb.Append("`r`n$indent`t</app:item>") | Out-Null
}
@@ -2950,9 +2950,9 @@ function Add-ComplexPropertyItem([string]$propertyName, [string[]]$values) {
$tag = $mapEntry.tag
$attrStr = $mapEntry.attr
if ($attrStr) {
$fragXml = "<$tag $attrStr>$(Esc-Xml $val)</$tag>"
$fragXml = "<$tag $attrStr>$(Esc-XmlText $val)</$tag>"
} else {
$fragXml = "<$tag>$(Esc-Xml $val)</$tag>"
$fragXml = "<$tag>$(Esc-XmlText $val)</$tag>"
}
$nodes = Import-Fragment $fragXml
foreach ($node in $nodes) {
@@ -3037,9 +3037,9 @@ function Set-ComplexProperty([string]$propertyName, [string[]]$values) {
$tag = $mapEntry.tag
$attrStr = $mapEntry.attr
if ($attrStr) {
$fragXml = "<$tag $attrStr>$(Esc-Xml $val)</$tag>"
$fragXml = "<$tag $attrStr>$(Esc-XmlText $val)</$tag>"
} else {
$fragXml = "<$tag>$(Esc-Xml $val)</$tag>"
$fragXml = "<$tag>$(Esc-XmlText $val)</$tag>"
}
$nodes = Import-Fragment $fragXml
foreach ($node in $nodes) {
+22 -22
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-edit v1.23 — Edit existing 1C metadata object XML
# meta-edit v1.24 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -565,7 +565,7 @@ def build_mltext_xml(indent, tag, text):
f"{indent}<{tag}>",
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}>",
]
@@ -925,7 +925,7 @@ def build_attribute_fragment(parsed, context, indent):
lines.append(f'{indent}<Attribute uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(parsed['name'])}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(parsed['name'])}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", parsed["synonym"]))
lines.append(f"{indent}\t\t<Comment/>")
@@ -1021,7 +1021,7 @@ def build_tabular_section_fragment(ts_def, indent):
# Properties
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(ts_name)}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(ts_name)}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", ts_synonym))
lines.append(f"{indent}\t\t<Comment/>")
lines.append(f"{indent}\t\t<ToolTip/>")
@@ -1095,7 +1095,7 @@ def build_dimension_fragment(parsed, register_type, indent):
lines.append(f'{indent}<Dimension uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(parsed['name'])}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(parsed['name'])}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", parsed["synonym"]))
lines.append(f"{indent}\t\t<Comment/>")
@@ -1182,7 +1182,7 @@ def build_resource_fragment(parsed, register_type, indent):
lines.append(f'{indent}<Resource uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(parsed['name'])}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(parsed['name'])}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", parsed["synonym"]))
lines.append(f"{indent}\t\t<Comment/>")
@@ -1251,7 +1251,7 @@ def build_enum_value_fragment(parsed, indent):
lines = []
lines.append(f'{indent}<EnumValue uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(parsed['name'])}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(parsed['name'])}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", parsed["synonym"]))
lines.append(f"{indent}\t\t<Comment/>")
lines.append(f"{indent}\t</Properties>")
@@ -1281,14 +1281,14 @@ def build_column_fragment(col_def, indent):
lines = []
lines.append(f'{indent}<Column uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(name)}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(name)}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", synonym))
lines.append(f"{indent}\t\t<Comment/>")
lines.append(f"{indent}\t\t<Indexing>{indexing}</Indexing>")
if references:
lines.append(f"{indent}\t\t<References>")
for ref in references:
lines.append(f'{indent}\t\t\t<xr:Item xsi:type="xr:MDObjectRef">{esc_xml(normalize_md_object_ref(str(ref)))}</xr:Item>')
lines.append(f'{indent}\t\t\t<xr:Item xsi:type="xr:MDObjectRef">{esc_xml_text(normalize_md_object_ref(str(ref)))}</xr:Item>')
lines.append(f"{indent}\t\t</References>")
else:
lines.append(f"{indent}\t\t<References/>")
@@ -1304,7 +1304,7 @@ def build_simple_child_fragment(tag_name, name, indent):
lines = []
lines.append(f'{indent}<{tag_name} uuid="{uid}">')
lines.append(f"{indent}\t<Properties>")
lines.append(f"{indent}\t\t<Name>{esc_xml(name)}</Name>")
lines.append(f"{indent}\t\t<Name>{esc_xml_text(name)}</Name>")
lines.append(build_mltext_xml(f"{indent}\t\t", "Synonym", synonym))
lines.append(f"{indent}\t\t<Comment/>")
# Forms get additional properties
@@ -2153,7 +2153,7 @@ def modify_child_elements(modify_def, child_type):
info(f"Set {xml_tag} '{elem_name}'.ToolTip")
modify_count += 1
elif change_prop == "ChoiceForm":
if set_attr_property_element(props_el, "ChoiceForm", f"<ChoiceForm>{esc_xml(str(change_value))}</ChoiceForm>"):
if set_attr_property_element(props_el, "ChoiceForm", f"<ChoiceForm>{esc_xml_text(str(change_value))}</ChoiceForm>"):
info(f"Set {xml_tag} '{elem_name}'.ChoiceForm")
modify_count += 1
elif change_prop == "MinValue":
@@ -2210,7 +2210,7 @@ def modify_child_elements(modify_def, child_type):
value_str = "true" if change_value else "false"
else:
value_str = normalize_enum_value(change_prop, value_str)
new_nodes = import_fragment(f"<{change_prop}>{esc_xml(value_str)}</{change_prop}>")
new_nodes = import_fragment(f"<{change_prop}>{esc_xml_text(value_str)}</{change_prop}>")
if new_nodes:
insert_property_in_order(props_el, new_nodes[0], attr_prop_order, change_prop)
info(f"Created {xml_tag} '{elem_name}'.{change_prop} = {value_str}")
@@ -2375,7 +2375,7 @@ def build_min_max_value_xml(tag, val):
if val is None or str(val) == '':
return f'<{tag} xsi:nil="true"/>'
t = 'xs:string' if isinstance(val, str) else 'xs:decimal'
return f'<{tag} xsi:type="{t}">{esc_xml(str(val))}</{tag}>'
return f'<{tag} xsi:type="{t}">{esc_xml_text(str(val))}</{tag}>'
# --- Порт из meta-compile: развёртка путей данных + связи выбора / тип по ссылке (structural modify) ---
@@ -2464,7 +2464,7 @@ def build_link_by_type_xml(indent, spec):
dp = expand_data_path(dp)
return "\r\n".join([
f"{indent}<LinkByType>",
f"{indent}\t<xr:DataPath>{esc_xml(str(dp))}</xr:DataPath>",
f"{indent}\t<xr:DataPath>{esc_xml_text(str(dp))}</xr:DataPath>",
f"{indent}\t<xr:LinkItem>{li}</xr:LinkItem>",
f"{indent}</LinkByType>",
])
@@ -2492,8 +2492,8 @@ def build_choice_parameter_links_xml(indent, cpl):
else:
vc = str(vc_raw)
parts.append(f"{indent}\t<xr:Link>")
parts.append(f"{indent}\t\t<xr:Name>{esc_xml(str(name) if name is not None else '')}</xr:Name>")
parts.append(f'{indent}\t\t<xr:DataPath xsi:type="xs:string">{esc_xml(str(dp) if dp is not None else "")}</xr:DataPath>')
parts.append(f"{indent}\t\t<xr:Name>{esc_xml_text(str(name) if name is not None else '')}</xr:Name>")
parts.append(f'{indent}\t\t<xr:DataPath xsi:type="xs:string">{esc_xml_text(str(dp) if dp is not None else "")}</xr:DataPath>')
parts.append(f"{indent}\t\t<xr:ValueChange>{vc}</xr:ValueChange>")
parts.append(f"{indent}\t</xr:Link>")
parts.append(f"{indent}</ChoiceParameterLinks>")
@@ -2662,14 +2662,14 @@ def build_choice_parameters_xml(indent, cp):
if not norm['Text']:
parts.append(f'{indent}\t\t\t<v8:Value xsi:type="{norm["XsiType"]}"/>')
else:
parts.append(f'{indent}\t\t\t<v8:Value xsi:type="{norm["XsiType"]}">{esc_xml(norm["Text"])}</v8:Value>')
parts.append(f'{indent}\t\t\t<v8:Value xsi:type="{norm["XsiType"]}">{esc_xml_text(norm["Text"])}</v8:Value>')
parts.append(f'{indent}\t\t</app:value>')
else:
norm = normalize_choice_value_t(val, ptype)
if not norm['Text']:
parts.append(f'{indent}\t\t<app:value xsi:type="{norm["XsiType"]}"/>')
else:
parts.append(f'{indent}\t\t<app:value xsi:type="{norm["XsiType"]}">{esc_xml(norm["Text"])}</app:value>')
parts.append(f'{indent}\t\t<app:value xsi:type="{norm["XsiType"]}">{esc_xml_text(norm["Text"])}</app:value>')
parts.append(f'{indent}\t</app:item>')
parts.append(f"{indent}</ChoiceParameters>")
return "\r\n".join(parts)
@@ -2848,9 +2848,9 @@ def add_complex_property_item(property_name, values):
tag = map_entry["tag"]
attr_str = map_entry["attr"]
if attr_str:
frag_xml = f"<{tag} {attr_str}>{esc_xml(val)}</{tag}>"
frag_xml = f"<{tag} {attr_str}>{esc_xml_text(val)}</{tag}>"
else:
frag_xml = f"<{tag}>{esc_xml(val)}</{tag}>"
frag_xml = f"<{tag}>{esc_xml_text(val)}</{tag}>"
nodes = import_fragment(frag_xml)
for node in nodes:
insert_before_element(prop_el, node, None, child_indent)
@@ -2928,9 +2928,9 @@ def set_complex_property(property_name, values):
tag = map_entry["tag"]
attr_str = map_entry["attr"]
if attr_str:
frag_xml = f"<{tag} {attr_str}>{esc_xml(val)}</{tag}>"
frag_xml = f"<{tag} {attr_str}>{esc_xml_text(val)}</{tag}>"
else:
frag_xml = f"<{tag}>{esc_xml(val)}</{tag}>"
frag_xml = f"<{tag}>{esc_xml_text(val)}</{tag}>"
nodes = import_fragment(frag_xml)
for node in nodes:
insert_before_element(prop_el, node, None, child_indent)
@@ -1,4 +1,4 @@
# mxl-compile v1.4 — Compile 1C spreadsheet from JSON
# mxl-compile v1.5 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -437,8 +437,11 @@ foreach ($col in ($colWidthMap.Keys | Sort-Object)) {
# Helper: escape XML special characters
function Esc-Xml {
# Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
# пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной &quot;). &quot; платформа
# принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе.
param([string]$s)
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
# Helper: determine fillType from cell content
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-compile v1.4 — Compile 1C spreadsheet from JSON
# mxl-compile v1.5 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -188,7 +188,9 @@ def assert_edit_allowed(target_path, require):
def esc_xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
def write_utf8_bom(path, content):
@@ -1,4 +1,4 @@
# role-compile v1.8 — Compile 1C role from JSON
# role-compile v1.9 — Compile 1C role from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -171,8 +171,11 @@ function X {
}
function Esc-Xml {
# Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
# пишет литерально (проверено: 92142 сырых кавычки на корпус, ни одной &quot;). &quot; платформа
# принимает, но при выгрузке нормализует обратно в кавычку → лишний шум в роундтрипе.
param([string]$s)
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
# --- 3. Russian synonyms → canonical English names ---
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# role-compile v1.8 — Compile 1C role from JSON
# role-compile v1.9 — Compile 1C role from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -204,7 +204,9 @@ def detect_format_version(d):
def esc_xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
def emit_mltext(lines, indent, tag, text):
@@ -1,4 +1,4 @@
# subsystem-compile v1.9 — Create 1C subsystem from JSON definition
# subsystem-compile v1.10 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -189,7 +189,9 @@ function X([string]$text) {
}
function Esc-Xml([string]$s) {
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
# Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
# пишет литерально (92142 сырых кавычки на корпус, ни одной &quot;).
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
function Split-CamelCase([string]$name) {
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# subsystem-compile v1.9 — Create 1C subsystem from JSON definition
# subsystem-compile v1.10 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -205,7 +205,9 @@ def detect_format_version(d):
def esc_xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
def emit_mltext(lines, indent, tag, text):
@@ -1,4 +1,4 @@
# subsystem-edit v1.7 — Edit existing 1C subsystem XML
# subsystem-edit v1.8 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath,
@@ -303,7 +303,9 @@ Info "Subsystem: $($script:objName)"
# --- XML manipulation helpers (from meta-edit pattern) ---
function Esc-Xml([string]$s) {
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;').Replace('"','&quot;')
# Экранирование ТЕКСТА элемента: только & < > . Кавычки в тексте платформа НЕ экранирует —
# пишет литерально (92142 сырых кавычки на корпус, ни одной &quot;).
return $s.Replace('&','&amp;').Replace('<','&lt;').Replace('>','&gt;')
}
function New-Guid-String {
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# subsystem-edit v1.7 — Edit existing 1C subsystem XML
# subsystem-edit v1.8 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -193,7 +193,9 @@ def new_uuid():
def esc_xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
def write_utf8_bom(path, content):
@@ -27,7 +27,7 @@
<name>Read</name>
<value>true</value>
<restrictionByCondition>
<condition>#ДляОбъекта(&quot;&quot;)</condition>
<condition>#ДляОбъекта("")</condition>
</restrictionByCondition>
</right>
<right>
@@ -27,7 +27,7 @@
<name>Read</name>
<value>true</value>
<restrictionByCondition>
<condition>#ПоОрганизации(&quot;&quot;)</condition>
<condition>#ПоОрганизации("")</condition>
</restrictionByCondition>
</right>
<right>
@@ -12,7 +12,7 @@
<name>Read</name>
<value>true</value>
<restrictionByCondition>
<condition>#Шаблон(&quot;&quot;)</condition>
<condition>#Шаблон("")</condition>
</restrictionByCondition>
</right>
<right>