mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-15 15:59:00 +03:00
fix(epf-build): версия и режим совместимости заглушки — из исходников
Заглушечная конфигурация зашивала version="2.17" и CompatibilityMode=Version8_3_24 независимо от собираемых исходников. Ограничение платформы одностороннее — она читает формат не новее себя, — поэтому на 8.3.20 такая заглушка не грузилась вовсе: «Неизвестная версия формата 2.17 загружаемого файла». Режим совместимости бил тише и потому опаснее: платформа рапортовала успешную загрузку с кодом 0, писала в лог «Для работы с конфигурацией необходима версия платформы не меньше, чем 8.3.24» и не создавала объекты — сборка падала уже на «Неизвестное имя типа», уводя диагностику в сторону. Оба значения выводятся из версии исходников. Для версии взят min(исходники, 2.17): заглушке нужна самая низкая работающая версия, а не версия исходников, поэтому на 2.17+ поведение остаётся прежним и опускается только под 2.13–2.16. Режим — по той же лестнице. Проверено на живой платформе: обработка 2.13 со ссылочным реквизитом собирается на 8.3.20 обоими портами; для исходников 2.17 заглушка по-прежнему 2.17/Version8_3_24. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
de76fde275
commit
beaa75c742
@@ -1,4 +1,4 @@
|
|||||||
# stub-db-create v1.7 — Create temp 1C infobase with metadata stubs for EPF/ERF build
|
# stub-db-create v1.8 — Create temp 1C infobase with metadata stubs for EPF/ERF build
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
@@ -163,14 +163,35 @@ function Format-ArgsForDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Версия формата как число: "2.20" → 220. Строковое сравнение неверно ("2.9" > "2.17").
|
||||||
|
function Get-FormatRank([string]$ver) {
|
||||||
|
if ($ver -match '^(\d+)\.(\d+)$') { return [int]$Matches[1] * 100 + [int]$Matches[2] }
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# --- 1. Scan XML files for reference types ---
|
# --- 1. Scan XML files for reference types ---
|
||||||
|
|
||||||
$typeMap = @{} # MetadataType -> @(Name1, Name2, ...)
|
$typeMap = @{} # MetadataType -> @(Name1, Name2, ...)
|
||||||
|
|
||||||
|
# Версия формата заглушечной конфигурации. Платформа грузит формат не новее себя, поэтому зашитая
|
||||||
|
# версия ломала бы сборку исходников более старого формата на соответствующей ей платформе. Берём
|
||||||
|
# версию из корня собираемого объекта (ExternalDataProcessor/ExternalReport); вложенные файлы —
|
||||||
|
# запасной вариант, если корень почему-то не попался.
|
||||||
|
$srcRootVersion = ""
|
||||||
|
$srcAnyVersion = ""
|
||||||
|
|
||||||
$xmlFiles = Get-ChildItem -Path $SourceDir -Filter "*.xml" -Recurse -File
|
$xmlFiles = Get-ChildItem -Path $SourceDir -Filter "*.xml" -Recurse -File
|
||||||
foreach ($f in $xmlFiles) {
|
foreach ($f in $xmlFiles) {
|
||||||
$content = [System.IO.File]::ReadAllText($f.FullName, [System.Text.Encoding]::UTF8)
|
$content = [System.IO.File]::ReadAllText($f.FullName, [System.Text.Encoding]::UTF8)
|
||||||
|
|
||||||
|
if ($content -match '<MetaDataObject[^>]+version="(\d+\.\d+)"') {
|
||||||
|
$ver = $Matches[1]
|
||||||
|
if (-not $srcAnyVersion) { $srcAnyVersion = $ver }
|
||||||
|
if (-not $srcRootVersion -and $content -match '<(ExternalDataProcessor|ExternalReport)[ >]') {
|
||||||
|
$srcRootVersion = $ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Ref types: cfg:CatalogRef.XXX or d5p1:CatalogRef.XXX (and similar depth prefixes d4p1, d3p1, etc.)
|
# Ref types: cfg:CatalogRef.XXX or d5p1:CatalogRef.XXX (and similar depth prefixes d4p1, d3p1, etc.)
|
||||||
$refPattern = '(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.([A-Za-z\u0400-\u04FF\d_]+)'
|
$refPattern = '(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.([A-Za-z\u0400-\u04FF\d_]+)'
|
||||||
foreach ($m in [regex]::Matches($content, $refPattern)) {
|
foreach ($m in [regex]::Matches($content, $refPattern)) {
|
||||||
@@ -337,7 +358,24 @@ if ($hasRefTypes) {
|
|||||||
$cfgDir = Join-Path $TempBasePath "cfg"
|
$cfgDir = Join-Path $TempBasePath "cfg"
|
||||||
New-Item -ItemType Directory -Path $cfgDir -Force | Out-Null
|
New-Item -ItemType Directory -Path $cfgDir -Force | Out-Null
|
||||||
|
|
||||||
$ns = 'xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17"'
|
# Заглушке нужна САМАЯ НИЗКАЯ работающая версия, а не версия исходников: ограничение платформы
|
||||||
|
# одностороннее — она читает формат не новее себя. Отсюда min(версия исходников, 2.17): на 2.17+
|
||||||
|
# заглушка остаётся 2.17 (как было), а под исходники 2.13-2.16 опускается до их версии, иначе
|
||||||
|
# конфигурация не загрузится платформой, которая эти исходники и выгрузила («Неизвестная версия
|
||||||
|
# формата 2.17 загружаемого файла», замерено на 8.3.20).
|
||||||
|
#
|
||||||
|
$srcVersion = if ($srcRootVersion) { $srcRootVersion } elseif ($srcAnyVersion) { $srcAnyVersion } else { "2.17" }
|
||||||
|
$srcRank = Get-FormatRank $srcVersion
|
||||||
|
$stubFormatVersion = if ($srcRank -gt 0 -and $srcRank -lt (Get-FormatRank "2.17")) { $srcVersion } else { "2.17" }
|
||||||
|
# Режим совместимости заглушки — по той же логике. Платформа отказывается работать с
|
||||||
|
# конфигурацией, чей режим выше её самой («Для работы с конфигурацией необходима версия
|
||||||
|
# платформы не меньше, чем 8.3.24»), и тогда объекты заглушки в базу не попадают: загрузка
|
||||||
|
# рапортует успех, а сборка падает на «Неизвестное имя типа». Ступени — лестница версий
|
||||||
|
# формата из docs/1c-configuration-spec.md.
|
||||||
|
$compatByFormat = @{ "2.13" = "Version8_3_20"; "2.14" = "Version8_3_21"; "2.15" = "Version8_3_22"; "2.16" = "Version8_3_23" }
|
||||||
|
$stubCompatMode = if ($compatByFormat.ContainsKey($stubFormatVersion)) { $compatByFormat[$stubFormatVersion] } else { "Version8_3_24" }
|
||||||
|
|
||||||
|
$ns = 'xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="' + $stubFormatVersion + '"'
|
||||||
|
|
||||||
# GeneratedType definitions per metadata type
|
# GeneratedType definitions per metadata type
|
||||||
$gtDefs = @{
|
$gtDefs = @{
|
||||||
@@ -521,7 +559,7 @@ if ($hasRefTypes) {
|
|||||||
<Synonym/>
|
<Synonym/>
|
||||||
<Comment/>
|
<Comment/>
|
||||||
<NamePrefix/>
|
<NamePrefix/>
|
||||||
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
|
<ConfigurationExtensionCompatibilityMode>$stubCompatMode</ConfigurationExtensionCompatibilityMode>
|
||||||
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||||
<UsePurposes>
|
<UsePurposes>
|
||||||
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||||
@@ -572,7 +610,7 @@ if ($hasRefTypes) {
|
|||||||
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
||||||
<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
||||||
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
||||||
<CompatibilityMode>Version8_3_24</CompatibilityMode>
|
<CompatibilityMode>$stubCompatMode</CompatibilityMode>
|
||||||
<DefaultConstantsForm/>
|
<DefaultConstantsForm/>
|
||||||
</Properties>
|
</Properties>
|
||||||
<ChildObjects>$childXml
|
<ChildObjects>$childXml
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# stub-db-create v1.7 — Create temp 1C infobase with metadata stubs for EPF/ERF build
|
# stub-db-create v1.8 — Create temp 1C infobase with metadata stubs for EPF/ERF build
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -339,6 +339,66 @@ def scan_ref_types(source_dir):
|
|||||||
return type_map
|
return type_map
|
||||||
|
|
||||||
|
|
||||||
|
def format_rank(ver):
|
||||||
|
""""2.20" → 220, "2.9" → 209. Строковое сравнение неверно ("2.9" > "2.17")."""
|
||||||
|
m = re.match(r'^(\d+)\.(\d+)$', ver or '')
|
||||||
|
return int(m.group(1)) * 100 + int(m.group(2)) if m else 0
|
||||||
|
|
||||||
|
|
||||||
|
def detect_stub_format_version(source_dir):
|
||||||
|
"""Версия формата заглушечной конфигурации.
|
||||||
|
|
||||||
|
Заглушке нужна САМАЯ НИЗКАЯ работающая версия, а не версия исходников: ограничение платформы
|
||||||
|
одностороннее — она читает формат не новее себя. Отсюда min(версия исходников, 2.17): на 2.17+
|
||||||
|
заглушка остаётся 2.17 (как было), а под исходники 2.13-2.16 опускается до их версии, иначе
|
||||||
|
конфигурация не загрузится платформой, которая эти исходники и выгрузила («Неизвестная версия
|
||||||
|
формата 2.17 загружаемого файла», замерено на 8.3.20).
|
||||||
|
|
||||||
|
Версию исходников берём из корня собираемого объекта (ExternalDataProcessor/ExternalReport);
|
||||||
|
вложенные файлы — запасной вариант, если корень почему-то не попался.
|
||||||
|
"""
|
||||||
|
root_version = ""
|
||||||
|
any_version = ""
|
||||||
|
ver_pattern = re.compile(r'<MetaDataObject[^>]+version="(\d+\.\d+)"')
|
||||||
|
root_pattern = re.compile(r'<(ExternalDataProcessor|ExternalReport)[ >]')
|
||||||
|
for dirpath, _, filenames in os.walk(source_dir):
|
||||||
|
for fn in filenames:
|
||||||
|
if not fn.endswith('.xml'):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
with open(os.path.join(dirpath, fn), 'r', encoding='utf-8-sig') as f:
|
||||||
|
content = f.read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
m = ver_pattern.search(content)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
if not any_version:
|
||||||
|
any_version = m.group(1)
|
||||||
|
if not root_version and root_pattern.search(content):
|
||||||
|
root_version = m.group(1)
|
||||||
|
src_version = root_version or any_version or "2.17"
|
||||||
|
src_rank = format_rank(src_version)
|
||||||
|
return src_version if 0 < src_rank < format_rank("2.17") else "2.17"
|
||||||
|
|
||||||
|
|
||||||
|
# Режим совместимости заглушки — по той же логике, что и версия формата. Платформа отказывается
|
||||||
|
# работать с конфигурацией, чей режим выше её самой («Для работы с конфигурацией необходима версия
|
||||||
|
# платформы не меньше, чем 8.3.24»), и тогда объекты заглушки в базу не попадают: загрузка
|
||||||
|
# рапортует успех, а сборка падает на «Неизвестное имя типа». Ступени — лестница версий формата
|
||||||
|
# из docs/1c-configuration-spec.md.
|
||||||
|
COMPAT_BY_FORMAT = {
|
||||||
|
"2.13": "Version8_3_20",
|
||||||
|
"2.14": "Version8_3_21",
|
||||||
|
"2.15": "Version8_3_22",
|
||||||
|
"2.16": "Version8_3_23",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def stub_compatibility_mode(format_version):
|
||||||
|
return COMPAT_BY_FORMAT.get(format_version, "Version8_3_24")
|
||||||
|
|
||||||
|
|
||||||
def scan_register_columns(source_dir):
|
def scan_register_columns(source_dir):
|
||||||
"""Scan Form.xml for register record set columns referenced via DataPath.
|
"""Scan Form.xml for register record set columns referenced via DataPath.
|
||||||
Returns {"RegisterType.RegisterName": {"col1": True, "col2": True}}."""
|
Returns {"RegisterType.RegisterName": {"col1": True, "col2": True}}."""
|
||||||
@@ -417,7 +477,7 @@ NS = (
|
|||||||
'xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" '
|
'xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" '
|
||||||
'xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" '
|
'xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" '
|
||||||
'xmlns:xs="http://www.w3.org/2001/XMLSchema" '
|
'xmlns:xs="http://www.w3.org/2001/XMLSchema" '
|
||||||
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17"'
|
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
|
||||||
)
|
)
|
||||||
|
|
||||||
CLASS_IDS = [
|
CLASS_IDS = [
|
||||||
@@ -1046,6 +1106,9 @@ def main():
|
|||||||
type_map = scan_ref_types(args.SourceDir)
|
type_map = scan_ref_types(args.SourceDir)
|
||||||
register_columns = scan_register_columns(args.SourceDir)
|
register_columns = scan_register_columns(args.SourceDir)
|
||||||
has_ref_types = len(type_map) > 0
|
has_ref_types = len(type_map) > 0
|
||||||
|
stub_format_version = detect_stub_format_version(args.SourceDir)
|
||||||
|
stub_compat = stub_compatibility_mode(stub_format_version)
|
||||||
|
ns_decl = f'{NS} version="{stub_format_version}"'
|
||||||
|
|
||||||
temp_base = args.TempBasePath or os.path.join(tempfile.gettempdir(), f'epf_stub_db_{random.randint(0,999999)}')
|
temp_base = args.TempBasePath or os.path.join(tempfile.gettempdir(), f'epf_stub_db_{random.randint(0,999999)}')
|
||||||
|
|
||||||
@@ -1077,7 +1140,7 @@ def main():
|
|||||||
child_xml += f'\n\t\t\t<{tag}>{name}</{tag}>'
|
child_xml += f'\n\t\t\t<{tag}>{name}</{tag}>'
|
||||||
|
|
||||||
cfg_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
cfg_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<MetaDataObject {NS}>
|
<MetaDataObject {ns_decl}>
|
||||||
\t<Configuration uuid="{uuid_cfg}">
|
\t<Configuration uuid="{uuid_cfg}">
|
||||||
\t\t<InternalInfo>{co_xml}
|
\t\t<InternalInfo>{co_xml}
|
||||||
\t\t</InternalInfo>
|
\t\t</InternalInfo>
|
||||||
@@ -1086,7 +1149,7 @@ def main():
|
|||||||
\t\t\t<Synonym/>
|
\t\t\t<Synonym/>
|
||||||
\t\t\t<Comment/>
|
\t\t\t<Comment/>
|
||||||
\t\t\t<NamePrefix/>
|
\t\t\t<NamePrefix/>
|
||||||
\t\t\t<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
|
\t\t\t<ConfigurationExtensionCompatibilityMode>{stub_compat}</ConfigurationExtensionCompatibilityMode>
|
||||||
\t\t\t<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
\t\t\t<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||||
\t\t\t<UsePurposes>
|
\t\t\t<UsePurposes>
|
||||||
\t\t\t\t<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
\t\t\t\t<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||||
@@ -1137,7 +1200,7 @@ def main():
|
|||||||
\t\t\t<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
\t\t\t<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
||||||
\t\t\t<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
\t\t\t<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
||||||
\t\t\t<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
\t\t\t<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
||||||
\t\t\t<CompatibilityMode>Version8_3_24</CompatibilityMode>
|
\t\t\t<CompatibilityMode>{stub_compat}</CompatibilityMode>
|
||||||
\t\t\t<DefaultConstantsForm/>
|
\t\t\t<DefaultConstantsForm/>
|
||||||
\t\t</Properties>
|
\t\t</Properties>
|
||||||
\t\t<ChildObjects>{child_xml}
|
\t\t<ChildObjects>{child_xml}
|
||||||
@@ -1151,7 +1214,7 @@ def main():
|
|||||||
lang_dir = os.path.join(cfg_dir, 'Languages')
|
lang_dir = os.path.join(cfg_dir, 'Languages')
|
||||||
os.makedirs(lang_dir, exist_ok=True)
|
os.makedirs(lang_dir, exist_ok=True)
|
||||||
lang_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
lang_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<MetaDataObject {NS}>
|
<MetaDataObject {ns_decl}>
|
||||||
\t<Language uuid="{uuid_lang}">
|
\t<Language uuid="{uuid_lang}">
|
||||||
\t\t<Properties>
|
\t\t<Properties>
|
||||||
\t\t\t<Name>\u0420\u0443\u0441\u0441\u043a\u0438\u0439</Name>
|
\t\t\t<Name>\u0420\u0443\u0441\u0441\u043a\u0438\u0439</Name>
|
||||||
@@ -1280,7 +1343,7 @@ def main():
|
|||||||
child_obj_xml = '\n\t\t<ChildObjects/>'
|
child_obj_xml = '\n\t\t<ChildObjects/>'
|
||||||
|
|
||||||
obj_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
obj_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<MetaDataObject {NS}>
|
<MetaDataObject {ns_decl}>
|
||||||
\t<{tag} uuid="{obj_uuid}">{internal_xml}
|
\t<{tag} uuid="{obj_uuid}">{internal_xml}
|
||||||
\t\t<Properties>
|
\t\t<Properties>
|
||||||
{props_xml}
|
{props_xml}
|
||||||
|
|||||||
Reference in New Issue
Block a user