New to TW - experienced dev - question

503 views
Skip to first unread message

Flan Mou

unread,
May 19, 2020, 12:14:29 AM5/19/20
to TiddlyWikiDev
Goal: to create a plugin(?) where: you type in an ISBN and it does a few HTTP GETs to get book information and populates a new tiddler with that info.

I'm pretty new to TiddlyWiki and I've been trying to make my way through tiddlywiki.com/dev looking for examples I can base things off. I haven't seen anything relevant.

I'd be happy for any points in useful directions.

Thanks.

PMario

unread,
May 19, 2020, 2:59:49 AM5/19/20
to TiddlyWikiDev
Hi,
Is there a public service, where you can GET the info? Without an API description, it's hard to do.
-mario

TonyM

unread,
May 19, 2020, 9:58:08 AM5/19/20
to TiddlyWikiDev
Flan,

I would tlove to see such a feature. There are APIs in some cloud solutions that may be doing something similar;


Then look at how he does it.

Regards
Tony

Flan Mou

unread,
May 19, 2020, 10:20:38 AM5/19/20
to TiddlyWikiDev
Yes, for example there is the OpenLibrary API:

https://openlibrary.org/developers/api

I'm fine with interacting with the API, it's just that I'm not sure how to build even a very simple request within TW. I was expecting to be able to create an HTML tiddler, add a <script> tag, and write some Javascript with XMLHttpRequest() within it. But it seems that's not possible. For example I created an HTML Tiddler with:

<script>
alert("Testing");
</script>

And that didn't work.

I guess my question is: is there a way to get TW to execute arbitrary Javascript within a tiddler? If not, how do I make that happen? Apologies if this is available in the documentation somewhere but I couldn't find it.

Flan Mou

unread,
May 19, 2020, 10:57:32 AM5/19/20
to TiddlyWikiDev
Update: I followed the instructions here:


And created a plugin which simply contains:

(function () { alert("Test alert"); })

Everything got packed and saved, and the plugin appears installed. But when I reload the alert box doesn't appear. I would have expected the code inside an installed plugin to run upon startup. How can I get TW to execute my plugin code?

Saq Imtiaz

unread,
May 20, 2020, 4:28:33 AM5/20/20
to TiddlyWikiDev
Plugin javascript tiddlers need the correct module-type to determine how they are made available/run.

Here is what I recommend to get you started:

To get the user input of what ISBN to look up, use an edit-text widget with a temporary tiddler like $:/myplugin/isbn-search

Since you want to execute an action on userinput, that is look up the isbn, use a button widget to trigger a custom action widget.

You can model your action widget on

For starters, accept a single parameter, the isbn, make the http request and in the callback, parse the response and create a tiddler.

For an action widget called action-getbookinfo, it would be invoked like this inside the button widget:
<$action-getbookinfo $isbn={{$:/myplugin/isbn-search}} />

Saq Imtiaz

unread,
May 20, 2020, 4:30:06 AM5/20/20
to TiddlyWikiDev
PS: also note the $tw.utils.httpRequest method which might be handy:

Flan Mou

unread,
May 20, 2020, 10:34:41 AM5/20/20
to TiddlyWikiDev
Hi Saq,

This is very useful, thanks. I'm now a bit confused: it sounds like I want to create a widget and not a plugin? What's the difference?

Is there any documentation available along the lines "How to create your first widget"?

If not:
- I'm looking at action-createtiddler.js as a reference example. 
- If I create an action-getbookinfo as you suggest, where/how do I install this so that Tiddlers can access the widget?
- Do I put it core/modules/widgets?
- Will it be available for use automatically or do I have to do something else? 

Thanks for all your help. I'm trying to use the online documentation but it's not very friendly to someone new to TW.

Saq Imtiaz

unread,
May 20, 2020, 10:42:27 AM5/20/20
to TiddlyWikiDev
@Flan: think of
plugin

as an umbrella term for tiddlers that add functionality to TiddlyWiki. A plugin consists of one or more tiddlers. A javascript tiddler with module-type
widget

can define a widget. A plugin can contain one or several such widgets.

In this case, one of your plugin tiddlers would be a tiddler that has the javascript code for your widget. For example:

If the plugin is $:/plugins/flan/bookinfo, it may contain several tiddlers including for example $:/plugins/flan/bookinfo/widgets/getbookinfo which has the code for your widget.

A tiddler defining a widget needs the field type with value application/javascript, and the field module-type with value widget

Saq Imtiaz

unread,
May 20, 2020, 10:44:59 AM5/20/20
to TiddlyWikiDev
Essentially plugins are about packaging together several tiddlers that add a certain featureset for easier distribution.

To get started you can also skip the packaging as a plugin and just create individual tiddlers.

To just add a widget, create a tiddler with type application/javascript, module-type widget and put your javascript code for your action-getbookinfo widget in there. Save and reload and that widget will be accessible in all tiddlers.

Flan Mou

unread,
May 20, 2020, 10:56:38 PM5/20/20
to TiddlyWikiDev
This worked out great! I now have exactly what I wanted. Thanks Saq and others for your help.

Mat

unread,
May 21, 2020, 4:04:49 AM5/21/20
to TiddlyWikiDev
Flan, please share your solution if you can.

Even if you have a special solution there can be bits of knowledge that are of use (for example, I'm curious how GETs are used as I hope for this one day, i.e to fetch cell data from Google Sheets into TW.)

Thanks

<:-)

Flan Mou

unread,
May 21, 2020, 10:17:20 AM5/21/20
to TiddlyWikiDev
Here it is:

