Separating user defined fields from, well, all the others

Hi Nemo,

As I wrote a moment ago, in my reply to Caspar, I wasn’t expecting to need to. Naive as that might seem now. I’d also prefer not to, though only for grammatical reasons, in that I sometimes use the field name as an input to a piece of text that’s constructed in part from the field values. But there’s no functional reason that I couldn’t do that, so thanks for the suggestion.

Regards,

Chris

There are at least three reasons a person might not want to do name all their fields with a user-specific prefix:

(1) When dynamic tables use field name as column header, these headers work out-of-box very nicely with compact readable field names.

(2) Sometimes we want a field names that can correspond to a straightforward tiddler name (some data structures benefit from this pattern)

(3) If one is regularly importing JSON or other structured data from external sources (with keys/field names not under our control), retaining routine workflows and compatibility can weigh in favor of retaining most field names as they appear in the external data.

Of course, one could write workarounds to add and subtract the custom prefix as needed, but … Tiddlywiki’s flexibility of field names is a beautiful and elegant thing!

For completeness: Note that we still have the grey area of field names that play a role in the wiki because they’re defined in some plugin or external data-set. They won’t be part of a standard list of TW’s out-of-box set of fields, but they also won’t be user-defined in the usual sense.

3 Likes

Hi Springer, thanks for that. Every additional insight helps as far as I’m concerned. Regards, Chris

So, in helpful, interesting and irritating equal measures, I’ve just discovered that the Control Panel displays “the full set of TiddlerFields in use in this wiki (including system tiddlers but excluding shadow tiddlers)” at Control Panel>Info>Advanced>Tiddler Fields! Doh! :roll_eyes:

In addition I’ve just noticed that the " Add a new field: field, which appears at the bottom of every tiddler, displays that same list of all the fields SPLIT INTO User fields followed by System fields! Nnnnngh! Double doh! :roll_eyes: :roll_eyes:

So at least I can now count them and copy them, even if I can’t generate the list myself. YET.

There are 16 supposedly user defined fields in the wiki now, created by me, plus the “list” field, which isn’t user defined as such but which I presume is in the list because it’s content is intended to be user populated.

Now I’m wondering how the tiddler knows which fields are user fields and which are system fields?

I thought the answer was going to be simple because the system fields all seem to have descriptions, whereas the user fields don’t. But that doesn’t hold true for the system field commit:, which doesn’t have any description. So that’s not how it does it.

Looking at the list in the Control Panel, I’ve now noticed that the user defined field that my filters keep missing, which is named source duplicates a system field that has the same name, vis: source: The source URL associated with a tiddler. So given that my filters both start with !is[system], that’s not very surprising that it isn’t listed by my filter. Sigh. Triple doh! :roll_eyes: :roll_eyes: :roll_eyes:

So it looks like I’ve got to the bottom of my first problem, vis why are my filters not generating a complete list of user defined fields. But now I need to:

(a) go back and do a bulk rename all the occurrences of my supposedly user defined field source, without renaming any of the occurrences of its system twin.

(b) work out how the drop down list that the tiddler generates for the the " Add a new field: field, knows how to split the user fields from the system fields?

And by the way, I’ve just noticed that whatever routine generates the drop down list construes the field named “source” as a user field, not a system field, whereas the list in the table in the Control Panel does the opposite.

Right, time for bed said Zebedee, it’s late. Busy tomorrow so back to this on Wednesday. In the meantime any wise thoughts, insights or humorous but supportive wisecracks are all welcome.

Regards, Chris

I know that I just said it’s late but I wanted to get a sense of what’s going to be involved in replacing my user defined field source with something that doesn’t duplicate a system field. And lo and behold, [!is[system]has:field[source]] tells me that I have 226 matches. It looks like it’s time that I plugged in Tiddler Commander or an equivalent, otherwise this is going to be like mucking out the Augean stables!

1 Like

And tracing that through $:/core/ui/ControlPanel/TiddlerFields to $:/snippets/allfields also gives us a way to see all the field names known to the core. I’ve been looking for this.

