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 '' . escapeHtml($row[0]) . ' ' . escapeHtml(billingMoney(floatval($row[3])) . ' · ' . productBilledText($frequency, $seat)) . ''; } return '' . escapeHtml(starterItemName($pack, $row)) . ''; } /* 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})\]/', '[$1]', (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) === '' ? '' : '
' . escapeHtml($label) . '
' . $html . '
'; } /* 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 = '

What it asks for

'; } /* 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 '' . escapeHtml($address) . ''; } /* * 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' ? '

' . escapeHtml($about) . '

' : ''; 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']) ? '' . escapeHtml((string) $row['portal']['title']) . ' - ' . escapeHtml((string) ($row['portal']['description'] ?? '')) . '
A request form in the client portal - it comes in turned off.
' : ''; $html .= '
' . 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) . '
'; $html .= '

What it says

' . starterPreviewFills($row['details'] ?? '') . '
'; $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 .= '

Tasks - ' . count($row['tasks']) . ', about ' . escapeHtml(starterMinutesText($minutes)) . '

    '; foreach ($row['tasks'] as $task) { $when = !empty($task['when']) ? ' when ' . escapeHtml($task['when']['fill_in'] . ' is ' . $task['when']['value']) . '' : ''; $html .= '
  1. ' . starterPreviewFills(escapeHtml((string) $task['name'])) . $when . (!empty($task['estimate']) ? ' ' . intval($task['estimate']) . ' min' : '') . '
  2. '; } $html .= '
'; } 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 .= '
' . starterPreviewFact('Goes in as', escapeHtml($types[$row['reply_type'] ?? ''] ?? ucfirst((string) ($row['reply_type'] ?? '')))) . starterPreviewFact('Offered on', $categories !== '' ? $categories : 'Every ticket') . starterPreviewFact('Sets the status to', escapeHtml((string) ($row['status'] ?? ''))) . starterPreviewFact('Adds time worked', !empty($row['minutes']) ? intval($row['minutes']) . ' minutes' : '') . '
'; return $html . '

What it says

' . starterPreviewFills($row['body'] ?? '') . '
'; case 'project_templates': $html .= '

Its tickets, in order

    '; foreach ((array) ($row['ticket_templates'] ?? []) as $ticket_template) { $html .= '
  1. ' . escapeHtml((string) $ticket_template) . '
  2. '; } return $html . '

Each from the ticket template of that name - one that isn\'t here is left out.

'; case 'vendor_templates': $html .= '
' . starterPreviewFact('Support site', starterPreviewLink($row['website'] ?? '')) . starterPreviewFact('Status page', starterPreviewLink($row['status_page'] ?? '')) . starterPreviewFact('Cases opened at', starterPreviewLink($row['portal'] ?? '')) . '
'; return $html . (!empty($row['notes']) ? '

What support asks for

' . nl2br(escapeHtml((string) $row['notes'])) . '
' : ''); case 'document_templates': $review = intval($row['review_months'] ?? 0); $html .= '
' . starterPreviewFact('Reviewed', $review ? 'Every ' . ($review === 1 ? 'month' : "$review months") : '') . '
'; $html .= starterPreviewFillIns((array) ($row['fill_ins'] ?? [])); return $html . '

What it says

' . starterPreviewFills($row['content'] ?? '') . '
'; case 'license_templates': $html .= '
' . 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'] ?? ''))) . '
'; return $html . (!empty($row['notes']) ? '

What to know

' . nl2br(escapeHtml((string) $row['notes'])) . '
' : ''); case 'products': [$frequency, $seat] = array_pad(explode(':', (string) ($row[6] ?? '')), 2, ''); $types = productTypes(); return '

' . escapeHtml((string) $row[5]) . '

' . 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 ? ' - priced on each quote' : '')) . 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' : '')) : '') . '

Prices are starting points - change them once it is here.

'; } 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(); ?>
Pick which >
$item) { if ($item['group'] !== '' && $item['group'] !== $group) { $group = $item['group']; ?>
>