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:
Zoe Forbes
2026-01-31 09:21:33 -06:00
parent 8c0689991e
commit c778825eb0
2 changed files with 136 additions and 2 deletions

View File

@@ -28,6 +28,8 @@ class SiloWorkbench(FreeCADGui.Workbench):
import silo_commands
self.toolbar_commands = [
"Silo_ToggleMode",
"Separator",
"Silo_Open",
"Silo_New",
"Silo_Save",

View File

@@ -50,6 +50,16 @@ def _get_ssl_context() -> ssl.SSLContext:
"""Build an SSL context based on the current SSL verification preference."""
if _get_ssl_verify():
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
# (its compiled-in path points to the build environment). Load the
# 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;")
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)
# Current effective values (read-only)
cert_display = param.GetString("SslCertPath", "") or "(system defaults)"
status_label = QtGui.QLabel(
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)
layout.addWidget(status_label)
@@ -1995,9 +2044,12 @@ class Silo_Settings:
url = url_input.text().strip()
param.SetString("ApiUrl", url)
param.SetBool("SslVerify", ssl_checkbox.isChecked())
cert_path = cert_input.text().strip()
param.SetString("SslCertPath", cert_path)
FreeCAD.Console.PrintMessage(
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()
@@ -2383,6 +2435,85 @@ class Silo_BOM:
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
FreeCADGui.addCommand("Silo_Open", Silo_Open())
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_SetStatus", Silo_SetStatus())
FreeCADGui.addCommand("Silo_Settings", Silo_Settings())
FreeCADGui.addCommand("Silo_ToggleMode", Silo_ToggleMode())