Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/PolicyDefinitions/SwitchCraft.admx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@
</elements>
</policy>

<policy name="CompanyName_Enf" class="User" displayName="$(string.CompanyName)" explainText="$(string.CompanyName_Help)" presentation="$(presentation.CompanyName)" key="Software\Policies\FaserF\SwitchCraft" valueName="CompanyName">
<parentCategory ref="General_Enf" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
<elements>
<text id="CompanyNameBox" valueName="CompanyName" required="true" />
</elements>
</policy>

<policy name="AIProvider_Enf" class="User" displayName="$(string.AIProvider)" explainText="$(string.AIProvider_Help)" presentation="$(presentation.AIProvider)" key="Software\Policies\FaserF\SwitchCraft" valueName="AIProvider">
<parentCategory ref="AI_Enf" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
Expand Down Expand Up @@ -211,6 +219,14 @@
</elements>
</policy>

<policy name="CompanyName_Def" class="User" displayName="$(string.CompanyName)" explainText="$(string.CompanyName_Help)" presentation="$(presentation.CompanyName)" key="Software\FaserF\SwitchCraft" valueName="CompanyName">
<parentCategory ref="General_Def" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
<elements>
<text id="CompanyNameBox" valueName="CompanyName" required="true" />
</elements>
</policy>

<policy name="AIProvider_Def" class="User" displayName="$(string.AIProvider)" explainText="$(string.AIProvider_Help)" presentation="$(presentation.AIProvider)" key="Software\FaserF\SwitchCraft" valueName="AIProvider">
<parentCategory ref="AI_Def" />
<supportedOn ref="windows:SUPPORTED_Windows10" />
Expand Down
8 changes: 8 additions & 0 deletions docs/PolicyDefinitions/de-DE/SwitchCraft.adml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Falls aktiviert, kann SwitchCraft nach Winget-Paketen suchen und Installer gener
<string id="GitRepoPath">Pfad zum Git-Repository</string>
<string id="GitRepoPath_Help">Legt den Pfad zum lokalen Git-Repository fest, das für die Konfigurationsspeicherung verwendet wird.</string>

<string id="CompanyName">Firmenname</string>
<string id="CompanyName_Help">Legt den Firmennamen fest, der in den generierten Skript-Headern verwendet wird.</string>

<!-- AI Strings -->
<string id="AIProvider">KI-Anbieter</string>
<string id="AIProvider_Help">Wählt den Backend-Anbieter für KI-Assistenzfunktionen aus.
Expand Down Expand Up @@ -108,6 +111,11 @@ WARNUNG: Dieser Wert wird in der Registry (HKCU/HKLM) gespeichert. Zugriffsberec
<label>Repository Pfad:</label>
</textBox>
</presentation>
<presentation id="CompanyName">
<textBox refId="CompanyNameBox">
<label>Firmenname:</label>
</textBox>
</presentation>
<presentation id="AIProvider">
<dropdownList refId="AIProviderDropdown" defaultItem="0">Anbieter:</dropdownList>
</presentation>
Expand Down
8 changes: 8 additions & 0 deletions docs/PolicyDefinitions/en-US/SwitchCraft.adml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ If enabled, SwitchCraft can search and generate installers for Winget packages.<
<string id="GitRepoPath">Git Repository Path</string>
<string id="GitRepoPath_Help">Sets the path to the local Git repository used for configuration storage.</string>

<string id="CompanyName">Company Name</string>
<string id="CompanyName_Help">Sets the Company Name used in generated script headers.</string>

