Subscribe to RSS feed

Posts tagged with "tiddlywiki"

Procrastination Specialist

, , ,

If there were awards for procrastination, I'd be a winner! o

Nearly five years ago in April 2003, my Dad and I took a two-and-a-bit week holiday out "bush". We visited all sorts of places, modern and historical. During the trip I kept a diary on my Psion netBook, even going to the trouble of beaming (via infrared) photo thumbnails from my digital camera to ensure I'd later match the correct photo to the narrative.

At the time I thought this work would mean a finished trip diary up within weeks of our return.

left

right

wait

whistle

Over this Christmas break I made the time to finally finish that trip diary, and bring the diary I made of a previous trip up to the same standard as this latest one.

You see, my Dad and I did a similar trip in April 2002 and I made a simple set of web pages for that, but I wasn't happy with how it had turned out and I wanted to rework the whole thing. That sort of explains the delay doing the second diary.

The latest trip, and now the first as well, have been done using TiddlyWiki, plus a couple of selected plugins: Saq Imtiaz's TiddlyLightBoxPlugin (with some significant alterations by yours truly), Eric Shulman's SinglePageModePlugin, StoryViewerPlugin and NestedSlidersPlugin. I also ported the Javascript from my original trip diary pages into a TiddlyWiki plugin of my own - code that performs latitude/longitude distance calculations.

Anyway, enough of all that. These trip diaries, for those who are interested, are at:

http://www.scss.dyndns.info/family/holidays/

Phew! That's one job off my long todo list! beer

Update Tiddlywiki Java Saver

, , , ...

I was poking around the TiddlyWiki Trac site and noticed a new ticket #253 opened on my Java saving code.

Apparently one or more of Gentoo Linux, the Linux JVM, or Opera for Linux require the saving code (and probably the loading code too) to be wrapping in a PrivilegedAction class into order to execute, regardless of Java security policy settings.

The ticket included some code, but sorry "thomaskammeyer", I don't like your style! eek

Here's my new Java code:
import java.io.*;
import java.security.*;

public class TiddlySaver extends java.applet.Applet {
  class PrivilegedLoad implements PrivilegedAction {
    private String filename;
    private String charset;

    public PrivilegedLoad(String filename, String charset) {
      this.filename = filename;
      this.charset  = charset;
    }

    public Object run() {
      try {
        if (charset.length() == 0) {
          StringBuffer data = new StringBuffer();
          BufferedReader r = new BufferedReader(new FileReader(filename));
          String line;
          while ((line = r.readLine()) != null) data.append(line).append("\n");
          r.close();
          return data.toString();
        } else {
          File f = new File(filename);
          FileInputStream i = new FileInputStream(f);
          byte[] b = new byte[(f.length() > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)f.length()];
          int offset = 0;
          int num = 0;
          while (offset < b.length && (num = i.read(b, offset, b.length - offset)) >= 0) {
            offset += num;
          }
          i.close();
          return new String(b, 0, offset, charset);
        }
      } catch (Exception x) {
        x.printStackTrace();
        return null;
      }
    }
  }

  class PrivilegedSave implements PrivilegedAction {
    private String filename;
    private String charset;
    private String data;

    public PrivilegedSave(String filename, String charset, String data) {
      this.filename = filename;
      this.charset  = charset;
      this.data     = data;
    }

    public Object run() {
      try {
        File f = new File(filename).getCanonicalFile();
        f.getParentFile().mkdirs();
        if (charset.length() == 0) {
          int e, s = 0;
          BufferedWriter w = new BufferedWriter(new FileWriter(f));
          do {
            e = data.indexOf('\n', s);
            if (e == -1) e = data.length();
            w.write(data, s, e - s);
            w.newLine();
            s = e + 1;
          } while (s < data.length());
          w.close();
          return Boolean.TRUE;
        } else {
          FileOutputStream o = new FileOutputStream(f);
          o.write(data.getBytes(charset));
          o.close();
          return Boolean.TRUE;
        }
      } catch (Exception x) {
        x.printStackTrace();
        return Boolean.FALSE;
      }
    }
  }

  public String loadFile(String filename, String charset) {
    return (String)AccessController.doPrivileged(new PrivilegedLoad(filename, charset));
  }
  public int saveFile(String filename, String charset, String data) {
    return ((Boolean)AccessController.doPrivileged(new PrivilegedSave(filename, charset, data))).booleanValue() ? 1 : 0;
  }
}
Here's the above compiled into a new JAR: TiddlySaver.jar
To Jeremy: If you're reading this, the build script needs a small change: The jar command needs to refer to all class files, i.e. "TiddlySaver*.class".

I've tested this on my WinXP machine using the standard policy and it worked fine, but could not test on MacOS or any flavour of Linux.

TiddlyWiki on the Mac

, ,

Things aren't all happy on the Mac. For me, Opera 9 has Java issues that prevent the applet from working.

