mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-18 07:35:52 +03:00
fix(role-edit): @файл читается в позиции значения, а не вместо всей строки
Было неинтуитивно и вдобавок молча портило файл: `@условие.txt` целиком заменял -Value, поэтому в файле должен был лежать ещё и адрес, а естественная форма "Catalog.Товары.Read: @условие.txt" записывала в условие литерал "@условие.txt" — без единого предупреждения. Теперь файл читается там, где стоит текст: условие RLS и тело шаблона — после двоеточия, синоним и комментарий — как всё значение. Адрес остаётся в команде. Пакет делится по ;; ДО чтения файла, поэтому ;; внутри условия больше не разделитель — прежний костыль с отключением пакета убран. Отсутствие файла — ошибка до записи. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
This commit is contained in:
co-authored by
Claude Opus 5
parent
2bd624a66f
commit
9c14715359
@@ -20,7 +20,7 @@ allowed-tools:
|
||||
|----------|----------|
|
||||
| `RolePath` | Каталог роли, `Roles/Имя.xml` или `Roles/Имя/Ext/Rights.xml` |
|
||||
| `Operation` | Операция из таблицы ниже (альтернатива `DefinitionFile`) |
|
||||
| `Value` | Значение операции. Пакет через `;;`. `@путь` — взять значение из файла |
|
||||
| `Value` | Значение операции. Пакет через `;;`. Текст можно взять из файла: `@путь` в позиции значения |
|
||||
| `DefinitionFile` | JSON-массив операций `[{ "operation": "...", "value": "..." }]` |
|
||||
| `NoValidate` | Не запускать `role-validate` после правки |
|
||||
|
||||
@@ -86,10 +86,19 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-edit.ps1" -Rol
|
||||
... -Operation set-rls -Value "Catalog.Товары.Read[Ссылка, Код, Наименование]: "
|
||||
```
|
||||
|
||||
Строка с тем же набором полей заменяется, с другим — добавляется. Многострочное условие передавай
|
||||
файлом: `-Value "@условие.txt"` (в этом режиме `;;` считается текстом, а не разделителем пакета).
|
||||
Строка с тем же набором полей заменяется, с другим — добавляется.
|
||||
Ограничение на невыданное право — ошибка: платформа такое молча игнорирует.
|
||||
|
||||
Длинное условие держи в файле — адрес остаётся в команде, из файла приходит только текст:
|
||||
|
||||
```powershell
|
||||
... -Operation set-rls -Value "Catalog.Товары.Read: @условие.txt"
|
||||
... -Operation add-template -Value "ПоОрганизации(Мод): @условие.txt"
|
||||
```
|
||||
|
||||
Так же берётся текст у `set-synonym` и `set-comment`: `-Value "@текст.txt"`. Разделитель пакета
|
||||
`;;` внутри файла остаётся обычным текстом.
|
||||
|
||||
Ссылка на шаблон — `#ИмяШаблона("")`; `&` в условиях экранируется автоматически.
|
||||
|
||||
## Верификация
|
||||
|
||||
@@ -1405,18 +1405,18 @@ $script:rightsPath = $script:paths.RightsPath
|
||||
$script:roleXmlPath = $script:paths.RoleXmlPath
|
||||
$script:configRoot = $script:paths.ConfigRoot
|
||||
|
||||
# -Value "@путь" — содержимое берётся из файла: так передают многострочное условие RLS,
|
||||
# которое инлайном ломается о кавычки и о разделитель пакета.
|
||||
$script:valueFromFile = $false
|
||||
if ($Value -and $Value.StartsWith("@")) {
|
||||
$valueFile = $Value.Substring(1)
|
||||
# "@путь" в позиции ТЕКСТА (условие RLS, тело шаблона, синоним) — содержимое берётся из файла:
|
||||
# многострочное условие инлайном ломается о кавычки и о разделитель пакета. Адрес при этом
|
||||
# остаётся в команде: "Catalog.Товары.Read: @условие.txt".
|
||||
function Resolve-TextValue([string]$text) {
|
||||
if (-not $text -or -not $text.StartsWith("@")) { return $text }
|
||||
$valueFile = $text.Substring(1).Trim()
|
||||
if (-not [System.IO.Path]::IsPathRooted($valueFile)) { $valueFile = Join-Path (Get-Location).Path $valueFile }
|
||||
if (-not (Test-Path -LiteralPath $valueFile -PathType Leaf)) {
|
||||
[Console]::Error.WriteLine("[role-edit] Файл значения не найден: $valueFile")
|
||||
exit 1
|
||||
Add-ValidationError "Файл значения не найден: $valueFile"
|
||||
return $text
|
||||
}
|
||||
$Value = [System.IO.File]::ReadAllText($valueFile).Trim()
|
||||
$script:valueFromFile = $true
|
||||
return [System.IO.File]::ReadAllText($valueFile).Trim()
|
||||
}
|
||||
|
||||
if ($DefinitionFile -and $Operation) {
|
||||
@@ -1453,8 +1453,7 @@ function Add-Note([string]$text) { $script:notes += $text }
|
||||
# --- Разбор значений операций ---
|
||||
|
||||
function Parse-BatchValue([string]$val) {
|
||||
# Значение из файла не режем: там живут многострочные условия, в которых ';;' — просто текст.
|
||||
if ($script:valueFromFile) { return ,@($val) }
|
||||
# Делим ДО чтения файлов, поэтому ';;' внутри условия из файла разделителем не становится.
|
||||
return @($val -split ';;' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
||||
}
|
||||
|
||||
@@ -1539,7 +1538,7 @@ function Parse-RlsAddress([string]$text, [switch]$ConditionRequired) {
|
||||
Add-ValidationError "$text : разобрано как объект '$objName' и право '$rightName'"
|
||||
return $null
|
||||
}
|
||||
return @{ Object = $objName; Right = $rightName; Fields = $fields; Condition = $condition }
|
||||
return @{ Object = $objName; Right = $rightName; Fields = $fields; Condition = (Resolve-TextValue $condition) }
|
||||
}
|
||||
|
||||
# "Имя(Пар1, Пар2): условие" — скобки принадлежат имени шаблона, разделитель ищем вне них.
|
||||
@@ -1550,7 +1549,7 @@ function Parse-TemplateSpec([string]$text, [switch]$NameOnly) {
|
||||
Add-ValidationError "$text : ожидается 'Имя(Параметры): условие'"
|
||||
return $null
|
||||
}
|
||||
return @{ Name = $split.Left; Condition = $split.Right }
|
||||
return @{ Name = $split.Left; Condition = (Resolve-TextValue $split.Right) }
|
||||
}
|
||||
|
||||
# --- Доступ к дереву прав ---
|
||||
@@ -2222,8 +2221,8 @@ foreach ($op in $operations) {
|
||||
"set-template" { Do-SetTemplate $opValue }
|
||||
"remove-template" { Do-RemoveTemplate $opValue }
|
||||
"modify-property" { Do-ModifyProperty $opValue }
|
||||
"set-synonym" { $script:pendingMeta += ,@{ Field = 'Synonym'; Text = $opValue } }
|
||||
"set-comment" { $script:pendingMeta += ,@{ Field = 'Comment'; Text = $opValue } }
|
||||
"set-synonym" { $script:pendingMeta += ,@{ Field = 'Synonym'; Text = (Resolve-TextValue $opValue) } }
|
||||
"set-comment" { $script:pendingMeta += ,@{ Field = 'Comment'; Text = (Resolve-TextValue $opValue) } }
|
||||
default {
|
||||
Add-ValidationError "Неизвестная операция: $opName"
|
||||
}
|
||||
|
||||
@@ -1461,9 +1461,8 @@ def resolve_role_paths(input_path):
|
||||
class Editor:
|
||||
"""Состояние правки: дерево прав, счётчики, отложенные операции."""
|
||||
|
||||
def __init__(self, paths, value_from_file):
|
||||
def __init__(self, paths):
|
||||
self.paths = paths
|
||||
self.value_from_file = value_from_file
|
||||
parser = etree.XMLParser(remove_blank_text=False)
|
||||
self.tree = etree.parse(paths["RightsPath"], parser)
|
||||
self.root = self.tree.getroot()
|
||||
@@ -1484,11 +1483,25 @@ class Editor:
|
||||
# --- Разбор значений операций ---
|
||||
|
||||
def parse_batch(self, value):
|
||||
# Значение из файла не режем: там живут многострочные условия, в которых ';;' — просто текст.
|
||||
if self.value_from_file:
|
||||
return [value]
|
||||
# Делим ДО чтения файлов, поэтому ';;' внутри условия из файла разделителем не становится.
|
||||
return [part.strip() for part in value.split(";;") if part.strip()]
|
||||
|
||||
@staticmethod
|
||||
def resolve_text_value(text):
|
||||
""""@путь" в позиции ТЕКСТА (условие RLS, тело шаблона, синоним) — содержимое из файла:
|
||||
многострочное условие инлайном ломается о кавычки и о разделитель пакета. Адрес при этом
|
||||
остаётся в команде: "Catalog.Товары.Read: @условие.txt"."""
|
||||
if not text or not text.startswith("@"):
|
||||
return text
|
||||
value_file = text[1:].strip()
|
||||
if not os.path.isabs(value_file):
|
||||
value_file = os.path.join(os.getcwd(), value_file)
|
||||
if not os.path.isfile(value_file):
|
||||
add_validation_error(f"Файл значения не найден: {value_file}")
|
||||
return text
|
||||
with open(value_file, encoding="utf-8-sig") as f:
|
||||
return f.read().strip()
|
||||
|
||||
@staticmethod
|
||||
def split_at_top_level_colon(text, open_char, close_char):
|
||||
depth = 0
|
||||
@@ -1552,7 +1565,8 @@ class Editor:
|
||||
# Показываем разбор: иначе непонятно, что навык откусил не тот сегмент.
|
||||
add_validation_error(f"{text} : разобрано как объект '{obj_name}' и право '{right_name}'")
|
||||
return None
|
||||
return {"Object": obj_name, "Right": right_name, "Fields": fields, "Condition": condition}
|
||||
return {"Object": obj_name, "Right": right_name, "Fields": fields,
|
||||
"Condition": self.resolve_text_value(condition)}
|
||||
|
||||
def parse_template_spec(self, text, name_only=False):
|
||||
left, right, found = self.split_at_top_level_colon(text, "(", ")")
|
||||
@@ -1561,7 +1575,7 @@ class Editor:
|
||||
if not found:
|
||||
add_validation_error(f"{text} : ожидается 'Имя(Параметры): условие'")
|
||||
return None
|
||||
return {"Name": left, "Condition": right}
|
||||
return {"Name": left, "Condition": self.resolve_text_value(right)}
|
||||
|
||||
# --- Доступ к дереву прав ---
|
||||
|
||||
@@ -2064,20 +2078,7 @@ def main():
|
||||
|
||||
paths = resolve_role_paths(args.RolePath)
|
||||
|
||||
# -Value "@путь" — содержимое берётся из файла: так передают многострочное условие RLS,
|
||||
# которое инлайном ломается о кавычки и о разделитель пакета.
|
||||
value = args.Value
|
||||
value_from_file = False
|
||||
if value and value.startswith("@"):
|
||||
value_file = value[1:]
|
||||
if not os.path.isabs(value_file):
|
||||
value_file = os.path.join(os.getcwd(), value_file)
|
||||
if not os.path.isfile(value_file):
|
||||
print(f"[role-edit] Файл значения не найден: {value_file}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
with open(value_file, encoding="utf-8-sig") as f:
|
||||
value = f.read().strip()
|
||||
value_from_file = True
|
||||
|
||||
if args.DefinitionFile and args.Operation:
|
||||
print("[role-edit] Укажите либо -DefinitionFile, либо -Operation, но не оба сразу", file=sys.stderr)
|
||||
@@ -2089,7 +2090,7 @@ def main():
|
||||
target_for_guard = paths["RoleXmlPath"] if os.path.isfile(paths["RoleXmlPath"]) else paths["RightsPath"]
|
||||
assert_edit_allowed(target_for_guard, "editable")
|
||||
|
||||
ed = Editor(paths, value_from_file)
|
||||
ed = Editor(paths)
|
||||
|
||||
operations = []
|
||||
if args.DefinitionFile:
|
||||
@@ -2148,9 +2149,9 @@ def main():
|
||||
continue
|
||||
pending.append((key, {"Name": canonical, "Value": val}))
|
||||
elif key == "set-synonym":
|
||||
pending.append((key, {"Field": "Synonym", "Text": op_value}))
|
||||
pending.append((key, {"Field": "Synonym", "Text": ed.resolve_text_value(op_value)}))
|
||||
elif key == "set-comment":
|
||||
pending.append((key, {"Field": "Comment", "Text": op_value}))
|
||||
pending.append((key, {"Field": "Comment", "Text": ed.resolve_text_value(op_value)}))
|
||||
else:
|
||||
add_validation_error(f"Неизвестная операция: {op_name}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user