<!-- AI Strings -->
<string id="AIProvider">AI Provider</string>
<string id="AIProvider_Help">Selects the backend provider for AI assistance features.
Expand Down Expand Up @@ -108,6 +111,11 @@ WARNING: This value is stored in the registry (HKCU/HKLM). Ensure access permiss
<label>Repository Path:</label>
</textBox>
</presentation>
<presentation id="CompanyName">
<textBox refId="CompanyNameBox">
<label>Company Name:</label>
</textBox>
</presentation>
<presentation id="AIProvider">
<dropdownList refId="AIProviderDropdown" defaultItem="0">Provider:</dropdownList>
</presentation>
Expand Down
1 change: 1 addition & 0 deletions src/switchcraft/assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@
"main_file": "Hauptdatei",
"installation_failed": "Installation fehlgeschlagen.",
"settings_general": "Allgemein",
"settings_company_name": "Firmenname",
"cloudsync_title": "Cloud-Synchronisierung",
"btn_login_github": "Mit GitHub anmelden",
"btn_logout": "Abmelden",
Expand Down
1 change: 1 addition & 0 deletions src/switchcraft/assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
"installation_failed": "Installation failed.",
"intune_chk_quiet": "Use Quiet Mode",
"settings_general": "General",
"settings_company_name": "Company Name",
"cloudsync_title": "Cloud Sync",
"btn_login_github": "Login with GitHub",
"btn_logout": "Logout",
Expand Down
21 changes: 18 additions & 3 deletions src/switchcraft/assets/templates/DefaultIntuneTemplate.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<#
.NOTES
Generated by SwitchCraft v{{SWITCHCRAFT_VERSION}}
{{HEADER_CREATED_BY}}
GitHub: {{SWITCHCRAFT_GITHUB}}

.DESCRIPTION
Expand Down Expand Up @@ -28,6 +28,11 @@ if (!(Test-Path $logpath)) {
}
$LogFile = Join-Path $logpath ("SwitchCraft-App-" + $AppName + ".log")

# --- Auto-Redirect MSI/EXE Logs to our LogPath ---
# Many admins want the specific installer logs in the same folder.
# We will append /l*v "$LogFile_MSI" to arguments if it's an MSI and missing logical arguments.


function Write-Log {
param(
[ValidateSet("Info", "Warning", "Error")] [string]$Type = "Info",
Expand Down Expand Up @@ -60,6 +65,8 @@ function Show-Notification {
} catch {
Write-Log -Type Warning "Toast Notification failed: $_"
}
} else {
Write-Log -Type Info "System Context detected. Skipping Toast: $Title - $Message"
}
}

Expand Down Expand Up @@ -137,12 +144,20 @@ try {
}
}
} else {
Write-Log -Type Warning "App not found for uninstall."
} else {
Write-Log -Type Warning "App not found based on filter '$SearchName'. Attempting fallback..."
# Fallback: exact match if user provided explicit MSI
if ($InstallMode -match "^(Uninstall|u)$" -and $InstallerFile -match "\.msi$") {
Write-Log -Type Info "Fallback: MSI Uninstall via ProductCode detection from file not yet implemented in template, but generic uninstall triggered."
}
}
}

} catch {
Write-Log -Type Error "CRITICAL: $_"
$ErrorMsg = $_.Exception.Message
$ErrorStack = $_.ScriptStackTrace
Write-Log -Type Error "CRITICAL ERROR: $ErrorMsg"
Write-Log -Type Error "Stack Trace: $ErrorStack"
$ExitCode = 1
}

Expand Down
16 changes: 16 additions & 0 deletions src/switchcraft/gui/views/settings_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ def _setup_tab_general(self, parent):

ctk.CTkLabel(scroll, text=i18n.get("settings_title"), font=ctk.CTkFont(size=20, weight="bold")).pack(pady=10)

# Company Name
frame_company = ctk.CTkFrame(scroll)
frame_company.pack(fill="x", padx=10, pady=5)
ctk.CTkLabel(frame_company, text=i18n.get("settings_company_name") or "Company Name").pack(side="left", padx=5)

self.company_entry = ctk.CTkEntry(frame_company, width=250)
self.company_entry.pack(side="right", padx=5)
self.company_entry.insert(0, SwitchCraftConfig.get_company_name())

