Files
pxe-server/webapp/templates/unattend_editor.html
cproudlock 76165495ff Shopfloor PC type system, webapp enhancements, slim Blancco GRUB
- Shopfloor PC type menu (CMM, WaxAndTrace, Keyence, Genspect, Display, Standard)
- Baseline scripts: OpenText CSF, Start Menu shortcuts, network/WinRM, power/display
- Standard type: eDNC + MarkZebra with 64-bit path mirroring
- CMM type: Hexagon CLM Tools, PC-DMIS 2016/2019 R2
- Display sub-type: Lobby vs Dashboard
- Webapp: enrollment management, image config editor, UI refresh
- Upload-Image.ps1: robocopy MCL cache to PXE server
- Download-Drivers.ps1: Dell driver download pipeline
- Slim Blancco GRUB EFI (10MB -> 660KB) for old hardware compat
- Shopfloor display imaging guide docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:25:07 -04:00

496 lines
19 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ friendly_name }} - Unattend Editor{% endblock %}
{% block extra_head %}
<style>
.editor-tabs .nav-link {
font-weight: 500;
}
.command-table tbody tr {
transition: background-color 0.15s;
}
.command-table tbody tr.dragging {
opacity: 0.5;
background-color: #e9ecef;
}
.raw-xml-editor {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 0.85rem;
min-height: 600px;
height: 70vh;
tab-size: 2;
white-space: pre;
resize: vertical;
}
.section-card {
margin-bottom: 1.5rem;
}
.section-card .card-header {
padding: 0.6rem 1rem;
font-size: 0.95rem;
}
.order-num {
width: 40px;
text-align: center;
font-weight: 600;
color: #6c757d;
}
</style>
{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="mb-1">{{ friendly_name }}</h2>
<small class="text-muted">
<code>{{ image_type }}/Deploy/FlatUnattendW10.xml</code>
</small>
</div>
<div>
<button type="button" class="btn btn-success" id="saveFormBtn">
Save
</button>
</div>
</div>
<!-- Tabs -->
<ul class="nav nav-tabs editor-tabs mb-3" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="form-tab" data-bs-toggle="tab"
href="#formView" role="tab">
Form Editor
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="raw-tab" data-bs-toggle="tab"
href="#rawView" role="tab">
Raw XML
</a>
</li>
</ul>
<form method="POST" id="unattendForm">
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
<div class="tab-content">
<!-- ==================== FORM VIEW ==================== -->
<div class="tab-pane fade show active" id="formView" role="tabpanel">
<!-- 1. Driver Paths -->
<div class="card section-card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Driver Paths</span>
<button type="button" class="btn btn-sm btn-outline-primary" id="addDriverPath">
Add
</button>
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0" id="driverPathsTable">
<thead class="table-light">
<tr>
<th style="width:40px">#</th>
<th>Path</th>
<th style="width:60px"></th>
</tr>
</thead>
<tbody>
{% for dp in data.driver_paths %}
<tr>
<td class="order-num">{{ loop.index }}</td>
<td>
<input type="text" class="form-control form-control-sm"
name="driver_path[]" value="{{ dp }}">
</td>
<td>
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
Remove
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not data.driver_paths %}
<div class="text-center text-muted py-3 empty-message" id="driverPathsEmpty">
No driver paths configured. Click <strong>Add</strong> to add one.
</div>
{% endif %}
</div>
</div>
<!-- 2. Machine Settings -->
<div class="card section-card">
<div class="card-header">
Machine Settings
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-semibold">Computer Name</label>
<input type="text" class="form-control" name="computer_name"
value="{{ data.computer_name }}" placeholder="* (auto-generate)">
<div class="form-text">Use * to let Windows auto-generate a name.</div>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Time Zone</label>
<input type="text" class="form-control" name="time_zone"
value="{{ data.time_zone }}" placeholder="Eastern Standard Time">
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Registered Organization</label>
<input type="text" class="form-control" name="registered_organization"
value="{{ data.registered_organization }}" placeholder="GE Aerospace">
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Registered Owner</label>
<input type="text" class="form-control" name="registered_owner"
value="{{ data.registered_owner }}" placeholder="GE Aerospace">
</div>
</div>
</div>
</div>
<!-- 3. Specialize Commands (RunSynchronous) -->
<div class="card section-card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Specialize Commands (RunSynchronous)</span>
<button type="button" class="btn btn-sm btn-outline-primary" id="addSpecCmd">
Add
</button>
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0 command-table" id="specCmdTable">
<thead class="table-light">
<tr>
<th style="width:30px"></th>
<th style="width:50px">Order</th>
<th>Path / Command</th>
<th>Description</th>
<th style="width:90px"></th>
</tr>
</thead>
<tbody>
{% for cmd in data.specialize_commands %}
<tr draggable="true">
<td class="drag-handle">::</td>
<td class="order-num">{{ loop.index }}</td>
<td>
<input type="text" class="form-control form-control-sm"
name="spec_cmd_path[]" value="{{ cmd.path }}">
</td>
<td>
<input type="text" class="form-control form-control-sm"
name="spec_cmd_desc[]" value="{{ cmd.description }}">
</td>
<td class="text-nowrap">
<button type="button" class="btn btn-outline-secondary btn-row-action move-up" title="Move up">
Up
</button>
<button type="button" class="btn btn-outline-secondary btn-row-action move-down" title="Move down">
Down
</button>
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
Remove
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not data.specialize_commands %}
<div class="text-center text-muted py-3 empty-message" id="specCmdEmpty">
No specialize commands configured. Click <strong>Add</strong> to add one.
</div>
{% endif %}
</div>
</div>
<!-- 4. OOBE Settings -->
<div class="card section-card">
<div class="card-header">
OOBE Settings
</div>
<div class="card-body">
<div class="row g-3">
{% set bool_oobe_fields = [
("HideEULAPage", "Hide EULA Page"),
("HideOEMRegistrationScreen", "Hide OEM Registration Screen"),
("HideOnlineAccountScreens", "Hide Online Account Screens"),
("HideWirelessSetupInOOBE", "Hide Wireless Setup in OOBE"),
("HideLocalAccountScreen", "Hide Local Account Screen"),
("SkipUserOOBE", "Skip User OOBE"),
("SkipMachineOOBE", "Skip Machine OOBE"),
] %}
{% for key, label in bool_oobe_fields %}
<div class="col-md-4">
<div class="form-check form-switch">
<input class="form-check-input oobe-toggle" type="checkbox"
id="oobe_{{ key }}"
data-field="oobe_{{ key }}"
{% if data.oobe[key]|lower == 'true' %}checked{% endif %}>
<label class="form-check-label" for="oobe_{{ key }}">{{ label }}</label>
<input type="hidden" name="oobe_{{ key }}" id="oobe_{{ key }}_val"
value="{{ data.oobe[key] }}">
</div>
</div>
{% endfor %}
<div class="col-md-4">
<label class="form-label fw-semibold">Network Location</label>
<select class="form-select form-select-sm" name="oobe_NetworkLocation">
{% for opt in ["Home", "Work", "Other"] %}
<option value="{{ opt }}" {% if data.oobe.NetworkLocation == opt %}selected{% endif %}>
{{ opt }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">ProtectYourPC</label>
<select class="form-select form-select-sm" name="oobe_ProtectYourPC">
{% for opt in ["1", "2", "3"] %}
<option value="{{ opt }}" {% if data.oobe.ProtectYourPC == opt %}selected{% endif %}>
{{ opt }}{% if opt == "1" %} (Recommended){% elif opt == "3" %} (Skip){% endif %}
</option>
{% endfor %}
</select>
<div class="form-text">1 = Recommended, 2 = Install only updates, 3 = Skip</div>
</div>
</div>
</div>
</div>
<!-- 5. User Accounts -->
<div class="card section-card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>User Accounts (Local)</span>
<button type="button" class="btn btn-sm btn-outline-primary" id="addUserAccount">
Add
</button>
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0" id="userAccountsTable">
<thead class="table-light">
<tr>
<th style="width:40px">#</th>
<th>Name</th>
<th>Password</th>
<th>Group</th>
<th>Display Name</th>
<th style="width:60px"></th>
</tr>
</thead>
<tbody>
{% for acct in data.user_accounts %}
<tr>
<td class="order-num">{{ loop.index }}</td>
<td>
<input type="text" class="form-control form-control-sm"
name="account_name_{{ loop.index0 }}" value="{{ acct.name }}">
</td>
<td>
<input type="text" class="form-control form-control-sm"
name="account_password_{{ loop.index0 }}" value="{{ acct.password }}">
</td>
<td>
<input type="text" class="form-control form-control-sm"
name="account_group_{{ loop.index0 }}" value="{{ acct.group }}">
</td>
<td>
<input type="text" class="form-control form-control-sm"
name="account_display_{{ loop.index0 }}" value="{{ acct.display_name }}">
</td>
<td>
<input type="hidden" name="account_plaintext_{{ loop.index0 }}" value="{{ acct.plain_text }}">
<button type="button" class="btn btn-outline-danger btn-row-action remove-account-row">
Remove
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not data.user_accounts %}
<div class="text-center text-muted py-3 empty-message" id="userAccountsEmpty">
No local accounts configured. Click <strong>Add</strong> to add one.
</div>
{% endif %}
</div>
</div>
<!-- 6. AutoLogon -->
<div class="card section-card">
<div class="card-header">
AutoLogon
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<div class="form-check form-switch mt-4">
<input class="form-check-input" type="checkbox" id="autologonEnabledToggle"
{% if data.autologon.enabled|lower == 'true' %}checked{% endif %}>
<label class="form-check-label fw-semibold" for="autologonEnabledToggle">Enabled</label>
<input type="hidden" name="autologon_enabled" id="autologon_enabled_val"
value="{{ data.autologon.enabled }}">
</div>
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">Username</label>
<input type="text" class="form-control" name="autologon_username"
value="{{ data.autologon.username }}" placeholder="e.g. SupportUser">
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">Password</label>
<input type="text" class="form-control" name="autologon_password"
value="{{ data.autologon.password }}">
<input type="hidden" name="autologon_plaintext" value="{{ data.autologon.plain_text }}">
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">Logon Count</label>
<input type="text" class="form-control" name="autologon_logoncount"
value="{{ data.autologon.logon_count }}" placeholder="e.g. 2 or 999">
<div class="form-text">Number of auto-logon attempts.</div>
</div>
</div>
</div>
</div>
<!-- 7. International Settings -->
<div class="card section-card">
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#intlCollapse"
role="button" style="cursor:pointer">
International Settings
<small class="text-muted ms-2">(click to expand)</small>
</div>
<div class="collapse {% if data.intl.input_locale or data.intl.system_locale or data.intl.ui_language or data.intl.user_locale %}show{% endif %}"
id="intlCollapse">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label fw-semibold">Input Locale</label>
<input type="text" class="form-control" name="intl_input_locale"
value="{{ data.intl.input_locale }}" placeholder="e.g. en-US">
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">System Locale</label>
<input type="text" class="form-control" name="intl_system_locale"
value="{{ data.intl.system_locale }}" placeholder="e.g. en-US">
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">UI Language</label>
<input type="text" class="form-control" name="intl_ui_language"
value="{{ data.intl.ui_language }}" placeholder="e.g. en-US">
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">User Locale</label>
<input type="text" class="form-control" name="intl_user_locale"
value="{{ data.intl.user_locale }}" placeholder="e.g. en-US">
</div>
</div>
</div>
</div>
</div>
<!-- 8. OOBE Time Zone -->
<div class="card section-card">
<div class="card-header">
OOBE Time Zone
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-semibold">Time Zone (oobeSystem pass)</label>
<input type="text" class="form-control" name="oobe_timezone"
value="{{ data.oobe_timezone }}" placeholder="e.g. Eastern Standard Time">
<div class="form-text">Separate from the specialize-pass time zone in Machine Settings above.</div>
</div>
</div>
</div>
</div>
<!-- 9. First Logon Commands -->
<div class="card section-card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>First Logon Commands</span>
<button type="button" class="btn btn-sm btn-outline-primary" id="addFlCmd">
Add
</button>
</div>
<div class="card-body p-0">
<table class="table table-sm mb-0 command-table" id="flCmdTable">
<thead class="table-light">
<tr>
<th style="width:30px"></th>
<th style="width:50px">Order</th>
<th>Command Line</th>
<th>Description</th>
<th style="width:90px"></th>
</tr>
</thead>
<tbody>
{% for cmd in data.firstlogon_commands %}
<tr draggable="true">
<td class="drag-handle">::</td>
<td class="order-num">{{ loop.index }}</td>
<td>
<input type="text" class="form-control form-control-sm"
name="fl_cmd_commandline[]" value="{{ cmd.commandline }}">
</td>
<td>
<input type="text" class="form-control form-control-sm"
name="fl_cmd_desc[]" value="{{ cmd.description }}">
</td>
<td class="text-nowrap">
<button type="button" class="btn btn-outline-secondary btn-row-action move-up" title="Move up">
Up
</button>
<button type="button" class="btn btn-outline-secondary btn-row-action move-down" title="Move down">
Down
</button>
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
Remove
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not data.firstlogon_commands %}
<div class="text-center text-muted py-3 empty-message" id="flCmdEmpty">
No first logon commands configured. Click <strong>Add</strong> to add one.
</div>
{% endif %}
</div>
</div>
</div><!-- end formView -->
<!-- ==================== RAW XML VIEW ==================== -->
<div class="tab-pane fade" id="rawView" role="tabpanel">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Raw XML</span>
<button type="button" class="btn btn-sm btn-success" id="saveRawBtn">
Save Raw XML
</button>
</div>
<div class="card-body">
<textarea class="form-control raw-xml-editor" name="raw_xml"
id="rawXmlEditor">{{ data.raw_xml }}</textarea>
</div>
</div>
</div>
</div><!-- end tab-content -->
<input type="hidden" name="save_mode" id="saveMode" value="form">
</form>
{% endblock %}
{% block extra_scripts %}
<script>
// Pass the image_type and API URL to JavaScript
window.PXE_IMAGE_TYPE = "{{ image_type }}";
window.PXE_API_URL = "{{ url_for('api_save_unattend', image_type=image_type) }}";
</script>
{% endblock %}