Improve User Manager: Workbench-style permissions dialog, live mode support, double-click to open
- Permissions dialog now uses two-level UI: compact Edit User with >> buttons opening a separate grid dialog (Groups x Operator/Admin columns) with Clear All / Set All and auto-cascade (write implies read, admin implies operator) - Provisioning has its own >> popup dialog - Removed live mode restrictions for add/edit/delete user operations - Broadened type detection and added safe int conversion for live mode values - Double-click on tree node or canvas box opens special view (User Manager for UserService, Properties for other components) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = "0.0.0.117"
|
VERSION = "0.0.0.118"
|
||||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||||
DEFAULT_GRID_SIZE = 10
|
DEFAULT_GRID_SIZE = 10
|
||||||
DEFAULT_ADD_TIMESTAMP = True
|
DEFAULT_ADD_TIMESTAMP = True
|
||||||
|
|||||||
@@ -985,6 +985,42 @@ def on_tree_right_click(event):
|
|||||||
tree_comp_context_menu.post(event.x_root, event.y_root)
|
tree_comp_context_menu.post(event.x_root, event.y_root)
|
||||||
|
|
||||||
|
|
||||||
|
def _open_special_view(comp):
|
||||||
|
"""Opens the type-specific view for a component, or Properties as fallback."""
|
||||||
|
if user_service_view.is_user_service(comp):
|
||||||
|
show_user_service(comp)
|
||||||
|
return
|
||||||
|
show_properties(comp)
|
||||||
|
|
||||||
|
|
||||||
|
def on_tree_double_click(event):
|
||||||
|
"""Double-click on a tree node opens its special view (or Properties)."""
|
||||||
|
tree = app_state.tree
|
||||||
|
item_id = tree.identify_row(event.y)
|
||||||
|
if not item_id or tree.parent(item_id) == "":
|
||||||
|
return
|
||||||
|
comp = app_state.tree_element_map.get(item_id)
|
||||||
|
if comp is not None:
|
||||||
|
_open_special_view(comp)
|
||||||
|
|
||||||
|
|
||||||
|
def on_canvas_double_click(event):
|
||||||
|
"""Double-click on a canvas component opens its special view (or Properties)."""
|
||||||
|
canvas = app_state.canvas
|
||||||
|
cx = canvas.canvasx(event.x)
|
||||||
|
cy = canvas.canvasy(event.y)
|
||||||
|
clicked = canvas.find_closest(cx, cy, halo=3)
|
||||||
|
if not clicked:
|
||||||
|
return
|
||||||
|
tags = canvas.gettags(clicked[0])
|
||||||
|
group_tag = next((t for t in tags if t.startswith("comp_group_")), None)
|
||||||
|
if group_tag is None:
|
||||||
|
return
|
||||||
|
comp = app_state.canvas_comp_map.get(group_tag)
|
||||||
|
if comp is not None:
|
||||||
|
_open_special_view(comp)
|
||||||
|
|
||||||
|
|
||||||
def on_canvas_click(event):
|
def on_canvas_click(event):
|
||||||
canvas = app_state.canvas
|
canvas = app_state.canvas
|
||||||
canvas.itemconfig("block_outline", outline="#7f9db9", width=2)
|
canvas.itemconfig("block_outline", outline="#7f9db9", width=2)
|
||||||
@@ -2502,6 +2538,7 @@ tree.bind("<<TreeviewSelect>>", on_tree_select)
|
|||||||
|
|
||||||
tree.bind("<ButtonPress-3>", on_tree_right_click)
|
tree.bind("<ButtonPress-3>", on_tree_right_click)
|
||||||
tree.bind("<ButtonPress-2>", on_tree_right_click)
|
tree.bind("<ButtonPress-2>", on_tree_right_click)
|
||||||
|
tree.bind("<Double-1>", on_tree_double_click)
|
||||||
|
|
||||||
left_pane.add(frame_tree, weight=3)
|
left_pane.add(frame_tree, weight=3)
|
||||||
|
|
||||||
@@ -2729,6 +2766,7 @@ canvas.bind("<Configure>", lambda e: wiresheet.draw_grid())
|
|||||||
|
|
||||||
canvas.bind("<ButtonPress-3>", on_canvas_right_click)
|
canvas.bind("<ButtonPress-3>", on_canvas_right_click)
|
||||||
canvas.bind("<ButtonPress-2>", on_canvas_right_click)
|
canvas.bind("<ButtonPress-2>", on_canvas_right_click)
|
||||||
|
canvas.bind("<Double-1>", on_canvas_double_click)
|
||||||
|
|
||||||
# Wheel zoom. Windows and macOS report a delta; X11 sends button 4 and 5 instead.
|
# Wheel zoom. Windows and macOS report a delta; X11 sends button 4 and 5 instead.
|
||||||
canvas.bind("<Control-MouseWheel>", on_canvas_wheel_zoom)
|
canvas.bind("<Control-MouseWheel>", on_canvas_wheel_zoom)
|
||||||
|
|||||||
+225
-75
@@ -99,6 +99,18 @@ def _get_prop(comp, name):
|
|||||||
return prop.get("val") if prop is not None else None
|
return prop.get("val") if prop is not None else None
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_int(val):
|
||||||
|
if val is None:
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
return int(val)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
try:
|
||||||
|
return int(float(val))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _get_users(service_comp):
|
def _get_users(service_comp):
|
||||||
"""Returns child comp elements, excluding uncached placeholders."""
|
"""Returns child comp elements, excluding uncached placeholders."""
|
||||||
return [c for c in service_comp
|
return [c for c in service_comp
|
||||||
@@ -124,23 +136,176 @@ def is_user_service(comp):
|
|||||||
if comp is None:
|
if comp is None:
|
||||||
return False
|
return False
|
||||||
type_name = comp.get("type", "")
|
type_name = comp.get("type", "")
|
||||||
if type_name.endswith("::UserService") or type_name == "UserService":
|
if "UserService" in type_name:
|
||||||
return True
|
return True
|
||||||
for child in comp:
|
for child in comp:
|
||||||
if child.tag == "comp" and child.get("type", "").endswith("::User"):
|
if child.tag == "comp" and "User" in child.get("type", ""):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class _PermissionsDialog:
|
||||||
|
"""Workbench-style permissions grid: Groups x Operator/Admin columns."""
|
||||||
|
|
||||||
|
def __init__(self, parent, perm_val):
|
||||||
|
self.result = None
|
||||||
|
groups = _decode_perm(perm_val)
|
||||||
|
|
||||||
|
self._dlg = tk.Toplevel(parent)
|
||||||
|
self._dlg.title("Permissions")
|
||||||
|
self._dlg.transient(parent)
|
||||||
|
self._dlg.grab_set()
|
||||||
|
self._dlg.resizable(False, False)
|
||||||
|
|
||||||
|
grid = tk.Frame(self._dlg, padx=8, pady=8)
|
||||||
|
grid.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
COL_HEADERS = [
|
||||||
|
("Operator", ["Read", "Write", "Invoke"]),
|
||||||
|
("Admin", ["Read", "Write", "Invoke", "User"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# header row 0: "Groups" + category spans
|
||||||
|
tk.Label(grid, text="Groups", relief=tk.GROOVE, bg="#e8e8e8",
|
||||||
|
padx=4, pady=2).grid(row=0, column=0, rowspan=2, sticky=tk.NSEW)
|
||||||
|
col = 1
|
||||||
|
for cat_name, sub_cols in COL_HEADERS:
|
||||||
|
span = len(sub_cols)
|
||||||
|
tk.Label(grid, text=cat_name, relief=tk.GROOVE, bg="#d8e8f0",
|
||||||
|
font=("TkDefaultFont", 8, "bold"),
|
||||||
|
padx=4, pady=2).grid(row=0, column=col, columnspan=span,
|
||||||
|
sticky=tk.NSEW)
|
||||||
|
col += span
|
||||||
|
|
||||||
|
# header row 1: sub-column names
|
||||||
|
col = 1
|
||||||
|
for _, sub_cols in COL_HEADERS:
|
||||||
|
for sub in sub_cols:
|
||||||
|
tk.Label(grid, text=sub, relief=tk.GROOVE, bg="#e8e8e8",
|
||||||
|
font=("TkDefaultFont", 7), padx=4, pady=1).grid(
|
||||||
|
row=1, column=col, sticky=tk.NSEW)
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
# data rows
|
||||||
|
self._perm_vars = []
|
||||||
|
for g in range(4):
|
||||||
|
r = g + 2
|
||||||
|
tk.Label(grid, text=f"Group {g + 1}", relief=tk.GROOVE,
|
||||||
|
bg="#f4f4f4", padx=4, pady=1).grid(row=r, column=0,
|
||||||
|
sticky=tk.NSEW)
|
||||||
|
gvars = []
|
||||||
|
for f_idx in range(7):
|
||||||
|
var = tk.BooleanVar(value=groups[g][f_idx])
|
||||||
|
var.trace_add("write", lambda *_a, g=g, f=f_idx: self._on_perm_toggle(g, f))
|
||||||
|
ttk.Checkbutton(grid, variable=var).grid(
|
||||||
|
row=r, column=f_idx + 1, padx=6, pady=1)
|
||||||
|
gvars.append(var)
|
||||||
|
self._perm_vars.append(gvars)
|
||||||
|
|
||||||
|
# Clear All / Set All
|
||||||
|
action_frame = tk.Frame(grid)
|
||||||
|
action_frame.grid(row=6, column=0, columnspan=8, sticky=tk.E, pady=(6, 0))
|
||||||
|
ttk.Button(action_frame, text="Clear All", width=8,
|
||||||
|
command=self._clear_all).pack(side=tk.LEFT, padx=2)
|
||||||
|
ttk.Button(action_frame, text="Set All", width=8,
|
||||||
|
command=self._set_all).pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
|
# OK / Cancel
|
||||||
|
btn_frame = tk.Frame(self._dlg, pady=6)
|
||||||
|
btn_frame.pack()
|
||||||
|
ttk.Button(btn_frame, text="OK", width=10, command=self._on_ok).pack(
|
||||||
|
side=tk.LEFT, padx=4)
|
||||||
|
ttk.Button(btn_frame, text="Cancel", width=10, command=self._on_cancel).pack(
|
||||||
|
side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
|
self._dlg.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
|
self._dlg.wait_window()
|
||||||
|
|
||||||
|
def _on_perm_toggle(self, group, flag_idx):
|
||||||
|
"""Auto-cascade: write implies read, admin implies operator."""
|
||||||
|
gvars = self._perm_vars[group]
|
||||||
|
if not gvars[flag_idx].get():
|
||||||
|
return
|
||||||
|
# Flags: 0=or, 1=ow, 2=oi, 3=ar, 4=aw, 5=ai, 6=ua
|
||||||
|
# Write (1) → Read (0); Admin Write (4) → Admin Read (3)
|
||||||
|
if flag_idx in (1, 4):
|
||||||
|
gvars[flag_idx - 1].set(True)
|
||||||
|
# Admin flag (3,4,5) → same Operator flag (0,1,2)
|
||||||
|
if 3 <= flag_idx <= 5:
|
||||||
|
gvars[flag_idx - 3].set(True)
|
||||||
|
|
||||||
|
def _clear_all(self):
|
||||||
|
for gvars in self._perm_vars:
|
||||||
|
for v in gvars:
|
||||||
|
v.set(False)
|
||||||
|
|
||||||
|
def _set_all(self):
|
||||||
|
for gvars in self._perm_vars:
|
||||||
|
for v in gvars:
|
||||||
|
v.set(True)
|
||||||
|
|
||||||
|
def _on_ok(self):
|
||||||
|
groups = [[v.get() for v in gvars] for gvars in self._perm_vars]
|
||||||
|
self.result = _encode_perm(groups)
|
||||||
|
self._dlg.destroy()
|
||||||
|
|
||||||
|
def _on_cancel(self):
|
||||||
|
self._dlg.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class _ProvisioningDialog:
|
||||||
|
"""Provisioning flags popup: app, kits, svm checkboxes."""
|
||||||
|
|
||||||
|
def __init__(self, parent, prov_val):
|
||||||
|
self.result = None
|
||||||
|
flags = _decode_prov(prov_val)
|
||||||
|
|
||||||
|
self._dlg = tk.Toplevel(parent)
|
||||||
|
self._dlg.title("Provisioning Permissions")
|
||||||
|
self._dlg.transient(parent)
|
||||||
|
self._dlg.grab_set()
|
||||||
|
self._dlg.resizable(False, False)
|
||||||
|
|
||||||
|
main = ttk.Frame(self._dlg, padding=10)
|
||||||
|
main.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
self._prov_vars = {}
|
||||||
|
for name, _ in PROV_FLAGS:
|
||||||
|
var = tk.BooleanVar(value=flags.get(name, False))
|
||||||
|
ttk.Checkbutton(main, text=name, variable=var).pack(
|
||||||
|
anchor=tk.W, pady=2)
|
||||||
|
self._prov_vars[name] = var
|
||||||
|
|
||||||
|
btn_frame = ttk.Frame(main)
|
||||||
|
btn_frame.pack(pady=(10, 0))
|
||||||
|
ttk.Button(btn_frame, text="OK", width=10, command=self._on_ok).pack(
|
||||||
|
side=tk.LEFT, padx=4)
|
||||||
|
ttk.Button(btn_frame, text="Cancel", width=10, command=self._on_cancel).pack(
|
||||||
|
side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
|
self._dlg.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
|
self._dlg.wait_window()
|
||||||
|
|
||||||
|
def _on_ok(self):
|
||||||
|
self.result = _encode_prov(
|
||||||
|
{name: var.get() for name, var in self._prov_vars.items()})
|
||||||
|
self._dlg.destroy()
|
||||||
|
|
||||||
|
def _on_cancel(self):
|
||||||
|
self._dlg.destroy()
|
||||||
|
|
||||||
|
|
||||||
class _UserDialog:
|
class _UserDialog:
|
||||||
"""Modal dialog for adding or editing a user."""
|
"""Compact Edit/Add User dialog with >> buttons opening Permissions/Provisioning popups."""
|
||||||
|
|
||||||
def __init__(self, parent, service_comp, user_elem, on_done):
|
def __init__(self, parent, service_comp, user_elem, on_done):
|
||||||
self._service_comp = service_comp
|
self._service_comp = service_comp
|
||||||
self._user_elem = user_elem
|
self._user_elem = user_elem
|
||||||
self._on_done = on_done
|
self._on_done = on_done
|
||||||
self._is_edit = user_elem is not None
|
self._is_edit = user_elem is not None
|
||||||
self.result = None
|
|
||||||
|
self._perm_val = _safe_int(_get_prop(user_elem, "perm")) if self._is_edit else 0
|
||||||
|
self._prov_val = _safe_int(_get_prop(user_elem, "prov")) if self._is_edit else 0
|
||||||
|
|
||||||
self._dlg = tk.Toplevel(parent)
|
self._dlg = tk.Toplevel(parent)
|
||||||
self._dlg.title("Edit User" if self._is_edit else "Add User")
|
self._dlg.title("Edit User" if self._is_edit else "Add User")
|
||||||
@@ -152,81 +317,78 @@ class _UserDialog:
|
|||||||
main.pack(fill=tk.BOTH, expand=True)
|
main.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
row = 0
|
row = 0
|
||||||
ttk.Label(main, text="Username:").grid(row=row, column=0, sticky=tk.W, pady=2)
|
ttk.Label(main, text="Username").grid(row=row, column=0, sticky=tk.W, pady=2)
|
||||||
self._name_var = tk.StringVar(
|
self._name_var = tk.StringVar(
|
||||||
value=user_elem.get("name", "") if self._is_edit else "")
|
value=user_elem.get("name", "") if self._is_edit else "")
|
||||||
name_entry = ttk.Entry(main, textvariable=self._name_var, width=24)
|
name_entry = ttk.Entry(main, textvariable=self._name_var, width=28)
|
||||||
name_entry.grid(row=row, column=1, columnspan=3, sticky=tk.EW, pady=2)
|
name_entry.grid(row=row, column=1, columnspan=2, sticky=tk.EW, pady=2)
|
||||||
if self._is_edit:
|
if self._is_edit:
|
||||||
name_entry.config(state="disabled")
|
name_entry.config(state="disabled")
|
||||||
|
|
||||||
row += 1
|
row += 1
|
||||||
ttk.Label(main, text="Password:").grid(row=row, column=0, sticky=tk.W, pady=2)
|
ttk.Label(main, text="Password").grid(row=row, column=0, sticky=tk.W, pady=2)
|
||||||
self._pass_var = tk.StringVar()
|
self._pass_var = tk.StringVar()
|
||||||
ttk.Entry(main, textvariable=self._pass_var, show="*", width=24).grid(
|
ttk.Entry(main, textvariable=self._pass_var, show="*", width=28).grid(
|
||||||
row=row, column=1, columnspan=3, sticky=tk.EW, pady=2)
|
row=row, column=1, columnspan=2, sticky=tk.EW, pady=2)
|
||||||
|
|
||||||
row += 1
|
row += 1
|
||||||
ttk.Label(main, text="Confirm:").grid(row=row, column=0, sticky=tk.W, pady=2)
|
ttk.Label(main, text="Confirm").grid(row=row, column=0, sticky=tk.W, pady=2)
|
||||||
self._confirm_var = tk.StringVar()
|
self._confirm_var = tk.StringVar()
|
||||||
ttk.Entry(main, textvariable=self._confirm_var, show="*", width=24).grid(
|
ttk.Entry(main, textvariable=self._confirm_var, show="*", width=28).grid(
|
||||||
row=row, column=1, columnspan=3, sticky=tk.EW, pady=2)
|
row=row, column=1, columnspan=2, sticky=tk.EW, pady=2)
|
||||||
|
|
||||||
# permissions
|
# permissions summary + >> button
|
||||||
row += 1
|
row += 1
|
||||||
ttk.Separator(main, orient=tk.HORIZONTAL).grid(
|
ttk.Separator(main, orient=tk.HORIZONTAL).grid(
|
||||||
row=row, column=0, columnspan=4, sticky=tk.EW, pady=6)
|
row=row, column=0, columnspan=3, sticky=tk.EW, pady=6)
|
||||||
|
|
||||||
row += 1
|
row += 1
|
||||||
ttk.Label(main, text="Group Permissions:").grid(
|
ttk.Label(main, text="Permissions").grid(row=row, column=0, sticky=tk.W, pady=2)
|
||||||
row=row, column=0, columnspan=4, sticky=tk.W, pady=2)
|
self._perm_label = tk.StringVar(value=_format_perm(self._perm_val))
|
||||||
|
ttk.Label(main, textvariable=self._perm_label,
|
||||||
|
font=("TkDefaultFont", 8)).grid(
|
||||||
|
row=row, column=1, sticky=tk.W, pady=2)
|
||||||
|
ttk.Button(main, text="»", width=3,
|
||||||
|
command=self._open_permissions).grid(
|
||||||
|
row=row, column=2, sticky=tk.E, pady=2)
|
||||||
|
|
||||||
perm_groups = (_decode_perm(_get_prop(user_elem, "perm"))
|
# provisioning summary + >> button
|
||||||
if self._is_edit else [[False] * 7 for _ in range(4)])
|
|
||||||
self._perm_vars = []
|
|
||||||
for g in range(4):
|
|
||||||
row += 1
|
|
||||||
ttk.Label(main, text=f"Group {g + 1}:").grid(row=row, column=0, sticky=tk.W)
|
|
||||||
gframe = ttk.Frame(main)
|
|
||||||
gframe.grid(row=row, column=1, columnspan=3, sticky=tk.W)
|
|
||||||
gvars = []
|
|
||||||
for f_idx, label in enumerate(PERM_LABELS):
|
|
||||||
var = tk.BooleanVar(value=perm_groups[g][f_idx])
|
|
||||||
ttk.Checkbutton(gframe, text=label, variable=var).pack(
|
|
||||||
side=tk.LEFT, padx=1)
|
|
||||||
gvars.append(var)
|
|
||||||
self._perm_vars.append(gvars)
|
|
||||||
|
|
||||||
# provisioning
|
|
||||||
row += 1
|
row += 1
|
||||||
ttk.Separator(main, orient=tk.HORIZONTAL).grid(
|
ttk.Label(main, text="Provisioning Permissions").grid(
|
||||||
row=row, column=0, columnspan=4, sticky=tk.EW, pady=6)
|
row=row, column=0, sticky=tk.W, pady=2)
|
||||||
|
self._prov_label = tk.StringVar(value=_format_prov(self._prov_val))
|
||||||
|
ttk.Label(main, textvariable=self._prov_label,
|
||||||
|
font=("TkDefaultFont", 8)).grid(
|
||||||
|
row=row, column=1, sticky=tk.W, pady=2)
|
||||||
|
ttk.Button(main, text="»", width=3,
|
||||||
|
command=self._open_provisioning).grid(
|
||||||
|
row=row, column=2, sticky=tk.E, pady=2)
|
||||||
|
|
||||||
row += 1
|
# OK / Cancel
|
||||||
ttk.Label(main, text="Provisioning:").grid(row=row, column=0, sticky=tk.W, pady=2)
|
|
||||||
prov_dict = (_decode_prov(_get_prop(user_elem, "prov"))
|
|
||||||
if self._is_edit else {n: False for n, _ in PROV_FLAGS})
|
|
||||||
prov_frame = ttk.Frame(main)
|
|
||||||
prov_frame.grid(row=row, column=1, columnspan=3, sticky=tk.W)
|
|
||||||
self._prov_vars = {}
|
|
||||||
for name, _ in PROV_FLAGS:
|
|
||||||
var = tk.BooleanVar(value=prov_dict.get(name, False))
|
|
||||||
ttk.Checkbutton(prov_frame, text=name, variable=var).pack(
|
|
||||||
side=tk.LEFT, padx=4)
|
|
||||||
self._prov_vars[name] = var
|
|
||||||
|
|
||||||
# buttons
|
|
||||||
row += 1
|
row += 1
|
||||||
btn_frame = ttk.Frame(main)
|
btn_frame = ttk.Frame(main)
|
||||||
btn_frame.grid(row=row, column=0, columnspan=4, pady=(10, 0))
|
btn_frame.grid(row=row, column=0, columnspan=3, pady=(10, 0))
|
||||||
ttk.Button(btn_frame, text="OK", command=self._on_ok).pack(side=tk.LEFT, padx=4)
|
ttk.Button(btn_frame, text="OK", width=10, command=self._on_ok).pack(
|
||||||
ttk.Button(btn_frame, text="Cancel", command=self._on_cancel).pack(
|
side=tk.LEFT, padx=4)
|
||||||
|
ttk.Button(btn_frame, text="Cancel", width=10, command=self._on_cancel).pack(
|
||||||
side=tk.LEFT, padx=4)
|
side=tk.LEFT, padx=4)
|
||||||
|
|
||||||
self._dlg.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
self._dlg.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
name_entry.focus_set()
|
name_entry.focus_set()
|
||||||
self._dlg.wait_window()
|
self._dlg.wait_window()
|
||||||
|
|
||||||
|
def _open_permissions(self):
|
||||||
|
dlg = _PermissionsDialog(self._dlg, self._perm_val)
|
||||||
|
if dlg.result is not None:
|
||||||
|
self._perm_val = dlg.result
|
||||||
|
self._perm_label.set(_format_perm(self._perm_val))
|
||||||
|
|
||||||
|
def _open_provisioning(self):
|
||||||
|
dlg = _ProvisioningDialog(self._dlg, self._prov_val)
|
||||||
|
if dlg.result is not None:
|
||||||
|
self._prov_val = dlg.result
|
||||||
|
self._prov_label.set(_format_prov(self._prov_val))
|
||||||
|
|
||||||
def _on_ok(self):
|
def _on_ok(self):
|
||||||
username = self._name_var.get().strip()
|
username = self._name_var.get().strip()
|
||||||
password = self._pass_var.get()
|
password = self._pass_var.get()
|
||||||
@@ -243,32 +405,29 @@ class _UserDialog:
|
|||||||
messagebox.showwarning("Validation", "Passwords do not match.", parent=self._dlg)
|
messagebox.showwarning("Validation", "Passwords do not match.", parent=self._dlg)
|
||||||
return
|
return
|
||||||
|
|
||||||
# check duplicate name on add
|
|
||||||
if not self._is_edit:
|
if not self._is_edit:
|
||||||
for c in _get_users(self._service_comp):
|
for c in _get_users(self._service_comp):
|
||||||
if c.get("name", "") == username:
|
if c.get("name", "") == username:
|
||||||
messagebox.showwarning("Validation", f"User '{username}' already exists.",
|
messagebox.showwarning("Validation",
|
||||||
|
f"User '{username}' already exists.",
|
||||||
parent=self._dlg)
|
parent=self._dlg)
|
||||||
return
|
return
|
||||||
|
|
||||||
perm_groups = [[v.get() for v in gvars] for gvars in self._perm_vars]
|
|
||||||
prov_dict = {name: var.get() for name, var in self._prov_vars.items()}
|
|
||||||
|
|
||||||
if self._is_edit:
|
if self._is_edit:
|
||||||
self._apply_edit(username, password, perm_groups, prov_dict)
|
self._apply_edit(username, password)
|
||||||
else:
|
else:
|
||||||
self._apply_add(username, password, perm_groups, prov_dict)
|
self._apply_add(username, password)
|
||||||
|
|
||||||
self._dlg.destroy()
|
self._dlg.destroy()
|
||||||
if self._on_done:
|
if self._on_done:
|
||||||
self._on_done()
|
self._on_done()
|
||||||
|
|
||||||
def _apply_add(self, username, password, perm_groups, prov_dict):
|
def _apply_add(self, username, password):
|
||||||
elem = ET.Element("comp", name=username, type="sys::User")
|
elem = ET.Element("comp", name=username, type="sys::User")
|
||||||
cred = _hash_credentials(username, password)
|
cred = _hash_credentials(username, password)
|
||||||
ET.SubElement(elem, "prop", name="cred", val=cred)
|
ET.SubElement(elem, "prop", name="cred", val=cred)
|
||||||
ET.SubElement(elem, "prop", name="perm", val=str(_encode_perm(perm_groups)))
|
ET.SubElement(elem, "prop", name="perm", val=str(self._perm_val))
|
||||||
ET.SubElement(elem, "prop", name="prov", val=str(_encode_prov(prov_dict)))
|
ET.SubElement(elem, "prop", name="prov", val=str(self._prov_val))
|
||||||
|
|
||||||
index = len(list(self._service_comp))
|
index = len(list(self._service_comp))
|
||||||
cmd = undo.AddComponentCommand(self._service_comp, index, elem, label="add user")
|
cmd = undo.AddComponentCommand(self._service_comp, index, elem, label="add user")
|
||||||
@@ -276,16 +435,16 @@ class _UserDialog:
|
|||||||
undo.push(cmd)
|
undo.push(cmd)
|
||||||
app_state.mark_dirty()
|
app_state.mark_dirty()
|
||||||
|
|
||||||
def _apply_edit(self, username, password, perm_groups, prov_dict):
|
def _apply_edit(self, username, password):
|
||||||
user = self._user_elem
|
user = self._user_elem
|
||||||
changes = []
|
changes = []
|
||||||
|
|
||||||
new_perm = str(_encode_perm(perm_groups))
|
new_perm = str(self._perm_val)
|
||||||
old_perm = _get_prop(user, "perm")
|
old_perm = _get_prop(user, "perm")
|
||||||
if new_perm != old_perm:
|
if new_perm != old_perm:
|
||||||
changes.append((user, "perm", old_perm, new_perm))
|
changes.append((user, "perm", old_perm, new_perm))
|
||||||
|
|
||||||
new_prov = str(_encode_prov(prov_dict))
|
new_prov = str(self._prov_val)
|
||||||
old_prov = _get_prop(user, "prov")
|
old_prov = _get_prop(user, "prov")
|
||||||
if new_prov != old_prov:
|
if new_prov != old_prov:
|
||||||
changes.append((user, "prov", old_prov, new_prov))
|
changes.append((user, "prov", old_prov, new_prov))
|
||||||
@@ -412,9 +571,6 @@ class UserServicePanel:
|
|||||||
self.on_change()
|
self.on_change()
|
||||||
|
|
||||||
def _on_add(self):
|
def _on_add(self):
|
||||||
if app_state.live_mode:
|
|
||||||
app_state.set_status("Add user is not available in live mode", warn=True)
|
|
||||||
return
|
|
||||||
_UserDialog(self.frame.winfo_toplevel(), self.comp, None, self._after_change)
|
_UserDialog(self.frame.winfo_toplevel(), self.comp, None, self._after_change)
|
||||||
|
|
||||||
def _on_edit(self):
|
def _on_edit(self):
|
||||||
@@ -423,9 +579,6 @@ class UserServicePanel:
|
|||||||
messagebox.showinfo("Edit User", "Select a user first.",
|
messagebox.showinfo("Edit User", "Select a user first.",
|
||||||
parent=self.frame.winfo_toplevel())
|
parent=self.frame.winfo_toplevel())
|
||||||
return
|
return
|
||||||
if app_state.live_mode:
|
|
||||||
app_state.set_status("Edit user is not available in live mode", warn=True)
|
|
||||||
return
|
|
||||||
_UserDialog(self.frame.winfo_toplevel(), self.comp, user_elem, self._after_change)
|
_UserDialog(self.frame.winfo_toplevel(), self.comp, user_elem, self._after_change)
|
||||||
|
|
||||||
def _on_delete(self):
|
def _on_delete(self):
|
||||||
@@ -434,9 +587,6 @@ class UserServicePanel:
|
|||||||
messagebox.showinfo("Delete User", "Select a user first.",
|
messagebox.showinfo("Delete User", "Select a user first.",
|
||||||
parent=self.frame.winfo_toplevel())
|
parent=self.frame.winfo_toplevel())
|
||||||
return
|
return
|
||||||
if app_state.live_mode:
|
|
||||||
app_state.set_status("Delete user is not available in live mode", warn=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
name = user_elem.get("name", "?")
|
name = user_elem.get("name", "?")
|
||||||
if not messagebox.askyesno("Delete User", f"Delete user '{name}'?",
|
if not messagebox.askyesno("Delete User", f"Delete user '{name}'?",
|
||||||
|
|||||||
Reference in New Issue
Block a user