def save_company(event=None):
SwitchCraftConfig.set_user_preference("CompanyName", self.company_entry.get())

# Save on focus out or return
self.company_entry.bind("<FocusOut>", save_company)
self.company_entry.bind("<Return>", save_company)

# Winget Toggle
frame_winget = ctk.CTkFrame(scroll)
frame_winget.pack(fill="x", padx=10, pady=10)
Expand Down
5 changes: 5 additions & 0 deletions src/switchcraft/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class SwitchCraftConfig:
POLICY_PATH = r"Software\Policies\FaserF\SwitchCraft"
PREFERENCE_PATH = r"Software\FaserF\SwitchCraft"

@classmethod
def get_company_name(cls) -> str:
"""Returns the configured company name or an empty string."""
return cls.get_value("CompanyName", "")

@classmethod
def get_value(cls, value_name: str, default: Any = None) -> Any:
"""
Expand Down
18 changes: 18 additions & 0 deletions src/switchcraft/utils/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ def generate(self, context: Dict[str, str], output_path: str) -> bool:
if "SWITCHCRAFT_GITHUB" not in context:
context["SWITCHCRAFT_GITHUB"] = "https://github.com/FaserF/SwitchCraft"

# --- New Context Variables ---
from switchcraft.utils.config import SwitchCraftConfig
import os

if "COMPANY_NAME" not in context:
context["COMPANY_NAME"] = SwitchCraftConfig.get_company_name() or ""

if "USER" not in context:
context["USER"] = os.environ.get("USERNAME", "System")

# Conditional Header
user = context["USER"]
company = context["COMPANY_NAME"]
if company and company != "Unknown Company":
context["HEADER_CREATED_BY"] = f'Created by "{user}" company "{company}" with SwitchCraft automatically.'
else:
context["HEADER_CREATED_BY"] = f'Created by "{user}" with SwitchCraft automatically.'

# 1. Enterprise Template Specific Logic (Regex Replacement)
# Detect if it's the specific enterprise template by looking for unique function names
# User requested anonymization of company name "PARI"
Expand Down
57 changes: 57 additions & 0 deletions tests/test_template_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock
from switchcraft.utils.templates import TemplateGenerator

class TestTemplateWithCompany(unittest.TestCase):
def setUp(self):
self.output_path = Path("test_output.ps1")

def tearDown(self):
if self.output_path.exists():
self.output_path.unlink()

@patch("switchcraft.utils.config.SwitchCraftConfig.get_company_name")
@patch("os.environ.get")
def test_generate_with_company_name(self, mock_env, mock_get_company):
mock_get_company.return_value = "Acme Corp"
mock_env.return_value = "TestUser"

generator = TemplateGenerator()

context = {
"INSTALLER_FILE": "setup.exe",
"INSTALL_ARGS": "/S",
"APP_NAME": "TestApp",
"PUBLISHER": "TestPub"
}

success = generator.generate(context, str(self.output_path))
self.assertTrue(success)

content = self.output_path.read_text(encoding="utf-8")

# Should contain full header
self.assertIn('company "Acme Corp"', content)
self.assertIn('Created by "TestUser"', content)
self.assertIn('with SwitchCraft automatically', content)

@patch("switchcraft.utils.config.SwitchCraftConfig.get_company_name")
@patch("os.environ.get")
def test_generate_without_company_name(self, mock_env, mock_get_company):
mock_get_company.return_value = "" # No company
mock_env.return_value = "TestUser"

generator = TemplateGenerator()
context = { "INSTALLER_FILE": "setup.exe", "INSTALL_ARGS": "/S" }

generator.generate(context, str(self.output_path))
content = self.output_path.read_text(encoding="utf-8")

# Should NOT contain "company" string in header
self.assertNotIn('company "', content)
# Should contain simpler header
self.assertIn('Created by "TestUser" with SwitchCraft automatically', content)

if __name__ == "__main__":
unittest.main()
Loading