Custom asset fields and import: user page + three developer guides
User page: Recording anything — custom asset fields and import.
Three developer guides rather than one, split by LAYER rather than by size:
Typed-Fields-Engine-Developer-Guide the shared machinery. Two consumers
today; adding a third should be a
descriptor and a query, not a new engine
Custom-Asset-Fields-Developer-Guide the assets consumer: catalogue, sets, the
two ways a set attaches, and every
surface the values have to reach
Asset-Import-Developer-Guide the feature on top: reconciliation, the
preview, the holding area
Each carries the traps rather than just the design, because that is what a
developer guide is for: the tenancy normalisation that would have duplicated the
whole estate on every run, per-row atomicity, row_number being reserved in
MySQL 8, mixed local/UTC timestamps, two phantom theme tokens, the guarded
script block, t() not being bootstrapped in API endpoints, toast.js using
textContent, and modal markup not being portable between two pages of the same
module.
Sidebar updated under Assets. Every internal link checked against an actual
file, and every source file named checked to exist.
6e40b60
Discussion #80: developer guides, sidebar, and the pages this made stale
New:
Email-Template-Sender-Rules-Developer-Guide
Email-Signatures-Developer-Guide
Updated because the change made them wrong, not merely incomplete:
Email-Send-Log - Result is now Sent / Failed / Not sent
Canned-Responses - [ticket_url] joins the merge code table
Tickets - sender rules and [ticket_url] on email templates
External-Issue-Trackers-Developer-Guide,
Azure-DevOps-Connector-Developer-Guide
- integrationsAbsoluteUrl now delegates to
publicAbsoluteUrl, plus the subfolder fault it had
_Sidebar - all five pages, nested under Tickets
Every internal link in the changed pages checked against a real file.
f4552b8
Email signatures: the reply-box half
9ee2356
Email signatures: per analyst, several allowed, one default
d5e7f48
Sender rules for email templates: specificity, not order
82172ec
Public web address: the setting behind [ticket_url] and tracker links
e5800f6
Issue #74: the default password could not be changed
Write-up of the reported fault and the three further instances found with it,
plus the Bugs-Resolved index row.
09ca658
Issue #79 write-ups, Watchtower settings pair, browser extension dev guide
Six new pages and five updated.
New:
- Issue-79-Ticket-Status-Not-Set — the reported status fault. Seven
creation paths, the priority that survived because "Normal" is the same
word in German, and the header fallback that displayed "Open" BECAUSE
no status was stored.
- Issue-79-Ticket-Source-Not-Set — the other half of the same report.
Why it could not be a one-line fix (ticket_origins has no is_default
and is company-scoped), the per-mailbox dropdown that dissolved it, and
the dismissible health mark.
- Configurable-Lookups-Developer-Guide — the through-line. Thirty-plus
places across five modules, three of them wrong in English on a stock
install; what to read instead of a name; the two derivations; the
"don't borrow a domain fact for a view decision" rule; and the
whole-payload rename diff that proves a fix.
- Watchtower-Settings + Watchtower-Settings-Developer-Guide — the pair.
- Browser-Extension-Developer-Guide — the endpoint, the payload keys, and
the trap of being the second consumer of a shared payload.
Updated:
- Watchtower — was describing eight cards and metrics that no longer
exist; now ten, with the settings link.
- Browser-Extension — corrected the badge description, added the update
notice and the dev guide link.
- Mailbox-Authentication — the Default ticket origin and the ! mark,
including why warnings can be dismissed and errors cannot.
- Bugs-Resolved — three rows.
- _Sidebar — nested entries under Watchtower, Browser Extension and Bugs
resolved.
Both validations clean across all 190 pages: every [Link] resolves to a
real .md, and every backticked path exists in the repo. Seven bare
filenames were promoted to full paths, as on the Forms and Slack pairs.
544cbe9
Time tracking: user guide + developer guide (discussion #72)
Time-Tracking
recording time, the two switches and why the second one matters, per company,
and what people will actually notice
Time-Tracking-Developer-Guide
why Tickets settings rather than System (every surface is a ticket surface),
the new per-company settings layer and why it was worth building when the ACL
table on #76 was not, why two switches, why per company is unusually clean
here, the three enforcement points, and how to add the next setting
Includes the trap this feature fell into twice in one day: a settings screen that
renders a boolean BEFORE it knows the answer, so a failed load is
indistinguishable from "off" — fixed on the Authentication page in the morning,
reproduced on this tab in the evening by the person who wrote the warning.
Sidebar entry under Tickets, beside Collision detection.
b3747b0
Attached documents: user guide + two developer pages (discussion #76)
Attached-Documents
what it does, in plain terms: a file OR a link, one document on several
records, who can see it, finding things, and the maintenance job
Attached-Documents-Developer-Guide
the colour-keyed file tables, the schema and the one decision that shaped it
(the parent is a ROW), adding a module, extraction's two tiers, orphan
collection, and the things that will bite
Attached-Documents-Permissions-and-Search
the rule and its consequences: why there is no permissions table, the two
shapes of the same question, where the check happens, why attaching is itself
a permission operation, why ⌘K asks twice, scale, the file on disk, and what
the tests actually cover
Split into two developer pages because it is a large feature and permissions is
the half somebody will come back to on its own.
Sidebar entry under Searching inside tickets, which is where the extraction
pipeline this borrows from already lives.
cee9679
Write-up for issue #77, and index it
A Microsoft 365 mailbox could only ever collect mail from Inbox. Covers why it
was never a regression (Graph accepts seven well-known folder names in a path
and treats everything else as an identifier, so reading a folder BY NAME had
never worked), why the screen contradicted itself (the move-to field had the
resolver and a Verify button, the read-from field had neither), what changed,
and what remains unproven -- a successful nested match, and the app-only
combination.
Adds the row to Bugs-Resolved and the sidebar entry.
f8e8995
Write-up for issue #78, and index it
The notification bell opened to nothing on the home page while working
everywhere else. Covers why the badge stayed correct (the count is fetched by a
function that touches no translations), why nothing was logged (the throw was
one line ABOVE the try, so the fetch never ran and the catch that would have
said "couldn't load" was never entered), and the two further pages that had the
identical fault and were fixed with it.
Adds the row to Bugs-Resolved and the sidebar entry.
80daa04
Bugs Resolved index + write-up for issue #70
A landing page for resolved bug reports, linked from the sidebar, and the
first dedicated write-up under it: why renaming the Operational impact level
split the status board in two.
The #70 page covers the reported symptom, the reason healthy services were
the broken ones (their status is derived, not stored, so six places invented
the literal 'Operational'), the four further faces of the same bug that were
not reported, the fix via the is_default flag, a colour-keyed file table, and
how it was verified including the positive control.
The index also lists two closed bug reports that have no write-up yet, so the
gap is visible rather than forgotten.
Service-Status gains a paragraph on how a healthy service's status is derived,
linking to the write-up.
838ad17
Directory sync: three open questions, and three decisions the page had lost
The open questions came from auditing this page's own 13-question list
against the shipped code. All three are gaps against decisions recorded
HERE, not new ideas - which is the point of keeping the list:
6. precedence between two directories holding the same person. §3.3
called multiple sources "already possible" and treated the plumbing
as the easy part. The plumbing is fine; the policy was never
decided. Every run claims the person, so values flap. Found by
reading the code - a single-directory install never sees it.
7. custom attributes (§3.7) not built.
8. the users screen has one door, not the two §3.12 decided on, and
§3.5's offboarding alert exists as a filter but not as a report.
Separately: checking whether the page captured the reasoning found that
it did not. Three decisions made DURING the build, all driven by using
the thing rather than designing it, were missing entirely:
4.4 the modal was the wrong container - and the finding was not "it is
cramped" but "I couldn't see the preview function". The button had
been there the whole slice, below the fold. A feature that exists
and cannot be found has not shipped, and neither the design nor
the tests could detect that because both checked whether it worked.
4.5 "Everyone" must mean everyone. The filter quietly applied
is_active = 1. The four scopes were named in the report itself and
adopted verbatim; Holding equipment includes leavers because a
leaver with a laptop is the most useful row on the screen.
4.6 a reporting line is stored once, pointing upwards, so "who does
Bob manage" did not exist. Importing a hierarchy is only half of
adopting one.
Section 4 retitled, since half of it is no longer about the first answer
being wrong.
df7416f
Wiki: developer guides for directory sync
LDAP-Developer-Guide covered sign-in and had one incidental mention of
importing across 291 lines, so the whole import subsystem was
undocumented for developers.
Split by reader intent rather than by size:
Directory-Sync-Developer-Guide how it works - scope model, the four
safety rules, matching, the run log,
the traps
Extending-Directory-Sync how to change it - the checklists
The split earns its keep because the second page is a set of lists that
must be exhaustive or they are actively harmful. Adding one imported
field touches FOURTEEN places, and missing one usually fails silently:
the column exists, the mapping box exists, nothing writes to it. That
list was produced by tracing `office` through the code, not from memory,
and the page says so, because a stale checklist is worse than none.
Every file path, function, constant and wiki link in both pages verified
to resolve.
LDAP-Developer-Guide now says plainly that it covers sign-in only.
1e2cad7
Wiki: give importing people its own page
It was a section two-thirds of the way down LDAP-and-Active-Directory,
which is a page about signing in. Anybody sent that link had to read
about bind accounts and group gating before reaching their answer, and
the question people actually ask - "how do I import my AD users?" - was
not the title of anything.
Three pages, three jobs:
LDAP-and-Active-Directory signing in
Importing-People-from-a-Directory the how-to <- the one to send people
Directory-Sync the design record
The sign-in page keeps a short pointer explaining why importing exists
at all (the people who hold equipment are largely the people who never
log in), so the link is worth following rather than a bare cross-ref.
Expanded on the way: prerequisites, step-by-step with the tree and the
mapping test, the full table of what happens when somebody leaves or a
branch is renamed, the CLI for scheduling until that is built, and a
troubleshooting table keyed on what you actually see on screen.
All links verified to resolve; sidebar updated.
90ce07e
Directory sync: record what shipped, and the design note that was half right
Directory-Sync.md was a design record for something now built. §3.8, §3.9
and §3.11 say how each idea actually turned out, the slice table marks
what is done, and scheduling is named as the only outstanding piece.
§3.9 had warned that storing DNs breaks when an OU is renamed. It was
right, and incomplete: it only considered a renamed INCLUDE. A renamed
EXCLUDE is worse - the people you carved out silently start being
imported, and the sanity brake cannot see it because the brake only
guards against a sudden DROP. Both are now detected; the page says so,
says GUID self-healing was not built, and says why detection came first.
LDAP-and-Active-Directory.md had NO mention of importing at all, so
pointing at it as "the manual" would have been false and anyone reading
it would never learn the feature exists. Added the how-to: what comes
across, choosing branches, checking the mapping, previewing, and the
table of what happens when somebody leaves or a branch is renamed.
Every file path and wiki link in both pages verified to resolve.
382a321
Directory sync: the full design conversation
Records the whole thing rather than just the conclusions -- the request, what
already exists, the thirteen questions and their answers with reasoning, the
slicing, and the open questions.
The most useful section is §4, "three things the first answer got wrong":
- "two records unless you match on email" was IMPOSSIBLE -- users.email carries
a UNIQUE index, so the second option could never have been built. The schema
comment had already anticipated the entire question. Lesson: check the
constraints before designing a setting that depends on them.
- the setting that survived is better shaped: adopt an existing person or leave
them alone and flag it -- with the consequence spelled out, that adopting kills
their portal password.
- usernames: a real problem but not the one it looked like. Global uniqueness
exists BECAUSE the multi-company portal routes by email domain, so a
mailbox-less user typing "smithj" has nothing to disambiguate them. Split into
directory_username (per provider, never mangled) and username (global, only
for people who actually sign in).
Each of those was caught by a question rather than by testing, which is worth
noting in itself.
Linked from the sidebar under LDAP & Active Directory.
f7b8479
Running on nginx: D009 section, dotfile rule now shipped on Apache too, .user.ini correction
dda7ac4
Running on nginx: table of all twelve .htaccess files, each tested against real nginx with a probe file
dee5a9d
Document the two-URL-depths trap and how it was fixed
Running-on-nginx gains a full write-up: what broke (relative redirects in auth/
resolving to /auth/index.php once the page was reachable at its real path), why
the routing tests passed anyway (they only covered signed-OUT journeys), the
rule to follow, the BASE_URL idiom for all four kinds of outbound path, and the
assertion that now guards it.
Includes the irony worth remembering: the auth/.htaccess guard removed in #68
existed for exactly this reason, but its comment named only "relative assets",
so the redirect targets were missed.
Root-Folder-Tidy's correction notice now carries the rule and links to it.
4934c90
Running on nginx (new); Root folder tidy correction notice for #68; Installation now says nginx needs its config
99bbf28
Searching inside tickets: the actual Tika docker command, the -full/OCR trap, localhost binding, and the full format list
5724435
Email send log: user page + developer guide (#1082), all eight routes
1b6c7c1
Issue #67: add the mode-switch postscript (#1081) and its troubleshooting row
9a9d413
Issue #67: app-only email sending — full write-up
New page explaining why app-only mailboxes could not send, in plain English:
what /me and /users/... actually mean, why the distinction only bites app-only,
the two further faults behind the reported one, the fix, and why the
implementation differs from the patch proposed in the issue.
Mailbox-Authentication gains a developer section on the three things auth_mode
changes, three troubleshooting rows, and a link to the new page. Sidebar updated.
70a76f7
Scheduled tasks: add the attachment extraction worker, and catch up
Checking rather than assuming turned up a real gap: the cron page listed
FOUR jobs and the attachment worker shipped as a fifth. Somebody setting
up a new install from that page would have configured everything except
the one added this week.
Added to the table and to both the Windows and Linux command blocks,
with a note that it is the odd one out: its absence is NOT silent
failure, because documents are still read opportunistically while people
use FreeITSM. The job keeps up with a busy desk rather than being the
only thing that works. That distinction matters on a page whose whole
premise is "these fail silently".
Full-Text-Search: #1078 and #1079 added to the shipped list.
Searching-Inside-Tickets: "if a document is not turning up" now says
System > Search names the file and the reason, rather than only implying
totals.
29d6b6d
Extraction guide: concurrency, the claim, and the page-hang bound
Ed asked what happens with two or three analysts logged in at once.
Nothing was ever at risk of corruption - every write is an idempotent
upsert - but two workers would select the same rows and send the same
files to the extractor, which for OCR is minutes of CPU paid for twice.
§9.4 documents the claim: a conditional atomic UPDATE to a transient
`extracting` status, why rowCount() is not enough to know which rows
were won, and the stale-claim reaper. Includes the trap that `extracting`
must ALSO be in the indexer's reconsider list, because the claiming
worker is the one that then indexes - the same shape as the `pending`
bug in §9.3, and worth stating as a rule: a transient status nothing
acts on is just a different kind of leak.
§9.5 on the opportunistic drain running inside somebody's request, and
why it needed all THREE bounds - one item, a wall-clock deadline, and a
shorter extractor timeout, because a single file cannot be interrupted
once curl is waiting on it so the deadline alone does not help. Plus why
that timeout is restored in a finally.
§9.6 on the new "attachments that are not searchable" list: counts
answer "is something wrong", only a filename answers "why can't I find
that invoice".
Status table gains `extracting` and marks `unsupported` as not final.
bd9bdf3
Search: attachment extraction tier 2 (#1076-#1077). Discussion #53 closed.
The extraction guide's §9 said "what tier 2 will need". It now describes
something that exists.
Documents the thing that shapes the whole tier: CONFIGURED and AVAILABLE
are different states. tikaExtract() returns three outcomes, and a
service being DOWN must write `pending`, never `failed` - a five-minute
outage must not blacklist every PDF that arrived during it. Also why
there is deliberately no health check before each file: the extraction
attempt IS the check, and a ping per attachment would be an extra round
trip each time.
Why both drains exist: a cron-only design does nothing at all on an
install that has not set one up, which includes every evaluation.
And §9.3 on the two bugs, because they share a shape worth recognising -
a queue that looks busy and clears nothing, which is much harder to
notice than a crash. One: the indexer would not reconsider `pending`, so
the drain could never move the depth. Two: the requeue was
indiscriminate and put files Tika is never asked about into a queue that
could not clear them. The general rule is written down: a status nothing
will ever act on is a leak, so make the worker able to terminate those.
Operational notes that will otherwise cost somebody an afternoon: use
the -full image or there is no OCR, and Tika has no authentication at
all so it must never be published.
User page: PDFs and scanned documents move from "not read" to "read once
you add a document reader", with where to set it up and where the
background-work switches live.
Full-Text-Search: all three of the discussion's original questions now
answered yes. §8.2's three tiers are two-and-a-half - the external
SEARCH ENGINE remains deliberately unbuilt and §9.5's argument still
stands.
All 13 files named verified to exist; all links resolve.
10e60f6
Add "Attachment text extraction — Developer Guide" (#1075)
A page of its own rather than another section on the corpus guide: the
subject carries more security reasoning than anything else in search,
and it needs room.
Covers the two tiers and, importantly, that the split is drawn on a
SECURITY line rather than a convenience one - tier 2 formats need a real
document parser, and the input arrives from anyone who can email the
service desk through an endpoint with no authentication at all.
Includes an answer to the question that will keep coming up: "a PDF with
a text layer is just compressed text, can't PHP do that?" Partly, and it
is the most reasonable-sounding wrong turn available. Font encodings,
compressed cross-reference tables, empty-password encryption and
positioned-not-flowed text each break it, and the objection is the
FAILURE MODE rather than the effort: unsupported is honest, silent
mojibake looks indexed and quietly matches nothing. Records what a
pure-PHP tier would have to promise (fail closed, never emit doubtful
text) if it is ever wanted.
Documents why attachment_text is the durable record and search_documents
the derived copy, with the two things it buys - a rebuild reads no
files, and whole-ticket reindexing re-opens nothing.
Every guard with the reason it exists, including that zip bombs are
refused on DECLARED sizes before an entry is read, and that writing the
test for that guard exhausted PHP's memory twice, which is the
demonstration.
The OOXML welding trap in full, since it is the one that silently ruins
search. Plus the known gaps stated honestly: docx headers/footers/
footnotes, xlsx inline numbers, pptx speaker notes.
User page: attachments are now a table of what IS and is not read,
leading on the fact that FreeITSM says which files it could not read
rather than staying quiet.
All links resolve; all eight files named verified to exist.
64cfe5c