Sync DB Seed data between setup and setup cli

This commit is contained in:
johnnyq
2026-08-03 22:41:31 -04:00
parent 8aaf5b450f
commit c30c12674a
7 changed files with 1561 additions and 245 deletions

View File

@@ -203,6 +203,12 @@
<p>Update</p>
</a>
</li>
<li class="nav-item">
<a href="/admin/starter_content.php" class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'starter_content.php' ? 'active' : '') ?>">
<i class="nav-icon fas fa-seedling"></i>
<p>Starter Content</p>
</a>
</li>
<!-- SETTINGS Section -->
<li class="nav-item has-treeview mt-2 <?= (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_providers.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : '') ?>">

View File

@@ -0,0 +1,47 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['load_starter_content'])) {
validateCSRFToken();
require_once 'starter_content_model.php';
$starter_content_packs = starterContentPacks();
$requested_pack = $_POST['load_starter_content'];
// Only registry keys are accepted - the value goes nowhere near a query, but
// an unknown pack should say so rather than silently do nothing
if ($requested_pack === 'all') {
$selected_packs = array_keys($starter_content_packs);
} elseif (isset($starter_content_packs[$requested_pack])) {
$selected_packs = [$requested_pack];
} else {
flashAlert("Unknown starter content pack", 'error');
redirect();
}
// Registry order is load order - categories before products, ticket
// templates before the project templates that link to them
$items_added = 0;
$summary = [];
foreach ($selected_packs as $pack) {
$pack_added = starterContentLoad($mysqli, $pack);
if ($pack_added) {
$items_added = $items_added + $pack_added;
$summary[] = "$pack_added " . $starter_content_packs[$pack]['label'];
}
}
if ($items_added) {
$summary = implode(', ', $summary);
logAudit("Starter Content", "Create", "$session_name loaded starter content - $summary");
flashAlert("Added <strong>$items_added</strong> items: $summary");
} else {
flashAlert("Nothing to add - everything in that selection is already here", 'info');
}
redirect();
}

File diff suppressed because it is too large Load Diff

109
admin/starter_content.php Normal file
View File

@@ -0,0 +1,109 @@
<?php
require_once "includes/inc_all_admin.php";
define('FROM_STARTER_CONTENT', true);
require_once "post/starter_content_model.php";
$starter_content_packs = starterContentPacks();
$starter_content_status = starterContentStatus($mysqli);
$total_missing = 0;
foreach ($starter_content_status as $pack_status) {
$total_missing = $total_missing + $pack_status['missing'];
}
?>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-seedling mr-2"></i>Starter Content</h3>
<?php if ($total_missing) { ?>
<div class="card-tools">
<form action="post.php" method="POST" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button type="submit" name="load_starter_content" value="all" class="btn btn-primary btn-sm">
<i class="fas fa-fw fa-download mr-2"></i>Add all <?= $total_missing ?> missing
</button>
</form>
</div>
<?php } ?>
</div>
<div class="card-body">
<p>
A library of opinionated defaults for a typical MSP. Add whichever packs are useful - on a
brand new install or on one that has been running for years.
</p>
<p class="text-muted">
Nothing here is overwritten or deleted. Anything already present under the same name is
skipped, so a pack can be added again later to pick up entries you removed by mistake, and
adding twice never produces duplicates. Everything added is editable and deletable
afterwards like any other record.
</p>
<div class="table-responsive">
<table class="table table-striped align-middle">
<thead>
<tr>
<th>Content</th>
<th class="text-center">In library</th>
<th class="text-center">Already here</th>
<th class="text-center">Will be added</th>
<th class="text-right"></th>
</tr>
</thead>
<tbody>
<?php foreach ($starter_content_packs as $pack => $pack_details) {
$pack_status = $starter_content_status[$pack];
?>
<tr>
<td>
<strong><i class="fas fa-fw <?= escapeHtml($pack_details['icon']) ?> mr-2"></i><?= escapeHtml($pack_details['label']) ?></strong>
<br><small class="text-muted"><?= escapeHtml($pack_details['description']) ?></small>
<?php
// Loading out of order works, it just produces less - say so up front
// rather than leaving someone with 77 uncategorised products
$requires = $pack_details['requires'] ?? '';
if ($requires && !empty($starter_content_status[$requires]['missing'])) {
?>
<br><small class="text-warning"><i class="fas fa-fw fa-exclamation-triangle mr-1"></i>Add <?= escapeHtml($starter_content_packs[$requires]['label']) ?> first, or these come in without them.</small>
<?php } ?>
</td>
<td class="text-center"><?= intval($pack_status['total']) ?></td>
<td class="text-center"><?= intval($pack_status['present']) ?></td>
<td class="text-center">
<?php if ($pack_status['missing']) { ?>
<span class="badge badge-warning p-2"><?= intval($pack_status['missing']) ?></span>
<?php } else { ?>
<span class="text-success"><i class="fas fa-fw fa-check"></i></span>
<?php } ?>
</td>
<td class="text-right">
<?php if ($pack_status['missing']) { ?>
<form action="post.php" method="POST" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button type="submit" name="load_starter_content" value="<?= escapeHtml($pack) ?>" class="btn btn-primary btn-sm">
<i class="fas fa-fw fa-plus mr-2"></i>Add
</button>
</form>
<?php } else { ?>
<span class="text-muted">Nothing to add</span>
<?php } ?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<p class="text-muted mb-0">
Ticket templates bring their task lists with them. Products are filed under income
categories and priced as starting points only - hardware and resold licensing come in at
zero because they are quoted per deal.
</p>
</div>
</div>
<?php require_once "../includes/footer.php"; ?>