- Move Flask to localhost:9010, Apache serves port 9009 with static file handling and reverse proxy to fix intermittent asset loading on remote clients - Add "PXE Manager" branding beneath logo in sidebar - Increase code editor size (startnet.cmd and unattend XML) to 70vh - Add test-lab.sh for full lab VM testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
349 lines
13 KiB
HTML
349 lines
13 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">
|
|
<i class="bi bi-file-earmark-code me-1"></i>
|
|
<code>{{ image_type }}/Deploy/FlatUnattendW10.xml</code>
|
|
</small>
|
|
</div>
|
|
<div>
|
|
<button type="button" class="btn btn-success" id="saveFormBtn">
|
|
<i class="bi bi-floppy me-1"></i> 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">
|
|
<i class="bi bi-ui-checks-grid me-1"></i> Form Editor
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" id="raw-tab" data-bs-toggle="tab"
|
|
href="#rawView" role="tab">
|
|
<i class="bi bi-code-slash me-1"></i> 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><i class="bi bi-motherboard me-1"></i> Driver Paths</span>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addDriverPath">
|
|
<i class="bi bi-plus-lg"></i> 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">
|
|
<i class="bi bi-trash"></i>
|
|
</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">
|
|
<i class="bi bi-pc-display me-1"></i> 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><i class="bi bi-terminal me-1"></i> Specialize Commands (RunSynchronous)</span>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addSpecCmd">
|
|
<i class="bi bi-plus-lg"></i> 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"><i class="bi bi-grip-vertical"></i></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">
|
|
<i class="bi bi-arrow-up"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-row-action move-down" title="Move down">
|
|
<i class="bi bi-arrow-down"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
|
<i class="bi bi-trash"></i>
|
|
</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">
|
|
<i class="bi bi-shield-check me-1"></i> 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. First Logon Commands -->
|
|
<div class="card section-card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="bi bi-play-circle me-1"></i> First Logon Commands</span>
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="addFlCmd">
|
|
<i class="bi bi-plus-lg"></i> 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"><i class="bi bi-grip-vertical"></i></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">
|
|
<i class="bi bi-arrow-up"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-row-action move-down" title="Move down">
|
|
<i class="bi bi-arrow-down"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger btn-row-action remove-row">
|
|
<i class="bi bi-trash"></i>
|
|
</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><i class="bi bi-code-slash me-1"></i> Raw XML</span>
|
|
<button type="button" class="btn btn-sm btn-success" id="saveRawBtn">
|
|
<i class="bi bi-floppy me-1"></i> 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 %}
|