ActionGetBookInfoWidget.prototype.invokeAction = function(triggeringWidget,event) {
  function dateNow() {
    var set = new Date();
    var getDate = set.getDate().toString();
    if (getDate.length == 1){ //example if 1 change to 01
     getDate = "0"+getDate;
    }
    var getMonth = (set.getMonth()+1).toString();
    if (getMonth.length == 1){
     getMonth = "0"+getMonth;
    }
    var getYear = set.getFullYear().toString();
    var dateNow = getYear +"-"+ getMonth +"-"+ getDate; //today
    return dateNow;
  }

  function createBook(title, authors, publisher, isbn, cover) {
    var author_line = "* \'\'Authors:\'\' ".concat(authors);
    var publisher_line = "* \'\'Publisher:\'\' ".concat(publisher);
    var isbn_line = "* \'\'ISBN:\'\' ".concat(isbn);
    var date_line = "* \'\'Date Added:\'\' ".concat(dateNow());
    var cover_line = "[img[".concat(cover,"]]\n");
    var notes_line = "* \'\'Notes:\'\'";
    var text = [author_line, publisher_line, isbn_line, date_line, cover_line, notes_line].join("\n");
    var fields = { tags: "Books", text: text }
    console.log(fields);

    $tw.wiki.addTiddler(new $tw.Tiddler($tw.wiki.getCreationFields(), fields, {title: title}, $tw.wiki.getModificationFields()));
  }

  // Create URL
  var base = "https://openlibrary.org/api/books?bibkeys=ISBN:";
  var isbn = this.isbn;
  var url = base.concat(isbn,"&format=json&jscmd=data");
  console.log(url);

  // Do HTTP request
  window.fetch(url)
    .then(
      function(response) {
        if (response.status !== 200) {
          console.log('Looks like there was a problem. Status Code: ' +
            response.status);
          return;
        }

        response.json().then(function(data) {
          console.log(JSON.stringify(data));
          var key = "ISBN:".concat(isbn);
          var title = data[key]["title"];
          var publisher = data[key]["publishers"][0]["name"];
          var authors = data[key]["authors"].map(a => a["name"]).join("|");
          var cover = data[key]["cover"]["medium"];
          createBook(title, authors, publisher, isbn, cover);
          alert("Created page for: " + title);
        });
      }
    )
    .catch(function(err) {
    });

  return true; // Action was invoked
};
There's also the input tiddler that just invokes this action:

New book ISBN: <$edit-text tiddler="$:/state/isbn" tag="input" default=""/>


<$button>
<$action-getbookinfo $isbn={{$:/state/isbn}}/>
Add New Book
</$button>

TonyM

unread,
May 21, 2020, 9:33:37 PM5/21/20
to TiddlyWikiDev
Can someone package this please?

It would be useful in itself but then could be cloned and repurposed.

I do not quie have all the knowledge re javascript modules.

Thank you if possible.

Tony

christia...@gmail.com

unread,
Jul 1, 2020, 9:55:46 PM7/1/20
to TiddlyWikiDev
Hi all,

I've packaged a modified version of this javascript and interface into a contained plugin. Its available here ->  http://ceebeetree.tiddlyspot.com/#Thursday%202nd%20July%202020:%5B%5BThursday%202nd%20July%202020%5D%5D%20%24%3A%2Fplugins%2Fceebee%2Fbookinfo  

There were a few improvements that i introduced as part of that packaging, namely:

  • using tiddler fields to store the results from openlibrary.org and a wikitext template to present these, instead of building a long text field
  • used the created date field and the <$view> widget and its formatting functions. This removed a lot of date handling in the original javascript

Also I provided the ability to add your own 'Notes' on a book  from within the text of the tiddler - while still using this a psuedo-field in the transcluson of the book template. I'm interested if this approach doesn't suffer from low usability  for other people.. so any comments welcome

Enjoy 
CB   

TonyM

unread,
Jul 2, 2020, 2:35:55 AM7/2/20
to TiddlyWikiDev
CB,

Great work and impressive. Perhaps you should package and publish in on the main thread. I am sure people there would like it.

I understand this only goes to openlibrary.org is there a way to query other ISBN titles?

The process itself is an exemplar of bringing content in from outside tiddly wiki, I would hope we could generalise it for obtaining content elsewhere and creating a tiddler from it.

For example I have a TiddlyDesktop wiki that can access a local file content.
<object width="100%" height="930" data="file:///C:\Data\batches\networkcheck.txt"></object>

But it would be nice if this could be captured in a tiddler with a click. However no one seems to be able to do this.

I feel you are doing something similar here.

Great Work

TW Tones.

Guido B

unread,
Jul 2, 2020, 7:41:32 AM7/2/20
to TiddlyWikiDev
Hi CB

This is brilliant. I noticed that there is an error when no cover is available. Not being a JS guy I don't know how to check for the existence of a cover (and, if the cover exists, whether the medium-size cover is available or not). Any suggestions are welcomed. 

Thanks, Guido

christia...@gmail.com

unread,
Jul 2, 2020, 7:54:36 PM7/2/20
to TiddlyWikiDev
Hi Guido,

If you can give me the ISBN number you used I'll give it a test and see what I can do to catch the error.. Probably won't maintain this for long though... I think its worth while trying to make the API call and JSON inspections more generic so you could support other sources 

Cheers
CB

Guido B

unread,
Jul 2, 2020, 11:55:27 PM7/2/20
to TiddlyWikiDev
This is an example of an ISBN without a cover: 9783858824875. It throws the following error: 

$:/plugins/ceebee/bookinfo/action-getbookinfo.js:36 Uncaught (in promise) TypeError: Cannot read property 'medium' of undefined
    at eval ($:/plugins/ceebee/bookinfo/action-getbookinfo.js:36)

Thanks, Guido
Reply all
Reply to author
Forward
0 new messages