[all[shadows]prefix[$:/language/Docs/Fields/]removeprefix[$:/language/Docs/Fields/]]

We can simplify that to

[all[shadows]removeprefix[$:/language/Docs/Fields/]]

although I personally have never been thrilled by that behavior of removeprefix.

<table>
<tr><th>Name</th><th>Description</th></tr>
<$list filter="[all[shadows]prefix[$:/language/Docs/Fields/]]" >
<tr><td><$text text={{{ [<currentTiddler>removeprefix[$:/language/Docs/Fields/]] }}} /></td><td><$text text={{{ [<currentTiddler>get[text]] }}} /></td></tr>
</$list>
</table>

yields

Name Description
_canonical_uri The full URI of an external image, audio, or html file
_is_skinny If present, indicates that the tiddler text field must be loaded from the server
author Name of the author of a plugin
bag The name of the bag from which a tiddler came
caption The text to be displayed on a tab or button
class The CSS class applied to a tiddler when rendering it - see [[Custom styles by user-class]]. Also used for [[Modals]]
code-body The view template will display the tiddler as code if set to ‘‘yes’’
color The CSS color value associated with a tiddler
component The name of the component responsible for an [[alert tiddler
core-version For a plugin, indicates what version of TiddlyWiki with which it is compatible
created The date a tiddler was created
creator The name of the person who created a tiddler
current-tiddler Used to cache the top tiddler in a [[history list
dependents For a plugin, lists the dependent plugin titles
description The descriptive text for a plugin, or a modal dialogue
draft.of For draft tiddlers, contains the title of the tiddler of which this is a draft
draft.title For draft tiddlers, contains the proposed new title of the tiddler
footer The footer text for a modal
hide-body The view template will hide bodies of tiddlers if set to ‘‘yes’’
icon The title of the tiddler containing the icon associated with a tiddler
library Indicates that a tiddler should be saved as a JavaScript library if set to ‘‘yes’’
list An ordered list of tiddler titles associated with a tiddler
list-after If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles, or at the end of the list if this field is present but empty
list-before If set, the title of a tiddler before which this tiddler should be added to the ordered list of tiddler titles, or at the start of the list if this field is present but empty
modified The date and time at which a tiddler was last modified
modifier The tiddler title associated with the person who last modified a tiddler
module-type For javascript tiddlers, specifies what kind of module it is
name The human readable name associated with a plugin tiddler
parent-plugin For a plugin, specifies which plugin of which it is a sub-plugin
plugin-priority A numerical value indicating the priority of a plugin tiddler
plugin-type The type of plugin in a plugin tiddler
released Date of a TiddlyWiki release
revision The revision of the tiddler held at the server
source The source URL associated with a tiddler
stability The development status of a plugin: deprecated, experimental, stable, or legacy
subtitle The subtitle text for a modal
tags A list of tags associated with a tiddler
text The body text of a tiddler
throttle.refresh If present, throttles refreshes of this tiddler
title The unique name of a tiddler
toc-link Suppresses the tiddler’s link in a Table of Contents tree if set to ‘‘no’’
type The content type of a tiddler
version Version information for a plugin

Does anyone see any reason not to use that as the (dynamic) definitive list of fields used by the core? Are there known fields which do have have this lingo mechanism applied?

If this is correct, then we could write a custom function,

\function custom.fields() [fields[]] -[all[shadows]removeprefix[$:/language/Docs/Fields/]]

to be used like

<<list-links filter:"[<currentTiddler>custom.fields[]]">>

to get a list of all the non-core fields in the current tiddler.

This is extremely useful to me. Thank you @ChrisH!

CoreFields.json (615 Bytes)

2 Likes

Commander is wonderful. But it’s also worth noting that it’s relatively easy to do these bulk changes on your own. Here is a button to rename "source" to "origin" across all non-system tiddlers:

<$button>Rename 'source' to 'origin'
  <$list filter=[!is[system]has:field[source]]>
    <$action-setfield origin={{!!source}} $timestamp="no" />      
    <$action-deletefield source $timestamp="no" />
  </$list>
</$button>

Note: The above has been edited to add the $timestamp attribute, as suggested by @Greyed. This avoids changing the modified date and therefore moving these to the top of the Recent tab.


Rename Source.json (411 Bytes)

Hi Scott,

I’m really glad that you found my post helpful. Thanks for letting me know. I’m also grateful to be reading your reply over breakfast with a coffee to hand because there’s a lot of learning in there for me to unpack. I’m a complete beginner when it comes to working under the hood of Tiddlywiki, or even polishing the chrome, to push the metaphor to the limit. Though I’ve been using TW for a good few years now.

The remove prefix operator is a particularly interesting discovery for starters. And the whole block of code under <table>, that creates the tabulated list of fields and their descriptions. I’ve been wanting to find out how to do that for a while. I presume it’s a macro.

I haven’t gone into those parts of the TW jungle yet, I’m just hovering at the edge of the clearing, listening to the noises. Looks like it’s time to put on my bush hat and head in. :cowboy_hat_face:

Regards, Chris

Whoah! That’s above and beyond! And great learning. Thank you Scott, that’s very good of you. Best regards, Chris

I assume you mean how removeprefix[...] does TWO actions:

  • filter out items that don’t have the specified prefix
  • remove the specified prefix from the items that are left

Although it is sometimes convenient to do both actions (as in your previously posted response), there are also times when you just want to remove the prefix while retaining all items, even if they don’t have that prefix. For this behavior, you should use trim:prefix[...] instead.

-e

2 Likes

Yes, exactly. I always prefer the simplicity of tools that do one thing, allowing me to combine them as I choose.

Oh, yes, thank you. I use trim often, but only as I would in JS, to remove leading/trailing whitespace. I guess I never looked at the docs to realize that it had other behavior as well. Very useful; thank you.

Yes, Your Honor, the defendent was found exceeding the posted limit of one extended metaphor per message… :slight_smile:

There are a lot of operators. It takes a long time to learn them all, or at least longer than I’ve been able to spend in the last few years.

And as seen in the conversation here, sometimes individual operators do multiple things, either by default, as in removeprefix or via configuration, as in trim.

There are various tools worth investigation. I assume that if you’ve been doing this a while, you’re at least passingly familiar with WikiText, but he others may be new to you.

It’s a combination of HTML markup and a ListWidget. If you’re familiar with HTML from another context, it works pretty well the same inside TW tiddlers. It’s difficult (at least for me) to get WikiText Tables to work with lists, so I usually end up with HTML for them.

How I work

Typically, I would start with a <<list-links>> macro to make sure I have the filter I want to use correct. Here I already tested the filter in the Advanced Search > Filter tab, but I still rechecked it here. So my first step would be:

<<list-links filter:"[all[shadows]prefix[$:/language/Docs/Fields/]]" >>

That displays a bulleted list of tiddler titles, and looks correct, so I mechanically change that to a <$list> widget like this:

<$list filter="[all[shadows]prefix[$:/language/Docs/Fields/]]" >
  <$link/><br/>
</$list>

Note that we now have opening and closing tags for the <$list> widget, and we’ve moved from the macro shortcut syntax of <<name ...params >> to the more HTML-like <name ...attributes> ...content </name>, where name uses the tiddlywiki convention of starting with $, so <$link ...>...</$link>. Also note the minor annoying need to switch from filter: to filter= as we switch from the macro shortcut syntax to a list widget. (This might be going away sometime soon.) Finally, we have some content that will be created for every value our filter creates. Here I use another placeholder, just to make sure my list widget is acting correctly. I include <$link />, which will create a link to the current title, followed by <br/> to add a line break between them.

When I’ve confirmed that the above is working correctly, I replace the content with the markup for my table row, and wrap the whole thing in <table> tags and add a header row. (I often skip this step, but it can help me avoid making too many changes at once.):

<table>
  <tr><th>Name</th><th>Description</th></tr>
<$list filter="[all[shadows]prefix[$:/language/Docs/Fields/]]" >
  <tr>
    <td>name will go here</td>
    <td>description will go here</td>
  </tr>
</$list>
</table>

I fill in with the actual content I want for my table cells, generally one at a time, saving and confirming after each change. First I want to start with the current tiddler and remove the prefix to leave just the name:

<td>{{{ [<currentTiddler>removeprefix[$:/language/Docs/Fields/]] }}}</td>

for name is almost fine, but I don’t want it to be a link, so I wrap it in a text widget

<td><$text text={{{ [<currentTiddler>removeprefix[$:/language/Docs/Fields/]] }}} /></td>

That works fine, and now I want the description:

<td><$text text={{{ [<currentTiddler>get[text]] }}} /></td>

Et Voila!, we have our table:

<table>
  <tr><th>Name</th><th>Description</th></tr>
<$list filter="[all[shadows]prefix[$:/language/Docs/Fields/]]" >
  <tr>
    <td><$text text={{{ [<currentTiddler>removeprefix[$:/language/Docs/Fields/]] }}} /></td>
    <td><$text text={{{ [<currentTiddler>get[text]] }}} /></td>
  </tr>
</$list>
</table>

This is my usual process, and most of it is mechanical and quick. Usually finding the exact filter expressions for the list – and sometimes for extracting the text cells – are the only time-consuming parts. This one took me maybe five minutes to create… and a lot longer to write up here.

Note that that bare-bones list made with <<list-links ...>> may last a good while. It’s perfect temporary filler for something I want to come back to later but don’t want to distract me now.

If you haven’t figured this out by now, then good news: this actually isn’t nearly as hard as you think it is! In fact, I figured out (b) in the course of trying to update Scott’s filter a couple posts ago by just poking around the core’s tiddlers a little.

  • Scott’s language-based filter in post #13 is fantastic to show all of the non-system fields, but it has a small flaw for your use case: it won’t include fields which are normally system fields that a user has added to their own tiddlers, like you said you’ve done with the source field.

Here’s how you can do (b):

Because we’re looking for how the core does the “field” dropdown in a tiddler’s “edit” mode, I did an Advanced Search for field edit, and clicked over to the Shadows tab where most of the core tiddlers will be. In that list is $:/config/EditMode/fieldname-filter – open it, and pop up its Info pane (in the More dropdown) and look at the Fields tab. (Or you can just edit the tiddler to view them.)

See how there’s two filters there, one in a field named first-search-filter and one in second-search-filter? Those are the two filters that are used to generate the two halves of the add-a-field’s dropdown menu. The first- one shows just the user fields, and the second- one shows just the system fields.

  • You can verify what each filter does by copying it into a $list in a new tiddler and seeing what it returns.

By default, the first-search-filter field’s contents are:

[!is[shadow]!is[system]fields[]search:title<userInput>sort[]] -created -creator -draft.of -draft.title -modified -modifier -tags -text -title -type
  • By the way, this also answers your question of “why is the system source field listed in the field dropdown’s user section now?” When you add a field which is normally a system field to your own tiddlers, it becomes considered a user field by the above filter – because it now has a non-shadow, non-system tiddler which includes that field, it no longer gets filtered out here!

We don’t actually need the part of the first filter run that says search:title<userInput>, because that’s what “trims down” the list of fields as you type into the add-a-field text box. So you could simply copy the above filter, remove that bit, and ta-da! you’ve got a nice, working list of user-created fields.

But! We can make it a little tidier by simply transcluding that field’s filter directly into our new filter:

[subfilter{$:/config/EditMode/fieldname-filter!!first-search-filter}]

Note the curly braces used for transclusion there. We also need to pass it to the $list as a subfilter for it to work.

  • Note that this method will keep the search operation, but I don’t think it should be too much of a performance hit because you probably won’t have a “userInput” variable defined in this tiddler anyways.

Regarding (a):

Scott’s provided code in post #14 is your best bet! I’d just like to add a little tip to it: if you need/want to keep the current last-modified timestamps on your edited tiddlers, both the $action-setfield and $action-deletefield widgets he uses in his code support the $timestamp="no" attribute (in TW version 5.3.4 or higher.)


…For fun (shh, I was on a roll), if you have more fields than just source to change, why not make a single button press change ALL of them at once? :smiley: Replace the “FIRST-FIELDNAME-OLD” and “-NEW” in the code with the actual first field’s old and new names, and so on for additional field names.

WARNING! POTENTIALLY DANGEROUS CODEmake a backup and double-check the output of the filters first by removing the $button widget plus the “fieldrenamingaction” calls, and replacing the latter with something like this to verify the components being used:

''Tiddler:''  <$link to=<<tidtochange>> /> ¦ ''Old field:'' <<fieldtochange>> ¦ ''New field:'' <<condition>> ¦ ''Contents:'' <$transclude $tiddler=<<tidtochange>> $field=<<fieldtochange>> />

(Also, I won’t lie: the below code is super clunky. If someone suggests an improvement to it or finds a typo I’ve made, listen to them instead! :sweat_smile:)

\procedure fieldrenamingactions()
<$action-setfield $tiddler=<<tidtochange>> $field=<<condition>> $value={{{ [<tidtochange>get<fieldtochange>] }}} />
<$action-deletefield $tiddler=<<tidtochange>> $field=<<fieldtochange>> />
\end

<$button>
Rename multiple fields

<$list filter="[subfilter{$:/config/EditMode/fieldname-filter!!first-search-filter}]" variable="fieldtochange">
<$list filter="[!is[system]!is[shadow]has:field<fieldtochange>]" variable="tidtochange">

<%if [<fieldtochange>match[FIRST-FIELDNAME-OLD]then[FIRST-FIELDNAME-NEW]] %>

<<fieldrenamingactions>>

<%elseif [<fieldtochange>match[SECOND-FIELDNAME-OLD]then[SECOND-FIELDNAME-NEW]] %>

<<fieldrenamingactions>>

<%elseif [...] %>

	(continue as before for each new field, to define the old field names and their new names)

<%endif%>
</$list>
</$list>
</$button>
  • The condition variable used in the procedure’s actions is automatically set by each conditional %if statement to be the output of its own filter, so in this case it will be each new fieldname you specified for that section of the %if.

To only change some of the user-fields, you can add %if / %elseif sections for only the old tid names you want to include; everything you don’t include should be ignored. Or, you can replace the initial $list’s filter that pulls ALL user-fields with [[a specific list]] [[of]] [[fieldnames]] +[enlist-input[]] . The former is less initial work, but the latter might be speedier if previewing the field changes makes your wiki choke or get laggy since it’s working with fewer starting tiddlers.

1 Like

(I’m adding this as a separate post, because it’s not actually quite what ChrisH’s particular case needs. It’s just a nice tweak I came up with, so I thought I’d share.)

This filter is amazingly creative, and solves a minor problem I’ve had for a while! But I found it to have one small flaw: it does not exclude fields used in plugins. So here’s a way to get the results without these plugin fields included:

[subfilter{$:/config/EditMode/fieldname-filter!!first-search-filter}] -[all[shadows]removeprefix[$:/language/Docs/Fields/]]

As noted in my post above, the $:/config/EditMode/fieldname-filter tiddler’s “first-search-filter” field contains the list of all user-created fields, which is used for the add-a-field dropdown list’s “User fields” section. By using this existing filter, we can take advantage of the core’s method which excludes plugins’ fields from the “User fields” section.

  • If we solely use the language method to filter, it would include any plugin field or core field that doesn’t have a language entry – for example, in my wiki, the “demo” field that the Relink plugin uses was included, as well as the “status” field that the core $:/Import tiddler uses.

By then adding your language-based method to exclude system fields, that also excludes originally-system fields like caption or code-body which might regularly be added by users.

Thus, the output of the above tweaked filter should be EXCLUSIVELY user-created fields – well, as long as nobody’s added their own language entry for them!

That’s a very nice addition, and I think there are times I’d want either one.

Often I don’t want to care about what’s in plugins. Mostly I would use this for when I want a list those fields I use in a particular tiddler that are not part of Tiddlywiki’s core. That Relink uses demo, AutoComplete uses rows and PlantUML uses documentation, would not stop me from using them for my own fields. But I’d probably always avoid type, or modified.

Other times, it’s possible that I’d want to scan the whole wiki, plugins included.

The big concern of course is that somebody adds these $:/language/Docs/Fields/* tiddlers for other fields, and ones we care about disappear from the list. I guess they shouldn’t. Jeremy has recently made it clear that he thinks of the $:/language namespace as reserved for the core. But it’s a concern. For my own uses, it would not be hard to whitelist back in a value that is otherwise restricted by these techniques, but it’s easy enough to imagine places where that is not possible.

Very good point. And in fact, I’m going to edit my post to include that.

Hi Scott,

Well, I don’t know what to say other than thank you very much for going to all the trouble and effort of writing such a comprehensive post. It’s very generous of you and I’m very grateful to you for it. The post is really helpful to me and I’ll try it all out tomorrow. It’ll also force me to try to get my head around working with HTML code, a subject that I know even less about than TiddlyWiki’s inner workings.

Best regards and thanks again,

Chris

1 Like

Hello Greyed,

It’s nice to make your aquaintance too. And thank you very much for this additional material! My reply to Scott just now clearly applies here with equal weight. It’s seems clear that the TW community is a friendly and generous crowd. I’m quite overwhelmed by everyone’s kindness, though you are all stacking me up a big study day tomorrow.

Best regards, Chris

I have explored fields deeply but will always use the fieldnames I want without prefix or suffix except if I want to groups the fields.

A smart approach is to use a field such as object-type (because type is in use) and use this when designing tiddlers with custom fields and thus you can list all fieldnames that belong to that object-type. You can call this self documenting.

  • There is rarely if ever a clash of fieldtypes if your solution has this other layer of naming, the object-type, just dont list or consider a custom field unless its in a known object-type.
  • Your solution will not have any problems due to others using the same fieldname else where. Other solutions rarly clash, although I avoid the system fields.
  • When using current tiddler you need only query the fields on the current tiddler. Include empty fields.

I will see if I can find my own field filters or lists I use in a function so I can filter fieldnames dynamicaly.

[Edit] Use relink plugin for fields as well

Hi Anthony,

Thanks for your advice, I appreciate it. But can you clarify for me one of the points that you made? It’s this phrase from your second paragraph.

I’m not sure how to interpret that. Do you mean that one should use the “object-type” field, for example"? I don’t think so because I can’t find such a field but thought I’d ask, just to be sure. Or are you proposing “object-type” as a generic naming convention for user defined fields. So that the “object” part is a field specific name, whereas the “type” part names a (possible) group of related fields?

I have used something like that format for a few of my own user defined fields, though my constructions take the form “type-object” not “object-type” but it serves the same function. For example: “game-date” to reference an arbitrary date in the game; “location-aft” and “location-fore” to reference locations on the game map that are immediately behind and just ahead of some character or group. You see what I mean.

I didn’t do that as a rule, however, because I preferred to use single nouns or verbs as names wherever possible, as you would, it seems.

Best regards, Chris

Imagine a wiki that tracks purchases for your online store. You might have tiddlers categories of Customer, Supplier, Item, LineItem, and Order. My own preference would be to add tags of Customer, Supplier, etc. to the appropriate tiddler. But some people like to keep their tag space very tidy. So instead of a tag, you could use a dedicated field, say, object-type:

title: Acme Manufacturing
tags: (whatever)
object-type: Supplier
...
title: Wile E Coyote
tags: (whatever)
object-type: Customer
...

While we might prefer to use type, that one is used by the core, and is quite important to the functioning of TW. We couldn’t well reuse it for something else.