mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-23 21:21:04 +03:00
fix(cfe-borrow): strip DataPath, Events and preserve form properties in Borrow-Form
Borrowed forms failed to load with "Неверный путь к данным" and "Событие не было загружено" errors. Root cause: base form elements contained DataPath and Events referencing attributes and handlers not present in the extension. Changes: - Strip <DataPath> from base elements in both AutoCommandBar and ChildItems - Strip element-level <Events> from both sections - Collect form-level properties (AutoTitle, WindowOpeningMode, etc.) and write them into both main and BaseForm sections - Update 1c-extension-spec.md with rules 5-7 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d94ffdea99
commit
c72f2210b5
@@ -476,13 +476,23 @@ function Borrow-Form {
|
|||||||
$formVersion = $srcFormEl.GetAttribute("version")
|
$formVersion = $srcFormEl.GetAttribute("version")
|
||||||
if (-not $formVersion) { $formVersion = "2.17" }
|
if (-not $formVersion) { $formVersion = "2.17" }
|
||||||
|
|
||||||
# Find direct children: AutoCommandBar, ChildItems (visual elements only)
|
# Find direct children: form properties, AutoCommandBar, ChildItems
|
||||||
$srcAutoCmd = $null
|
$srcAutoCmd = $null
|
||||||
$srcChildItems = $null
|
$srcChildItems = $null
|
||||||
|
$formProps = @()
|
||||||
|
$reachedVisual = $false
|
||||||
foreach ($fc in $srcFormEl.ChildNodes) {
|
foreach ($fc in $srcFormEl.ChildNodes) {
|
||||||
if ($fc.NodeType -ne 'Element') { continue }
|
if ($fc.NodeType -ne 'Element') { continue }
|
||||||
if ($fc.LocalName -eq 'AutoCommandBar' -and -not $srcAutoCmd) { $srcAutoCmd = $fc }
|
if ($fc.LocalName -eq 'AutoCommandBar' -and -not $srcAutoCmd) {
|
||||||
elseif ($fc.LocalName -eq 'ChildItems' -and -not $srcChildItems) { $srcChildItems = $fc }
|
$reachedVisual = $true; $srcAutoCmd = $fc; continue
|
||||||
|
}
|
||||||
|
if ($fc.LocalName -eq 'ChildItems' -and -not $srcChildItems) {
|
||||||
|
$reachedVisual = $true; $srcChildItems = $fc; continue
|
||||||
|
}
|
||||||
|
if (-not $reachedVisual) {
|
||||||
|
# Form-level properties before AutoCommandBar (WindowOpeningMode, AutoFillCheck, etc.)
|
||||||
|
$formProps += $fc.OuterXml
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get OuterXml and strip redundant namespace redeclarations (they're on root <Form>)
|
# Get OuterXml and strip redundant namespace redeclarations (they're on root <Form>)
|
||||||
@@ -496,6 +506,10 @@ function Borrow-Form {
|
|||||||
$autoCmdXml = [regex]::Replace($autoCmdXml, '<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>')
|
$autoCmdXml = [regex]::Replace($autoCmdXml, '<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>')
|
||||||
# Replace Autofill true → false
|
# Replace Autofill true → false
|
||||||
$autoCmdXml = $autoCmdXml -replace '<Autofill>true</Autofill>', '<Autofill>false</Autofill>'
|
$autoCmdXml = $autoCmdXml -replace '<Autofill>true</Autofill>', '<Autofill>false</Autofill>'
|
||||||
|
# Strip DataPath (references base form attributes not present in extension)
|
||||||
|
$autoCmdXml = [regex]::Replace($autoCmdXml, '\s*<DataPath>[^<]*</DataPath>', '')
|
||||||
|
# Strip element-level Events (base form event handlers not present in extension)
|
||||||
|
$autoCmdXml = [regex]::Replace($autoCmdXml, '(?s)\s*<Events>.*?</Events>', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
$childItemsXml = ""
|
$childItemsXml = ""
|
||||||
@@ -504,6 +518,9 @@ function Borrow-Form {
|
|||||||
$childItemsXml = [regex]::Replace($childItemsXml, $nsStripPattern, '')
|
$childItemsXml = [regex]::Replace($childItemsXml, $nsStripPattern, '')
|
||||||
# Replace all CommandName values with 0 in ChildItems too
|
# Replace all CommandName values with 0 in ChildItems too
|
||||||
$childItemsXml = [regex]::Replace($childItemsXml, '<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>')
|
$childItemsXml = [regex]::Replace($childItemsXml, '<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>')
|
||||||
|
# Strip DataPath and element-level Events
|
||||||
|
$childItemsXml = [regex]::Replace($childItemsXml, '\s*<DataPath>[^<]*</DataPath>', '')
|
||||||
|
$childItemsXml = [regex]::Replace($childItemsXml, '(?s)\s*<Events>.*?</Events>', '')
|
||||||
} else {
|
} else {
|
||||||
$childItemsXml = "<ChildItems/>"
|
$childItemsXml = "<ChildItems/>"
|
||||||
}
|
}
|
||||||
@@ -521,7 +538,11 @@ function Borrow-Form {
|
|||||||
$formXmlSb.Append($formTag) | Out-Null
|
$formXmlSb.Append($formTag) | Out-Null
|
||||||
$formXmlSb.Append("`r`n") | Out-Null
|
$formXmlSb.Append("`r`n") | Out-Null
|
||||||
|
|
||||||
# Part 1: visual elements (add leading tab to first line of each block)
|
# Part 1: form properties + visual elements
|
||||||
|
foreach ($propXml in $formProps) {
|
||||||
|
$propXml = [regex]::Replace($propXml, $nsStripPattern, '')
|
||||||
|
$formXmlSb.Append("`t$propXml`r`n") | Out-Null
|
||||||
|
}
|
||||||
if ($autoCmdXml) {
|
if ($autoCmdXml) {
|
||||||
$formXmlSb.Append("`t$autoCmdXml") | Out-Null
|
$formXmlSb.Append("`t$autoCmdXml") | Out-Null
|
||||||
$formXmlSb.Append("`r`n") | Out-Null
|
$formXmlSb.Append("`r`n") | Out-Null
|
||||||
@@ -531,10 +552,14 @@ function Borrow-Form {
|
|||||||
$formXmlSb.Append("`t<Attributes/>") | Out-Null
|
$formXmlSb.Append("`t<Attributes/>") | Out-Null
|
||||||
$formXmlSb.Append("`r`n") | Out-Null
|
$formXmlSb.Append("`r`n") | Out-Null
|
||||||
|
|
||||||
# BaseForm: same visual elements, indented one more level
|
# BaseForm: form properties + same visual elements, indented one more level
|
||||||
$formXmlSb.Append("`t<BaseForm version=`"${formVersion}`">") | Out-Null
|
$formXmlSb.Append("`t<BaseForm version=`"${formVersion}`">") | Out-Null
|
||||||
$formXmlSb.Append("`r`n") | Out-Null
|
$formXmlSb.Append("`r`n") | Out-Null
|
||||||
|
|
||||||
|
foreach ($propXml in $formProps) {
|
||||||
|
$propXml = [regex]::Replace($propXml, $nsStripPattern, '')
|
||||||
|
$formXmlSb.Append("`t`t$propXml`r`n") | Out-Null
|
||||||
|
}
|
||||||
if ($autoCmdXml) {
|
if ($autoCmdXml) {
|
||||||
# Reindent for BaseForm: first line gets 2 tabs, other lines get +1 tab
|
# Reindent for BaseForm: first line gets 2 tabs, other lines get +1 tab
|
||||||
$acLines = $autoCmdXml -split "`r?`n"
|
$acLines = $autoCmdXml -split "`r?`n"
|
||||||
|
|||||||
@@ -653,14 +653,23 @@ def main():
|
|||||||
|
|
||||||
src_auto_cmd = None
|
src_auto_cmd = None
|
||||||
src_child_items = None
|
src_child_items = None
|
||||||
|
form_props = []
|
||||||
|
reached_visual = False
|
||||||
for fc in src_form_el:
|
for fc in src_form_el:
|
||||||
if not isinstance(fc.tag, str):
|
if not isinstance(fc.tag, str):
|
||||||
continue
|
continue
|
||||||
ln = localname(fc)
|
ln = localname(fc)
|
||||||
if ln == "AutoCommandBar" and src_auto_cmd is None:
|
if ln == "AutoCommandBar" and src_auto_cmd is None:
|
||||||
|
reached_visual = True
|
||||||
src_auto_cmd = fc
|
src_auto_cmd = fc
|
||||||
elif ln == "ChildItems" and src_child_items is None:
|
continue
|
||||||
|
if ln == "ChildItems" and src_child_items is None:
|
||||||
|
reached_visual = True
|
||||||
src_child_items = fc
|
src_child_items = fc
|
||||||
|
continue
|
||||||
|
if not reached_visual:
|
||||||
|
# Form-level properties before AutoCommandBar (WindowOpeningMode, AutoFillCheck, etc.)
|
||||||
|
form_props.append(etree.tostring(fc, encoding="unicode"))
|
||||||
|
|
||||||
ns_strip_pattern = re.compile(r'\s+xmlns(?::\w+)?="[^"]*"')
|
ns_strip_pattern = re.compile(r'\s+xmlns(?::\w+)?="[^"]*"')
|
||||||
|
|
||||||
@@ -670,12 +679,19 @@ def main():
|
|||||||
auto_cmd_xml = ns_strip_pattern.sub("", auto_cmd_xml)
|
auto_cmd_xml = ns_strip_pattern.sub("", auto_cmd_xml)
|
||||||
auto_cmd_xml = re.sub(r'<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>', auto_cmd_xml)
|
auto_cmd_xml = re.sub(r'<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>', auto_cmd_xml)
|
||||||
auto_cmd_xml = auto_cmd_xml.replace('<Autofill>true</Autofill>', '<Autofill>false</Autofill>')
|
auto_cmd_xml = auto_cmd_xml.replace('<Autofill>true</Autofill>', '<Autofill>false</Autofill>')
|
||||||
|
# Strip DataPath (references base form attributes not present in extension)
|
||||||
|
auto_cmd_xml = re.sub(r'\s*<DataPath>[^<]*</DataPath>', '', auto_cmd_xml)
|
||||||
|
# Strip element-level Events (base form event handlers not present in extension)
|
||||||
|
auto_cmd_xml = re.sub(r'\s*<Events>.*?</Events>', '', auto_cmd_xml, flags=re.DOTALL)
|
||||||
|
|
||||||
child_items_xml = ""
|
child_items_xml = ""
|
||||||
if src_child_items is not None:
|
if src_child_items is not None:
|
||||||
child_items_xml = etree.tostring(src_child_items, encoding="unicode")
|
child_items_xml = etree.tostring(src_child_items, encoding="unicode")
|
||||||
child_items_xml = ns_strip_pattern.sub("", child_items_xml)
|
child_items_xml = ns_strip_pattern.sub("", child_items_xml)
|
||||||
child_items_xml = re.sub(r'<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>', child_items_xml)
|
child_items_xml = re.sub(r'<CommandName>[^<]*</CommandName>', '<CommandName>0</CommandName>', child_items_xml)
|
||||||
|
# Strip DataPath and element-level Events
|
||||||
|
child_items_xml = re.sub(r'\s*<DataPath>[^<]*</DataPath>', '', child_items_xml)
|
||||||
|
child_items_xml = re.sub(r'\s*<Events>.*?</Events>', '', child_items_xml, flags=re.DOTALL)
|
||||||
else:
|
else:
|
||||||
child_items_xml = "<ChildItems/>"
|
child_items_xml = "<ChildItems/>"
|
||||||
|
|
||||||
@@ -696,6 +712,10 @@ def main():
|
|||||||
parts.append(form_tag)
|
parts.append(form_tag)
|
||||||
parts.append("\r\n")
|
parts.append("\r\n")
|
||||||
|
|
||||||
|
# Form properties (WindowOpeningMode, AutoFillCheck, etc.)
|
||||||
|
for prop_xml in form_props:
|
||||||
|
prop_xml_clean = ns_strip_pattern.sub("", prop_xml)
|
||||||
|
parts.append(f"\t{prop_xml_clean}\r\n")
|
||||||
if auto_cmd_xml:
|
if auto_cmd_xml:
|
||||||
parts.append(f"\t{auto_cmd_xml}\r\n")
|
parts.append(f"\t{auto_cmd_xml}\r\n")
|
||||||
parts.append(f"\t{child_items_xml}\r\n")
|
parts.append(f"\t{child_items_xml}\r\n")
|
||||||
@@ -704,6 +724,9 @@ def main():
|
|||||||
# BaseForm
|
# BaseForm
|
||||||
parts.append(f'\t<BaseForm version="{form_version}">\r\n')
|
parts.append(f'\t<BaseForm version="{form_version}">\r\n')
|
||||||
|
|
||||||
|
for prop_xml in form_props:
|
||||||
|
prop_xml_clean = ns_strip_pattern.sub("", prop_xml)
|
||||||
|
parts.append(f"\t\t{prop_xml_clean}\r\n")
|
||||||
if auto_cmd_xml:
|
if auto_cmd_xml:
|
||||||
ac_lines = auto_cmd_xml.split("\n")
|
ac_lines = auto_cmd_xml.split("\n")
|
||||||
for li, line in enumerate(ac_lines):
|
for li, line in enumerate(ac_lines):
|
||||||
|
|||||||
@@ -422,6 +422,12 @@ Form.xml заимствованной формы — **двухчастный ф
|
|||||||
|
|
||||||
4. Элемент `<BaseForm>` всегда идёт **последним** в `<Form>` и имеет атрибут `version`.
|
4. Элемент `<BaseForm>` всегда идёт **последним** в `<Form>` и имеет атрибут `version`.
|
||||||
|
|
||||||
|
5. **Правило `<DataPath>`: удаление** — все элементы `<DataPath>...</DataPath>` из базовых визуальных элементов удаляются (и в Part 1, и в BaseForm). DataPath ссылается на реквизиты формы базовой конфигурации, которые не включены в расширение. Сохраняются только DataPath элементов, добавленных расширением (ссылающихся на собственные реквизиты расширения).
|
||||||
|
|
||||||
|
6. **Правило `<Events>` элементов: удаление** — все блоки `<Events>` внутри визуальных элементов (AutoCommandBar и ChildItems) удаляются (и в Part 1, и в BaseForm). Обработчики событий базовой конфигурации не переносятся. При модификации формы обработчики расширения добавляются только в Part 1 с атрибутом `callType`.
|
||||||
|
|
||||||
|
7. **Свойства формы** — элементы между `<Form>` и `<AutoCommandBar>` (например, `WindowOpeningMode`, `AutoFillCheck`, `AutoTitle`, `AutoTime`, `UsePostingMode`, `RepostOnWrite`, `Customizable`, `CommandBarLocation`) копируются из исходной формы в обе части (Part 1 и BaseForm).
|
||||||
|
|
||||||
#### 5.4.3. Нумерация ID элементов
|
#### 5.4.3. Нумерация ID элементов
|
||||||
|
|
||||||
| Диапазон | Принадлежность |
|
| Диапазон | Принадлежность |
|
||||||
|
|||||||
Reference in New Issue
Block a user