My Opera 9 is using the 1.4.2_09 Java runtime. When it attempts to use the applet to save, the following exception is generated:
java.lang.NoClassDefFoundError: java/lang/StringBuilder
at com.opera.AppletClassLoader.getThreadGroup(AppletClassLoader.java:88)
at com.opera.AppletClassLoader.grab(AppletClassLoader.java:124)
at com.opera.AppletContext.getLCClassLoader(AppletContext.java:59)
at com.opera.LiveConnectPrivilegedActions.doPrivileged(LiveConnectPrivilegedActions.java:151)
at com.opera.LiveConnectPrivilegedActions.findClass(LiveConnectPrivilegedActions.java:54)
at com.opera.FindClassThread.run(MethodResolver.java:443)

In short, the class "java.lang.StringBuilder" cannot be found.

StringBuilder is a new class in Java 1.5. So the problem cannot be in the Mac Java runtime. My applet is designed to run with at least Java 1.2 (and maybe 1.1 or even 1.0, I'd need to check). I've also inspected the compiled class file - no StringBuilder mentioned anywhere.

The only remaining place is Opera's Java support. My suspicion is that Opera have compiled their Java support code using a v1.5 Java Development kit with the default options - those being an assumption of v1.5 source and a target v1.5 runtime. When building my applet, I had to override those defaults, something like:
javac -source 1.2 -target 1.2 tiddlywikiJavaSaver.java

What's also interesting is that while Opera 9 uses the Java 1.4.2_09 runtime, when I use Opera 8.54, it finds the Java 1.3.1_16 runtime - and works fine!

TiddlyWiki Security

, ,

I've been experimenting with TiddlyWiki lately, including improved Opera support, and I've found something a little disquieting.

TiddlyWiki is basically a locally editable web page that implements a wiki. In order to be editable it must be able to read and write to a local file (itself) on your computer. Normally pages can't do that.

For IE, it uses an ActiveX object. By default, it's disabled and must be manually re-enabled the first time you load a local TiddlyWiki after starting the browser. I believe it's possible to always allow the ActiveX object access, but that would entail allowing every local web page (i.e. those in the My Computer zone) to read/write any local file. There is no way to say "only web pages in this folder may read/write files in that folder. Not very good. At least it's a hassle to figure out how to allow ActiveX access by default.

Firefox is similar. It requires giving TiddlyWiki UniversalXPConnect privileges. What's not immediately clear, though, is that saying by "yes", you're giving any "file:" web page those privileges - the ability to read/write any local file! What's worse is that Firefox has a helpful "remember this" checkbox, thereby making it trivial to allow your TiddlyWiki, and any other web page you might in the future save to your computer or a network share accessible to your computer, the ability to read/write any file they like. This is even worse than Internet Explorer, which distinguishes between files on your computer (the "My Computer" zone) and network shares (in the "Local Intranet" zone).

In theory, Firefox supports Per-File Permissions (note that that page indicates a pipe (|) character is required after the drive letter, it seems that's wrong and the character should be a colon (:)), thereby only allowing web pages in a specific folder the ability to read/write any file. In practice, I haven't been able to get it to work. Even if I could, I still consider it only a marginal improvement - allowing read/write access to any local file is rather scary!

Update: Thanks to a reader comment, I've been shown the error of my ways. Firefox's per-file permissions really are per-file! You have to specify the complete filename of the page to grant privileges to, a folder name (as is possible with Java policies) won't work. Each of your tiddlywikis (if you have more than one), will need to be separately granted permission.

Opera is different again. It has no built-in support to read/write files at all. To read/write local files, Opera must make use of Java. Even then, custom changes to the local user security policy are required, so it's not stupidly easy to grant these special privileges. Nor should it be. The best part is that Java allows files in only a specific folder to be granted specific privileges: in this case, the ability to read/write files in a specific folder. Files outside the specified folder are not granted extra privileges, and those that are inside it, can only read/write into the folder specified, not any files outside it.

Note that I'm talking about unsigned code. Signed code is given full access to your computer, but requires an expensive security certificate that must be regularly renewed. Even without that hassle, I still prefer the fine-grained control you get with unsigned code.

So, why not use Java for IE and Firefox? Sadly, it seems there's a bug in the Java plugin preventing the granting of privileges to web pages in a specific folder. Instead, you must grant the ability to read/write files in a folder to all local Java code. In technical terms, you cannot use the codeBase parameter in the grant policy statement. Opera isn't affected by this issue because it doesn't use the Java plugin, but accesses Java directly. I've made some enquiries into this bug, but so far I've had no responses.

In summary, only Opera supports the most secure arrangement: allowing only files in a specific folder access to files in another (or the same) specific folder. Without using Java, both IE and Firefox will allow any local file full access to any other local file (very scary). With Java, you can at least limit files that can be modified to a specific folder, even if you can't limit which files can do the modifying (not so scary).

Big TiddlyWeekend

, , ,

NOTE: If you're keeping up-to-date with the latest betas, everything in this post has already been incorporated - you don't need to do anything.

This weekend I seem to have spent the whole thing fooling about with TiddlyWiki.

Loading and Saving

For a start I've been revisiting some old code I knocked up to get the file loading/saving working with Opera. It uses LiveConnect to access the locally installed Java runtime to do the file access. Unfortunately, there are large overheads involved for each transition from JavaScript to Java and when loading each line involves several of those, things can take a while. Like between 53 and 83 seconds on my Athlon64 3200+! zzz

Saving is able to transfer all the data in one go, and takes a fraction of a second.

Sadly, the only inwards bulk data transfer available in Java relies on byte[], and those don't survive passing between Java and JavaScript - you end up with NullPointerExceptions. I've tried all sort of contortions involving StringWriters, FileChannels, ByteBuffers, and others. Nothing could make loading any faster than about 53 seconds.

The only solution is to not use LiveConnect and use an applet instead. That has the downside of requiring an extra file. The upside is it's much faster, only 1KB and can be shared amongst several TiddlyWikis in the same folder:

tiddlywikiJavaSaver.jar

Then some changes need to be made to the TiddlyWiki code:

  • Insert at beginning of operaSaveFile:
if (document.applets[0] && document.applets[0].saveFile) {
  return document.applets[0].saveFile(operaUrlToFilename(filePath),content);
}

  • Insert at beginning of operaLoadFile:
if (document.applets[0] && document.applets[0].loadFile) {
  return String(document.applets[0].loadFile(operaUrlToFilename(filePath)));
}

  • Insert after the detectPlugin("TiddyWiki Saver") block of code at the bottom of the file:
if (window.opera)
  {
  document.write('<applet style="position:absolute;left:-1px" code="tiddlywikiJavaSaver.class" archive="tiddlywikiJavaSaver.jar" width="1" height="1"></applet>');
  }

As with my LiveConnect code, a special Java security policy is required to allow the Java code to write to your local file system. This time I've been able to create a policy that gives everything in a specified folder read/write access, so if you keep all your TiddlyWiki files in the same folder, you'll only need to make this change once. I've also figured out the correct Mac OS X version:
// Windows (C:\Documents and Settings\your-user-name\.java.policy):

grant codeBase "file:${user.home}/My Documents/tiddlywiki-folder/*" {
  permission java.io.FilePermission "${user.home}${/}My Documents${/}tiddlywiki-folder${/}*", "read,write";
};

// Mac OS X (/Users/your-user-name/.java.policy):

grant codeBase "file:${user.home}/Documents/tiddlywiki-folder/*" {
  permission java.io.FilePermission "${user.home}${/}Documents${/}tiddlywiki-folder${/}*", "read,write";
};

Where "tiddlywiki-folder" is a folder dedicated to TiddlyWikis!

Windows makes it difficult to name a file starting with a dot, so here's a file to download. The same file is suitable for Macs too, just edit it and delete the "My " bit, leaving just "Documents". Make sure you save it in the right place for each operating system!: .java.policy

Finally, just for completeness, here's the complete applet source code:
import java.io.*;

public class tiddlywikiJavaSaver extends java.applet.Applet {
  public String loadFile(String filename) {
    try {
      StringBuffer data = new StringBuffer();
      BufferedReader r = new BufferedReader(new FileReader(filename));
      String line;
      while ((line = r.readLine()) != null) data.append(line).append("\n");
      r.close();
      return data.toString();
    } catch (Exception x) {
      x.printStackTrace();
      return null;
    }
  }
  public int saveFile(String filename, String data) {
    try {
      int e, s = 0;
      BufferedWriter w = new BufferedWriter(new FileWriter(filename));
      do {
        e = data.indexOf('\n', s);
        if (e == -1) e = data.length();
        w.write(data, s, e - s);
        w.newLine();
        s = e + 1;
      } while (s < data.length());
      w.close();
      return 1;
    } catch (Exception x) {
      x.printStackTrace();
      return 0;
    }
  }
}

I've been emailing Jeremy (the TiddlyWiki developer) about this, and while it won't make v2.1, it should be in a later release.

Other Issues

Of course, while loading and saving is the biggest issue, the Opera error console provided a few others.

One was Opera complaining about null being an invalid value for z-index. That code is in a function called "Cascade.prototype.tick". Fixed by changing:
this.targetElement.style.zIndex = null;
to
this.targetElement.style.zIndex = "";


Another was a JavaScript "syntax error". This is due to an internal limitation in Opera where it does not correctly handle text nodes larger than 32KB. A bug in Opera (#145740). The new TiddlyWiki beta (v2.1.0) already incorporates a workaround for that.

Last was a problem with the demo Periodic Table. It shows up fine in IE and Firefox, but Opera just gets an error message. What's happening is that TiddlyWiki is using the DOM function Node.insertBefore to insert a node before itself. Other browsers handle that just fine, Opera generates an exception. I've reported that as Opera bug #222744. There's a simple workaround for it anyway. Just before the code:
table.insertBefore(rowContainer,table.firstChild);
add
if (rowContainer != table.firstChild)

After all those changes, TiddlyWiki works very well in Opera on both Windows and Mac.

Phew! It's TV for the rest of the weekend... coffee
June 2013
S M T W T F S
May 2013July 2013
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29