I’ve spoken of a current project in three different threads a few months ago. The idea is to port the current policy manual for my regional school system from a pile of PDFs to a single wiki.
After a hiatus, I’ve come back with a significant refactoring, and I’d love to hear thoughts on the new approach, and to find if others have similar needs. If they do—if there’s an appetite for this approach—I probably still will not try up front to make it more generic, but I will at least try to keep in mind such needs while I make design decisions.
A policy manual is something like a collection of legal documents. One of the major goals of this project is to follow the Philosophy of Tiddlers and ensure that our tiddlers are the “smallest semantically meaningful units”. And one step further, we want each of those units to be easily addressable, easy to link to, etc. These should be built up into documents which combined their children in various outline or block styles to match the original PDFs.
The old approach
The tiddler Restrictions on Use of School Facilities (#Policy1410(C)) looks like this:
title: Policy1410(C)
tags: Policy1410
caption: Restrictions on Use of School Facilities
marker: C
parent: Policy1410
The following restrictions shall apply to the use of school facilities:
<<children outline-block>>
Any violation of this Policy or any applicable Administrative Regulations may result in
permanent revocation of the privilege to use school facilities against the organization and/or
individuals involved.
And its child, Refreshment Restrictions (#Policy1410(C)(3)) looks like this:
title: Policy1410(C)(3)
tags: Policy1410(C)
caption: Refreshment restrictions
marker: 3
parent: Policy1410(C)
Refreshments may not be prepared, served or consumed without the prior
approval. Notwithstanding, only those beverages permitted by state law may be
sold during the school day. Upon approval, refreshments may be prepared, served
and consumed only in areas designated by the responsible administrator.
I used tags
for one of the TOC macros. But for various bits of custom code, like the breadcrumbs at the top, I used the parent
node. I used the marker
field to show the C
or 3
in the appropriate lists. The original PDFs have many different outline structures. I used the <<children outline-block>>
inside the text
to show where the child nodes are to be placed and and which of the many structure styles I would use. (That is, another section might include <<children plain>>
, <<children definition-quoted>>
, or a dozen others.)
The infrastructure code to use all this was relatively simple, as I had named fields for everything. But the per-tiddler setup was fiddly and time-consuming.
Another goal is to eventually remove myself from the maintenance of this. The admin likely to take over has no TW experience. I’m still going to need to develop tools for maintenance, but I didn’t like the conceptual weight – even if my tools automated the creation of all the appropriate fields, I wanted this to be simpler to understand as well.
So I refactored
The newer approach
The current code for Restrictions on Use of School Facilities (#Policy1410(C)) looks like this:
title: Policy1410(C)
caption: Restrictions on Use of School Facilities
structure: outline-block
The following restrictions shall apply to the use of school facilities:
<<sections>>
Any violation of this Policy or any applicable Administrative Regulations may result in
permanent revocation of the privilege to use school facilities against the organization and/or
individuals involved.
and its child, Refreshment Restrictions (#Policy1410(C)(3)) looks like this:
title: Policy1410(C)(3)
caption: Refreshment restrictions
Refreshments may not be prepared, served or consumed without the prior
approval. Notwithstanding, only those beverages permitted by state law may be
sold during the school day. Upon approval, refreshments may be prepared, served
and consumed only in areas designated by the responsible administrator.
What should be obvious is the reduced number of fields involved. There are no more tags
. parent
, and marker
fields, reducing the conceptual weight. There are occasional other uses of tags, but we are not cluttering up the tag namespace with every non-leaf node in the tree.
Techniques
So how did we accomplish this?
By using the titles to derive all this information.
We had used the parent
field to derive our breadcrumbs. Well, we can find the equivalent algorithmically. The parent of Policy1410(C)(3)
, is found by removing the last parenthesized portion of the title, giving Policy1410(C)
, and doing it again, we find its grandparent is Policy1410
, and using just the initial 1
in the title, we can find its great-grandparent is Section1000
.
Code
In $:/_/rham/procedures/breadcrumbs
, we use these function to find the parents of any of our main tiddlers:
In $:/_/rham/procedures/sections
, we have
\function get.parent(tid)
[<tid>!prefix[Policy]then[]]
[<tid>regexp<marker>then<tid>search-replace::regexp<marker>,[$1]]
[<tid>removeprefix[Policy]split[]first[]addsuffix[000]addprefix[Section]]
+[first[]]
\end get.parent
We had used the marker
field to show outline markers, usually as links to the related tiddlers. Now we can just extract that last parenthesized portion of the title as our marker
.
Code
In $:/_/rham/procedures/sections
, we have
\define markerRegex() .*\(([^\(\)]+)\)$
<!-- ... -->
<$let marker={{{ [<tid>search-replace::regexp<markerRegex>,[$1]] }}}>
and that marker
is then passed to any of the outline handlers.
And we had used tags
to define the hierarchy to show in the TOC. Now we wrote out own TOC macro, patterned on the core versions, but simplified, and using functions that filter all tiddlers to find the children of a given Section, Policy, or Policy section, just based on the title.
Code
The macro is in $:/_/rham/procedures/toc
. It’s too long to include here, but suffice it to say that it’s patterned very closely on the core’s toc-selective-expandable
.
The functions it uses to list child-nodes are in $:/_/rham/functions/children
:
\function sec.children(pol)
[<pol>removeprefix[Section]removesuffix[000]addprefix[Policy]] :map[all[tiddlers]prefix<currentTiddler>!regexp[\(]enlist-input[]join[ ]]
\end sec.children
<!-- todo: simplify and combine regexes -->
\function pol.children(pol)
[prefix<pol>] -[<pol>] :filter[trim:prefix<pol>regexp[^\(]!regexp[.*\(.*\(]] +[enlist-input[]join[ ]]
\end pol.children
\function children(pol)
[function[sec.children],<pol>]
[function[pol.children],<pol>]
:filter[!is[empty]first[]]
\end children
We’ve also moved the outline-block
from a macro parameter to a structure
field, and renamed children
to sections
. (In a document on education policy, children
really has another more important meaning.) Moving this doesn’t remove any additional conceptual weight, but it simply seemed more logical as field metadata rather than as a macro parameter in the text
section.
(One might think that we should be able to use a ViewTemplate
to avoid having to call a procedure for this altogether. I chose this example in part to demonstrate why we can’t. Note that the procedure call is nested between other blocks of text. For some policies or policy sections, it will be the only text content. For others, there will be preceding text. For others there will be following text. And sometimes, like here, there’s both. And that makes doing it with a template next to impossible. [Yes, I can think of ways, but they’re terrible!])
Note that the new infrastructure code is significantly more complex than its older counterpart. But this seems worthwhile if it makes content entry and maintenance much easier.
Questions
This brings to mind a few questions. I’d love to hear your feedback:
-
Would you use a more generic version of this idea for any of your wikis? The idea, in short, is that we structure our titles in a manner that allows us to derive document structure. Here we just use short parenthesized markers and the simple relationship between titles like
Policy3141
andSection3000
to derive a hierarchical table of contents, lists of breadcrumbs, outline makers and the like. -
Do you have suggested improvements? I’d love to hear anything, from typo corrections to major algorithmic alterations. But I’m especially interested in simplifications to the infrastructure code (i.e. anything in the
$:/_/rham/
namespace. -
Do you have any ideas on how to make a useful content creation/modification interface? I would really like to make it simple to append a sub-sub-section to a tiddler. Or a section. If we’re inserting into the middle of a outline list, we should automatically renumber later entries. Have you built or seen any components that would help with this?