mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-15 18:34:57 +03:00
Auto-build: copilot (python) from 7fa279c
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
# role-info v1.0 — Analyze 1C role rights
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][Alias('Path')][string]$RightsPath,
|
||||
[switch]$ShowDenied,
|
||||
[int]$Limit = 150,
|
||||
[int]$Offset = 0,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Output helper (always collect, paginate at the end) ---
|
||||
$script:lines = @()
|
||||
function Out([string]$text) { $script:lines += $text }
|
||||
|
||||
# --- Resolve paths ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($RightsPath)) {
|
||||
$RightsPath = Join-Path (Get-Location).Path $RightsPath
|
||||
}
|
||||
|
||||
if (-not (Test-Path $RightsPath)) {
|
||||
Write-Host "[ERROR] File not found: $RightsPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Try to find metadata file for role name/synonym ---
|
||||
$roleName = ""
|
||||
$roleSynonym = ""
|
||||
$extDir = Split-Path $RightsPath # .../Ext
|
||||
$roleDir = Split-Path $extDir # .../RoleName
|
||||
$rolesDir = Split-Path $roleDir # .../Roles
|
||||
$roleFolderName = Split-Path $roleDir -Leaf
|
||||
$metaPath = Join-Path $rolesDir "$roleFolderName.xml"
|
||||
|
||||
if (Test-Path $metaPath) {
|
||||
try {
|
||||
[xml]$metaXml = Get-Content -Path $metaPath -Encoding UTF8
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($metaXml.NameTable)
|
||||
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||
$nameNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Name", $ns)
|
||||
if ($nameNode) { $roleName = $nameNode.InnerText }
|
||||
$synNode = $metaXml.SelectSingleNode("//md:Role/md:Properties/md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
||||
if ($synNode) { $roleSynonym = $synNode.InnerText }
|
||||
} catch {
|
||||
# Ignore metadata parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $roleName) { $roleName = $roleFolderName }
|
||||
|
||||
# --- Parse Rights.xml ---
|
||||
[xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8
|
||||
$root = $xml.DocumentElement
|
||||
$rightsNs = "http://v8.1c.ru/8.2/roles"
|
||||
|
||||
# Global flags
|
||||
$setForNew = $root.setForNewObjects
|
||||
$setForAttrs = $root.setForAttributesByDefault
|
||||
$independentChild = $root.independentRightsOfChildObjects
|
||||
|
||||
# --- Collect objects ---
|
||||
# Structure: grouped by type prefix, then by object short name
|
||||
$allowed = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] }
|
||||
$denied = [ordered]@{} # type -> [ordered]@{ shortName -> [list of rights] }
|
||||
$rlsObjects = @()
|
||||
$totalAllowed = 0
|
||||
$totalDenied = 0
|
||||
|
||||
$objects = $root.GetElementsByTagName("object", $rightsNs)
|
||||
foreach ($obj in $objects) {
|
||||
$objName = ""
|
||||
$rights = @()
|
||||
|
||||
foreach ($child in $obj.ChildNodes) {
|
||||
if ($child.LocalName -eq "name" -and $child.NamespaceURI -eq $rightsNs) {
|
||||
$objName = $child.InnerText
|
||||
}
|
||||
if ($child.LocalName -eq "right" -and $child.NamespaceURI -eq $rightsNs) {
|
||||
$rName = ""
|
||||
$rValue = ""
|
||||
$hasRLS = $false
|
||||
foreach ($rc in $child.ChildNodes) {
|
||||
if ($rc.LocalName -eq "name") { $rName = $rc.InnerText }
|
||||
if ($rc.LocalName -eq "value") { $rValue = $rc.InnerText }
|
||||
if ($rc.LocalName -eq "restrictionByCondition") { $hasRLS = $true }
|
||||
}
|
||||
if ($rName -and $rValue) {
|
||||
$rights += @{ name = $rName; value = $rValue; rls = $hasRLS }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $objName -or $rights.Count -eq 0) { continue }
|
||||
|
||||
# Split into type prefix and short name
|
||||
$dotIdx = $objName.IndexOf(".")
|
||||
if ($dotIdx -lt 0) { continue }
|
||||
$typePrefix = $objName.Substring(0, $dotIdx)
|
||||
$shortName = $objName.Substring($dotIdx + 1)
|
||||
|
||||
foreach ($r in $rights) {
|
||||
if ($r.value -eq "true") {
|
||||
$totalAllowed++
|
||||
if (-not $allowed.Contains($typePrefix)) {
|
||||
$allowed[$typePrefix] = [ordered]@{}
|
||||
}
|
||||
if (-not $allowed[$typePrefix].Contains($shortName)) {
|
||||
$allowed[$typePrefix][$shortName] = @()
|
||||
}
|
||||
$suffix = $r.name
|
||||
if ($r.rls) {
|
||||
$suffix += " [RLS]"
|
||||
$rlsObjects += "$typePrefix.$shortName ($($r.name))"
|
||||
}
|
||||
$allowed[$typePrefix][$shortName] += $suffix
|
||||
}
|
||||
else {
|
||||
$totalDenied++
|
||||
if (-not $denied.Contains($typePrefix)) {
|
||||
$denied[$typePrefix] = [ordered]@{}
|
||||
}
|
||||
if (-not $denied[$typePrefix].Contains($shortName)) {
|
||||
$denied[$typePrefix][$shortName] = @()
|
||||
}
|
||||
$denied[$typePrefix][$shortName] += $r.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Restriction templates ---
|
||||
$templates = @()
|
||||
$tplNodes = $root.GetElementsByTagName("restrictionTemplate", $rightsNs)
|
||||
foreach ($tpl in $tplNodes) {
|
||||
foreach ($child in $tpl.ChildNodes) {
|
||||
if ($child.LocalName -eq "name") {
|
||||
$tName = $child.InnerText
|
||||
# Extract just the name part before parentheses
|
||||
$parenIdx = $tName.IndexOf("(")
|
||||
if ($parenIdx -gt 0) { $tName = $tName.Substring(0, $parenIdx) }
|
||||
$templates += $tName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Output ---
|
||||
$header = "=== Role: $roleName"
|
||||
if ($roleSynonym) { $header += " --- `"$roleSynonym`"" }
|
||||
$header += " ==="
|
||||
Out $header
|
||||
Out ""
|
||||
|
||||
Out "Properties: setForNewObjects=$setForNew, setForAttributesByDefault=$setForAttrs, independentRightsOfChildObjects=$independentChild"
|
||||
Out ""
|
||||
|
||||
# Helper: output group
|
||||
function OutGroup($objMap, [string]$prefix, [switch]$isDenied) {
|
||||
foreach ($shortName in @($objMap.Keys)) {
|
||||
if ($isDenied) {
|
||||
$rightsList = ($objMap[$shortName] | ForEach-Object { "-$_" }) -join ", "
|
||||
} else {
|
||||
$rightsList = $objMap[$shortName] -join ", "
|
||||
}
|
||||
Out " ${shortName}: $rightsList"
|
||||
}
|
||||
}
|
||||
|
||||
# Allowed rights grouped by type
|
||||
if ($allowed.Count -gt 0) {
|
||||
Out "Allowed rights:"
|
||||
Out ""
|
||||
foreach ($typePrefix in $allowed.Keys) {
|
||||
$objMap = $allowed[$typePrefix]
|
||||
Out " $typePrefix ($($objMap.Count)):"
|
||||
OutGroup $objMap $typePrefix
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
else {
|
||||
Out "(no allowed rights)"
|
||||
Out ""
|
||||
}
|
||||
|
||||
# Denied rights
|
||||
if ($ShowDenied -and $denied.Count -gt 0) {
|
||||
Out "Denied rights:"
|
||||
Out ""
|
||||
foreach ($typePrefix in $denied.Keys) {
|
||||
$objMap = $denied[$typePrefix]
|
||||
Out " $typePrefix ($($objMap.Count)):"
|
||||
OutGroup $objMap $typePrefix -isDenied
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
elseif ($totalDenied -gt 0) {
|
||||
Out "Denied: $totalDenied rights (use -ShowDenied to list)"
|
||||
Out ""
|
||||
}
|
||||
|
||||
# RLS summary
|
||||
if ($rlsObjects.Count -gt 0) {
|
||||
Out "RLS: $($rlsObjects.Count) restrictions"
|
||||
}
|
||||
|
||||
# Templates
|
||||
if ($templates.Count -gt 0) {
|
||||
Out "Templates: $($templates -join ', ')"
|
||||
}
|
||||
|
||||
Out ""
|
||||
Out "---"
|
||||
Out "Total: $totalAllowed allowed, $totalDenied denied"
|
||||
|
||||
# --- Pagination and output ---
|
||||
$totalLines = $script:lines.Count
|
||||
$lines = $script:lines
|
||||
|
||||
if ($Offset -gt 0) {
|
||||
if ($Offset -ge $totalLines) {
|
||||
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
|
||||
exit 0
|
||||
}
|
||||
$lines = $lines[$Offset..($totalLines - 1)]
|
||||
}
|
||||
|
||||
if ($Limit -gt 0 -and $lines.Count -gt $Limit) {
|
||||
$shown = $lines[0..($Limit - 1)]
|
||||
$remaining = $totalLines - $Offset - $Limit
|
||||
$shown += ""
|
||||
$shown += "[TRUNCATED] Shown $Limit of $totalLines lines. Use -Offset $($Offset + $Limit) to continue."
|
||||
$lines = $shown
|
||||
}
|
||||
|
||||
if ($OutFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
|
||||
$OutFile = Join-Path (Get-Location).Path $OutFile
|
||||
}
|
||||
$utf8 = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllLines($OutFile, $lines, $utf8)
|
||||
Write-Host "Output written to $OutFile"
|
||||
} else {
|
||||
foreach ($l in $lines) { Write-Host $l }
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-info v1.0 — Analyze 1C role rights
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
from lxml import etree
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
sys.stderr.reconfigure(encoding="utf-8")
|
||||
|
||||
# --- Argument parsing ---
|
||||
parser = argparse.ArgumentParser(description="Analyze 1C role rights", allow_abbrev=False)
|
||||
parser.add_argument("-RightsPath", "-Path", required=True, help="Path to Rights.xml")
|
||||
parser.add_argument("-ShowDenied", action="store_true", default=False, help="Show denied rights")
|
||||
parser.add_argument("-Limit", type=int, default=150, help="Max lines to show")
|
||||
parser.add_argument("-Offset", type=int, default=0, help="Lines to skip")
|
||||
parser.add_argument("-OutFile", default="", help="Write output to file")
|
||||
args = parser.parse_args()
|
||||
|
||||
# --- Output helper (collect all, paginate at the end) ---
|
||||
lines_buf = []
|
||||
|
||||
def out(text=""):
|
||||
lines_buf.append(text)
|
||||
|
||||
# --- Resolve paths ---
|
||||
rights_path = args.RightsPath
|
||||
if not os.path.isabs(rights_path):
|
||||
rights_path = os.path.join(os.getcwd(), rights_path)
|
||||
|
||||
if not os.path.isfile(rights_path):
|
||||
print(f"[ERROR] File not found: {rights_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# --- Try to find metadata file for role name/synonym ---
|
||||
role_name = ""
|
||||
role_synonym = ""
|
||||
ext_dir = os.path.dirname(rights_path) # .../Ext
|
||||
role_dir = os.path.dirname(ext_dir) # .../RoleName
|
||||
roles_dir = os.path.dirname(role_dir) # .../Roles
|
||||
role_folder_name = os.path.basename(role_dir)
|
||||
meta_path = os.path.join(roles_dir, f"{role_folder_name}.xml")
|
||||
|
||||
if os.path.isfile(meta_path):
|
||||
try:
|
||||
meta_tree = etree.parse(meta_path, etree.XMLParser(remove_blank_text=False))
|
||||
meta_root = meta_tree.getroot()
|
||||
meta_ns = {
|
||||
"md": "http://v8.1c.ru/8.3/MDClasses",
|
||||
"v8": "http://v8.1c.ru/8.1/data/core",
|
||||
}
|
||||
name_node = meta_root.find(".//md:Role/md:Properties/md:Name", meta_ns)
|
||||
if name_node is not None and name_node.text:
|
||||
role_name = name_node.text
|
||||
syn_node = meta_root.find(
|
||||
".//md:Role/md:Properties/md:Synonym/v8:item[v8:lang='ru']/v8:content", meta_ns
|
||||
)
|
||||
if syn_node is not None and syn_node.text:
|
||||
role_synonym = syn_node.text
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not role_name:
|
||||
role_name = role_folder_name
|
||||
|
||||
# --- Parse Rights.xml ---
|
||||
tree = etree.parse(rights_path, etree.XMLParser(remove_blank_text=False))
|
||||
root = tree.getroot()
|
||||
rights_ns = "http://v8.1c.ru/8.2/roles"
|
||||
NSMAP = {"r": rights_ns}
|
||||
|
||||
# Global flags
|
||||
set_for_new = root.get("setForNewObjects", "")
|
||||
set_for_attrs = root.get("setForAttributesByDefault", "")
|
||||
independent_child = root.get("independentRightsOfChildObjects", "")
|
||||
|
||||
# --- Collect objects ---
|
||||
allowed = OrderedDict() # type -> OrderedDict { shortName -> [rights] }
|
||||
denied = OrderedDict()
|
||||
rls_objects = []
|
||||
total_allowed = 0
|
||||
total_denied = 0
|
||||
|
||||
for obj in root.findall("r:object", NSMAP):
|
||||
obj_name = ""
|
||||
rights = []
|
||||
|
||||
for child in obj:
|
||||
local = etree.QName(child.tag).localname
|
||||
if local == "name" and child.tag == f"{{{rights_ns}}}name":
|
||||
obj_name = child.text or ""
|
||||
if local == "right" and child.tag == f"{{{rights_ns}}}right":
|
||||
r_name = ""
|
||||
r_value = ""
|
||||
has_rls = False
|
||||
for rc in child:
|
||||
rc_local = etree.QName(rc.tag).localname
|
||||
if rc_local == "name":
|
||||
r_name = rc.text or ""
|
||||
if rc_local == "value":
|
||||
r_value = rc.text or ""
|
||||
if rc_local == "restrictionByCondition":
|
||||
has_rls = True
|
||||
if r_name and r_value:
|
||||
rights.append({"name": r_name, "value": r_value, "rls": has_rls})
|
||||
|
||||
if not obj_name or len(rights) == 0:
|
||||
continue
|
||||
|
||||
dot_idx = obj_name.find(".")
|
||||
if dot_idx < 0:
|
||||
continue
|
||||
type_prefix = obj_name[:dot_idx]
|
||||
short_name = obj_name[dot_idx + 1:]
|
||||
|
||||
for r in rights:
|
||||
if r["value"] == "true":
|
||||
total_allowed += 1
|
||||
if type_prefix not in allowed:
|
||||
allowed[type_prefix] = OrderedDict()
|
||||
if short_name not in allowed[type_prefix]:
|
||||
allowed[type_prefix][short_name] = []
|
||||
suffix = r["name"]
|
||||
if r["rls"]:
|
||||
suffix += " [RLS]"
|
||||
rls_objects.append(f"{type_prefix}.{short_name} ({r['name']})")
|
||||
allowed[type_prefix][short_name].append(suffix)
|
||||
else:
|
||||
total_denied += 1
|
||||
if type_prefix not in denied:
|
||||
denied[type_prefix] = OrderedDict()
|
||||
if short_name not in denied[type_prefix]:
|
||||
denied[type_prefix][short_name] = []
|
||||
denied[type_prefix][short_name].append(r["name"])
|
||||
|
||||
# --- Restriction templates ---
|
||||
templates = []
|
||||
for tpl in root.findall("r:restrictionTemplate", NSMAP):
|
||||
for child in tpl:
|
||||
if etree.QName(child.tag).localname == "name":
|
||||
t_name = child.text or ""
|
||||
paren_idx = t_name.find("(")
|
||||
if paren_idx > 0:
|
||||
t_name = t_name[:paren_idx]
|
||||
templates.append(t_name)
|
||||
|
||||
# --- Output ---
|
||||
header = f"=== Role: {role_name}"
|
||||
if role_synonym:
|
||||
header += f' --- "{role_synonym}"'
|
||||
header += " ==="
|
||||
out(header)
|
||||
out()
|
||||
|
||||
out(f"Properties: setForNewObjects={set_for_new}, setForAttributesByDefault={set_for_attrs}, independentRightsOfChildObjects={independent_child}")
|
||||
out()
|
||||
|
||||
# Helper: output group
|
||||
def out_group(obj_map, is_denied=False):
|
||||
for short_name, rights_list in obj_map.items():
|
||||
if is_denied:
|
||||
rights_str = ", ".join(f"-{r}" for r in rights_list)
|
||||
else:
|
||||
rights_str = ", ".join(rights_list)
|
||||
out(f" {short_name}: {rights_str}")
|
||||
|
||||
# Allowed rights grouped by type
|
||||
if len(allowed) > 0:
|
||||
out("Allowed rights:")
|
||||
out()
|
||||
for type_prefix, obj_map in allowed.items():
|
||||
out(f" {type_prefix} ({len(obj_map)}):")
|
||||
out_group(obj_map)
|
||||
out()
|
||||
else:
|
||||
out("(no allowed rights)")
|
||||
out()
|
||||
|
||||
# Denied rights
|
||||
if args.ShowDenied and len(denied) > 0:
|
||||
out("Denied rights:")
|
||||
out()
|
||||
for type_prefix, obj_map in denied.items():
|
||||
out(f" {type_prefix} ({len(obj_map)}):")
|
||||
out_group(obj_map, is_denied=True)
|
||||
out()
|
||||
elif total_denied > 0:
|
||||
out(f"Denied: {total_denied} rights (use -ShowDenied to list)")
|
||||
out()
|
||||
|
||||
# RLS summary
|
||||
if len(rls_objects) > 0:
|
||||
out(f"RLS: {len(rls_objects)} restrictions")
|
||||
|
||||
# Templates
|
||||
if len(templates) > 0:
|
||||
out(f"Templates: {', '.join(templates)}")
|
||||
|
||||
out()
|
||||
out("---")
|
||||
out(f"Total: {total_allowed} allowed, {total_denied} denied")
|
||||
|
||||
# --- Pagination and output ---
|
||||
total_lines = len(lines_buf)
|
||||
out_lines = lines_buf[:]
|
||||
|
||||
if args.Offset > 0:
|
||||
if args.Offset >= total_lines:
|
||||
print(f"[INFO] Offset {args.Offset} exceeds total lines ({total_lines}). Nothing to show.")
|
||||
sys.exit(0)
|
||||
out_lines = out_lines[args.Offset:]
|
||||
|
||||
if args.Limit > 0 and len(out_lines) > args.Limit:
|
||||
shown = out_lines[:args.Limit]
|
||||
remaining = total_lines - args.Offset - args.Limit
|
||||
shown.append("")
|
||||
shown.append(f"[TRUNCATED] Shown {args.Limit} of {total_lines} lines. Use -Offset {args.Offset + args.Limit} to continue.")
|
||||
out_lines = shown
|
||||
|
||||
if args.OutFile:
|
||||
out_file = args.OutFile
|
||||
if not os.path.isabs(out_file):
|
||||
out_file = os.path.join(os.getcwd(), out_file)
|
||||
with open(out_file, "w", encoding="utf-8-sig") as f:
|
||||
f.write("\n".join(out_lines))
|
||||
print(f"Output written to {out_file}")
|
||||
else:
|
||||
for line in out_lines:
|
||||
print(line)
|
||||
Reference in New Issue
Block a user