Add Silo mode toggle, SSL cert browsing, and BOM menu integration
- Silo_ToggleMode: checkable toolbar button that swaps Ctrl+O/S/N between standard FreeCAD file commands and Silo equivalents - _swap_shortcuts() helper stores/restores original QAction shortcuts - SSL settings: add CA certificate file browser (SslCertPath preference) with QFileDialog for .pem/.crt/.cer, loaded in _get_ssl_context() - Integrate Silo_BOM into workbench toolbar (after upstream BOM merge) - Add Silo_ToggleMode to toolbar as first item with separator
This commit is contained in:
@@ -28,6 +28,8 @@ class SiloWorkbench(FreeCADGui.Workbench):
|
|||||||
import silo_commands
|
import silo_commands
|
||||||
|
|
||||||
self.toolbar_commands = [
|
self.toolbar_commands = [
|
||||||
|
"Silo_ToggleMode",
|
||||||
|
"Separator",
|
||||||
"Silo_Open",
|
"Silo_Open",
|
||||||
"Silo_New",
|
"Silo_New",
|
||||||
"Silo_Save",
|
"Silo_Save",
|
||||||
|
|||||||
@@ -50,6 +50,16 @@ def _get_ssl_context() -> ssl.SSLContext:
|
|||||||
"""Build an SSL context based on the current SSL verification preference."""
|
"""Build an SSL context based on the current SSL verification preference."""
|
||||||
if _get_ssl_verify():
|
if _get_ssl_verify():
|
||||||
ctx = ssl.create_default_context()
|
ctx = ssl.create_default_context()
|
||||||
|
# Load custom CA certificate if configured (for internal CAs)
|
||||||
|
param = FreeCAD.ParamGet(_PREF_GROUP)
|
||||||
|
custom_cert = param.GetString("SslCertPath", "")
|
||||||
|
if custom_cert and os.path.isfile(custom_cert):
|
||||||
|
try:
|
||||||
|
ctx.load_verify_locations(custom_cert)
|
||||||
|
except Exception as e:
|
||||||
|
FreeCAD.Console.PrintWarning(
|
||||||
|
f"Silo: Failed to load custom cert {custom_cert}: {e}\n"
|
||||||
|
)
|
||||||
# The bundled Python may not find the system CA store automatically
|
# The bundled Python may not find the system CA store automatically
|
||||||
# (its compiled-in path points to the build environment). Load the
|
# (its compiled-in path points to the build environment). Load the
|
||||||
# system CA bundle explicitly so internal CAs (e.g. FreeIPA) are trusted.
|
# system CA bundle explicitly so internal CAs (e.g. FreeIPA) are trusted.
|
||||||
@@ -1970,12 +1980,51 @@ class Silo_Settings:
|
|||||||
ssl_hint.setStyleSheet("color: #888; font-size: 11px;")
|
ssl_hint.setStyleSheet("color: #888; font-size: 11px;")
|
||||||
layout.addWidget(ssl_hint)
|
layout.addWidget(ssl_hint)
|
||||||
|
|
||||||
|
layout.addSpacing(5)
|
||||||
|
|
||||||
|
# Custom CA certificate
|
||||||
|
cert_label = QtGui.QLabel("Custom CA certificate file:")
|
||||||
|
layout.addWidget(cert_label)
|
||||||
|
|
||||||
|
cert_row = QtGui.QHBoxLayout()
|
||||||
|
cert_input = QtGui.QLineEdit()
|
||||||
|
cert_input.setPlaceholderText("(Use system CA certificates)")
|
||||||
|
current_cert = param.GetString("SslCertPath", "")
|
||||||
|
if current_cert:
|
||||||
|
cert_input.setText(current_cert)
|
||||||
|
cert_browse = QtGui.QPushButton("Browse...")
|
||||||
|
cert_row.addWidget(cert_input)
|
||||||
|
cert_row.addWidget(cert_browse)
|
||||||
|
layout.addLayout(cert_row)
|
||||||
|
|
||||||
|
cert_hint = QtGui.QLabel(
|
||||||
|
"Path to a PEM/CRT file for internal CAs. "
|
||||||
|
"Leave empty for system certificates only."
|
||||||
|
)
|
||||||
|
cert_hint.setWordWrap(True)
|
||||||
|
cert_hint.setStyleSheet("color: #888; font-size: 11px;")
|
||||||
|
layout.addWidget(cert_hint)
|
||||||
|
|
||||||
|
def on_browse_cert():
|
||||||
|
path, _ = QtGui.QFileDialog.getOpenFileName(
|
||||||
|
dialog,
|
||||||
|
"Select CA Certificate",
|
||||||
|
os.path.dirname(cert_input.text()) or "/etc/ssl/certs",
|
||||||
|
"Certificates (*.pem *.crt *.cer);;All Files (*)",
|
||||||
|
)
|
||||||
|
if path:
|
||||||
|
cert_input.setText(path)
|
||||||
|
|
||||||
|
cert_browse.clicked.connect(on_browse_cert)
|
||||||
|
|
||||||
layout.addSpacing(10)
|
layout.addSpacing(10)
|
||||||
|
|
||||||
# Current effective values (read-only)
|
# Current effective values (read-only)
|
||||||
|
cert_display = param.GetString("SslCertPath", "") or "(system defaults)"
|
||||||
status_label = QtGui.QLabel(
|
status_label = QtGui.QLabel(
|
||||||
f"<b>Active URL:</b> {_get_api_url()}<br>"
|
f"<b>Active URL:</b> {_get_api_url()}<br>"
|
||||||
f"<b>SSL verification:</b> {'enabled' if _get_ssl_verify() else 'disabled'}"
|
f"<b>SSL verification:</b> {'enabled' if _get_ssl_verify() else 'disabled'}<br>"
|
||||||
|
f"<b>CA certificate:</b> {cert_display}"
|
||||||
)
|
)
|
||||||
status_label.setTextFormat(QtCore.Qt.RichText)
|
status_label.setTextFormat(QtCore.Qt.RichText)
|
||||||
layout.addWidget(status_label)
|
layout.addWidget(status_label)
|
||||||
@@ -1995,9 +2044,12 @@ class Silo_Settings:
|
|||||||
url = url_input.text().strip()
|
url = url_input.text().strip()
|
||||||
param.SetString("ApiUrl", url)
|
param.SetString("ApiUrl", url)
|
||||||
param.SetBool("SslVerify", ssl_checkbox.isChecked())
|
param.SetBool("SslVerify", ssl_checkbox.isChecked())
|
||||||
|
cert_path = cert_input.text().strip()
|
||||||
|
param.SetString("SslCertPath", cert_path)
|
||||||
FreeCAD.Console.PrintMessage(
|
FreeCAD.Console.PrintMessage(
|
||||||
f"Silo settings saved. URL: {_get_api_url()}, "
|
f"Silo settings saved. URL: {_get_api_url()}, "
|
||||||
f"SSL verify: {_get_ssl_verify()}\n"
|
f"SSL verify: {_get_ssl_verify()}, "
|
||||||
|
f"Cert: {cert_path or '(system)'}\n"
|
||||||
)
|
)
|
||||||
dialog.accept()
|
dialog.accept()
|
||||||
|
|
||||||
@@ -2383,6 +2435,85 @@ class Silo_BOM:
|
|||||||
return FreeCAD.ActiveDocument is not None
|
return FreeCAD.ActiveDocument is not None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Silo Mode toggle - swap Ctrl+O/S/N between standard and Silo commands
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Stored original shortcuts so they can be restored on toggle-off
|
||||||
|
_original_shortcuts: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _swap_shortcuts(mapping, enable_silo):
|
||||||
|
"""Swap keyboard shortcuts between standard and Silo commands.
|
||||||
|
|
||||||
|
mapping: list of (std_cmd, silo_cmd, shortcut) tuples
|
||||||
|
enable_silo: True to assign shortcuts to Silo commands, False to restore.
|
||||||
|
"""
|
||||||
|
from PySide import QtGui
|
||||||
|
|
||||||
|
mw = FreeCADGui.getMainWindow()
|
||||||
|
if mw is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
for std_cmd, silo_cmd, shortcut in mapping:
|
||||||
|
if enable_silo:
|
||||||
|
# Save and clear the standard command's shortcut
|
||||||
|
std_action = mw.findChild(QtGui.QAction, std_cmd)
|
||||||
|
if std_action:
|
||||||
|
_original_shortcuts[std_cmd] = std_action.shortcut().toString()
|
||||||
|
std_action.setShortcut("")
|
||||||
|
# Assign the shortcut to the Silo command
|
||||||
|
silo_action = mw.findChild(QtGui.QAction, silo_cmd)
|
||||||
|
if silo_action:
|
||||||
|
silo_action.setShortcut(shortcut)
|
||||||
|
else:
|
||||||
|
# Clear the Silo command's shortcut
|
||||||
|
silo_action = mw.findChild(QtGui.QAction, silo_cmd)
|
||||||
|
if silo_action:
|
||||||
|
silo_action.setShortcut("")
|
||||||
|
# Restore the standard command's original shortcut
|
||||||
|
std_action = mw.findChild(QtGui.QAction, std_cmd)
|
||||||
|
if std_action and std_cmd in _original_shortcuts:
|
||||||
|
std_action.setShortcut(_original_shortcuts.pop(std_cmd))
|
||||||
|
|
||||||
|
|
||||||
|
_SHORTCUT_MAP = [
|
||||||
|
("Std_Open", "Silo_Open", "Ctrl+O"),
|
||||||
|
("Std_Save", "Silo_Save", "Ctrl+S"),
|
||||||
|
("Std_New", "Silo_New", "Ctrl+N"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Silo_ToggleMode:
|
||||||
|
"""Toggle between standard file operations and Silo equivalents."""
|
||||||
|
|
||||||
|
def GetResources(self):
|
||||||
|
return {
|
||||||
|
"MenuText": "Silo Mode",
|
||||||
|
"ToolTip": (
|
||||||
|
"Toggle between standard file operations and Silo equivalents.\n"
|
||||||
|
"When ON: Ctrl+O/S/N use Silo Open/Save/New.\n"
|
||||||
|
"When OFF: Standard FreeCAD file operations."
|
||||||
|
),
|
||||||
|
"Pixmap": _icon("silo"),
|
||||||
|
"Checkable": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def Activated(self, checked):
|
||||||
|
param = FreeCAD.ParamGet(_PREF_GROUP)
|
||||||
|
if checked:
|
||||||
|
_swap_shortcuts(_SHORTCUT_MAP, enable_silo=True)
|
||||||
|
param.SetBool("SiloMode", True)
|
||||||
|
FreeCAD.Console.PrintMessage("Silo mode enabled\n")
|
||||||
|
else:
|
||||||
|
_swap_shortcuts(_SHORTCUT_MAP, enable_silo=False)
|
||||||
|
param.SetBool("SiloMode", False)
|
||||||
|
FreeCAD.Console.PrintMessage("Silo mode disabled\n")
|
||||||
|
|
||||||
|
def IsActive(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# Register commands
|
# Register commands
|
||||||
FreeCADGui.addCommand("Silo_Open", Silo_Open())
|
FreeCADGui.addCommand("Silo_Open", Silo_Open())
|
||||||
FreeCADGui.addCommand("Silo_New", Silo_New())
|
FreeCADGui.addCommand("Silo_New", Silo_New())
|
||||||
@@ -2396,3 +2527,4 @@ FreeCADGui.addCommand("Silo_TagProjects", Silo_TagProjects())
|
|||||||
FreeCADGui.addCommand("Silo_Rollback", Silo_Rollback())
|
FreeCADGui.addCommand("Silo_Rollback", Silo_Rollback())
|
||||||
FreeCADGui.addCommand("Silo_SetStatus", Silo_SetStatus())
|
FreeCADGui.addCommand("Silo_SetStatus", Silo_SetStatus())
|
||||||
FreeCADGui.addCommand("Silo_Settings", Silo_Settings())
|
FreeCADGui.addCommand("Silo_Settings", Silo_Settings())
|
||||||
|
FreeCADGui.addCommand("Silo_ToggleMode", Silo_ToggleMode())
|
||||||
|
|||||||
Reference in New Issue
Block a user