Rename tiddlers to datetime IDs (unique identifier)

123 views
Skip to first unread message

bimlas

unread,
Oct 22, 2020, 3:21:03 AM10/22/20
to TiddlyWiki
If you want to use the Zettelkasten method, you probably feel the need to mark your notes with a unique, numeric identifier. Although you can create a link to an ID that is stored in a field, it is not as clear as if the ID was the name of the tiddler (for example, in the Node.js wiki, filenames contain the name of the tiddler). Use the following solution to rename TiddlyWiki tiddlers to datetime IDs (DTIDs) created from the value of the created field.

Attention! Since using this script can lead to errors, do not use it on your own wiki, just for testing purposes!

After installing the Relink plugin (https://flibbles.github.io/tw5-relink/), issue this command in the Javascript console. You can test it in Relink's own demo, because most of the other wikis has tiddlers with the same (duplicated) "created" field, which is why the script didn't run (to prevent data loss).

Negatives:
  • Due to the renaming, it marks the current date as the date of the last modification
  • Because the tiddler name will be the date it was created, it cannot be displayed in alphabetical order (e.g. for search results)
To keep the tiddler's original name, it inserts it into the tiddler's text as a headline. For tiddlers that start with a macro definition (e.g., Frodo tiddler in the Relink demo), write the original title of the tiddler to the end of the tiddler instead of the beginning, so as not to cause a problem (to see the result, first open the Frodo tiddler for editing and save it because it basically does not contain a "created" field and thus will not be renamed). You need to repair these tiddlers yourself afterwards.

It modifies the links to the tiddler by leaving the original title of the tiddler and then enclosing the DTID link in parentheses ([[Welcome]] -> Welcome ([[123456]])). Because it does this with a simple text search (so it doesn't monitor whether it's a macro parameter), it can cause an error.

@Flibbles, how could the Relink plugin be modified to make the change in the tiddler text this way?

To see what problems the script has encountered, you can filter console messages from the drop-down menu at the top of the console window to show only the errors (if you're done, don't forget to reset).

var filterAlreadyRenamed = "!regexp[^\\d{14}$]";
var filterHashtagLinks = "!prefix[#]!search[#Referencia]";
var filterExpression = "[!is[system]" + filterAlreadyRenamed + filterHashtagLinks + "]";

function generateId(fields) {
  return fields.created 
    ? $tw.utils.formatDateString(fields.created,"YYYY0MM0DD0hh0mm0ss")
    : undefined;
}

if (hasDuplicatedIds(filterExpression)) {
  throw new Error("Duplicate IDs are found, please resolve this issue before running the script");
}

$tw.utils.each($tw.wiki.filterTiddlers(filterExpression),
  function (current) {
    var fields = Object.assign({}, $tw.wiki.getTiddler(current).fields);
    var id = generateId(fields);
    if (!id) {
      console.error("Skip tiddler (cannot generate ID):", current);
      return;
    }
    addHeadline(fields);
    $tw.wiki.renameTiddler(fields.title, id);
    makeBacklinksVerbose(id, fields.title);
  }
);

function hasDuplicatedIds(filterExpression) {
  var idCounter = {};
  $tw.utils.each($tw.wiki.filterTiddlers(filterExpression),
    function (current) {
      var fields = $tw.wiki.getTiddler(current).fields;
      var id = generateId(fields);
      if (!id) {
        return;
      }
      if (!idCounter[id]) {
        idCounter[id] = [];
      }
      idCounter[id].push(current);
    }
  );
  var results = false;
  $tw.utils.each(idCounter,
    function (current) {
      if (current.length > 1) {
        console.error("Duplicated ID:", current); 
        results = true;
      }
    }
  );
  return results;
}

function addHeadline(fields) {
  if (fields.text.match(/\\define/)) {
    console.error("Headline added to end of the body (contains macros):", fields.title);
    fields.text = fields.text + "\n\n! " + fields.title;
  } else {
    fields.text = "! " + fields.title + "\n\n" + fields.text;
  }
  $tw.wiki.addTiddler(fields);
}

function makeBacklinksVerbose(id, verboseTitle) {
  $tw.utils.each($tw.wiki.filterTiddlers("[[" + id + "]backlinks[]]"),
    function (backlink) {
      var backlinkFields = Object.assign({}, $tw.wiki.getTiddler(backlink).fields);
      backlinkFields.text = backlinkFields.text.replace("[[" + id +"]]", verboseTitle + " ([[" + id + "]])");
      $tw.wiki.addTiddler(backlinkFields);
    }
  );
}

To keep the headlines readable in the search results, change the $:/core/ui/ListItemTemplate tiddler: if the text contains a headline, print it and include the title, which is either the DTID or for unmodified (e.g., shadow) tiddlers, the original title.

<div class="tc-menu-list-item">
<$link><$text text={{{ [all[current]get[text]splitregexp[\n]removeprefix[! ]first[]] }}}/> <$view field="title"/></$link>
</div>

bimlas

unread,
Oct 22, 2020, 3:46:47 AM10/22/20
to TiddlyWiki
You can also create IDs based on a counter, so you can be sure to avoid name conflicts, and each tiddler will be given a unique ID (even one that does not have a created field). In this case, also modify the body of the generateID function and the filterAlreadyRenamed filter.

var filterAlreadyRenamed = "!regexp[^ZK\\d{8}$]";

// ...

var lastId = (parseInt($tw.wiki.filterTiddlers("[regexp[^ZK\\d{8}$]nsort[]last[]removeprefix[ZK]]")[0]) + 1) || 1;
function generateId(fields) {
  var idLength = 8;
  var idPrefix = "ZK";
  var id = lastId.toString();
  lastId += 1;
  while (id.length < idLength) id = "0" + id;
  return idPrefix + id;
}

Flibbles

unread,
Dec 15, 2020, 8:23:27 PM12/15/20
to TiddlyWiki
I don't normally hang around in the Google group, so I didn't see this until now. I read what you wrote several times but I'm still not clear on what Relink is doing wrong, or what it's supposed to be doing but isn't.

You don't still need help, do you?

-Flibbles
Reply all
Reply to author
Forward
0 new messages