mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 07:01:16 +00:00
Show each starter content item as it will look under Pick which - a tag as its pill, a category as its chip, a product with its price and billing, with what each is for - and Preview the whole of a ticket, project, document, vendor or license template, canned response or product in a window, the words filled in as it is used marked; Maintenance > Starter Content gets Pick which too, its Add bringing only what is ticked and Will be added following the ticks, sharing admin/includes/starter_content_pick.php, admin/starter_content_preview.php and js/starter_content.js with Get started
This commit is contained in:
@@ -188,6 +188,7 @@ define('FROM_GETTING_STARTED', true);
|
||||
<script src="/js/app.js"></script>
|
||||
<script src="/js/settings.js"></script>
|
||||
<script src="/js/getting_started.js"></script>
|
||||
<script src="/js/starter_content.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
/*
|
||||
* Get started - Starter content: the packs Maintenance > Starter Content has,
|
||||
* each with how much it would add, ticked to add (getting_started_content).
|
||||
* Pick which, under each, lists what it would add - every item ticked, so the
|
||||
* whole pack goes in unless some are unticked (picked[pack] says the list was
|
||||
* there, items[pack][] what is ticked in it; js/getting_started.js keeps the
|
||||
* pack's tick and count in step). Only what isn't here already is listed or
|
||||
* added.
|
||||
* Pick which, under each, lists what it would add as it will look, with
|
||||
* Preview - every item ticked, so the whole pack goes in unless some are
|
||||
* unticked (includes/starter_content_pick.php, js/starter_content.js). Only
|
||||
* what isn't here already is listed or added.
|
||||
*/
|
||||
|
||||
defined('FROM_GETTING_STARTED') || die("Direct file access is not allowed");
|
||||
|
||||
defined('FROM_STARTER_CONTENT') || define('FROM_STARTER_CONTENT', true);
|
||||
require_once dirname(__DIR__) . '/post/starter_content_model.php';
|
||||
require_once dirname(__DIR__) . '/includes/starter_content_pick.php';
|
||||
|
||||
$gs_packs = starterContentPacks();
|
||||
|
||||
@@ -26,11 +26,10 @@ $gs_packs = starterContentPacks();
|
||||
$gs_items = starterContentItems($mysqli, $gs_key);
|
||||
$gs_total = starterContentTotal($gs_key);
|
||||
$gs_missing = count($gs_items);
|
||||
$gs_all_here = $gs_missing === 0;
|
||||
$gs_count = $gs_all_here ? '' : ($gs_missing < $gs_total ? "$gs_missing more to add" : "$gs_missing to add"); ?>
|
||||
<div class="list-group-item<?= $gs_all_here ? ' is-here' : '' ?>" data-gs-pack="<?= $gs_key ?>">
|
||||
$gs_all_here = $gs_missing === 0; ?>
|
||||
<div class="list-group-item<?= $gs_all_here ? ' is-here' : '' ?>" data-gs-pack="<?= $gs_key ?>" data-starter-pick>
|
||||
<div class="d-flex gap-3 align-items-start">
|
||||
<input class="form-check-input flex-shrink-0 mt-1" type="checkbox" name="packs[]" value="<?= $gs_key ?>" id="gsPack_<?= $gs_key ?>"<?= $gs_all_here ? ' disabled' : ' checked' ?> data-gs-pack-check>
|
||||
<input class="form-check-input flex-shrink-0 mt-1" type="checkbox" name="packs[]" value="<?= $gs_key ?>" id="gsPack_<?= $gs_key ?>"<?= $gs_all_here ? ' disabled' : ' checked' ?> data-starter-pack-check>
|
||||
<span class="itflow-gs-pack-icon" aria-hidden="true"><i class="fas fa-fw <?= escapeHtml($gs_pack['icon']) ?>"></i></span>
|
||||
<div class="min-w-0 flex-grow-1">
|
||||
<label class="d-flex flex-wrap align-items-baseline gap-2 mb-0" for="gsPack_<?= $gs_key ?>">
|
||||
@@ -38,36 +37,11 @@ $gs_packs = starterContentPacks();
|
||||
<?php if ($gs_all_here) { ?>
|
||||
<span class="small text-body-secondary"><i class="fas fa-check text-success me-1"></i>All <?= $gs_total ?> here</span>
|
||||
<?php } else { ?>
|
||||
<span class="small text-body-secondary" data-gs-pack-count data-gs-of="<?= $gs_missing ?>" data-gs-more="<?= $gs_missing < $gs_total ? 1 : 0 ?>"><?= $gs_count ?></span>
|
||||
<span class="small text-body-secondary" data-starter-count data-starter-more="<?= $gs_missing < $gs_total ? 1 : 0 ?>"><?= $gs_missing < $gs_total ? "$gs_missing more to add" : "$gs_missing to add" ?></span>
|
||||
<?php } ?>
|
||||
</label>
|
||||
<div class="small text-body-secondary itflow-gs-pack-about" title="<?= escapeHtml($gs_pack['description']) ?>"><?= escapeHtml($gs_pack['description']) ?></div>
|
||||
<?php if (!$gs_all_here) { ?>
|
||||
<details class="itflow-gs-pick mt-1" data-gs-pick>
|
||||
<summary class="small">Pick which</summary>
|
||||
<input type="hidden" name="picked[<?= $gs_key ?>]" value="1">
|
||||
<div class="itflow-gs-pick-tools small mt-2">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 align-baseline" data-gs-pick-all>All</button>
|
||||
<span class="text-body-secondary" aria-hidden="true">·</span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 align-baseline" data-gs-pick-none>None</button>
|
||||
</div>
|
||||
<div class="itflow-gs-pick-list mt-2">
|
||||
<div class="itflow-gs-pick-cols">
|
||||
<?php $gs_group = null;
|
||||
foreach ($gs_items as $gs_n => $gs_item) {
|
||||
if ($gs_item['group'] !== '' && $gs_item['group'] !== $gs_group) {
|
||||
$gs_group = $gs_item['group']; ?>
|
||||
<div class="itflow-gs-pick-group"><?= escapeHtml($gs_group) ?></div>
|
||||
<?php } ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="items[<?= $gs_key ?>][]" value="<?= escapeHtml($gs_item['key']) ?>" id="gsItem_<?= $gs_key ?>_<?= $gs_n ?>" checked data-gs-item>
|
||||
<label class="form-check-label" for="gsItem_<?= $gs_key ?>_<?= $gs_n ?>"><?= escapeHtml($gs_item['name']) ?></label>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<?php } ?>
|
||||
<?= starterPickHtml($gs_key, $gs_items) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -77,3 +51,4 @@ $gs_packs = starterContentPacks();
|
||||
|
||||
<button type="submit" name="getting_started_content" value="1" class="btn btn-primary" data-gs-save>Add the ones ticked<i class="fas fa-arrow-right ms-2"></i></button>
|
||||
</form>
|
||||
<?= starterPreviewModalHtml() ?>
|
||||
|
||||
281
admin/includes/starter_content_pick.php
Normal file
281
admin/includes/starter_content_pick.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Pick which - choosing a starter content pack's items one by one, as
|
||||
* Maintenance > Starter Content (admin/starter_content.php) and the Starter
|
||||
* content step of Get started (admin/getting_started/content.php) both offer
|
||||
* it. Each item is shown as it will look - a tag as its pill, a category as
|
||||
* its chip, a product with its price - with what it is for, and a template or
|
||||
* canned response has Preview, the whole of it in a window
|
||||
* (admin/starter_content_preview.php answers it). js/starter_content.js keeps
|
||||
* the ticks and counts in step and opens the previews.
|
||||
*
|
||||
* The form sends picked[pack] (the list was there) and items[pack][] (what is
|
||||
* ticked, by starterItemKey()); the item lists come from
|
||||
* starterContentItems() in post/starter_content_model.php, which has to be
|
||||
* loaded first.
|
||||
*/
|
||||
|
||||
defined('FROM_STARTER_CONTENT') || die("Direct file access is not allowed");
|
||||
|
||||
/* What an item is for, in a line - '' when the library says nothing. */
|
||||
function starterItemAbout($pack, $row) {
|
||||
switch ($pack) {
|
||||
case 'categories':
|
||||
return (string) ($row['description'] ?? '');
|
||||
case 'tags':
|
||||
return '';
|
||||
case 'products':
|
||||
return (string) ($row[5] ?? '');
|
||||
}
|
||||
return (string) ($row['description'] ?? '');
|
||||
}
|
||||
|
||||
/*
|
||||
* An item as it will look - a tag as its pill, a category as its chip, a
|
||||
* product with its price and how it is billed, anything else its name.
|
||||
*/
|
||||
function starterItemLook($pack, $row) {
|
||||
switch ($pack) {
|
||||
case 'tags':
|
||||
return tagPill(['tag_name' => $row[1], 'tag_color' => $row[2], 'tag_icon' => $row[3]]);
|
||||
case 'categories':
|
||||
return categoryChip(['category_name' => $row['name'], 'category_type' => $row['type'], 'category_color' => $row['color'], 'category_icon' => $row['icon']]);
|
||||
case 'products':
|
||||
[$frequency, $seat] = array_pad(explode(':', (string) ($row[6] ?? '')), 2, '');
|
||||
return '<span class="fw-semibold">' . escapeHtml($row[0]) . '</span> <span class="text-body-secondary text-nowrap">'
|
||||
. escapeHtml(billingMoney(floatval($row[3])) . ' · ' . productBilledText($frequency, $seat)) . '</span>';
|
||||
}
|
||||
return '<span class="fw-semibold">' . escapeHtml(starterItemName($pack, $row)) . '</span>';
|
||||
}
|
||||
|
||||
/* The packs whose items have more to them than a line - shown whole by Preview. */
|
||||
function starterPreviewPacks() {
|
||||
return ['ticket_templates', 'project_templates', 'canned_responses', 'vendor_templates', 'document_templates', 'license_templates', 'products'];
|
||||
}
|
||||
|
||||
/* [Words in brackets] in a template, marked as the bits filled in as it is used. */
|
||||
function starterPreviewFills($html) {
|
||||
return preg_replace('/\[([A-Za-z][A-Za-z0-9 ,.\'\/&;-]{0,40})\]/', '<span class="itflow-starter-fill">[$1]</span>', (string) $html);
|
||||
}
|
||||
|
||||
/* A fact of a preview - its label and the value (html), or nothing when there isn't one. */
|
||||
function starterPreviewFact($label, $html) {
|
||||
return trim((string) $html) === '' ? '' : '<dt>' . escapeHtml($label) . '</dt><dd>' . $html . '</dd>';
|
||||
}
|
||||
|
||||
/* What a template asks for, as a list - each with its kind, choices and whether it has to be answered. */
|
||||
function starterPreviewFillIns(array $fill_ins) {
|
||||
if (!$fill_ins) {
|
||||
return '';
|
||||
}
|
||||
$kinds = ['text' => 'text', 'textarea' => 'longer text', 'date' => 'a date', 'number' => 'a number', 'yesno' => 'yes or no', 'list' => 'one of', 'email' => 'an email address'];
|
||||
$html = '<h3 class="itflow-starter-preview-head">What it asks for</h3><ul class="itflow-starter-preview-list">';
|
||||
foreach ($fill_ins as $fill_in) {
|
||||
$type = (string) ($fill_in['type'] ?? 'text');
|
||||
$options = (array) ($fill_in['options'] ?? []);
|
||||
$kind = $kinds[$type] ?? $type;
|
||||
if ($type === 'list' && $options) {
|
||||
$kind .= ' ' . implode(', ', array_map('strval', $options));
|
||||
}
|
||||
$html .= '<li><span class="fw-semibold">' . escapeHtml((string) $fill_in['name']) . '</span> <span class="text-body-secondary">- ' . escapeHtml($kind)
|
||||
. (!empty($fill_in['required']) ? ', needed' : '') . '</span>'
|
||||
. (!empty($fill_in['help']) ? '<div class="small text-body-secondary">' . escapeHtml((string) $fill_in['help']) . '</div>' : '') . '</li>';
|
||||
}
|
||||
return $html . '</ul>';
|
||||
}
|
||||
|
||||
/* A web address as a link out - its host and path as given, https:// in front. */
|
||||
function starterPreviewLink($address) {
|
||||
$address = trim((string) $address);
|
||||
if ($address === '') {
|
||||
return '';
|
||||
}
|
||||
$url = preg_match('#^https?://#i', $address) ? $address : "https://$address";
|
||||
return '<a href="' . escapeHtml($url) . '" target="_blank" rel="noopener noreferrer">' . escapeHtml($address) . '<i class="fas fa-external-link-alt small ms-1"></i></a>';
|
||||
}
|
||||
|
||||
/*
|
||||
* An item whole, for Preview - what it says and sets up, as it will come in.
|
||||
* '' for a pack whose items are all there is to them (starterItemLook()).
|
||||
*/
|
||||
function starterItemPreview($pack, $row) {
|
||||
$about = starterItemAbout($pack, $row);
|
||||
$html = $about !== '' && $pack !== 'products' ? '<p class="text-body-secondary">' . escapeHtml($about) . '</p>' : '';
|
||||
switch ($pack) {
|
||||
case 'ticket_templates':
|
||||
$category = !empty($row['category']) ? categoryChip(['category_name' => $row['category']['name'], 'category_type' => 'Ticket', 'category_color' => $row['category']['color'] ?? '', 'category_icon' => $row['category']['icon'] ?? '']) : '';
|
||||
$tags = implode(' ', array_map(function ($tag) {
|
||||
return tagPill(['tag_name' => $tag['name'], 'tag_color' => $tag['color'] ?? '', 'tag_icon' => $tag['icon'] ?? '']);
|
||||
}, (array) ($row['tags'] ?? [])));
|
||||
$due = intval($row['due_hours'] ?? 0);
|
||||
$portal = !empty($row['portal']) ? '<span class="fw-semibold">' . escapeHtml((string) $row['portal']['title']) . '</span> - ' . escapeHtml((string) ($row['portal']['description'] ?? ''))
|
||||
. '<div class="small text-body-secondary">A request form in the client portal - it comes in turned off.</div>' : '';
|
||||
$html .= '<dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Subject', starterPreviewFills(escapeHtml((string) $row['subject'])))
|
||||
. starterPreviewFact('Category', $category)
|
||||
. starterPreviewFact('Priority', escapeHtml((string) ($row['priority'] ?? '')))
|
||||
. starterPreviewFact('Due', $due ? ($due % 24 === 0 ? ($due / 24) . ' day' . ($due === 24 ? '' : 's') : "$due hour" . ($due === 1 ? '' : 's')) . ' after it is raised' : '')
|
||||
. starterPreviewFact('Tags', $tags)
|
||||
. starterPreviewFact('Clients ask with', $portal)
|
||||
. '</dl>';
|
||||
$html .= '<h3 class="itflow-starter-preview-head">What it says</h3><div class="itflow-starter-preview-doc">' . starterPreviewFills($row['details'] ?? '') . '</div>';
|
||||
$html .= starterPreviewFillIns((array) ($row['fill_ins'] ?? []));
|
||||
if (!empty($row['tasks'])) {
|
||||
$minutes = array_sum(array_map(function ($task) {
|
||||
return intval($task['estimate'] ?? 0);
|
||||
}, $row['tasks']));
|
||||
$html .= '<h3 class="itflow-starter-preview-head">Tasks <span class="fw-normal text-body-secondary">- ' . count($row['tasks']) . ', about ' . escapeHtml(starterMinutesText($minutes)) . '</span></h3><ol class="itflow-starter-preview-list">';
|
||||
foreach ($row['tasks'] as $task) {
|
||||
$when = !empty($task['when']) ? ' <span class="badge rounded-pill bg-body-tertiary text-body border fw-normal">when ' . escapeHtml($task['when']['fill_in'] . ' is ' . $task['when']['value']) . '</span>' : '';
|
||||
$html .= '<li>' . starterPreviewFills(escapeHtml((string) $task['name'])) . $when
|
||||
. (!empty($task['estimate']) ? ' <span class="text-body-secondary small">' . intval($task['estimate']) . ' min</span>' : '') . '</li>';
|
||||
}
|
||||
$html .= '</ol>';
|
||||
}
|
||||
return $html;
|
||||
|
||||
case 'canned_responses':
|
||||
$types = ['internal' => 'An internal note', 'public' => 'A public note', 'email' => 'An email reply'];
|
||||
$categories = implode(' ', array_map(function ($category) {
|
||||
return categoryChip(['category_name' => $category['name'], 'category_type' => 'Ticket', 'category_color' => $category['color'] ?? '', 'category_icon' => $category['icon'] ?? '']);
|
||||
}, (array) ($row['categories'] ?? [])));
|
||||
$html .= '<dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Goes in as', escapeHtml($types[$row['reply_type'] ?? ''] ?? ucfirst((string) ($row['reply_type'] ?? ''))))
|
||||
. starterPreviewFact('Offered on', $categories !== '' ? $categories : '<span class="text-body-secondary">Every ticket</span>')
|
||||
. starterPreviewFact('Sets the status to', escapeHtml((string) ($row['status'] ?? '')))
|
||||
. starterPreviewFact('Adds time worked', !empty($row['minutes']) ? intval($row['minutes']) . ' minutes' : '')
|
||||
. '</dl>';
|
||||
return $html . '<h3 class="itflow-starter-preview-head">What it says</h3><div class="itflow-starter-preview-doc">' . starterPreviewFills($row['body'] ?? '') . '</div>';
|
||||
|
||||
case 'project_templates':
|
||||
$html .= '<h3 class="itflow-starter-preview-head">Its tickets, in order</h3><ol class="itflow-starter-preview-list">';
|
||||
foreach ((array) ($row['ticket_templates'] ?? []) as $ticket_template) {
|
||||
$html .= '<li>' . escapeHtml((string) $ticket_template) . '</li>';
|
||||
}
|
||||
return $html . '</ol><p class="small text-body-secondary mb-0">Each from the ticket template of that name - one that isn\'t here is left out.</p>';
|
||||
|
||||
case 'vendor_templates':
|
||||
$html .= '<dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Support site', starterPreviewLink($row['website'] ?? ''))
|
||||
. starterPreviewFact('Status page', starterPreviewLink($row['status_page'] ?? ''))
|
||||
. starterPreviewFact('Cases opened at', starterPreviewLink($row['portal'] ?? ''))
|
||||
. '</dl>';
|
||||
return $html . (!empty($row['notes']) ? '<h3 class="itflow-starter-preview-head">What support asks for</h3><div class="itflow-starter-preview-notes">' . nl2br(escapeHtml((string) $row['notes'])) . '</div>' : '');
|
||||
|
||||
case 'document_templates':
|
||||
$review = intval($row['review_months'] ?? 0);
|
||||
$html .= '<dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Reviewed', $review ? 'Every ' . ($review === 1 ? 'month' : "$review months") : '')
|
||||
. '</dl>';
|
||||
$html .= starterPreviewFillIns((array) ($row['fill_ins'] ?? []));
|
||||
return $html . '<h3 class="itflow-starter-preview-head">What it says</h3><div class="itflow-starter-preview-doc">' . starterPreviewFills($row['content'] ?? '') . '</div>';
|
||||
|
||||
case 'license_templates':
|
||||
$html .= '<dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Type', escapeHtml((string) ($row['type'] ?? '')))
|
||||
. starterPreviewFact('Licensed per', escapeHtml((string) ($row['license_type'] ?? '')))
|
||||
. starterPreviewFact('Renews every', escapeHtml(SOFTWARE_RENEWAL_TERMS[(string) ($row['term'] ?? '')] ?? ''))
|
||||
. starterPreviewFact('Vendor', escapeHtml((string) ($row['vendor']['name'] ?? '')))
|
||||
. starterPreviewFact('Sold as', escapeHtml((string) ($row['product'] ?? '')))
|
||||
. '</dl>';
|
||||
return $html . (!empty($row['notes']) ? '<h3 class="itflow-starter-preview-head">What to know</h3><div class="itflow-starter-preview-notes">' . nl2br(escapeHtml((string) $row['notes'])) . '</div>' : '');
|
||||
|
||||
case 'products':
|
||||
[$frequency, $seat] = array_pad(explode(':', (string) ($row[6] ?? '')), 2, '');
|
||||
$types = productTypes();
|
||||
return '<p class="text-body-secondary">' . escapeHtml((string) $row[5]) . '</p><dl class="itflow-starter-preview-facts">'
|
||||
. starterPreviewFact('Type', escapeHtml($types[$row[1]]['label'] ?? ucfirst((string) $row[1])))
|
||||
. starterPreviewFact('Code', escapeHtml((string) $row[2]))
|
||||
. starterPreviewFact('Price', escapeHtml(billingMoney(floatval($row[3]))) . (floatval($row[3]) == 0 ? ' <span class="text-body-secondary">- priced on each quote</span>' : ''))
|
||||
. starterPreviewFact('Billed', escapeHtml(productBilledText($frequency, $seat)))
|
||||
. starterPreviewFact('Income category', escapeHtml((string) $row[4]))
|
||||
. starterPreviewFact('Becomes', !empty($row[7]) ? escapeHtml('A client asset (' . $row[7] . ') when sold' . (!empty($row[8]) ? ', with ' . intval($row[8]) . ' months of warranty' : '')) : '')
|
||||
. '</dl><p class="small text-body-secondary mb-0">Prices are starting points - change them once it is here.</p>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/* Minutes as words without the business-hours qualifier - "3 hours 15 minutes". */
|
||||
function starterMinutesText($minutes) {
|
||||
$minutes = intval($minutes);
|
||||
if ($minutes < 60) {
|
||||
return $minutes . ' minute' . ($minutes === 1 ? '' : 's');
|
||||
}
|
||||
$hours = intdiv($minutes, 60);
|
||||
$rest = $minutes % 60;
|
||||
return $hours . ' hour' . ($hours === 1 ? '' : 's') . ($rest ? " $rest minutes" : '');
|
||||
}
|
||||
|
||||
/*
|
||||
* A pack's Pick which - its items not here yet, each ticked, shown as they
|
||||
* will look with what they are for, and Preview where there is more to see.
|
||||
* $form is the id of the form the ticks go with, when they sit outside it (a
|
||||
* table row's Add). $items from starterContentItems().
|
||||
*/
|
||||
function starterPickHtml($pack, array $items, $form = '') {
|
||||
if (!$items) {
|
||||
return '';
|
||||
}
|
||||
$form_attr = $form !== '' ? ' form="' . escapeHtml($form) . '"' : '';
|
||||
$previews = in_array($pack, starterPreviewPacks(), true);
|
||||
$kind = $pack === 'tags' ? ' is-pills' : '';
|
||||
ob_start(); ?>
|
||||
<details class="itflow-starter-pick mt-1" data-starter-pick-list>
|
||||
<summary class="small">Pick which</summary>
|
||||
<input type="hidden" name="picked[<?= escapeHtml($pack) ?>]" value="1"<?= $form_attr ?>>
|
||||
<div class="itflow-starter-pick-tools small mt-2">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 align-baseline" data-starter-all>All</button>
|
||||
<span class="text-body-secondary" aria-hidden="true">·</span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 align-baseline" data-starter-none>None</button>
|
||||
</div>
|
||||
<div class="itflow-starter-pick-box mt-2">
|
||||
<div class="itflow-starter-pick-items<?= $kind ?>">
|
||||
<?php $group = null;
|
||||
foreach ($items as $n => $item) {
|
||||
if ($item['group'] !== '' && $item['group'] !== $group) {
|
||||
$group = $item['group']; ?>
|
||||
<div class="itflow-starter-pick-group"><?= escapeHtml($group) ?></div>
|
||||
<?php }
|
||||
$id = 'starterItem_' . $pack . '_' . $n;
|
||||
$about = $pack === 'products' ? '' : starterItemAbout($pack, $item['row']); ?>
|
||||
<div class="itflow-starter-item" data-starter-item-row>
|
||||
<input class="form-check-input flex-shrink-0" type="checkbox" name="items[<?= escapeHtml($pack) ?>][]" value="<?= escapeHtml($item['key']) ?>" id="<?= escapeHtml($id) ?>" checked data-starter-item<?= $form_attr ?>>
|
||||
<label class="itflow-starter-item-label min-w-0" for="<?= escapeHtml($id) ?>">
|
||||
<?= starterItemLook($pack, $item['row']) ?>
|
||||
<?php if ($about !== '') { ?>
|
||||
<span class="d-block small text-body-secondary itflow-starter-item-about"><?= escapeHtml($about) ?></span>
|
||||
<?php } ?>
|
||||
</label>
|
||||
<?php if ($previews) { ?>
|
||||
<button type="button" class="btn btn-link btn-sm text-decoration-none flex-shrink-0 ms-auto py-0" data-starter-preview="<?= escapeHtml($pack) ?>" data-starter-key="<?= escapeHtml($item['key']) ?>"
|
||||
title="See all of <?= escapeHtml($item['name']) ?>"><i class="far fa-eye me-1"></i>Preview</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<?php return ob_get_clean();
|
||||
}
|
||||
|
||||
/* The window Preview opens - once on a page. */
|
||||
function starterPreviewModalHtml() {
|
||||
ob_start(); ?>
|
||||
<div class="modal fade" id="starterPreview" tabindex="-1" aria-labelledby="starterPreviewTitle" aria-hidden="true" data-starter-preview-url="/admin/starter_content_preview.php">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title h5" id="starterPreviewTitle">Preview</h2>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body itflow-starter-preview" data-starter-preview-body></div>
|
||||
<div class="modal-footer">
|
||||
<span class="small text-body-secondary me-auto">As it comes in - yours to change once it is here.</span>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php return ob_get_clean();
|
||||
}
|
||||
@@ -22,12 +22,23 @@ if (isset($_POST['load_starter_content'])) {
|
||||
redirect();
|
||||
}
|
||||
|
||||
// One pack's Add takes the items ticked under its Pick which
|
||||
// (includes/starter_content_pick.php) - the whole pack when the list wasn't sent
|
||||
$only = null;
|
||||
if ($requested_pack !== 'all' && isset($_POST['picked'][$requested_pack])) {
|
||||
$only = array_map('strval', array_filter((array) ($_POST['items'][$requested_pack] ?? []), 'is_string'));
|
||||
if (!$only) {
|
||||
flashAlert("Nothing is ticked under Pick which - tick what to add", 'info');
|
||||
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);
|
||||
$pack_added = starterContentLoad($mysqli, $pack, false, $only);
|
||||
if ($pack_added) {
|
||||
$items_added = $items_added + $pack_added;
|
||||
$summary[] = "$pack_added " . $starter_content_packs[$pack]['label'];
|
||||
|
||||
@@ -276,9 +276,10 @@ function starterOnly($pack, array $rows, $only) {
|
||||
|
||||
// ------------------------------
|
||||
// starterContentItems
|
||||
// What loading a pack would add - its items not here yet, each as key, name
|
||||
// and group - for picking which. Here is as each loader judges it: the same
|
||||
// name (and type, for a category or tag), whatever its case.
|
||||
// What loading a pack would add - its items not here yet, each as key, name,
|
||||
// group and the library's row - for picking which. Here is as each loader
|
||||
// judges it: the same name (and type, for a category or tag), whatever its
|
||||
// case.
|
||||
// ------------------------------
|
||||
function starterContentItems($mysqli, $pack) {
|
||||
switch ($pack) {
|
||||
@@ -316,7 +317,7 @@ function starterContentItems($mysqli, $pack) {
|
||||
$items = [];
|
||||
foreach (starterContentRows($pack) as $row) {
|
||||
if (!$here($row)) {
|
||||
$items[] = ['key' => starterItemKey($pack, $row), 'name' => starterItemName($pack, $row), 'group' => starterItemGroup($pack, $row)];
|
||||
$items[] = ['key' => starterItemKey($pack, $row), 'name' => starterItemName($pack, $row), 'group' => starterItemGroup($pack, $row), 'row' => $row];
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
|
||||
@@ -5,9 +5,19 @@ require_once "includes/inc_all_admin.php";
|
||||
define('FROM_STARTER_CONTENT', true);
|
||||
require_once "post/starter_content_model.php";
|
||||
require_once "post/demo_data_model.php";
|
||||
require_once "includes/starter_content_pick.php";
|
||||
|
||||
$starter_content_packs = starterContentPacks();
|
||||
$starter_content_status = starterContentStatus($mysqli);
|
||||
|
||||
// Each pack's items not here yet - for Pick which, and how many Add brings
|
||||
$starter_content_items = [];
|
||||
$starter_content_status = [];
|
||||
foreach ($starter_content_packs as $pack => $pack_details) {
|
||||
$starter_content_items[$pack] = starterContentItems($mysqli, $pack);
|
||||
$total = starterContentTotal($pack);
|
||||
$missing = count($starter_content_items[$pack]);
|
||||
$starter_content_status[$pack] = ['total' => $total, 'missing' => $missing, 'present' => $total - $missing];
|
||||
}
|
||||
|
||||
$demo_data_status = demoDataStatus($mysqli);
|
||||
$demo_data_loaded = count($demo_data_status['loaded']);
|
||||
@@ -42,7 +52,8 @@ foreach ($starter_content_status as $pack_status) {
|
||||
<p class="text-muted">
|
||||
Adding never overwrites or deletes anything. 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
|
||||
adding twice never produces duplicates. Pick which, under a pack, lists what it would add - as it will
|
||||
look, with a preview of each template - to add only some of it. Everything added is editable and deletable
|
||||
afterwards like any other record. Ticket templates, canned responses, vendor templates, document templates and license
|
||||
templates added from an earlier version can be brought up to this one with Update - that one does replace what they hold.
|
||||
</p>
|
||||
@@ -62,8 +73,8 @@ foreach ($starter_content_status as $pack_status) {
|
||||
<?php foreach ($starter_content_packs as $pack => $pack_details) {
|
||||
$pack_status = $starter_content_status[$pack];
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<tr data-starter-pick>
|
||||
<td class="itflow-starter-pack-cell">
|
||||
<strong><i class="fas fa-fw <?= escapeHtml($pack_details['icon']) ?> me-2"></i><?= escapeHtml($pack_details['label']) ?></strong>
|
||||
<br><small class="text-muted"><?= escapeHtml($pack_details['description']) ?></small>
|
||||
<?php
|
||||
@@ -74,12 +85,13 @@ foreach ($starter_content_status as $pack_status) {
|
||||
?>
|
||||
<br><small class="text-warning"><i class="fas fa-fw fa-exclamation-triangle me-1"></i>Add <?= escapeHtml($starter_content_packs[$requires]['label']) ?> first, or these come in without them.</small>
|
||||
<?php } ?>
|
||||
<?= starterPickHtml($pack, $starter_content_items[$pack], "starterAdd_$pack") ?>
|
||||
</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 bg-warning text-dark p-2"><?= intval($pack_status['missing']) ?></span>
|
||||
<span class="badge bg-warning text-dark p-2" data-starter-count data-starter-format="short"><?= intval($pack_status['missing']) ?></span>
|
||||
<?php } else { ?>
|
||||
<span class="text-success"><i class="fas fa-fw fa-check"></i></span>
|
||||
<?php } ?>
|
||||
@@ -87,9 +99,9 @@ foreach ($starter_content_status as $pack_status) {
|
||||
<td class="text-end">
|
||||
<div class="d-inline-flex flex-wrap justify-content-end gap-2">
|
||||
<?php if ($pack_status['missing']) { ?>
|
||||
<form action="post.php" method="POST" autocomplete="off">
|
||||
<form action="post.php" method="POST" autocomplete="off" id="starterAdd_<?= escapeHtml($pack) ?>">
|
||||
<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">
|
||||
<button type="submit" name="load_starter_content" value="<?= escapeHtml($pack) ?>" class="btn btn-primary btn-sm text-nowrap" data-starter-add>
|
||||
<i class="fas fa-fw fa-plus me-2"></i>Add
|
||||
</button>
|
||||
</form>
|
||||
@@ -210,4 +222,7 @@ foreach ($starter_content_status as $pack_status) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= starterPreviewModalHtml() ?>
|
||||
<script src="/js/starter_content.js"></script>
|
||||
|
||||
<?php require_once "../includes/footer.php"; ?>
|
||||
|
||||
40
admin/starter_content_preview.php
Normal file
40
admin/starter_content_preview.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* A starter content item whole, for Preview under Pick which
|
||||
* (admin/includes/starter_content_pick.php) - ?pack=&item= (its
|
||||
* starterItemKey()), answered in JSON as its name and the preview's html.
|
||||
* Admins only, as the two pages that ask are.
|
||||
*/
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/config.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/functions.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/check_login.php';
|
||||
enforceAdminPermission();
|
||||
|
||||
define('FROM_STARTER_CONTENT', true);
|
||||
require_once __DIR__ . '/post/starter_content_model.php';
|
||||
require_once __DIR__ . '/includes/starter_content_pick.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
|
||||
$pack = (string) ($_GET['pack'] ?? '');
|
||||
$key = (string) ($_GET['item'] ?? '');
|
||||
$row = null;
|
||||
if (isset(starterContentPacks()[$pack]) && in_array($pack, starterPreviewPacks(), true)) {
|
||||
foreach (starterContentRows($pack) as $candidate) {
|
||||
if (starterItemKey($pack, $candidate) === $key) {
|
||||
$row = $candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$row) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'There is no such starter content.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['title' => starterItemName($pack, $row), 'pack' => starterContentPacks()[$pack]['label'], 'html' => starterItemPreview($pack, $row)]);
|
||||
Reference in New Issue
Block a user