diff --git a/pkg/freecad/InitGui.py b/pkg/freecad/InitGui.py index 293569a..3a95a64 100644 --- a/pkg/freecad/InitGui.py +++ b/pkg/freecad/InitGui.py @@ -28,6 +28,8 @@ class SiloWorkbench(FreeCADGui.Workbench): import silo_commands self.toolbar_commands = [ + "Silo_ToggleMode", + "Separator", "Silo_Open", "Silo_New", "Silo_Save", diff --git a/pkg/freecad/silo_commands.py b/pkg/freecad/silo_commands.py index e27297a..1c18809 100644 --- a/pkg/freecad/silo_commands.py +++ b/pkg/freecad/silo_commands.py @@ -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"Active URL: {_get_api_url()}
" - f"SSL verification: {'enabled' if _get_ssl_verify() else 'disabled'}" + f"SSL verification: {'enabled' if _get_ssl_verify() else 'disabled'}
" + f"CA certificate: {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())